diff --git a/src/Makefile.am b/src/Makefile.am index 18b3ee85..f1d7ddb0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,7 +62,7 @@ nghttpd_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} nghttpd.cc \ HttpServer.cc HttpServer.h NGHTTPX_SRCS = \ - util.cc util.h timegm.c timegm.h base64.h \ + util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \ shrpx_config.cc shrpx_config.h \ shrpx_error.h \ shrpx_listen_handler.cc shrpx_listen_handler.h \ @@ -95,8 +95,8 @@ 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 \ + http2_test.cc http2_test.h \ util_test.cc util_test.h \ ${NGHTTPX_SRCS} nghttpx_unittest_CPPFLAGS = ${AM_CPPFLAGS}\ diff --git a/src/http2.cc b/src/http2.cc new file mode 100644 index 00000000..e94cfc31 --- /dev/null +++ b/src/http2.cc @@ -0,0 +1,312 @@ +/* + * nghttp2 - HTTP/2.0 C Library + * + * Copyright (c) 2012 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 "http2.h" + +#include "util.h" + +namespace nghttp2 { + +namespace http2 { + +const char* get_status_string(int status_code) +{ + switch(status_code) { + case 100: return "100 Continue"; + case 101: return "101 Switching Protocols"; + case 200: return "200 OK"; + case 201: return "201 Created"; + case 202: return "202 Accepted"; + case 203: return "203 Non-Authoritative Information"; + case 204: return "204 No Content"; + case 205: return "205 Reset Content"; + case 206: return "206 Partial Content"; + case 300: return "300 Multiple Choices"; + case 301: return "301 Moved Permanently"; + case 302: return "302 Found"; + case 303: return "303 See Other"; + case 304: return "304 Not Modified"; + case 305: return "305 Use Proxy"; + // case 306: return "306 (Unused)"; + case 307: return "307 Temporary Redirect"; + case 400: return "400 Bad Request"; + case 401: return "401 Unauthorized"; + case 402: return "402 Payment Required"; + case 403: return "403 Forbidden"; + case 404: return "404 Not Found"; + case 405: return "405 Method Not Allowed"; + case 406: return "406 Not Acceptable"; + case 407: return "407 Proxy Authentication Required"; + case 408: return "408 Request Timeout"; + case 409: return "409 Conflict"; + case 410: return "410 Gone"; + case 411: return "411 Length Required"; + case 412: return "412 Precondition Failed"; + case 413: return "413 Request Entity Too Large"; + case 414: return "414 Request-URI Too Long"; + case 415: return "415 Unsupported Media Type"; + case 416: return "416 Requested Range Not Satisfiable"; + case 417: return "417 Expectation Failed"; + case 500: return "500 Internal Server Error"; + case 501: return "501 Not Implemented"; + case 502: return "502 Bad Gateway"; + case 503: return "503 Service Unavailable"; + case 504: return "504 Gateway Timeout"; + case 505: return "505 HTTP Version Not Supported"; + default: return ""; + } +} + +void capitalize(std::string& s, size_t offset) +{ + s[offset] = util::upcase(s[offset]); + for(size_t i = offset+1, eoi = s.size(); i < eoi; ++i) { + if(s[i-1] == '-') { + s[i] = util::upcase(s[i]); + } else { + s[i] = util::lowcase(s[i]); + } + } +} + +void sanitize_header_value(std::string& s, size_t offset) +{ + for(size_t i = offset, eoi = s.size(); i < eoi; ++i) { + if(s[i] == '\r' || s[i] == '\n') { + s[i] = ' '; + } + } +} + +void copy_url_component(std::string& dest, http_parser_url *u, int field, + const char* url) +{ + if(u->field_set & (1 << field)) { + dest.assign(url+u->field_data[field].off, u->field_data[field].len); + } +} + +bool check_http2_allowed_header(const char *name) +{ + return check_http2_allowed_header(reinterpret_cast(name), + strlen(name)); +} + +bool check_http2_allowed_header(const uint8_t *name, size_t namelen) +{ + return + !util::strieq("connection", name, namelen) && + !util::strieq("host", name, namelen) && + !util::strieq("keep-alive", name, namelen) && + !util::strieq("proxy-connection", name, namelen) && + !util::strieq("te", name, namelen) && + !util::strieq("transfer-encoding", name, 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(nv->name), nv->namelen); +} + +std::string value_to_str(const nghttp2_nv *nv) +{ + return std::string(reinterpret_cast(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>& 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>& 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; + 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; + capitalize(hdrs, hdrs.size()-headers[i].first.size()); + hdrs += ": "; + hdrs += headers[i].second; + hdrs += "\r\n"; + } +} + +} // namespace http2 + +} // namespace nghttp2 diff --git a/src/http2.h b/src/http2.h new file mode 100644 index 00000000..77d8de68 --- /dev/null +++ b/src/http2.h @@ -0,0 +1,104 @@ +/* + * 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 HTTP2_H +#define HTTP2_H + +#include "nghttp2_config.h" + +#include +#include + +#include + +#include "http-parser/http_parser.h" + +namespace nghttp2 { + +namespace http2 { + +const char* get_status_string(int status_code); + +void capitalize(std::string& s, size_t offset); + +void sanitize_header_value(std::string& s, size_t offset); + +// Copies the |field| component value from |u| and |url| to the +// |dest|. If |u| does not have |field|, then this function does +// nothing. +void copy_url_component(std::string& dest, http_parser_url *u, int field, + const char* url); + +// Returns true if the header field |name| with length |namelen| bytes +// is valid for HTTP/2.0. +bool check_http2_allowed_header(const uint8_t *name, size_t namelen); + +// Calls check_http2_allowed_header with |name| and strlen(name), +// 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>& 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>& headers); + +} // namespace http2 + +} // namespace nghttp2 + +#endif // HTTP2_H diff --git a/src/shrpx_http_test.cc b/src/http2_test.cc similarity index 79% rename from src/shrpx_http_test.cc rename to src/http2_test.cc index d6f6ef73..aaf59926 100644 --- a/src/shrpx_http_test.cc +++ b/src/http2_test.cc @@ -22,13 +22,13 @@ * 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 "http2_test.h" #include #include -#include "shrpx_http.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -37,25 +37,25 @@ using namespace nghttp2; namespace shrpx { -void test_http_check_http2_headers(void) +void test_http2_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)); + CU_ASSERT(!http2::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)); + CU_ASSERT(!http2::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)); + CU_ASSERT(http2::check_http2_headers(nv3, 3)); } -void test_http_get_unique_header(void) +void test_http2_get_unique_header(void) { nghttp2_nv nv[] = {MAKE_NV("alpha", "1"), MAKE_NV("bravo", "2"), @@ -65,18 +65,18 @@ void test_http_get_unique_header(void) MAKE_NV("echo", "6"),}; size_t nvlen = sizeof(nv)/sizeof(nv[0]); const nghttp2_nv *rv; - rv = http::get_unique_header(nv, nvlen, "delta"); + rv = http2::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"); + rv = http2::get_unique_header(nv, nvlen, "bravo"); CU_ASSERT(rv == nullptr); - rv = http::get_unique_header(nv, nvlen, "foxtrot"); + rv = http2::get_unique_header(nv, nvlen, "foxtrot"); CU_ASSERT(rv == nullptr); } -void test_http_get_header(void) +void test_http2_get_header(void) { nghttp2_nv nv[] = {MAKE_NV("alpha", "1"), MAKE_NV("bravo", "2"), @@ -86,30 +86,30 @@ void test_http_get_header(void) MAKE_NV("echo", "6"),}; size_t nvlen = sizeof(nv)/sizeof(nv[0]); const nghttp2_nv *rv; - rv = http::get_header(nv, nvlen, "delta"); + rv = http2::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"); + rv = http2::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"); + rv = http2::get_header(nv, nvlen, "foxtrot"); CU_ASSERT(rv == nullptr); } -void test_http_value_lws(void) +void test_http2_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])); + CU_ASSERT(!http2::value_lws(&nv[0])); + CU_ASSERT(!http2::value_lws(&nv[1])); + CU_ASSERT(http2::value_lws(&nv[2])); + CU_ASSERT(http2::value_lws(&nv[3])); + CU_ASSERT(!http2::value_lws(&nv[4])); } namespace { @@ -129,10 +129,10 @@ auto headers = std::vector> {"zulu", "12"}}; } // namespace -void test_http_copy_norm_headers_to_nv(void) +void test_http2_copy_norm_headers_to_nv(void) { const char* nv[30]; - size_t nvlen = http::copy_norm_headers_to_nv(nv, headers); + size_t nvlen = http2::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); @@ -148,10 +148,10 @@ void test_http_copy_norm_headers_to_nv(void) CU_ASSERT(strcmp(nv[11], "12") == 0); } -void test_http_build_http1_headers_from_norm_headers(void) +void test_http2_build_http1_headers_from_norm_headers(void) { std::string hdrs; - http::build_http1_headers_from_norm_headers(hdrs, headers); + http2::build_http1_headers_from_norm_headers(hdrs, headers); CU_ASSERT(hdrs == "Alpha: 0\r\n" "Bravo: 1\r\n" diff --git a/src/shrpx_http_test.h b/src/http2_test.h similarity index 77% rename from src/shrpx_http_test.h rename to src/http2_test.h index 9f8fa9b5..488b923a 100644 --- a/src/shrpx_http_test.h +++ b/src/http2_test.h @@ -22,18 +22,18 @@ * 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 +#ifndef SHRPX_HTTP2_TEST_H +#define SHRPX_HTTP2_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); +void test_http2_check_http2_headers(void); +void test_http2_get_unique_header(void); +void test_http2_get_header(void); +void test_http2_value_lws(void); +void test_http2_copy_norm_headers_to_nv(void); +void test_http2_build_http1_headers_from_norm_headers(void); } // namespace shrpx -#endif // SHRPX_HTTP_TEST_H +#endif // SHRPX_HTTP2_TEST_H diff --git a/src/shrpx-unittest.cc b/src/shrpx-unittest.cc index 8d78f23e..74b90387 100644 --- a/src/shrpx-unittest.cc +++ b/src/shrpx-unittest.cc @@ -29,8 +29,8 @@ #include /* include test cases' include files here */ #include "shrpx_ssl_test.h" -#include "shrpx_http_test.h" #include "shrpx_downstream_test.h" +#include "http2_test.h" #include "util_test.h" static int init_suite1(void) @@ -69,18 +69,18 @@ int main(int argc, char* argv[]) 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) || - !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, "http2_check_http2_headers", + shrpx::test_http2_check_http2_headers) || + !CU_add_test(pSuite, "http2_get_unique_header", + shrpx::test_http2_get_unique_header) || + !CU_add_test(pSuite, "http2_get_header", + shrpx::test_http2_get_header) || + !CU_add_test(pSuite, "http2_value_lws", + shrpx::test_http2_value_lws) || + !CU_add_test(pSuite, "http2_copy_norm_headers_to_nv", + shrpx::test_http2_copy_norm_headers_to_nv) || + !CU_add_test(pSuite, "http2_build_http1_headers_from_norm_headers", + shrpx::test_http2_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", diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index ee67dd99..b7f057fb 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -38,9 +38,12 @@ #include +#include "http-parser/http_parser.h" + #include "shrpx_log.h" #include "shrpx_ssl.h" #include "shrpx_http.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -341,7 +344,7 @@ int parse_config(const char *opt, const char *optarg) if(rv == 0) { std::string val; if(u.field_set & UF_USERINFO) { - http::copy_url_component(val, &u, UF_USERINFO, optarg); + http2::copy_url_component(val, &u, UF_USERINFO, optarg); // Surprisingly, u.field_set & UF_USERINFO is nonzero even if // userinfo component is empty string. if(!val.empty()) { @@ -351,7 +354,7 @@ int parse_config(const char *opt, const char *optarg) } } if(u.field_set & UF_HOST) { - http::copy_url_component(val, &u, UF_HOST, optarg); + http2::copy_url_component(val, &u, UF_HOST, optarg); set_config_str(&mod_config()->downstream_http_proxy_host, val.c_str()); } else { LOG(ERROR) << "backend-http-proxy-uri does not contain hostname"; diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc index eefde0c2..d825b183 100644 --- a/src/shrpx_http.cc +++ b/src/shrpx_http.cc @@ -26,6 +26,7 @@ #include "shrpx_config.h" #include "shrpx_log.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -34,59 +35,11 @@ namespace shrpx { namespace http { -const char* get_status_string(int status_code) -{ - switch(status_code) { - case 100: return "100 Continue"; - case 101: return "101 Switching Protocols"; - case 200: return "200 OK"; - case 201: return "201 Created"; - case 202: return "202 Accepted"; - case 203: return "203 Non-Authoritative Information"; - case 204: return "204 No Content"; - case 205: return "205 Reset Content"; - case 206: return "206 Partial Content"; - case 300: return "300 Multiple Choices"; - case 301: return "301 Moved Permanently"; - case 302: return "302 Found"; - case 303: return "303 See Other"; - case 304: return "304 Not Modified"; - case 305: return "305 Use Proxy"; - // case 306: return "306 (Unused)"; - case 307: return "307 Temporary Redirect"; - case 400: return "400 Bad Request"; - case 401: return "401 Unauthorized"; - case 402: return "402 Payment Required"; - case 403: return "403 Forbidden"; - case 404: return "404 Not Found"; - case 405: return "405 Method Not Allowed"; - case 406: return "406 Not Acceptable"; - case 407: return "407 Proxy Authentication Required"; - case 408: return "408 Request Timeout"; - case 409: return "409 Conflict"; - case 410: return "410 Gone"; - case 411: return "411 Length Required"; - case 412: return "412 Precondition Failed"; - case 413: return "413 Request Entity Too Large"; - case 414: return "414 Request-URI Too Long"; - case 415: return "415 Unsupported Media Type"; - case 416: return "416 Requested Range Not Satisfiable"; - case 417: return "417 Expectation Failed"; - case 500: return "500 Internal Server Error"; - case 501: return "501 Not Implemented"; - case 502: return "502 Bad Gateway"; - case 503: return "503 Service Unavailable"; - case 504: return "504 Gateway Timeout"; - case 505: return "505 HTTP Version Not Supported"; - default: return ""; - } -} - std::string create_error_html(int status_code) { std::string res; res.reserve(512); - const char *status = http::get_status_string(status_code); + const char *status = http2::get_status_string(status_code); res += ""; res += status; res += "

"; @@ -110,27 +63,6 @@ std::string create_via_header_value(int major, int minor) return hdrs; } -void capitalize(std::string& s, size_t offset) -{ - s[offset] = util::upcase(s[offset]); - for(size_t i = offset+1, eoi = s.size(); i < eoi; ++i) { - if(s[i-1] == '-') { - s[i] = util::upcase(s[i]); - } else { - s[i] = util::lowcase(s[i]); - } - } -} - -void sanitize_header_value(std::string& s, size_t offset) -{ - for(size_t i = offset, eoi = s.size(); i < eoi; ++i) { - if(s[i] == '\r' || s[i] == '\n') { - s[i] = ' '; - } - } -} - std::string colorizeHeaders(const char *hdrs) { std::string nhdrs; @@ -162,214 +94,6 @@ std::string colorizeHeaders(const char *hdrs) return nhdrs; } -void copy_url_component(std::string& dest, http_parser_url *u, int field, - const char* url) -{ - if(u->field_set & (1 << field)) { - dest.assign(url+u->field_data[field].off, u->field_data[field].len); - } -} - -bool check_http2_allowed_header(const char *name) -{ - return check_http2_allowed_header(reinterpret_cast(name), - strlen(name)); -} - -bool check_http2_allowed_header(const uint8_t *name, size_t namelen) -{ - return - !util::strieq("connection", name, namelen) && - !util::strieq("host", name, namelen) && - !util::strieq("keep-alive", name, namelen) && - !util::strieq("proxy-connection", name, namelen) && - !util::strieq("te", name, namelen) && - !util::strieq("transfer-encoding", name, 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(nv->name), nv->namelen); -} - -std::string value_to_str(const nghttp2_nv *nv) -{ - return std::string(reinterpret_cast(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>& 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>& 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 diff --git a/src/shrpx_http.h b/src/shrpx_http.h index 3f2eff96..01bc91da 100644 --- a/src/shrpx_http.h +++ b/src/shrpx_http.h @@ -25,83 +25,21 @@ #ifndef SHRPX_HTTP_H #define SHRPX_HTTP_H +#include "shrpx.h" + #include -#include - -#include - -#include "http-parser/http_parser.h" namespace shrpx { namespace http { -const char* get_status_string(int status_code); - std::string create_error_html(int status_code); std::string create_via_header_value(int major, int minor); -void capitalize(std::string& s, size_t offset); - -void sanitize_header_value(std::string& s, size_t offset); - // Adds ANSI color codes to HTTP headers |hdrs|. std::string colorizeHeaders(const char *hdrs); -// Copies the |field| component value from |u| and |url| to the -// |dest|. If |u| does not have |field|, then this function does -// nothing. -void copy_url_component(std::string& dest, http_parser_url *u, int field, - const char* url); - -// Returns true if the header field |name| with length |namelen| bytes -// is valid for HTTP/2.0. -bool check_http2_allowed_header(const uint8_t *name, size_t namelen); - -// Calls check_http2_allowed_header with |name| and strlen(name), -// 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>& 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>& headers); - } // namespace http } // namespace shrpx diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 25a807e8..b87ac819 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -36,6 +36,7 @@ #include "shrpx_config.h" #include "shrpx_http.h" #include "shrpx_accesslog.h" +#include "http2.h" #include "util.h" #include "base64.h" @@ -209,35 +210,35 @@ void on_frame_recv_callback } // Assuming that nva is sorted by name. - if(!http::check_http2_headers(nva, nvlen)) { + if(!http2::check_http2_headers(nva, nvlen)) { upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); return; } 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])); + downstream->add_request_header(http2::name_to_str(&nva[i]), + http2::value_to_str(&nva[i])); } } - 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"); + auto host = http2::get_unique_header(nva, nvlen, ":host"); + auto path = http2::get_unique_header(nva, nvlen, ":path"); + auto method = http2::get_unique_header(nva, nvlen, ":method"); + auto scheme = http2::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)))) { + http2::value_lws(host) || http2::value_lws(path) || + http2::value_lws(method) || + (!is_connect && (!scheme || http2::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)) { + auto content_length = http2::get_header(nva, nvlen, "content-length"); + if(!content_length || http2::value_lws(content_length)) { // If content-length is missing, // Downstream::push_upload_data_chunk will fail and upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); @@ -245,22 +246,22 @@ void on_frame_recv_callback } } - downstream->set_request_method(http::value_to_str(method)); + downstream->set_request_method(http2::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) && scheme && path->value[0] == '/') { - std::string reqpath(http::value_to_str(scheme)); + std::string reqpath(http2::value_to_str(scheme)); reqpath += "://"; - reqpath += http::value_to_str(host); - reqpath += http::value_to_str(path); + reqpath += http2::value_to_str(host); + reqpath += http2::value_to_str(path); downstream->set_request_path(reqpath); } else { - downstream->set_request_path(http::value_to_str(path)); + downstream->set_request_path(http2::value_to_str(path)); } - downstream->add_request_header("host", http::value_to_str(host)); + downstream->add_request_header("host", http2::value_to_str(host)); downstream->check_upgrade_request(); auto dconn = upstream->get_client_handler()->get_downstream_connection(); @@ -701,7 +702,7 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr) } // namespace int Http2Upstream::rst_stream(Downstream *downstream, - nghttp2_error_code error_code) + nghttp2_error_code error_code) { if(LOG_ENABLED(INFO)) { ULOG(INFO, this) << "RST_STREAM stream_id=" @@ -863,8 +864,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) nv[hdidx++] = ":status"; nv[hdidx++] = response_status.c_str(); - hdidx += http::copy_norm_headers_to_nv(&nv[hdidx], - downstream->get_response_headers()); + hdidx += http2::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) { @@ -914,7 +915,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) // WARNING: Never call directly or indirectly nghttp2_session_send or // nghttp2_session_recv. These calls may delete downstream. int Http2Upstream::on_downstream_body(Downstream *downstream, - const uint8_t *data, size_t len) + const uint8_t *data, size_t len) { evbuffer *body = downstream->get_response_body_buf(); int rv = evbuffer_add(body, data, len); diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index a79175cc..3a797eac 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -30,6 +30,7 @@ #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_http.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -120,7 +121,7 @@ int HttpDownstreamConnection::push_request_headers() hdrs += "HTTP/1.1\r\n"; downstream_->normalize_request_headers(); auto end_headers = std::end(downstream_->get_request_headers()); - http::build_http1_headers_from_norm_headers + http2::build_http1_headers_from_norm_headers (hdrs, downstream_->get_request_headers()); if(downstream_->get_request_connection_close()) { diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 439488fd..3fd1dc05 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -36,6 +36,7 @@ #include "shrpx_config.h" #include "shrpx_error.h" #include "shrpx_accesslog.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -583,7 +584,7 @@ int HttpsUpstream::error_reply(int status_code) std::string header; header.reserve(512); header += "HTTP/1.1 "; - header += http::get_status_string(status_code); + header += http2::get_status_string(status_code); header += "\r\nServer: "; header += get_config()->server_name; header += "\r\nContent-Length: "; @@ -659,11 +660,11 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) downstream->get_request_major(), downstream->get_request_minor()); std::string hdrs = temp; - hdrs += http::get_status_string(downstream->get_response_http_status()); + hdrs += http2::get_status_string(downstream->get_response_http_status()); hdrs += "\r\n"; downstream->normalize_response_headers(); auto end_headers = std::end(downstream->get_response_headers()); - http::build_http1_headers_from_norm_headers + http2::build_http1_headers_from_norm_headers (hdrs, downstream->get_response_headers()); // We check downstream->get_response_connection_close() in case when diff --git a/src/shrpx_spdy_downstream_connection.cc b/src/shrpx_spdy_downstream_connection.cc index d166951d..84408734 100644 --- a/src/shrpx_spdy_downstream_connection.cc +++ b/src/shrpx_spdy_downstream_connection.cc @@ -39,6 +39,7 @@ #include "shrpx_error.h" #include "shrpx_http.h" #include "shrpx_spdy_session.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -252,9 +253,9 @@ int SpdyDownstreamConnection::push_request_headers() downstream_->get_request_path().size(), 0, &u); if(rv == 0) { - http::copy_url_component(scheme, &u, UF_SCHEMA, url); - http::copy_url_component(path, &u, UF_PATH, url); - http::copy_url_component(query, &u, UF_QUERY, url); + http2::copy_url_component(scheme, &u, UF_SCHEMA, url); + http2::copy_url_component(path, &u, UF_PATH, url); + http2::copy_url_component(query, &u, UF_QUERY, url); if(path.empty()) { path = "/"; } @@ -282,8 +283,8 @@ int SpdyDownstreamConnection::push_request_headers() nv[hdidx++] = ":method"; nv[hdidx++] = downstream_->get_request_method().c_str(); - hdidx += http::copy_norm_headers_to_nv(&nv[hdidx], - downstream_->get_request_headers()); + hdidx += http2::copy_norm_headers_to_nv(&nv[hdidx], + downstream_->get_request_headers()); auto host = downstream_->get_norm_request_header("host"); if(host == end_headers) { diff --git a/src/shrpx_spdy_session.cc b/src/shrpx_spdy_session.cc index c768da41..702d4032 100644 --- a/src/shrpx_spdy_session.cc +++ b/src/shrpx_spdy_session.cc @@ -40,6 +40,7 @@ #include "shrpx_client_handler.h" #include "shrpx_ssl.h" #include "shrpx_http.h" +#include "http2.h" #include "util.h" #include "base64.h" @@ -749,7 +750,7 @@ void on_frame_recv_callback auto nvlen = frame->headers.nvlen; // Assuming that nva is sorted by name. - if(!http::check_http2_headers(nva, nvlen)) { + if(!http2::check_http2_headers(nva, nvlen)) { nghttp2_submit_rst_stream(session, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); downstream->set_response_state(Downstream::MSG_RESET); @@ -759,13 +760,13 @@ void on_frame_recv_callback 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])); + downstream->add_response_header(http2::name_to_str(&nva[i]), + http2::value_to_str(&nva[i])); } } - auto status = http::get_unique_header(nva, nvlen, ":status"); - if(!status || http::value_lws(status)) { + auto status = http2::get_unique_header(nva, nvlen, ":status"); + if(!status || http2::value_lws(status)) { nghttp2_submit_rst_stream(session, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR); downstream->set_response_state(Downstream::MSG_RESET); @@ -773,14 +774,14 @@ void on_frame_recv_callback return; } downstream->set_response_http_status - (strtoul(http::value_to_str(status).c_str(), nullptr, 10)); + (strtoul(http2::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"); + auto content_length = http2::get_header(nva, nvlen, "content-length"); if(!content_length && downstream->get_request_method() != "HEAD" && downstream->get_request_method() != "CONNECT") { unsigned int status; diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index a13883ef..403140c3 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -37,6 +37,7 @@ #include "shrpx_config.h" #include "shrpx_http.h" #include "shrpx_accesslog.h" +#include "http2.h" #include "util.h" using namespace nghttp2; @@ -740,7 +741,7 @@ int SpdyUpstream::error_reply(Downstream *downstream, int status_code) std::string content_length = util::utos(html.size()); const char *nv[] = { - ":status", http::get_status_string(status_code), + ":status", http2::get_status_string(status_code), ":version", "http/1.1", "content-type", "text/html; charset=UTF-8", "server", get_config()->server_name, @@ -810,7 +811,8 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) size_t hdidx = 0; std::string via_value; nv[hdidx++] = ":status"; - nv[hdidx++] = http::get_status_string(downstream->get_response_http_status()); + nv[hdidx++] = http2::get_status_string + (downstream->get_response_http_status()); nv[hdidx++] = ":version"; nv[hdidx++] = "HTTP/1.1"; for(Headers::const_iterator i = downstream->get_response_headers().begin();