src: Don't push if Link header field includes nopush
This commit is contained in:
parent
5da38b22c0
commit
9afc017532
199
src/http2.cc
199
src/http2.cc
|
@ -864,6 +864,29 @@ bool check_link_param_empty(const char *first, const char *last,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Returns true if link-param consists of only parmname, and it
|
||||||
|
// matches string [pat, pat + patlen).
|
||||||
|
bool check_link_param_without_value(const char *first, const char *last,
|
||||||
|
const char *pat, size_t patlen) {
|
||||||
|
if (first + patlen > last) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first + patlen == last) {
|
||||||
|
return std::equal(pat, pat + patlen, first, util::CaseCmp());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*(first + patlen)) {
|
||||||
|
case ';':
|
||||||
|
case ',':
|
||||||
|
return std::equal(pat, pat + patlen, first, util::CaseCmp());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::pair<LinkHeader, const char *>
|
std::pair<LinkHeader, const char *>
|
||||||
parse_next_link_header_once(const char *first, const char *last) {
|
parse_next_link_header_once(const char *first, const char *last) {
|
||||||
|
@ -900,99 +923,109 @@ parse_next_link_header_once(const char *first, const char *last) {
|
||||||
}
|
}
|
||||||
// we expect link-param
|
// we expect link-param
|
||||||
|
|
||||||
// rel can take several relations using quoted form.
|
if (!ign) {
|
||||||
static constexpr char PLP[] = "rel=\"";
|
// rel can take several relations using quoted form.
|
||||||
static constexpr size_t PLPLEN = sizeof(PLP) - 1;
|
static constexpr char PLP[] = "rel=\"";
|
||||||
|
static constexpr size_t PLPLEN = sizeof(PLP) - 1;
|
||||||
|
|
||||||
static constexpr char PLT[] = "preload";
|
static constexpr char PLT[] = "preload";
|
||||||
static constexpr size_t PLTLEN = sizeof(PLT) - 1;
|
static constexpr size_t PLTLEN = sizeof(PLT) - 1;
|
||||||
if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
|
if (first + PLPLEN < last && *(first + PLPLEN - 1) == '"' &&
|
||||||
std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
|
std::equal(PLP, PLP + PLPLEN, first, util::CaseCmp())) {
|
||||||
// we have to search preload in whitespace separated list:
|
// we have to search preload in whitespace separated list:
|
||||||
// rel="preload something http://example.org/foo"
|
// rel="preload something http://example.org/foo"
|
||||||
first += PLPLEN;
|
first += PLPLEN;
|
||||||
auto start = first;
|
auto start = first;
|
||||||
for (; first != last;) {
|
for (; first != last;) {
|
||||||
if (*first != ' ' && *first != '"') {
|
if (*first != ' ' && *first != '"') {
|
||||||
|
++first;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == first) {
|
||||||
|
return {{{nullptr, nullptr}}, last};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok && start + PLTLEN == first &&
|
||||||
|
std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*first == '"') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
first = skip_lws(first, last);
|
||||||
|
start = first;
|
||||||
|
}
|
||||||
|
if (first == last) {
|
||||||
|
return {{{nullptr, nullptr}}, first};
|
||||||
|
}
|
||||||
|
assert(*first == '"');
|
||||||
|
++first;
|
||||||
|
if (first == last || *first == ',') {
|
||||||
|
goto almost_done;
|
||||||
|
}
|
||||||
|
if (*first == ';') {
|
||||||
++first;
|
++first;
|
||||||
|
// parse next link-param
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
return {{{nullptr, nullptr}}, last};
|
||||||
if (start == first) {
|
}
|
||||||
return {{{nullptr, nullptr}}, last};
|
// we are only interested in rel=preload parameter. Others are
|
||||||
|
// simply skipped.
|
||||||
|
static constexpr char PL[] = "rel=preload";
|
||||||
|
static constexpr size_t PLLEN = sizeof(PL) - 1;
|
||||||
|
if (first + PLLEN == last) {
|
||||||
|
if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||||
|
// ok = true;
|
||||||
|
// this is the end of sequence
|
||||||
|
return {{{url_first, url_last}}, last};
|
||||||
}
|
}
|
||||||
|
} else if (first + PLLEN + 1 <= last) {
|
||||||
if (!ok && start + PLTLEN == first &&
|
switch (*(first + PLLEN)) {
|
||||||
std::equal(PLT, PLT + PLTLEN, start, util::CaseCmp())) {
|
case ',':
|
||||||
|
if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ok = true;
|
||||||
|
// skip including ','
|
||||||
|
first += PLLEN + 1;
|
||||||
|
return {{{url_first, url_last}}, first};
|
||||||
|
case ';':
|
||||||
|
if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
ok = true;
|
ok = true;
|
||||||
|
// skip including ';'
|
||||||
|
first += PLLEN + 1;
|
||||||
|
// continue parse next link-param
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// we have to reject URI if we have nonempty anchor parameter.
|
||||||
|
static constexpr char ANCHOR[] = "anchor=";
|
||||||
|
static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1;
|
||||||
|
if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
|
||||||
|
ign = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (*first == '"') {
|
// reject URI if we have non-empty loadpolicy. This could be
|
||||||
break;
|
// tightened up to just pick up "next" or "insert".
|
||||||
}
|
static constexpr char LOADPOLICY[] = "loadpolicy=";
|
||||||
first = skip_lws(first, last);
|
static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1;
|
||||||
start = first;
|
if (!ign &&
|
||||||
|
!check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
|
||||||
|
ign = true;
|
||||||
}
|
}
|
||||||
if (first == last) {
|
|
||||||
return {{{nullptr, nullptr}}, first};
|
|
||||||
}
|
|
||||||
assert(*first == '"');
|
|
||||||
++first;
|
|
||||||
if (first == last || *first == ',') {
|
|
||||||
goto almost_done;
|
|
||||||
}
|
|
||||||
if (*first == ';') {
|
|
||||||
++first;
|
|
||||||
// parse next link-param
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return {{{nullptr, nullptr}}, last};
|
|
||||||
}
|
|
||||||
// we are only interested in rel=preload parameter. Others are
|
|
||||||
// simply skipped.
|
|
||||||
static constexpr char PL[] = "rel=preload";
|
|
||||||
static constexpr size_t PLLEN = sizeof(PL) - 1;
|
|
||||||
if (first + PLLEN == last) {
|
|
||||||
if (std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
|
||||||
// ok = true;
|
|
||||||
// this is the end of sequence
|
|
||||||
return {{{url_first, url_last}}, last};
|
|
||||||
}
|
|
||||||
} else if (first + PLLEN + 1 <= last) {
|
|
||||||
switch (*(first + PLLEN)) {
|
|
||||||
case ',':
|
|
||||||
if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// ok = true;
|
|
||||||
// skip including ','
|
|
||||||
first += PLLEN + 1;
|
|
||||||
return {{{url_first, url_last}}, first};
|
|
||||||
case ';':
|
|
||||||
if (!std::equal(PL, PL + PLLEN, first, util::CaseCmp())) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ok = true;
|
|
||||||
// skip including ';'
|
|
||||||
first += PLLEN + 1;
|
|
||||||
// continue parse next link-param
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// we have to reject URI if we have nonempty anchor parameter.
|
|
||||||
static constexpr char ANCHOR[] = "anchor=";
|
|
||||||
static constexpr size_t ANCHORLEN = sizeof(ANCHOR) - 1;
|
|
||||||
if (!ign && !check_link_param_empty(first, last, ANCHOR, ANCHORLEN)) {
|
|
||||||
ign = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reject URI if we have non-empty loadpolicy. This could be
|
// reject URI if we have nopush attribute.
|
||||||
// tightened up to just pick up "next" or "insert".
|
static constexpr char NOPUSH[] = "nopush";
|
||||||
static constexpr char LOADPOLICY[] = "loadpolicy=";
|
static constexpr size_t NOPUSHLEN = str_size(NOPUSH);
|
||||||
static constexpr size_t LOADPOLICYLEN = sizeof(LOADPOLICY) - 1;
|
if (!ign &&
|
||||||
if (!ign &&
|
check_link_param_without_value(first, last, NOPUSH, NOPUSHLEN)) {
|
||||||
!check_link_param_empty(first, last, LOADPOLICY, LOADPOLICYLEN)) {
|
ign = true;
|
||||||
ign = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto param_first = first;
|
auto param_first = first;
|
||||||
|
|
|
@ -625,6 +625,38 @@ void test_http2_parse_link_header(void) {
|
||||||
CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
|
CU_ASSERT(std::make_pair(&s[36], &s[39]) == res[0].uri);
|
||||||
CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri);
|
CU_ASSERT(std::make_pair(&s[42 + 14], &s[42 + 17]) == res[1].uri);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// nopush at the end of input
|
||||||
|
constexpr char s[] = "<url>; rel=preload; nopush";
|
||||||
|
auto res = http2::parse_link_header(s, str_size(s));
|
||||||
|
CU_ASSERT(0 == res.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// nopush followed by ';'
|
||||||
|
constexpr char s[] = "<url>; rel=preload; nopush; foo";
|
||||||
|
auto res = http2::parse_link_header(s, str_size(s));
|
||||||
|
CU_ASSERT(0 == res.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// nopush followed by ','
|
||||||
|
constexpr char s[] = "<url>; nopush; rel=preload";
|
||||||
|
auto res = http2::parse_link_header(s, str_size(s));
|
||||||
|
CU_ASSERT(0 == res.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// string whose prefix is nopush
|
||||||
|
constexpr char s[] = "<url>; nopushyes; rel=preload";
|
||||||
|
auto res = http2::parse_link_header(s, str_size(s));
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// rel=preload twice
|
||||||
|
constexpr char s[] = "<url>; rel=preload; rel=preload";
|
||||||
|
auto res = http2::parse_link_header(s, str_size(s));
|
||||||
|
CU_ASSERT(1 == res.size());
|
||||||
|
CU_ASSERT(std::make_pair(&s[1], &s[4]) == res[0].uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_http2_path_join(void) {
|
void test_http2_path_join(void) {
|
||||||
|
|
Loading…
Reference in New Issue