From 1b00bc1929ed9018e84294db6170420322a56034 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 9 Feb 2015 00:37:01 +0900 Subject: [PATCH] src: Support rel with quoted value in Link header parser --- src/http2.cc | 48 +++++++++++++++++++++ src/http2_test.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/src/http2.cc b/src/http2.cc index d5804816..7c5d7aff 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -741,6 +741,54 @@ parse_next_link_header_once(const char *first, const char *last) { } // we expect link-param + // rel can take several relations using quoted form. + static const char PLP[] = "rel=\""; + static const size_t PLPLEN = sizeof(PLP) - 1; + + static const char PLT[] = "preload"; + static const size_t PLTLEN = sizeof(PLT) - 1; + if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' && + std::equal(PLP, PLP + PLPLEN, first)) { + // we have to search preload in whitespace separated list: + // rel="preload something http://example.org/foo" + first += PLPLEN; + auto start = first; + for (; first != last;) { + if (*first != ' ' && *first != '"') { + ++first; + continue; + } + + if (start == first) { + return {{{0, 0}}, last}; + } + + if (!ok && start + PLTLEN == first && *(start + PLTLEN - 1) == 'd' && + std::equal(PLT, PLT + PLTLEN, start)) { + ok = true; + } + + if (*first == '"') { + break; + } + first = skip_lws(first, last); + start = first; + } + if (first == last) { + return {{{0, 0}}, first}; + } + assert(*first == '"'); + ++first; + if (first == last || *first == ',') { + goto almost_done; + } + if (*first == ';') { + ++first; + // parse next link-param + continue; + } + return {{{0, 0}}, last}; + } // we are only interested in rel=preload parameter. Others are // simply skipped. static const char PL[] = "rel=preload"; diff --git a/src/http2_test.cc b/src/http2_test.cc index d7df2383..9e77c920 100644 --- a/src/http2_test.cc +++ b/src/http2_test.cc @@ -489,6 +489,110 @@ void test_http2_parse_link_header(void) { auto res = http2::parse_link_header(s, sizeof(s) - 1); CU_ASSERT(0 == res.size()); } + { + // preload in relation-types list + const char s[] = R"(; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list followed by another parameter + const char s[] = R"(; rel="preload foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list following another parameter + const char s[] = R"(; rel="foo preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list between other parameters + const char s[] = R"(; rel="foo preload bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // no preload in relation-types list + const char s[] = R"(; rel="foo")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // no preload in relation-types list, multiple unrelated elements. + const char s[] = R"(; rel="foo bar")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by another link-value. + const char s[] = R"(; rel="preload", )"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, following another link-value. + const char s[] = R"(, ; rel="preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[8], &s[11]) == res[0].uri); + } + { + // preload in relation-types list, followed by another link-param. + const char s[] = R"(; rel="preload"; as="font")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list, followed by character other + // than ';' or ',' + const char s[] = R"(; rel="preload".)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ';' but it + // terminates input + const char s[] = R"(; rel="preload";)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list, followed by ',' but it + // terminates input + const char s[] = R"(; rel="preload",)"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(1 == res.size()); + CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri); + } + { + // preload in relation-types list but there is preceding white + // space. + const char s[] = R"(; rel=" preload")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } + { + // preload in relation-types list but there is trailing white + // space. + const char s[] = R"(; rel="preload ")"; + auto res = http2::parse_link_header(s, sizeof(s) - 1); + CU_ASSERT(0 == res.size()); + } } void test_http2_path_join(void) {