nghttpx: Rewrite header handling
This commit is contained in:
parent
89cd2ff479
commit
1f3b96e233
|
@ -1766,6 +1766,16 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
int32_t stream_id,
|
||||
int32_t window_size_increment);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
* Compares lhs->name with lhs->namelen bytes and rhs->name with
|
||||
* rhs->namelen bytes. Returns negative integer if lhs->name is found
|
||||
* to be less than rhs->name; or returns positive integer if lhs->name
|
||||
* is found to be greater than rhs->name; or returns 0 otherwise.
|
||||
*/
|
||||
int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs);
|
||||
|
||||
/**
|
||||
* @function
|
||||
*
|
||||
|
|
|
@ -766,6 +766,11 @@ 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)
|
||||
{
|
||||
return nghttp2_nv_name_compar(lhs, rhs);
|
||||
}
|
||||
|
||||
void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
qsort(nva, nvlen, sizeof(nghttp2_nv), nghttp2_nv_name_compar);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
nghttp
|
||||
nghttpd
|
||||
nghttpx
|
||||
nghttpx-unittest
|
||||
nghttpx-unittest.log
|
||||
nghttpx-unittest.trs
|
||||
test-suite.log
|
||||
|
|
|
@ -91,16 +91,18 @@ endif # HAVE_SPDYLAY
|
|||
|
||||
nghttpx_SOURCES = ${NGHTTPX_SRCS} shrpx.cc shrpx.h
|
||||
|
||||
# if HAVE_CUNIT
|
||||
# check_PROGRAMS += shrpx-unittest
|
||||
# shrpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
# shrpx_ssl_test.cc shrpx_ssl_test.h\
|
||||
# ${SHRPX_SRCS}
|
||||
# shrpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
|
||||
# -DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
# shrpx_unittest_LDFLAGS = -static @OPENSSL_LIBS@ @LIBEVENT_OPENSSL_LIBS@\
|
||||
# @SRC_LIBS@ @CUNIT_LIBS@ @TESTS_LIBS@
|
||||
# TESTS += shrpx-unittest
|
||||
# endif # HAVE_CUNIT
|
||||
if HAVE_CUNIT
|
||||
check_PROGRAMS += nghttpx-unittest
|
||||
nghttpx_unittest_SOURCES = shrpx-unittest.cc \
|
||||
shrpx_ssl_test.cc shrpx_ssl_test.h \
|
||||
shrpx_http_test.cc shrpx_http_test.h \
|
||||
shrpx_downstream_test.cc shrpx_downstream_test.h \
|
||||
util_test.cc util_test.h \
|
||||
${NGHTTPX_SRCS}
|
||||
nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\
|
||||
-DNGHTTP2_TESTS_DIR=\"$(top_srcdir)/tests\"
|
||||
nghttpx_unittest_LDFLAGS = -static ${AM_LDFLAGS} @CUNIT_LIBS@ @TESTS_LIBS@
|
||||
TESTS += nghttpx-unittest
|
||||
endif # HAVE_CUNIT
|
||||
|
||||
endif # ENABLE_SRC
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include <openssl/err.h>
|
||||
/* include test cases' include files here */
|
||||
#include "shrpx_ssl_test.h"
|
||||
#include "shrpx_http_test.h"
|
||||
#include "shrpx_downstream_test.h"
|
||||
#include "util_test.h"
|
||||
|
||||
static int init_suite1(void)
|
||||
{
|
||||
|
@ -65,7 +68,30 @@ int main(int argc, char* argv[])
|
|||
if(!CU_add_test(pSuite, "ssl_create_lookup_tree",
|
||||
shrpx::test_shrpx_ssl_create_lookup_tree) ||
|
||||
!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, "http_check_http2_headers",
|
||||
shrpx::test_http_check_http2_headers) ||
|
||||
!CU_add_test(pSuite, "http_get_unique_header",
|
||||
shrpx::test_http_get_unique_header) ||
|
||||
!CU_add_test(pSuite, "http_get_header",
|
||||
shrpx::test_http_get_header) ||
|
||||
!CU_add_test(pSuite, "http_value_lws",
|
||||
shrpx::test_http_value_lws) ||
|
||||
!CU_add_test(pSuite, "http_copy_norm_headers_to_nv",
|
||||
shrpx::test_http_copy_norm_headers_to_nv) ||
|
||||
!CU_add_test(pSuite, "http_build_http1_headers_from_norm_headers",
|
||||
shrpx::test_http_build_http1_headers_from_norm_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_normalize_request_headers",
|
||||
shrpx::test_downstream_normalize_request_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_normalize_response_headers",
|
||||
shrpx::test_downstream_normalize_response_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_get_norm_request_header",
|
||||
shrpx::test_downstream_get_norm_request_header) ||
|
||||
!CU_add_test(pSuite, "downstream_get_norm_response_header",
|
||||
shrpx::test_downstream_get_norm_response_header) ||
|
||||
!CU_add_test(pSuite, "util_streq", shrpx::test_util_streq) ||
|
||||
!CU_add_test(pSuite, "util_inp_strlower",
|
||||
shrpx::test_util_inp_strlower)) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
|
|
@ -157,11 +157,53 @@ void check_connection_close(bool *connection_close,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto name_less = [](const Headers::value_type& lhs,
|
||||
const Headers::value_type& rhs)
|
||||
{
|
||||
return lhs.first < rhs.first;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
void normalize_headers(Headers& headers)
|
||||
{
|
||||
for(auto& kv : headers) {
|
||||
util::inp_strlower(kv.first);
|
||||
}
|
||||
std::sort(std::begin(headers), std::end(headers), name_less);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
Headers::const_iterator get_norm_header(const Headers& headers,
|
||||
const std::string& name)
|
||||
{
|
||||
auto i = std::lower_bound(std::begin(headers), std::end(headers),
|
||||
std::make_pair(name, std::string()), name_less);
|
||||
if(i != std::end(headers) && (*i).first == name) {
|
||||
return i;
|
||||
}
|
||||
return std::end(headers);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const Headers& Downstream::get_request_headers() const
|
||||
{
|
||||
return request_headers_;
|
||||
}
|
||||
|
||||
void Downstream::normalize_request_headers()
|
||||
{
|
||||
normalize_headers(request_headers_);
|
||||
}
|
||||
|
||||
Headers::const_iterator Downstream::get_norm_request_header
|
||||
(const std::string& name) const
|
||||
{
|
||||
return get_norm_header(request_headers_, name);
|
||||
}
|
||||
|
||||
void Downstream::add_request_header(const std::string& name,
|
||||
const std::string& value)
|
||||
{
|
||||
|
@ -334,6 +376,17 @@ const Headers& Downstream::get_response_headers() const
|
|||
return response_headers_;
|
||||
}
|
||||
|
||||
void Downstream::normalize_response_headers()
|
||||
{
|
||||
normalize_headers(response_headers_);
|
||||
}
|
||||
|
||||
Headers::const_iterator Downstream::get_norm_response_header
|
||||
(const std::string& name) const
|
||||
{
|
||||
return get_norm_header(response_headers_, name);
|
||||
}
|
||||
|
||||
void Downstream::add_response_header(const std::string& name,
|
||||
const std::string& value)
|
||||
{
|
||||
|
|
|
@ -84,6 +84,15 @@ public:
|
|||
bool http2_upgrade_request() const;
|
||||
// downstream request API
|
||||
const Headers& get_request_headers() const;
|
||||
// Makes key lowercase and sort headers by name using <
|
||||
void normalize_request_headers();
|
||||
// Returns iterator pointing to the request header with the name
|
||||
// |name|. If multiple header have |name| as name, return first
|
||||
// occurrence from the beginning. If no such header is found,
|
||||
// returns std::end(get_request_headers()). This function must be
|
||||
// called after calling normalize_request_headers().
|
||||
Headers::const_iterator get_norm_request_header
|
||||
(const std::string& name) const;
|
||||
void add_request_header(const std::string& name, const std::string& value);
|
||||
void set_last_request_header_value(const std::string& value);
|
||||
|
||||
|
@ -120,6 +129,15 @@ public:
|
|||
int get_request_state() const;
|
||||
// downstream response API
|
||||
const Headers& get_response_headers() const;
|
||||
// Makes key lowercase and sort headers by name using <
|
||||
void normalize_response_headers();
|
||||
// Returns iterator pointing to the response header with the name
|
||||
// |name|. If multiple header have |name| as name, return first
|
||||
// occurrence from the beginning. If no such header is found,
|
||||
// returns std::end(get_response_headers()). This function must be
|
||||
// called after calling normalize_response_headers().
|
||||
Headers::const_iterator get_norm_response_header
|
||||
(const std::string& name) const;
|
||||
void add_response_header(const std::string& name, const std::string& value);
|
||||
void set_last_response_header_value(const std::string& value);
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_downstream_test.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "shrpx_downstream.h"
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_downstream_normalize_request_headers(void)
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header("Charlie", "0");
|
||||
d.add_request_header("Alpha", "1");
|
||||
d.add_request_header("Delta", "2");
|
||||
d.add_request_header("BravO", "3");
|
||||
d.normalize_request_headers();
|
||||
|
||||
auto ans = Headers{
|
||||
{"alpha", "1"},
|
||||
{"bravo", "3"},
|
||||
{"charlie", "0"},
|
||||
{"delta", "2"}
|
||||
};
|
||||
CU_ASSERT(ans == d.get_request_headers());
|
||||
}
|
||||
|
||||
void test_downstream_normalize_response_headers(void)
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_response_header("Charlie", "0");
|
||||
d.add_response_header("Alpha", "1");
|
||||
d.add_response_header("Delta", "2");
|
||||
d.add_response_header("BravO", "3");
|
||||
d.normalize_response_headers();
|
||||
|
||||
auto ans = Headers{
|
||||
{"alpha", "1"},
|
||||
{"bravo", "3"},
|
||||
{"charlie", "0"},
|
||||
{"delta", "2"}
|
||||
};
|
||||
CU_ASSERT(ans == d.get_response_headers());
|
||||
}
|
||||
|
||||
void test_downstream_get_norm_request_header(void)
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header("alpha", "0");
|
||||
d.add_request_header("bravo", "1");
|
||||
d.add_request_header("bravo", "2");
|
||||
d.add_request_header("charlie", "3");
|
||||
d.add_request_header("delta", "4");
|
||||
d.add_request_header("echo", "5");
|
||||
auto i = d.get_norm_request_header("alpha");
|
||||
CU_ASSERT(std::make_pair(std::string("alpha"), std::string("0")) == *i);
|
||||
i = d.get_norm_request_header("bravo");
|
||||
CU_ASSERT(std::make_pair(std::string("bravo"), std::string("1")) == *i);
|
||||
i = d.get_norm_request_header("delta");
|
||||
CU_ASSERT(std::make_pair(std::string("delta"), std::string("4")) == *i);
|
||||
i = d.get_norm_request_header("echo");
|
||||
CU_ASSERT(std::make_pair(std::string("echo"), std::string("5")) == *i);
|
||||
i = d.get_norm_request_header("foxtrot");
|
||||
CU_ASSERT(i == std::end(d.get_request_headers()));
|
||||
}
|
||||
|
||||
void test_downstream_get_norm_response_header(void)
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_response_header("alpha", "0");
|
||||
d.add_response_header("bravo", "1");
|
||||
d.add_response_header("bravo", "2");
|
||||
d.add_response_header("charlie", "3");
|
||||
d.add_response_header("delta", "4");
|
||||
d.add_response_header("echo", "5");
|
||||
auto i = d.get_norm_response_header("alpha");
|
||||
CU_ASSERT(std::make_pair(std::string("alpha"), std::string("0")) == *i);
|
||||
i = d.get_norm_response_header("bravo");
|
||||
CU_ASSERT(std::make_pair(std::string("bravo"), std::string("1")) == *i);
|
||||
i = d.get_norm_response_header("delta");
|
||||
CU_ASSERT(std::make_pair(std::string("delta"), std::string("4")) == *i);
|
||||
i = d.get_norm_response_header("echo");
|
||||
CU_ASSERT(std::make_pair(std::string("echo"), std::string("5")) == *i);
|
||||
i = d.get_norm_response_header("foxtrot");
|
||||
CU_ASSERT(i == std::end(d.get_response_headers()));
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SHRPX_DOWNSTREAM_TEST_H
|
||||
#define SHRPX_DOWNSTREAM_TEST_H
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_downstream_normalize_request_headers(void);
|
||||
void test_downstream_normalize_response_headers(void);
|
||||
void test_downstream_get_norm_request_header(void);
|
||||
void test_downstream_get_norm_response_header(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_DOWNSTREAM_TEST_H
|
|
@ -188,6 +188,188 @@ bool check_http2_allowed_header(const uint8_t *name, size_t namelen)
|
|||
!util::strieq("upgrade", name, namelen);
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char *DISALLOWED_HD[] = {
|
||||
"connection",
|
||||
"host",
|
||||
"keep-alive",
|
||||
"proxy-connection",
|
||||
"te",
|
||||
"transfer-encoding",
|
||||
"upgrade",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t DISALLOWED_HDLEN = sizeof(DISALLOWED_HD)/sizeof(DISALLOWED_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *IGN_HD[] = {
|
||||
"connection",
|
||||
"expect",
|
||||
"host",
|
||||
"http2-settings",
|
||||
"keep-alive",
|
||||
"proxy-connection",
|
||||
"te",
|
||||
"transfer-encoding",
|
||||
"upgrade",
|
||||
"via",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-proto",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t IGN_HDLEN = sizeof(IGN_HD)/sizeof(IGN_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *HTTP1_IGN_HD[] = {
|
||||
"connection",
|
||||
"expect",
|
||||
"http2-settings",
|
||||
"keep-alive",
|
||||
"proxy-connection",
|
||||
"upgrade",
|
||||
"via",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-proto",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
size_t HTTP1_IGN_HDLEN = sizeof(HTTP1_IGN_HD)/sizeof(HTTP1_IGN_HD[0]);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto nv_name_less = [](const nghttp2_nv& lhs, const nghttp2_nv& rhs)
|
||||
{
|
||||
return nghttp2_nv_compare_name(&lhs, &rhs) < 0;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool check_http2_headers(const nghttp2_nv *nva, size_t nvlen)
|
||||
{
|
||||
for(size_t i = 0; i < DISALLOWED_HDLEN; ++i) {
|
||||
nghttp2_nv nv = {(uint8_t*)DISALLOWED_HD[i], nullptr,
|
||||
(uint16_t)strlen(DISALLOWED_HD[i]), 0};
|
||||
if(std::binary_search(&nva[0], &nva[nvlen], nv, nv_name_less)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const nghttp2_nv* get_unique_header(const nghttp2_nv *nva, size_t nvlen,
|
||||
const char *name)
|
||||
{
|
||||
size_t namelen = strlen(name);
|
||||
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
||||
auto i = std::lower_bound(&nva[0], &nva[nvlen], nv, nv_name_less);
|
||||
if(i != &nva[nvlen] && util::streq(i->name, i->namelen,
|
||||
(const uint8_t*)name, namelen)) {
|
||||
auto j = i + 1;
|
||||
if(j == &nva[nvlen] || !util::streq(j->name, j->namelen,
|
||||
(const uint8_t*)name, namelen)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const nghttp2_nv* get_header(const nghttp2_nv *nva, size_t nvlen,
|
||||
const char *name)
|
||||
{
|
||||
size_t namelen = strlen(name);
|
||||
nghttp2_nv nv = {(uint8_t*)name, nullptr, (uint16_t)namelen, 0};
|
||||
auto i = std::lower_bound(&nva[0], &nva[nvlen], nv, nv_name_less);
|
||||
if(i != &nva[nvlen] && util::streq(i->name, i->namelen,
|
||||
(const uint8_t*)name, namelen)) {
|
||||
return i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string name_to_str(const nghttp2_nv *nv)
|
||||
{
|
||||
return std::string(reinterpret_cast<const char*>(nv->name), nv->namelen);
|
||||
}
|
||||
|
||||
std::string value_to_str(const nghttp2_nv *nv)
|
||||
{
|
||||
return std::string(reinterpret_cast<const char*>(nv->value), nv->valuelen);
|
||||
}
|
||||
|
||||
bool value_lws(const nghttp2_nv *nv)
|
||||
{
|
||||
for(size_t i = 0; i < nv->valuelen; ++i) {
|
||||
switch(nv->value[i]) {
|
||||
case '\t':
|
||||
case ' ':
|
||||
continue;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t copy_norm_headers_to_nv
|
||||
(const char **nv,
|
||||
const std::vector<std::pair<std::string, std::string>>& headers)
|
||||
{
|
||||
size_t i, j, nvlen = 0;
|
||||
for(i = 0, j = 0; i < headers.size() && j < IGN_HDLEN;) {
|
||||
int rv = strcmp(headers[i].first.c_str(), IGN_HD[j]);
|
||||
if(rv < 0) {
|
||||
nv[nvlen++] = headers[i].first.c_str();
|
||||
nv[nvlen++] = headers[i].second.c_str();
|
||||
++i;
|
||||
} else if(rv > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for(; i < headers.size(); ++i) {
|
||||
nv[nvlen++] = headers[i].first.c_str();
|
||||
nv[nvlen++] = headers[i].second.c_str();
|
||||
}
|
||||
return nvlen;
|
||||
}
|
||||
|
||||
void build_http1_headers_from_norm_headers
|
||||
(std::string& hdrs,
|
||||
const std::vector<std::pair<std::string,
|
||||
std::string>>& headers)
|
||||
{
|
||||
size_t i, j;
|
||||
for(i = 0, j = 0; i < headers.size() && j < HTTP1_IGN_HDLEN;) {
|
||||
int rv = strcmp(headers[i].first.c_str(), HTTP1_IGN_HD[j]);
|
||||
if(rv < 0) {
|
||||
hdrs += headers[i].first;
|
||||
http::capitalize(hdrs, hdrs.size()-headers[i].first.size());
|
||||
hdrs += ": ";
|
||||
hdrs += headers[i].second;
|
||||
hdrs += "\r\n";
|
||||
++i;
|
||||
} else if(rv > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for(; i < headers.size(); ++i) {
|
||||
hdrs += headers[i].first;
|
||||
http::capitalize(hdrs, hdrs.size()-headers[i].first.size());
|
||||
hdrs += ": ";
|
||||
hdrs += headers[i].second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#define SHRPX_HTTP_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "http-parser/http_parser.h"
|
||||
|
||||
|
@ -60,6 +63,45 @@ bool check_http2_allowed_header(const uint8_t *name, size_t namelen);
|
|||
// assuming |name| is null-terminated string.
|
||||
bool check_http2_allowed_header(const char *name);
|
||||
|
||||
// Checks that headers |nva| including |nvlen| entries do not contain
|
||||
// disallowed header fields in HTTP/2.0 spec. This function returns
|
||||
// true if |nva| does not contains such headers.
|
||||
bool check_http2_headers(const nghttp2_nv *nva, size_t nvlen);
|
||||
|
||||
// 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
|
||||
// nullptr.
|
||||
const nghttp2_nv* get_unique_header(const nghttp2_nv *nva, size_t nvlen,
|
||||
const char *name);
|
||||
|
||||
// Returns the poiter to the entry in |nva| which has name |name|. If
|
||||
// more than one entries which have the name |name|, first occurrence
|
||||
// in |nva| is returned. If no such entry exist, returns nullptr.
|
||||
const nghttp2_nv* get_header(const nghttp2_nv *nva, size_t nvlen,
|
||||
const char *name);
|
||||
|
||||
// Returns std::string version of nv->name with nv->namelen bytes.
|
||||
std::string name_to_str(const nghttp2_nv *nv);
|
||||
// Returns std::string version of nv->value with nv->valuelen bytes.
|
||||
std::string value_to_str(const nghttp2_nv *nv);
|
||||
|
||||
// Returns true if the value of |nv| includes only ' ' (0x20) or '\t'.
|
||||
bool value_lws(const nghttp2_nv *nv);
|
||||
|
||||
// Copies headers in |headers| to |nv|. Certain headers, including
|
||||
// disallowed headers in HTTP/2.0 spec and headers which require
|
||||
// special handling (i.e. via), are not copied.
|
||||
size_t copy_norm_headers_to_nv
|
||||
(const char **nv,
|
||||
const std::vector<std::pair<std::string, std::string>>& headers);
|
||||
|
||||
// Appends HTTP/1.1 style header lines to |hdrs| from headers in
|
||||
// |headers|. Certain headers, which requires special handling
|
||||
// (i.e. via), are not appended.
|
||||
void build_http1_headers_from_norm_headers
|
||||
(std::string& hdrs,
|
||||
const std::vector<std::pair<std::string, std::string>>& headers);
|
||||
|
||||
} // namespace http
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -192,6 +192,7 @@ void on_frame_recv_callback
|
|||
downstream->init_response_body_buf();
|
||||
|
||||
auto nva = frame->headers.nva;
|
||||
auto nvlen = frame->headers.nvlen;
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
|
@ -208,116 +209,58 @@ void on_frame_recv_callback
|
|||
}
|
||||
|
||||
// Assuming that nva is sorted by name.
|
||||
const char *req_headers[] = {":host", ":method", ":path", ":scheme",
|
||||
"content-length"};
|
||||
const size_t req_hdlen = sizeof(req_headers)/sizeof(req_headers[0]);
|
||||
int req_hdidx[req_hdlen];
|
||||
memset(req_hdidx, -1, sizeof(req_hdidx));
|
||||
bool bad_req = false;
|
||||
{
|
||||
size_t i, j;
|
||||
for(i = 0, j = 0; i < frame->headers.nvlen && j < req_hdlen;) {
|
||||
if(!http::check_http2_allowed_header(nva[i].name, nva[i].namelen)) {
|
||||
bad_req = true;
|
||||
break;
|
||||
}
|
||||
int rv = util::strcompare(req_headers[j], nva[i].name, nva[i].namelen);
|
||||
if(rv > 0) {
|
||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
||||
downstream->add_request_header
|
||||
(std::string(reinterpret_cast<char*>(nva[i].name),
|
||||
nva[i].namelen),
|
||||
std::string(reinterpret_cast<char*>(nva[i].value),
|
||||
nva[i].valuelen));
|
||||
}
|
||||
++i;
|
||||
} else if(rv < 0) {
|
||||
++j;
|
||||
} else {
|
||||
if(req_hdidx[j] != -1) {
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
ULOG(INFO, upstream) << "multiple " << req_headers[j]
|
||||
<< " found in the request";
|
||||
}
|
||||
bad_req = true;
|
||||
break;
|
||||
}
|
||||
req_hdidx[j] = i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if(!bad_req) {
|
||||
// Here :scheme is optional, because with CONNECT method, it
|
||||
// is omitted. content-length is mandatory if END_STREAM is
|
||||
// not set.
|
||||
for(j = 0; j < 3; ++j) {
|
||||
if(req_hdidx[j] == -1) {
|
||||
bad_req = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!bad_req &&
|
||||
!util::strieq("CONNECT",
|
||||
nva[req_hdidx[1]].value,
|
||||
nva[req_hdidx[1]].valuelen) &&
|
||||
(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0 &&
|
||||
req_hdidx[4] == -1) {
|
||||
// If content-length is missing,
|
||||
// Downstream::push_upload_data_chunk will fail and
|
||||
// RST_STREAM will be sent.
|
||||
bad_req = true;
|
||||
}
|
||||
if(!bad_req) {
|
||||
for(; i < frame->headers.nvlen; ++i) {
|
||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
||||
downstream->add_request_header
|
||||
(std::string(reinterpret_cast<char*>(nva[i].name),
|
||||
nva[i].namelen),
|
||||
std::string(reinterpret_cast<char*>(nva[i].value),
|
||||
nva[i].valuelen));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bad_req) {
|
||||
if(!http::check_http2_headers(nva, nvlen)) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if(req_hdidx[4] != -1) {
|
||||
downstream->add_request_header
|
||||
(std::string(reinterpret_cast<char*>(nva[req_hdidx[4]].name),
|
||||
nva[req_hdidx[4]].namelen),
|
||||
std::string(reinterpret_cast<char*>(nva[req_hdidx[4]].value),
|
||||
nva[req_hdidx[4]].valuelen));
|
||||
for(size_t i = 0; i < nvlen; ++i) {
|
||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
||||
downstream->add_request_header(http::name_to_str(&nva[i]),
|
||||
http::value_to_str(&nva[i]));
|
||||
}
|
||||
}
|
||||
|
||||
std::string host(reinterpret_cast<char*>(nva[req_hdidx[0]].value),
|
||||
nva[req_hdidx[0]].valuelen);
|
||||
std::string method(reinterpret_cast<char*>(nva[req_hdidx[1]].value),
|
||||
nva[req_hdidx[1]].valuelen);
|
||||
std::string path(reinterpret_cast<char*>(nva[req_hdidx[2]].value),
|
||||
nva[req_hdidx[2]].valuelen);
|
||||
auto host = http::get_unique_header(nva, nvlen, ":host");
|
||||
auto path = http::get_unique_header(nva, nvlen, ":path");
|
||||
auto method = http::get_unique_header(nva, nvlen, ":method");
|
||||
auto scheme = http::get_unique_header(nva, nvlen, ":scheme");
|
||||
bool is_connect = method &&
|
||||
util::streq("CONNECT", method->value, method->valuelen);
|
||||
if(!host || !path || !method ||
|
||||
http::value_lws(host) || http::value_lws(path) ||
|
||||
http::value_lws(method) ||
|
||||
(!is_connect && (!scheme || http::value_lws(scheme)))) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
if(!is_connect &&
|
||||
(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
||||
auto content_length = http::get_header(nva, nvlen, "content-length");
|
||||
if(!content_length || http::value_lws(content_length)) {
|
||||
// If content-length is missing,
|
||||
// Downstream::push_upload_data_chunk will fail and
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
downstream->set_request_method(method);
|
||||
downstream->set_request_method(http::value_to_str(method));
|
||||
|
||||
// SpdyDownstreamConnection examines request path to find
|
||||
// scheme. We construct abs URI for spdy_bridge mode as well as
|
||||
// spdy_proxy mode.
|
||||
if((get_config()->spdy_proxy || get_config()->spdy_bridge) &&
|
||||
req_hdidx[3] != -1 && path[0] == '/') {
|
||||
std::string reqpath(reinterpret_cast<char*>(nva[req_hdidx[3]].value),
|
||||
nva[req_hdidx[3]].valuelen);
|
||||
scheme && path->value[0] == '/') {
|
||||
std::string reqpath(http::value_to_str(scheme));
|
||||
reqpath += "://";
|
||||
reqpath += host;
|
||||
reqpath += path;
|
||||
reqpath += http::value_to_str(host);
|
||||
reqpath += http::value_to_str(path);
|
||||
downstream->set_request_path(reqpath);
|
||||
} else {
|
||||
downstream->set_request_path(path);
|
||||
downstream->set_request_path(http::value_to_str(path));
|
||||
}
|
||||
|
||||
downstream->add_request_header("host", host);
|
||||
downstream->add_request_header("host", http::value_to_str(host));
|
||||
downstream->check_upgrade_request();
|
||||
|
||||
auto dconn = upstream->get_client_handler()->get_downstream_connection();
|
||||
|
@ -908,6 +851,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
if(LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
downstream->normalize_response_headers();
|
||||
auto end_headers = std::end(downstream->get_response_headers());
|
||||
size_t nheader = downstream->get_response_headers().size();
|
||||
// 4 means :status and possible via header field.
|
||||
const char **nv = new const char*[nheader * 2 + 4 + 1];
|
||||
|
@ -917,20 +862,18 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
std::to_string(downstream->get_response_http_status());
|
||||
nv[hdidx++] = ":status";
|
||||
nv[hdidx++] = response_status.c_str();
|
||||
for(Headers::const_iterator i = downstream->get_response_headers().begin();
|
||||
i != downstream->get_response_headers().end(); ++i) {
|
||||
if(!http::check_http2_allowed_header((*i).first.c_str())) {
|
||||
// These are ignored
|
||||
} else if(!get_config()->no_via &&
|
||||
util::strieq((*i).first.c_str(), "via")) {
|
||||
via_value = (*i).second;
|
||||
|
||||
hdidx += http::copy_norm_headers_to_nv(&nv[hdidx],
|
||||
downstream->get_response_headers());
|
||||
auto via = downstream->get_norm_response_header("via");
|
||||
if(get_config()->no_via) {
|
||||
if(via != end_headers) {
|
||||
nv[hdidx++] = "via";
|
||||
nv[hdidx++] = (*via).second.c_str();
|
||||
}
|
||||
} else {
|
||||
nv[hdidx++] = (*i).first.c_str();
|
||||
nv[hdidx++] = (*i).second.c_str();
|
||||
}
|
||||
}
|
||||
if(!get_config()->no_via) {
|
||||
if(!via_value.empty()) {
|
||||
if(via != end_headers) {
|
||||
via_value = (*via).second;
|
||||
via_value += ", ";
|
||||
}
|
||||
via_value += http::create_via_header_value
|
||||
|
@ -938,7 +881,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
|
|||
nv[hdidx++] = "via";
|
||||
nv[hdidx++] = via_value.c_str();
|
||||
}
|
||||
nv[hdidx++] = 0;
|
||||
nv[hdidx++] = nullptr;
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
for(size_t i = 0; nv[i]; i += 2) {
|
||||
|
|
|
@ -118,69 +118,26 @@ int HttpDownstreamConnection::push_request_headers()
|
|||
hdrs += downstream_->get_request_path();
|
||||
hdrs += " ";
|
||||
hdrs += "HTTP/1.1\r\n";
|
||||
std::string connection_upgrade;
|
||||
std::string via_value;
|
||||
std::string xff_value;
|
||||
const Headers& request_headers = downstream_->get_request_headers();
|
||||
for(Headers::const_iterator i = request_headers.begin();
|
||||
i != request_headers.end(); ++i) {
|
||||
if(util::strieq((*i).first.c_str(), "connection")) {
|
||||
// nghttpx handles HTTP/2.0 upgrade and does not relay it to the
|
||||
// downstream.
|
||||
if(util::strifind((*i).second.c_str(), "upgrade") &&
|
||||
!util::strifind((*i).second.c_str(), "http2-settings")) {
|
||||
connection_upgrade = (*i).second;
|
||||
}
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "upgrade")) {
|
||||
// nghttpx handles HTTP/2.0 upgrade and does not relay it to the
|
||||
// downstream.
|
||||
if(util::strieq((*i).second.c_str(), NGHTTP2_PROTO_VERSION_ID)) {
|
||||
continue;
|
||||
}
|
||||
} else if(util::strieq((*i).first.c_str(), "x-forwarded-proto") ||
|
||||
util::strieq((*i).first.c_str(), "keep-alive") ||
|
||||
util::strieq((*i).first.c_str(), "proxy-connection") ||
|
||||
util::strieq((*i).first.c_str(), "http2-settings")) {
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "via")) {
|
||||
if(!get_config()->no_via) {
|
||||
via_value = (*i).second;
|
||||
continue;
|
||||
}
|
||||
} else if(util::strieq((*i).first.c_str(), "x-forwarded-for")) {
|
||||
xff_value = (*i).second;
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "expect")) {
|
||||
if(util::strifind((*i).second.c_str(), "100-continue")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
hdrs += (*i).first;
|
||||
http::capitalize(hdrs, hdrs.size()-(*i).first.size());
|
||||
hdrs += ": ";
|
||||
hdrs += (*i).second;
|
||||
http::sanitize_header_value(hdrs, hdrs.size()-(*i).second.size());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
downstream_->normalize_request_headers();
|
||||
auto end_headers = std::end(downstream_->get_request_headers());
|
||||
http::build_http1_headers_from_norm_headers
|
||||
(hdrs, downstream_->get_request_headers());
|
||||
|
||||
if(downstream_->get_request_connection_close()) {
|
||||
hdrs += "Connection: close\r\n";
|
||||
} else if(!connection_upgrade.empty()) {
|
||||
hdrs += "Connection: ";
|
||||
hdrs += connection_upgrade;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
|
||||
if(get_config()->add_x_forwarded_for) {
|
||||
hdrs += "X-Forwarded-For: ";
|
||||
if(!xff_value.empty()) {
|
||||
hdrs += xff_value;
|
||||
if(xff != end_headers) {
|
||||
hdrs += (*xff).second;
|
||||
hdrs += ", ";
|
||||
}
|
||||
hdrs += downstream_->get_upstream()->get_client_handler()->get_ipaddr();
|
||||
hdrs += "\r\n";
|
||||
} else if(!xff_value.empty()) {
|
||||
} else if(xff != end_headers) {
|
||||
hdrs += "X-Forwarded-For: ";
|
||||
hdrs += xff_value;
|
||||
hdrs += (*xff).second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
if(downstream_->get_request_method() != "CONNECT") {
|
||||
|
@ -192,10 +149,24 @@ int HttpDownstreamConnection::push_request_headers()
|
|||
}
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
if(!get_config()->no_via) {
|
||||
auto expect = downstream_->get_norm_request_header("expect");
|
||||
if(expect != end_headers &&
|
||||
util::strifind((*expect).second.c_str(), "100-continue")) {
|
||||
hdrs += "Expect: ";
|
||||
hdrs += (*expect).second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
auto via = downstream_->get_norm_request_header("via");
|
||||
if(get_config()->no_via) {
|
||||
if(via != end_headers) {
|
||||
hdrs += "Via: ";
|
||||
if(!via_value.empty()) {
|
||||
hdrs += via_value;
|
||||
hdrs += (*via).second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
} else {
|
||||
hdrs += "Via: ";
|
||||
if(via != end_headers) {
|
||||
hdrs += (*via).second;
|
||||
hdrs += ", ";
|
||||
}
|
||||
hdrs += http::create_via_header_value(downstream_->get_request_major(),
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "shrpx_http_test.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "shrpx_http.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
#define MAKE_NV(K, V) {(uint8_t*)K, (uint8_t*)V, strlen(K), strlen(V)}
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_http_check_http2_headers(void)
|
||||
{
|
||||
nghttp2_nv nv1[] = {MAKE_NV("alpha", "1"),
|
||||
MAKE_NV("bravo", "2"),
|
||||
MAKE_NV("upgrade", "http2")};
|
||||
CU_ASSERT(!http::check_http2_headers(nv1, 3));
|
||||
|
||||
nghttp2_nv nv2[] = {MAKE_NV("connection", "1"),
|
||||
MAKE_NV("delta", "2"),
|
||||
MAKE_NV("echo", "3")};
|
||||
CU_ASSERT(!http::check_http2_headers(nv2, 3));
|
||||
|
||||
nghttp2_nv nv3[] = {MAKE_NV("alpha", "1"),
|
||||
MAKE_NV("bravo", "2"),
|
||||
MAKE_NV("te2", "3")};
|
||||
CU_ASSERT(http::check_http2_headers(nv3, 3));
|
||||
}
|
||||
|
||||
void test_http_get_unique_header(void)
|
||||
{
|
||||
nghttp2_nv nv[] = {MAKE_NV("alpha", "1"),
|
||||
MAKE_NV("bravo", "2"),
|
||||
MAKE_NV("bravo", "3"),
|
||||
MAKE_NV("charlie", "4"),
|
||||
MAKE_NV("delta", "5"),
|
||||
MAKE_NV("echo", "6"),};
|
||||
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
||||
const nghttp2_nv *rv;
|
||||
rv = http::get_unique_header(nv, nvlen, "delta");
|
||||
CU_ASSERT(rv != nullptr);
|
||||
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
||||
|
||||
rv = http::get_unique_header(nv, nvlen, "bravo");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
|
||||
rv = http::get_unique_header(nv, nvlen, "foxtrot");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
}
|
||||
|
||||
void test_http_get_header(void)
|
||||
{
|
||||
nghttp2_nv nv[] = {MAKE_NV("alpha", "1"),
|
||||
MAKE_NV("bravo", "2"),
|
||||
MAKE_NV("bravo", "3"),
|
||||
MAKE_NV("charlie", "4"),
|
||||
MAKE_NV("delta", "5"),
|
||||
MAKE_NV("echo", "6"),};
|
||||
size_t nvlen = sizeof(nv)/sizeof(nv[0]);
|
||||
const nghttp2_nv *rv;
|
||||
rv = http::get_header(nv, nvlen, "delta");
|
||||
CU_ASSERT(rv != nullptr);
|
||||
CU_ASSERT(util::streq("delta", rv->name, rv->namelen));
|
||||
|
||||
rv = http::get_header(nv, nvlen, "bravo");
|
||||
CU_ASSERT(rv != nullptr);
|
||||
CU_ASSERT(util::streq("bravo", rv->name, rv->namelen));
|
||||
|
||||
rv = http::get_header(nv, nvlen, "foxtrot");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
}
|
||||
|
||||
void test_http_value_lws(void)
|
||||
{
|
||||
nghttp2_nv nv[] = {MAKE_NV("0", "alpha"),
|
||||
MAKE_NV("1", " alpha"),
|
||||
MAKE_NV("2", ""),
|
||||
MAKE_NV("3", " "),
|
||||
MAKE_NV("4", " a ")};
|
||||
CU_ASSERT(!http::value_lws(&nv[0]));
|
||||
CU_ASSERT(!http::value_lws(&nv[1]));
|
||||
CU_ASSERT(http::value_lws(&nv[2]));
|
||||
CU_ASSERT(http::value_lws(&nv[3]));
|
||||
CU_ASSERT(!http::value_lws(&nv[4]));
|
||||
}
|
||||
|
||||
namespace {
|
||||
auto headers = std::vector<std::pair<std::string, std::string>>
|
||||
{{"alpha", "0"},
|
||||
{"bravo", "1"},
|
||||
{"connection", "2"},
|
||||
{"connection", "3"},
|
||||
{"delta", "4"},
|
||||
{"expect", "5"},
|
||||
{"foxtrot", "6"},
|
||||
{"tango", "7"},
|
||||
{"te", "8"},
|
||||
{"te", "9"},
|
||||
{"x-forwarded-proto", "10"},
|
||||
{"x-forwarded-proto", "11"},
|
||||
{"zulu", "12"}};
|
||||
} // namespace
|
||||
|
||||
void test_http_copy_norm_headers_to_nv(void)
|
||||
{
|
||||
const char* nv[30];
|
||||
size_t nvlen = http::copy_norm_headers_to_nv(nv, headers);
|
||||
CU_ASSERT(12 == nvlen);
|
||||
CU_ASSERT(strcmp(nv[0], "alpha") == 0);
|
||||
CU_ASSERT(strcmp(nv[1], "0") == 0);
|
||||
CU_ASSERT(strcmp(nv[2], "bravo") == 0);
|
||||
CU_ASSERT(strcmp(nv[3], "1") == 0);
|
||||
CU_ASSERT(strcmp(nv[4], "delta") == 0);
|
||||
CU_ASSERT(strcmp(nv[5], "4") == 0);
|
||||
CU_ASSERT(strcmp(nv[6], "foxtrot") == 0);
|
||||
CU_ASSERT(strcmp(nv[7], "6") == 0);
|
||||
CU_ASSERT(strcmp(nv[8], "tango") == 0);
|
||||
CU_ASSERT(strcmp(nv[9], "7") == 0);
|
||||
CU_ASSERT(strcmp(nv[10], "zulu") == 0);
|
||||
CU_ASSERT(strcmp(nv[11], "12") == 0);
|
||||
}
|
||||
|
||||
void test_http_build_http1_headers_from_norm_headers(void)
|
||||
{
|
||||
std::string hdrs;
|
||||
http::build_http1_headers_from_norm_headers(hdrs, headers);
|
||||
CU_ASSERT(hdrs ==
|
||||
"Alpha: 0\r\n"
|
||||
"Bravo: 1\r\n"
|
||||
"Delta: 4\r\n"
|
||||
"Foxtrot: 6\r\n"
|
||||
"Tango: 7\r\n"
|
||||
"Te: 8\r\n"
|
||||
"Te: 9\r\n"
|
||||
"Zulu: 12\r\n");
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SHRPX_HTTP_TEST_H
|
||||
#define SHRPX_HTTP_TEST_H
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_http_check_http2_headers(void);
|
||||
void test_http_get_unique_header(void);
|
||||
void test_http_get_header(void);
|
||||
void test_http_value_lws(void);
|
||||
void test_http_copy_norm_headers_to_nv(void);
|
||||
void test_http_build_http1_headers_from_norm_headers(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // SHRPX_HTTP_TEST_H
|
|
@ -654,8 +654,6 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
if(LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
bool connection_upgrade = false;
|
||||
std::string via_value;
|
||||
char temp[16];
|
||||
snprintf(temp, sizeof(temp), "HTTP/%d.%d ",
|
||||
downstream->get_request_major(),
|
||||
|
@ -663,26 +661,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
std::string hdrs = temp;
|
||||
hdrs += http::get_status_string(downstream->get_response_http_status());
|
||||
hdrs += "\r\n";
|
||||
for(Headers::const_iterator i = downstream->get_response_headers().begin();
|
||||
i != downstream->get_response_headers().end(); ++i) {
|
||||
if(util::strieq((*i).first.c_str(), "connection")) {
|
||||
if(util::strifind((*i).second.c_str(), "upgrade")) {
|
||||
connection_upgrade = true;
|
||||
}
|
||||
} else if(util::strieq((*i).first.c_str(), "keep-alive") || // HTTP/1.0?
|
||||
util:: strieq((*i).first.c_str(), "proxy-connection")) {
|
||||
// These are ignored
|
||||
} else if(!get_config()->no_via &&
|
||||
util::strieq((*i).first.c_str(), "via")) {
|
||||
via_value = (*i).second;
|
||||
} else {
|
||||
hdrs += (*i).first;
|
||||
http::capitalize(hdrs, hdrs.size()-(*i).first.size());
|
||||
hdrs += ": ";
|
||||
hdrs += (*i).second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
}
|
||||
downstream->normalize_response_headers();
|
||||
auto end_headers = std::end(downstream->get_response_headers());
|
||||
http::build_http1_headers_from_norm_headers
|
||||
(hdrs, downstream->get_response_headers());
|
||||
|
||||
// We check downstream->get_response_connection_close() in case when
|
||||
// the Content-Length is not available.
|
||||
|
@ -692,23 +674,27 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
|||
downstream->get_request_minor() <= 0) {
|
||||
// We add this header for HTTP/1.0 or HTTP/0.9 clients
|
||||
hdrs += "Connection: Keep-Alive\r\n";
|
||||
} else if(connection_upgrade) {
|
||||
hdrs += "Connection: upgrade\r\n";
|
||||
}
|
||||
} else if(!downstream->get_upgraded()) {
|
||||
} else {
|
||||
hdrs += "Connection: close\r\n";
|
||||
}
|
||||
if(!get_config()->no_via) {
|
||||
auto via = downstream->get_norm_response_header("via");
|
||||
if(get_config()->no_via) {
|
||||
if(via != end_headers) {
|
||||
hdrs += "Via: ";
|
||||
if(!via_value.empty()) {
|
||||
hdrs += via_value;
|
||||
hdrs += (*via).second;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
} else {
|
||||
hdrs += "Via: ";
|
||||
if(via != end_headers) {
|
||||
hdrs += (*via).second;
|
||||
hdrs += ", ";
|
||||
}
|
||||
hdrs += http::create_via_header_value
|
||||
(downstream->get_response_major(), downstream->get_response_minor());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
|
||||
hdrs += "\r\n";
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
const char *hdrp;
|
||||
|
|
|
@ -230,6 +230,8 @@ int SpdyDownstreamConnection::push_request_headers()
|
|||
return 0;
|
||||
}
|
||||
size_t nheader = downstream_->get_request_headers().size();
|
||||
downstream_->normalize_request_headers();
|
||||
auto end_headers = std::end(downstream_->get_request_headers());
|
||||
// 10 means :method, :scheme, :path and possible via and
|
||||
// x-forwarded-for header fields. We rename host header field as
|
||||
// :host.
|
||||
|
@ -280,61 +282,63 @@ int SpdyDownstreamConnection::push_request_headers()
|
|||
nv[hdidx++] = ":method";
|
||||
nv[hdidx++] = downstream_->get_request_method().c_str();
|
||||
|
||||
bool chunked_encoding = false;
|
||||
bool content_length = false;
|
||||
for(Headers::const_iterator i = downstream_->get_request_headers().begin();
|
||||
i != downstream_->get_request_headers().end(); ++i) {
|
||||
if(util::strieq((*i).first.c_str(), "transfer-encoding")) {
|
||||
if(util::strieq((*i).second.c_str(), "chunked")) {
|
||||
chunked_encoding = true;
|
||||
hdidx += http::copy_norm_headers_to_nv(&nv[hdidx],
|
||||
downstream_->get_request_headers());
|
||||
|
||||
auto host = downstream_->get_norm_request_header("host");
|
||||
if(host == end_headers) {
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "host header field missing";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
// Ignore transfer-encoding
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "x-forwarded-proto") ||
|
||||
util::strieq((*i).first.c_str(), "keep-alive") ||
|
||||
util::strieq((*i).first.c_str(), "connection") ||
|
||||
util::strieq((*i).first.c_str(), "proxy-connection") ||
|
||||
util::strieq((*i).first.c_str(), "te") ||
|
||||
util::strieq((*i).first.c_str(), "upgrade") ||
|
||||
util::strieq((*i).first.c_str(), "http2-settings")) {
|
||||
// These are ignored
|
||||
continue;
|
||||
} else if(!get_config()->no_via &&
|
||||
util::strieq((*i).first.c_str(), "via")) {
|
||||
via_value = (*i).second;
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "x-forwarded-for")) {
|
||||
xff_value = (*i).second;
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "expect") &&
|
||||
util::strifind((*i).second.c_str(), "100-continue")) {
|
||||
// Ignore
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "host")) {
|
||||
nv[hdidx++] = ":host";
|
||||
nv[hdidx++] = (*i).second.c_str();
|
||||
continue;
|
||||
} else if(util::strieq((*i).first.c_str(), "content-length")) {
|
||||
nv[hdidx++] = (*host).second.c_str();
|
||||
|
||||
bool content_length = false;
|
||||
if(downstream_->get_norm_request_header("content-length") != end_headers) {
|
||||
content_length = true;
|
||||
}
|
||||
nv[hdidx++] = (*i).first.c_str();
|
||||
nv[hdidx++] = (*i).second.c_str();
|
||||
|
||||
auto expect = downstream_->get_norm_request_header("expect");
|
||||
if(expect != end_headers &&
|
||||
util::strifind((*expect).second.c_str(), "100-continue")) {
|
||||
nv[hdidx++] = "expect";
|
||||
nv[hdidx++] = (*expect).second.c_str();
|
||||
}
|
||||
|
||||
bool chunked_encoding = false;
|
||||
auto transfer_encoding =
|
||||
downstream_->get_norm_request_header("transfer-encoding");
|
||||
if(transfer_encoding != end_headers &&
|
||||
util::strieq((*transfer_encoding).second.c_str(), "chunked")) {
|
||||
chunked_encoding = true;
|
||||
}
|
||||
|
||||
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
|
||||
if(get_config()->add_x_forwarded_for) {
|
||||
nv[hdidx++] = "x-forwarded-for";
|
||||
if(!xff_value.empty()) {
|
||||
if(xff != end_headers) {
|
||||
xff_value = (*xff).second;
|
||||
xff_value += ", ";
|
||||
}
|
||||
xff_value += downstream_->get_upstream()->get_client_handler()->
|
||||
get_ipaddr();
|
||||
nv[hdidx++] = xff_value.c_str();
|
||||
} else if(!xff_value.empty()) {
|
||||
} else if(xff != end_headers) {
|
||||
nv[hdidx++] = "x-forwarded-for";
|
||||
nv[hdidx++] = xff_value.c_str();
|
||||
nv[hdidx++] = (*xff).second.c_str();
|
||||
}
|
||||
if(!get_config()->no_via) {
|
||||
if(!via_value.empty()) {
|
||||
|
||||
auto via = downstream_->get_norm_request_header("via");
|
||||
if(get_config()->no_via) {
|
||||
if(via != end_headers) {
|
||||
nv[hdidx++] = "via";
|
||||
nv[hdidx++] = (*via).second.c_str();
|
||||
}
|
||||
} else {
|
||||
if(via != end_headers) {
|
||||
via_value = (*via).second;
|
||||
via_value += ", ";
|
||||
}
|
||||
via_value += http::create_via_header_value
|
||||
|
@ -342,7 +346,8 @@ int SpdyDownstreamConnection::push_request_headers()
|
|||
nv[hdidx++] = "via";
|
||||
nv[hdidx++] = via_value.c_str();
|
||||
}
|
||||
nv[hdidx++] = 0;
|
||||
nv[hdidx++] = nullptr;
|
||||
|
||||
if(LOG_ENABLED(INFO)) {
|
||||
std::stringstream ss;
|
||||
for(size_t i = 0; nv[i]; i += 2) {
|
||||
|
|
|
@ -746,35 +746,10 @@ void on_frame_recv_callback
|
|||
break;
|
||||
}
|
||||
auto nva = frame->headers.nva;
|
||||
std::string status, content_length;
|
||||
for(size_t i = 0; i < frame->headers.nvlen; ++i) {
|
||||
if(!http::check_http2_allowed_header(nva[i].name, nva[i].namelen)) {
|
||||
status.clear();
|
||||
break;
|
||||
}
|
||||
if(util::strieq(":status", nva[i].name, nva[i].namelen)) {
|
||||
status.assign(reinterpret_cast<char*>(nva[i].value),
|
||||
nva[i].valuelen);
|
||||
auto code = strtoul(status.c_str(), nullptr, 10);
|
||||
downstream->set_response_http_status(code);
|
||||
} else if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
||||
if(util::strieq("content-length", nva[i].name, nva[i].namelen)) {
|
||||
content_length.assign(reinterpret_cast<char*>(nva[i].value),
|
||||
nva[i].valuelen);
|
||||
}
|
||||
downstream->add_response_header
|
||||
(std::string(reinterpret_cast<char*>(nva[i].name),
|
||||
nva[i].namelen),
|
||||
std::string(reinterpret_cast<char*>(nva[i].value),
|
||||
nva[i].valuelen));
|
||||
}
|
||||
}
|
||||
// Just assume it is HTTP/1.1. But we really consider to say 2.0
|
||||
// here.
|
||||
downstream->set_response_major(1);
|
||||
downstream->set_response_minor(1);
|
||||
auto nvlen = frame->headers.nvlen;
|
||||
|
||||
if(status.empty()) {
|
||||
// Assuming that nva is sorted by name.
|
||||
if(!http::check_http2_headers(nva, nvlen)) {
|
||||
nghttp2_submit_rst_stream(session, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
|
@ -782,7 +757,31 @@ void on_frame_recv_callback
|
|||
return;
|
||||
}
|
||||
|
||||
if(content_length.empty() && downstream->get_request_method() != "HEAD" &&
|
||||
for(size_t i = 0; i < nvlen; ++i) {
|
||||
if(nva[i].namelen > 0 && nva[i].name[0] != ':') {
|
||||
downstream->add_response_header(http::name_to_str(&nva[i]),
|
||||
http::value_to_str(&nva[i]));
|
||||
}
|
||||
}
|
||||
|
||||
auto status = http::get_unique_header(nva, nvlen, ":status");
|
||||
if(!status || http::value_lws(status)) {
|
||||
nghttp2_submit_rst_stream(session, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
call_downstream_readcb(spdy, downstream);
|
||||
return;
|
||||
}
|
||||
downstream->set_response_http_status
|
||||
(strtoul(http::value_to_str(status).c_str(), nullptr, 10));
|
||||
|
||||
// Just assume it is HTTP/1.1. But we really consider to say 2.0
|
||||
// here.
|
||||
downstream->set_response_major(1);
|
||||
downstream->set_response_minor(1);
|
||||
|
||||
auto content_length = http::get_header(nva, nvlen, "content-length");
|
||||
if(!content_length && downstream->get_request_method() != "HEAD" &&
|
||||
downstream->get_request_method() != "CONNECT") {
|
||||
unsigned int status;
|
||||
status = downstream->get_response_http_status();
|
||||
|
|
|
@ -184,7 +184,7 @@ void on_ctrl_recv_callback
|
|||
return;
|
||||
}
|
||||
// Require content-length if FIN flag is not set.
|
||||
if(!util::strieq("CONNECT", method) &&
|
||||
if(strcmp("CONNECT", method) == 0 &&
|
||||
(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) == 0 &&
|
||||
!content_length) {
|
||||
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
||||
|
|
27
src/util.cc
27
src/util.cc
|
@ -171,6 +171,24 @@ bool strieq(const char *a, const uint8_t *b, size_t bn)
|
|||
return !*a && b == blast;
|
||||
}
|
||||
|
||||
bool streq(const char *a, const uint8_t *b, size_t bn)
|
||||
{
|
||||
if(!a || !b) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t *blast = b + bn;
|
||||
for(; *a && b != blast && *a == *b; ++a, ++b);
|
||||
return !*a && b == blast;
|
||||
}
|
||||
|
||||
bool streq(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen)
|
||||
{
|
||||
if(alen != blen) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(a, b, alen) == 0;
|
||||
}
|
||||
|
||||
int strcompare(const char *a, const uint8_t *b, size_t bn)
|
||||
{
|
||||
assert(a && b);
|
||||
|
@ -269,6 +287,15 @@ void to_base64(std::string& token68str)
|
|||
return;
|
||||
}
|
||||
|
||||
void inp_strlower(std::string& s)
|
||||
{
|
||||
for(auto i = std::begin(s); i != std::end(s); ++i) {
|
||||
if('A' <= *i && *i <= 'Z') {
|
||||
*i = (*i) - 'A' + 'a';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace nghttp2
|
||||
|
|
|
@ -299,6 +299,10 @@ bool strieq(const char *a, const char *b);
|
|||
|
||||
bool strieq(const char *a, const uint8_t *b, size_t n);
|
||||
|
||||
bool streq(const char *a, const uint8_t *b, size_t bn);
|
||||
|
||||
bool streq(const uint8_t *a, size_t alen, const uint8_t *b, size_t blen);
|
||||
|
||||
bool strifind(const char *a, const char *b);
|
||||
|
||||
char upcase(char c);
|
||||
|
@ -344,6 +348,9 @@ inline char lowcase(char c)
|
|||
return tbl[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// Lowercase |s| in place.
|
||||
void inp_strlower(std::string& s);
|
||||
|
||||
template<typename T>
|
||||
std::string utos(T n)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "util_test.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
using namespace nghttp2;
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_util_streq(void)
|
||||
{
|
||||
CU_ASSERT(util::streq("alpha", (const uint8_t*)"alpha", 5));
|
||||
CU_ASSERT(util::streq("alpha", (const uint8_t*)"alphabravo", 5));
|
||||
CU_ASSERT(!util::streq("alpha", (const uint8_t*)"alphabravo", 6));
|
||||
CU_ASSERT(!util::streq("alphabravo", (const uint8_t*)"alpha", 5));
|
||||
CU_ASSERT(!util::streq("alpha", (const uint8_t*)"alphA", 5));
|
||||
CU_ASSERT(!util::streq("", (const uint8_t*)"a", 1));
|
||||
CU_ASSERT(util::streq("", (const uint8_t*)"", 0));
|
||||
CU_ASSERT(!util::streq("alpha", (const uint8_t*)"", 0));
|
||||
|
||||
CU_ASSERT(util::streq((const uint8_t*)"alpha", 5,
|
||||
(const uint8_t*)"alpha", 5));
|
||||
CU_ASSERT(!util::streq((const uint8_t*)"alpha", 4,
|
||||
(const uint8_t*)"alpha", 5));
|
||||
CU_ASSERT(!util::streq((const uint8_t*)"alpha", 5,
|
||||
(const uint8_t*)"alpha", 4));
|
||||
CU_ASSERT(!util::streq((const uint8_t*)"alpha", 5,
|
||||
(const uint8_t*)"alphA", 5));
|
||||
CU_ASSERT(util::streq(nullptr, 0, nullptr, 0));
|
||||
}
|
||||
|
||||
void test_util_inp_strlower(void)
|
||||
{
|
||||
std::string a("alPha");
|
||||
util::inp_strlower(a);
|
||||
CU_ASSERT("alpha" == a);
|
||||
|
||||
a = "ALPHA123BRAVO";
|
||||
util::inp_strlower(a);
|
||||
CU_ASSERT("alpha123bravo" == a);
|
||||
|
||||
a = "";
|
||||
util::inp_strlower(a);
|
||||
CU_ASSERT("" == a);
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef UTIL_TEST_H
|
||||
#define UTIL_TEST_H
|
||||
|
||||
namespace shrpx {
|
||||
|
||||
void test_util_streq(void);
|
||||
void test_util_inp_strlower(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
#endif // UTIL_TEST_H
|
Loading…
Reference in New Issue