Don't sort headers in library code
Remove sorting headers from library code. The application must sort them if necessary. nghttpx and nghttpd do the sorting of the headers in stable way if names are equal.
This commit is contained in:
parent
0ba2883940
commit
40347487c9
|
@ -621,20 +621,20 @@ void nghttp2_nv_array_del(nghttp2_nv *nva)
|
||||||
free(nva);
|
free(nva);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nghttp2_nv_name_compar(const void *lhs, const void *rhs)
|
static int bytes_compar(const uint8_t *a, size_t alen,
|
||||||
|
const uint8_t *b, size_t blen)
|
||||||
{
|
{
|
||||||
nghttp2_nv *a = (nghttp2_nv*)lhs, *b = (nghttp2_nv*)rhs;
|
if(alen == blen) {
|
||||||
if(a->namelen == b->namelen) {
|
return memcmp(a, b, alen);
|
||||||
return memcmp(a->name, b->name, a->namelen);
|
} else if(alen < blen) {
|
||||||
} else if(a->namelen < b->namelen) {
|
int rv = memcmp(a, b, alen);
|
||||||
int rv = memcmp(a->name, b->name, a->namelen);
|
|
||||||
if(rv == 0) {
|
if(rv == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int rv = memcmp(a->name, b->name, b->namelen);
|
int rv = memcmp(a, b, blen);
|
||||||
if(rv == 0) {
|
if(rv == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -645,12 +645,24 @@ static int nghttp2_nv_name_compar(const void *lhs, const void *rhs)
|
||||||
|
|
||||||
int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs)
|
int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs)
|
||||||
{
|
{
|
||||||
return nghttp2_nv_name_compar(lhs, rhs);
|
return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nv_compar(const void *lhs, const void *rhs)
|
||||||
|
{
|
||||||
|
const nghttp2_nv *a = (const nghttp2_nv*)lhs;
|
||||||
|
const nghttp2_nv *b = (const nghttp2_nv*)rhs;
|
||||||
|
int rv;
|
||||||
|
rv = bytes_compar(a->name, a->namelen, b->name, b->namelen);
|
||||||
|
if(rv == 0) {
|
||||||
|
return bytes_compar(a->value, a->valuelen, b->value, b->valuelen);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen)
|
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen)
|
||||||
{
|
{
|
||||||
qsort(nva, nvlen, sizeof(nghttp2_nv), nghttp2_nv_name_compar);
|
qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t nghttp2_nv_array_from_cstr(nghttp2_nv **nva_ptr, const char **nv)
|
ssize_t nghttp2_nv_array_from_cstr(nghttp2_nv **nva_ptr, const char **nv)
|
||||||
|
@ -694,7 +706,6 @@ ssize_t nghttp2_nv_array_from_cstr(nghttp2_nv **nva_ptr, const char **nv)
|
||||||
data += len;
|
data += len;
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
nghttp2_nv_array_sort(*nva_ptr, nvlen);
|
|
||||||
return nvlen;
|
return nvlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,7 +748,6 @@ ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr,
|
||||||
data += nva[i].valuelen;
|
data += nva[i].valuelen;
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
nghttp2_nv_array_sort(*nva_ptr, nvlen);
|
|
||||||
return nvlen;
|
return nvlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -523,8 +523,8 @@ nghttp2_settings_entry* nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv,
|
||||||
int nghttp2_frame_nv_check_null(const char **nv);
|
int nghttp2_frame_nv_check_null(const char **nv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sorts the |nva| in ascending order of name. The relative order of
|
* Sorts the |nva| in ascending order of name and value. If names are
|
||||||
* the same name pair is undefined.
|
* equivalent, sort them by value.
|
||||||
*/
|
*/
|
||||||
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
|
|
|
@ -1352,7 +1352,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nghttp2_nv_array_sort(nva_out.nva, nva_out.nvlen);
|
|
||||||
*nva_ptr = nva_out.nva;
|
*nva_ptr = nva_out.nva;
|
||||||
return nva_out.nvlen;
|
return nva_out.nvlen;
|
||||||
fail:
|
fail:
|
||||||
|
|
|
@ -707,13 +707,12 @@ void prepare_response(Request *req, Http2Handler *hd)
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void append_nv(Request *req, nghttp2_nv *nva, size_t nvlen)
|
void append_nv(Request *req, const std::vector<const nghttp2_nv*>& nva)
|
||||||
{
|
{
|
||||||
for(size_t i = 0; i < nvlen; ++i) {
|
for(auto nv : nva) {
|
||||||
req->headers.push_back({
|
req->headers.push_back(std::make_pair
|
||||||
std::string(nva[i].name, nva[i].name + nva[i].namelen),
|
(std::string(nv->name, nv->name + nv->namelen),
|
||||||
std::string(nva[i].value, nva[i].value + nva[i].valuelen)
|
std::string(nv->value, nv->value + nv->valuelen)));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -738,16 +737,14 @@ int hd_on_frame_recv_callback
|
||||||
switch(frame->headers.cat) {
|
switch(frame->headers.cat) {
|
||||||
case NGHTTP2_HCAT_REQUEST: {
|
case NGHTTP2_HCAT_REQUEST: {
|
||||||
int32_t stream_id = frame->hd.stream_id;
|
int32_t stream_id = frame->hd.stream_id;
|
||||||
if(!http2::check_http2_headers(frame->headers.nva,
|
auto nva = http2::sort_nva(frame->headers.nva, frame->headers.nvlen);
|
||||||
frame->headers.nvlen)) {
|
if(!http2::check_http2_headers(nva)) {
|
||||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||||
NGHTTP2_PROTOCOL_ERROR);
|
NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
for(size_t i = 0; REQUIRED_HEADERS[i]; ++i) {
|
for(size_t i = 0; REQUIRED_HEADERS[i]; ++i) {
|
||||||
if(!http2::get_unique_header(frame->headers.nva,
|
if(!http2::get_unique_header(nva, REQUIRED_HEADERS[i])) {
|
||||||
frame->headers.nvlen,
|
|
||||||
REQUIRED_HEADERS[i])) {
|
|
||||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||||
NGHTTP2_PROTOCOL_ERROR);
|
NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -756,18 +753,14 @@ int hd_on_frame_recv_callback
|
||||||
// intermediary translating from HTTP/1 request to HTTP/2 may
|
// intermediary translating from HTTP/1 request to HTTP/2 may
|
||||||
// not produce :authority header field. In this case, it should
|
// not produce :authority header field. In this case, it should
|
||||||
// provide host HTTP/1.1 header field.
|
// provide host HTTP/1.1 header field.
|
||||||
if(!http2::get_unique_header(frame->headers.nva,
|
if(!http2::get_unique_header(nva, ":authority") &&
|
||||||
frame->headers.nvlen,
|
!http2::get_unique_header(nva, "host")) {
|
||||||
":authority") &&
|
|
||||||
!http2::get_unique_header(frame->headers.nva,
|
|
||||||
frame->headers.nvlen,
|
|
||||||
"host")) {
|
|
||||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||||
NGHTTP2_PROTOCOL_ERROR);
|
NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto req = util::make_unique<Request>(stream_id);
|
auto req = util::make_unique<Request>(stream_id);
|
||||||
append_nv(req.get(), frame->headers.nva, frame->headers.nvlen);
|
append_nv(req.get(), nva);
|
||||||
hd->add_stream(stream_id, std::move(req));
|
hd->add_stream(stream_id, std::move(req));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
51
src/http2.cc
51
src/http2.cc
|
@ -195,50 +195,69 @@ size_t HTTP1_IGN_HDLEN = sizeof(HTTP1_IGN_HD)/sizeof(HTTP1_IGN_HD[0]);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
auto nv_name_less = [](const nghttp2_nv& lhs, const nghttp2_nv& rhs)
|
auto nv_name_less = [](const nghttp2_nv *lhs, const nghttp2_nv *rhs)
|
||||||
{
|
{
|
||||||
return nghttp2_nv_compare_name(&lhs, &rhs) < 0;
|
return nghttp2_nv_compare_name(lhs, rhs) < 0;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool check_http2_headers(const nghttp2_nv *nva, size_t nvlen)
|
bool check_http2_headers(const std::vector<const nghttp2_nv*>& nva)
|
||||||
{
|
{
|
||||||
for(size_t i = 0; i < DISALLOWED_HDLEN; ++i) {
|
for(size_t i = 0; i < DISALLOWED_HDLEN; ++i) {
|
||||||
nghttp2_nv nv = {(uint8_t*)DISALLOWED_HD[i], nullptr,
|
nghttp2_nv nv = {(uint8_t*)DISALLOWED_HD[i], nullptr,
|
||||||
(uint16_t)strlen(DISALLOWED_HD[i]), 0};
|
(uint16_t)strlen(DISALLOWED_HD[i]), 0};
|
||||||
if(std::binary_search(&nva[0], &nva[nvlen], nv, nv_name_less)) {
|
if(std::binary_search(std::begin(nva), std::end(nva), &nv, nv_name_less)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nghttp2_nv* get_unique_header(const nghttp2_nv *nva, size_t nvlen,
|
std::vector<const nghttp2_nv*> sort_nva(const nghttp2_nv *nva, size_t nvlen)
|
||||||
|
{
|
||||||
|
auto res = std::vector<const nghttp2_nv*>();
|
||||||
|
res.reserve(nvlen);
|
||||||
|
for(size_t i = 0; i < nvlen; ++i) {
|
||||||
|
res.push_back(&nva[i]);
|
||||||
|
}
|
||||||
|
std::sort(std::begin(res), std::end(res),
|
||||||
|
[](const nghttp2_nv *lhs, const nghttp2_nv *rhs)
|
||||||
|
{
|
||||||
|
auto rv = nghttp2_nv_compare_name(lhs, rhs);
|
||||||
|
if(rv == 0) {
|
||||||
|
return lhs < rhs;
|
||||||
|
}
|
||||||
|
return rv < 0;
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nghttp2_nv* get_unique_header(const std::vector<const nghttp2_nv*>& nva,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
size_t namelen = strlen(name);
|
size_t namelen = strlen(name);
|
||||||
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
||||||
auto i = std::lower_bound(&nva[0], &nva[nvlen], nv, nv_name_less);
|
auto i = std::lower_bound(std::begin(nva), std::end(nva), &nv, nv_name_less);
|
||||||
if(i != &nva[nvlen] && util::streq(i->name, i->namelen,
|
if(i != std::end(nva) && util::streq((*i)->name, (*i)->namelen,
|
||||||
(const uint8_t*)name, namelen)) {
|
(const uint8_t*)name, namelen)) {
|
||||||
auto j = i + 1;
|
auto j = i + 1;
|
||||||
if(j == &nva[nvlen] || !util::streq(j->name, j->namelen,
|
if(j == std::end(nva) || !util::streq((*j)->name, (*j)->namelen,
|
||||||
(const uint8_t*)name, namelen)) {
|
(const uint8_t*)name, namelen)) {
|
||||||
return i;
|
return *i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nghttp2_nv* get_header(const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv* get_header(const std::vector<const nghttp2_nv*>& nva,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
size_t namelen = strlen(name);
|
size_t namelen = strlen(name);
|
||||||
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
||||||
auto i = std::lower_bound(&nva[0], &nva[nvlen], nv, nv_name_less);
|
auto i = std::lower_bound(std::begin(nva), std::end(nva), &nv, nv_name_less);
|
||||||
if(i != &nva[nvlen] && util::streq(i->name, i->namelen,
|
if(i != std::end(nva) && util::streq((*i)->name, (*i)->namelen,
|
||||||
(const uint8_t*)name, namelen)) {
|
(const uint8_t*)name, namelen)) {
|
||||||
return i;
|
return *i;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
10
src/http2.h
10
src/http2.h
|
@ -67,18 +67,22 @@ bool check_http2_allowed_header(const char *name);
|
||||||
// Checks that headers |nva| including |nvlen| entries do not contain
|
// Checks that headers |nva| including |nvlen| entries do not contain
|
||||||
// disallowed header fields in HTTP/2.0 spec. This function returns
|
// disallowed header fields in HTTP/2.0 spec. This function returns
|
||||||
// true if |nva| does not contains such headers.
|
// true if |nva| does not contains such headers.
|
||||||
bool check_http2_headers(const nghttp2_nv *nva, size_t nvlen);
|
bool check_http2_headers(const std::vector<const nghttp2_nv*>& nva);
|
||||||
|
|
||||||
|
// Returns sorted |nva| with |nvlen| elements. This sort is stable
|
||||||
|
// sort.
|
||||||
|
std::vector<const nghttp2_nv*> sort_nva(const nghttp2_nv *nva, size_t nvlen);
|
||||||
|
|
||||||
// Returns the pointer to the entry in |nva| which has name |name| and
|
// Returns the pointer to the entry in |nva| which has name |name| and
|
||||||
// the |name| is uinque in the |nva|. If no such entry exist, returns
|
// the |name| is uinque in the |nva|. If no such entry exist, returns
|
||||||
// nullptr.
|
// nullptr.
|
||||||
const nghttp2_nv* get_unique_header(const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv* get_unique_header(const std::vector<const nghttp2_nv*>& nva,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
// Returns the poiter to the entry in |nva| which has name |name|. If
|
// Returns the poiter to the entry in |nva| which has name |name|. If
|
||||||
// more than one entries which have the name |name|, first occurrence
|
// more than one entries which have the name |name|, first occurrence
|
||||||
// in |nva| is returned. If no such entry exist, returns nullptr.
|
// in |nva| is returned. If no such entry exist, returns nullptr.
|
||||||
const nghttp2_nv* get_header(const nghttp2_nv *nva, size_t nvlen,
|
const nghttp2_nv* get_header(const std::vector<const nghttp2_nv*>& nva,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
// Returns std::string version of nv->name with nv->namelen bytes.
|
// Returns std::string version of nv->name with nv->namelen bytes.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
*/
|
*/
|
||||||
#include "http2_test.h"
|
#include "http2_test.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <CUnit/CUnit.h>
|
#include <CUnit/CUnit.h>
|
||||||
|
@ -38,22 +39,53 @@ using namespace nghttp2;
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename cstr1, typename cstr2>
|
||||||
|
int bstrcmp(cstr1 *a, cstr2 *b)
|
||||||
|
{
|
||||||
|
return strcmp((const char*)a, (const char*)b);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void test_http2_sort_nva(void)
|
||||||
|
{
|
||||||
|
nghttp2_nv nv[] = {MAKE_NV("alpha", "1"),
|
||||||
|
MAKE_NV("bravo", "9"),
|
||||||
|
MAKE_NV("bravo", "8"),
|
||||||
|
MAKE_NV("charlie", "5"),
|
||||||
|
MAKE_NV("bravo", "3"),
|
||||||
|
MAKE_NV("bravo", "4")};
|
||||||
|
auto nvlen = sizeof(nv)/sizeof(nv[0]);
|
||||||
|
auto nva = http2::sort_nva(nv, nvlen);
|
||||||
|
CU_ASSERT(nvlen == nva.size());
|
||||||
|
CU_ASSERT(0 == bstrcmp("alpha", nva[0]->name));
|
||||||
|
CU_ASSERT(0 == bstrcmp("bravo", nva[1]->name));
|
||||||
|
CU_ASSERT(0 == bstrcmp("9", nva[1]->value));
|
||||||
|
CU_ASSERT(0 == bstrcmp("bravo", nva[2]->name));
|
||||||
|
CU_ASSERT(0 == bstrcmp("8", nva[2]->value));
|
||||||
|
CU_ASSERT(0 == bstrcmp("bravo", nva[3]->name));
|
||||||
|
CU_ASSERT(0 == bstrcmp("3", nva[3]->value));
|
||||||
|
CU_ASSERT(0 == bstrcmp("bravo", nva[4]->name));
|
||||||
|
CU_ASSERT(0 == bstrcmp("4", nva[4]->value));
|
||||||
|
CU_ASSERT(0 == bstrcmp("charlie", nva[5]->name));
|
||||||
|
}
|
||||||
|
|
||||||
void test_http2_check_http2_headers(void)
|
void test_http2_check_http2_headers(void)
|
||||||
{
|
{
|
||||||
nghttp2_nv nv1[] = {MAKE_NV("alpha", "1"),
|
nghttp2_nv nv1[] = {MAKE_NV("alpha", "1"),
|
||||||
MAKE_NV("bravo", "2"),
|
MAKE_NV("bravo", "2"),
|
||||||
MAKE_NV("upgrade", "http2")};
|
MAKE_NV("upgrade", "http2")};
|
||||||
CU_ASSERT(!http2::check_http2_headers(nv1, 3));
|
CU_ASSERT(!http2::check_http2_headers(http2::sort_nva(nv1, 3)));
|
||||||
|
|
||||||
nghttp2_nv nv2[] = {MAKE_NV("connection", "1"),
|
nghttp2_nv nv2[] = {MAKE_NV("connection", "1"),
|
||||||
MAKE_NV("delta", "2"),
|
MAKE_NV("delta", "2"),
|
||||||
MAKE_NV("echo", "3")};
|
MAKE_NV("echo", "3")};
|
||||||
CU_ASSERT(!http2::check_http2_headers(nv2, 3));
|
CU_ASSERT(!http2::check_http2_headers(http2::sort_nva(nv2, 3)));
|
||||||
|
|
||||||
nghttp2_nv nv3[] = {MAKE_NV("alpha", "1"),
|
nghttp2_nv nv3[] = {MAKE_NV("alpha", "1"),
|
||||||
MAKE_NV("bravo", "2"),
|
MAKE_NV("bravo", "2"),
|
||||||
MAKE_NV("te2", "3")};
|
MAKE_NV("te2", "3")};
|
||||||
CU_ASSERT(http2::check_http2_headers(nv3, 3));
|
CU_ASSERT(http2::check_http2_headers(http2::sort_nva(nv3, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_http2_get_unique_header(void)
|
void test_http2_get_unique_header(void)
|
||||||
|
@ -65,15 +97,16 @@ void test_http2_get_unique_header(void)
|
||||||
MAKE_NV("delta", "5"),
|
MAKE_NV("delta", "5"),
|
||||||
MAKE_NV("echo", "6"),};
|
MAKE_NV("echo", "6"),};
|
||||||
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
||||||
|
auto nva = http2::sort_nva(nv, nvlen);
|
||||||
const nghttp2_nv *rv;
|
const nghttp2_nv *rv;
|
||||||
rv = http2::get_unique_header(nv, nvlen, "delta");
|
rv = http2::get_unique_header(nva, "delta");
|
||||||
CU_ASSERT(rv != nullptr);
|
CU_ASSERT(rv != nullptr);
|
||||||
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
||||||
|
|
||||||
rv = http2::get_unique_header(nv, nvlen, "bravo");
|
rv = http2::get_unique_header(nva, "bravo");
|
||||||
CU_ASSERT(rv == nullptr);
|
CU_ASSERT(rv == nullptr);
|
||||||
|
|
||||||
rv = http2::get_unique_header(nv, nvlen, "foxtrot");
|
rv = http2::get_unique_header(nva, "foxtrot");
|
||||||
CU_ASSERT(rv == nullptr);
|
CU_ASSERT(rv == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,16 +119,17 @@ void test_http2_get_header(void)
|
||||||
MAKE_NV("delta", "5"),
|
MAKE_NV("delta", "5"),
|
||||||
MAKE_NV("echo", "6"),};
|
MAKE_NV("echo", "6"),};
|
||||||
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
||||||
|
auto nva = http2::sort_nva(nv, nvlen);
|
||||||
const nghttp2_nv *rv;
|
const nghttp2_nv *rv;
|
||||||
rv = http2::get_header(nv, nvlen, "delta");
|
rv = http2::get_header(nva, "delta");
|
||||||
CU_ASSERT(rv != nullptr);
|
CU_ASSERT(rv != nullptr);
|
||||||
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
||||||
|
|
||||||
rv = http2::get_header(nv, nvlen, "bravo");
|
rv = http2::get_header(nva, "bravo");
|
||||||
CU_ASSERT(rv != nullptr);
|
CU_ASSERT(rv != nullptr);
|
||||||
CU_ASSERT(util::streq("bravo", rv->name, rv->namelen));
|
CU_ASSERT(util::streq("bravo", rv->name, rv->namelen));
|
||||||
|
|
||||||
rv = http2::get_header(nv, nvlen, "foxtrot");
|
rv = http2::get_header(nva, "foxtrot");
|
||||||
CU_ASSERT(rv == nullptr);
|
CU_ASSERT(rv == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
void test_http2_sort_nva(void);
|
||||||
void test_http2_check_http2_headers(void);
|
void test_http2_check_http2_headers(void);
|
||||||
void test_http2_get_unique_header(void);
|
void test_http2_get_unique_header(void);
|
||||||
void test_http2_get_header(void);
|
void test_http2_get_header(void);
|
||||||
|
|
|
@ -69,6 +69,7 @@ int main(int argc, char* argv[])
|
||||||
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
||||||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file",
|
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file",
|
||||||
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) ||
|
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) ||
|
||||||
|
!CU_add_test(pSuite, "http2_sort_nva", shrpx::test_http2_sort_nva) ||
|
||||||
!CU_add_test(pSuite, "http2_check_http2_headers",
|
!CU_add_test(pSuite, "http2_check_http2_headers",
|
||||||
shrpx::test_http2_check_http2_headers) ||
|
shrpx::test_http2_check_http2_headers) ||
|
||||||
!CU_add_test(pSuite, "http2_get_unique_header",
|
!CU_add_test(pSuite, "http2_get_unique_header",
|
||||||
|
|
|
@ -156,7 +156,7 @@ void normalize_headers(Headers& headers)
|
||||||
for(auto& kv : headers) {
|
for(auto& kv : headers) {
|
||||||
util::inp_strlower(kv.first);
|
util::inp_strlower(kv.first);
|
||||||
}
|
}
|
||||||
std::sort(std::begin(headers), std::end(headers), name_less);
|
std::stable_sort(std::begin(headers), std::end(headers), name_less);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -820,11 +820,10 @@ int on_frame_recv_callback
|
||||||
NGHTTP2_INTERNAL_ERROR);
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto nva = frame->headers.nva;
|
// nva is no longer sorted
|
||||||
auto nvlen = frame->headers.nvlen;
|
auto nva = http2::sort_nva(frame->headers.nva, frame->headers.nvlen);
|
||||||
|
|
||||||
// Assuming that nva is sorted by name.
|
if(!http2::check_http2_headers(nva)) {
|
||||||
if(!http2::check_http2_headers(nva, nvlen)) {
|
|
||||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||||
NGHTTP2_PROTOCOL_ERROR);
|
NGHTTP2_PROTOCOL_ERROR);
|
||||||
downstream->set_response_state(Downstream::MSG_RESET);
|
downstream->set_response_state(Downstream::MSG_RESET);
|
||||||
|
@ -832,14 +831,14 @@ int on_frame_recv_callback
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < nvlen; ++i) {
|
for(auto nv : nva) {
|
||||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
if(nv->namelen > 0 && nv->name[0] != ':') {
|
||||||
downstream->add_response_header(http2::name_to_str(&nva[i]),
|
downstream->add_response_header(http2::name_to_str(nv),
|
||||||
http2::value_to_str(&nva[i]));
|
http2::value_to_str(nv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto status = http2::get_unique_header(nva, nvlen, ":status");
|
auto status = http2::get_unique_header(nva, ":status");
|
||||||
if(!status || http2::value_lws(status)) {
|
if(!status || http2::value_lws(status)) {
|
||||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||||
NGHTTP2_PROTOCOL_ERROR);
|
NGHTTP2_PROTOCOL_ERROR);
|
||||||
|
@ -855,7 +854,7 @@ int on_frame_recv_callback
|
||||||
downstream->set_response_major(1);
|
downstream->set_response_major(1);
|
||||||
downstream->set_response_minor(1);
|
downstream->set_response_minor(1);
|
||||||
|
|
||||||
auto content_length = http2::get_header(nva, nvlen, "content-length");
|
auto content_length = http2::get_header(nva, "content-length");
|
||||||
if(!content_length && downstream->get_request_method() != "HEAD" &&
|
if(!content_length && downstream->get_request_method() != "HEAD" &&
|
||||||
downstream->get_request_method() != "CONNECT") {
|
downstream->get_request_method() != "CONNECT") {
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
|
@ -879,11 +878,11 @@ int on_frame_recv_callback
|
||||||
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for(size_t i = 0; i < frame->headers.nvlen; ++i) {
|
for(auto nv : nva) {
|
||||||
ss << TTY_HTTP_HD;
|
ss << TTY_HTTP_HD;
|
||||||
ss.write(reinterpret_cast<char*>(nva[i].name), nva[i].namelen);
|
ss.write(reinterpret_cast<char*>(nv->name), nv->namelen);
|
||||||
ss << TTY_RST << ": ";
|
ss << TTY_RST << ": ";
|
||||||
ss.write(reinterpret_cast<char*>(nva[i].value), nva[i].valuelen);
|
ss.write(reinterpret_cast<char*>(nv->value), nv->valuelen);
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
}
|
}
|
||||||
SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
|
SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
|
||||||
|
|
|
@ -237,16 +237,16 @@ int on_frame_recv_callback
|
||||||
upstream->add_downstream(downstream);
|
upstream->add_downstream(downstream);
|
||||||
downstream->init_response_body_buf();
|
downstream->init_response_body_buf();
|
||||||
|
|
||||||
auto nva = frame->headers.nva;
|
// nva is no longer sorted
|
||||||
auto nvlen = frame->headers.nvlen;
|
auto nva = http2::sort_nva(frame->headers.nva, frame->headers.nvlen);
|
||||||
|
|
||||||
if(LOG_ENABLED(INFO)) {
|
if(LOG_ENABLED(INFO)) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for(size_t i = 0; i < frame->headers.nvlen; ++i) {
|
for(auto nv : nva) {
|
||||||
ss << TTY_HTTP_HD;
|
ss << TTY_HTTP_HD;
|
||||||
ss.write(reinterpret_cast<char*>(nva[i].name), nva[i].namelen);
|
ss.write(reinterpret_cast<char*>(nv->name), nv->namelen);
|
||||||
ss << TTY_RST << ": ";
|
ss << TTY_RST << ": ";
|
||||||
ss.write(reinterpret_cast<char*>(nva[i].value), nva[i].valuelen);
|
ss.write(reinterpret_cast<char*>(nv->value), nv->valuelen);
|
||||||
ss << "\n";
|
ss << "\n";
|
||||||
}
|
}
|
||||||
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
|
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
|
||||||
|
@ -254,24 +254,23 @@ int on_frame_recv_callback
|
||||||
<< "\n" << ss.str();
|
<< "\n" << ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assuming that nva is sorted by name.
|
if(!http2::check_http2_headers(nva)) {
|
||||||
if(!http2::check_http2_headers(nva, nvlen)) {
|
|
||||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < nvlen; ++i) {
|
for(auto nv : nva) {
|
||||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
if(nv->namelen > 0 && nv->name[0] != ':') {
|
||||||
downstream->add_request_header(http2::name_to_str(&nva[i]),
|
downstream->add_request_header(http2::name_to_str(nv),
|
||||||
http2::value_to_str(&nva[i]));
|
http2::value_to_str(nv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto host = http2::get_unique_header(nva, nvlen, "host");
|
auto host = http2::get_unique_header(nva, "host");
|
||||||
auto authority = http2::get_unique_header(nva, nvlen, ":authority");
|
auto authority = http2::get_unique_header(nva, ":authority");
|
||||||
auto path = http2::get_unique_header(nva, nvlen, ":path");
|
auto path = http2::get_unique_header(nva, ":path");
|
||||||
auto method = http2::get_unique_header(nva, nvlen, ":method");
|
auto method = http2::get_unique_header(nva, ":method");
|
||||||
auto scheme = http2::get_unique_header(nva, nvlen, ":scheme");
|
auto scheme = http2::get_unique_header(nva, ":scheme");
|
||||||
bool is_connect = method &&
|
bool is_connect = method &&
|
||||||
util::streq("CONNECT", method->value, method->valuelen);
|
util::streq("CONNECT", method->value, method->valuelen);
|
||||||
bool having_host = http2::non_empty_value(host);
|
bool having_host = http2::non_empty_value(host);
|
||||||
|
@ -297,7 +296,7 @@ int on_frame_recv_callback
|
||||||
}
|
}
|
||||||
if(!is_connect &&
|
if(!is_connect &&
|
||||||
(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||||
auto content_length = http2::get_header(nva, nvlen, "content-length");
|
auto content_length = http2::get_header(nva, "content-length");
|
||||||
if(!content_length || http2::value_lws(content_length)) {
|
if(!content_length || http2::value_lws(content_length)) {
|
||||||
// If content-length is missing,
|
// If content-length is missing,
|
||||||
// Downstream::push_upload_data_chunk will fail and
|
// Downstream::push_upload_data_chunk will fail and
|
||||||
|
|
|
@ -118,6 +118,7 @@ void test_nghttp2_frame_pack_headers()
|
||||||
NGHTTP2_FLAG_PRIORITY,
|
NGHTTP2_FLAG_PRIORITY,
|
||||||
1000000007, &oframe.hd);
|
1000000007, &oframe.hd);
|
||||||
CU_ASSERT(1 << 20 == oframe.pri);
|
CU_ASSERT(1 << 20 == oframe.pri);
|
||||||
|
nghttp2_nv_array_sort(oframe.nva, oframe.nvlen);
|
||||||
CU_ASSERT(nvnameeq("method", &oframe.nva[0]));
|
CU_ASSERT(nvnameeq("method", &oframe.nva[0]));
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
static void assert_nv_equal(nghttp2_nv *a, nghttp2_nv *b, size_t len)
|
static void assert_nv_equal(nghttp2_nv *a, nghttp2_nv *b, size_t len)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
nghttp2_nv_array_sort(b, len);
|
||||||
for(i = 0; i < len; ++i, ++a, ++b) {
|
for(i = 0; i < len; ++i, ++a, ++b) {
|
||||||
CU_ASSERT(nghttp2_nv_equal(a, b));
|
CU_ASSERT(nghttp2_nv_equal(a, b));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue