diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 5e21502c..d3e5c55c 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -85,7 +85,7 @@ template void append_nv(Stream *stream, const Array &nva) { http2::index_header(stream->hdidx, token, i); } http2::add_header(stream->headers, nv.name, nv.namelen, nv.value, - nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX); + nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token); } } } // namespace @@ -1061,7 +1061,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, http2::index_header(stream->hdidx, token, stream->headers.size()); http2::add_header(stream->headers, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX); + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); return 0; } } // namespace diff --git a/src/http2.cc b/src/http2.cc index d8da3cf9..2e77e1ec 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -165,14 +165,15 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field, Headers::value_type to_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, - bool no_index) { + bool no_index, int16_t token) { return Header(std::string(reinterpret_cast(name), namelen), std::string(reinterpret_cast(value), valuelen), - no_index); + no_index, token); } void add_header(Headers &nva, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index) { + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token) { if (valuelen > 0) { size_t i, j; for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i) @@ -182,7 +183,7 @@ void add_header(Headers &nva, const uint8_t *name, size_t namelen, value += i; valuelen -= i + (valuelen - j - 1); } - nva.push_back(to_header(name, namelen, value, valuelen, no_index)); + nva.push_back(to_header(name, namelen, value, valuelen, no_index, token)); } const Headers::value_type *get_header(const Headers &nva, const char *name) { @@ -221,7 +222,7 @@ void copy_headers_to_nva(std::vector &nva, const Headers &headers) { if (kv.name.empty() || kv.name[0] == ':') { continue; } - switch (lookup_token(kv.name)) { + switch (kv.token) { case HD_COOKIE: case HD_CONNECTION: case HD_HOST: @@ -247,7 +248,7 @@ void build_http1_headers_from_headers(std::string &hdrs, if (kv.name.empty() || kv.name[0] == ':') { continue; } - switch (lookup_token(kv.name)) { + switch (kv.token) { case HD_CONNECTION: case HD_COOKIE: case HD_HOST: @@ -608,18 +609,7 @@ void init_hdidx(HeaderIndex &hdidx) { std::fill(std::begin(hdidx), std::end(hdidx), -1); } -void index_headers(HeaderIndex &hdidx, const Headers &headers) { - for (size_t i = 0; i < headers.size(); ++i) { - auto &kv = headers[i]; - auto token = lookup_token( - reinterpret_cast(kv.name.c_str()), kv.name.size()); - if (token >= 0) { - http2::index_header(hdidx, token, i); - } - } -} - -void index_header(HeaderIndex &hdidx, int token, size_t idx) { +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) { if (token == -1) { return; } @@ -627,7 +617,8 @@ void index_header(HeaderIndex &hdidx, int token, size_t idx) { hdidx[token] = idx; } -bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token) { +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { switch (token) { case HD__AUTHORITY: case HD__METHOD: @@ -639,7 +630,8 @@ bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token) { } } -bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token) { +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token) { switch (token) { case HD__STATUS: return hdidx[token] == -1; @@ -648,7 +640,7 @@ bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token) { } } -bool http2_header_allowed(int token) { +bool http2_header_allowed(int16_t token) { switch (token) { case HD_CONNECTION: case HD_KEEP_ALIVE: @@ -670,7 +662,7 @@ bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) { return true; } -const Headers::value_type *get_header(const HeaderIndex &hdidx, int token, +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, const Headers &nva) { auto i = hdidx[token]; if (i == -1) { diff --git a/src/http2.h b/src/http2.h index b3f1c32b..f9d9d68e 100644 --- a/src/http2.h +++ b/src/http2.h @@ -40,10 +40,12 @@ namespace nghttp2 { struct Header { - Header(std::string name, std::string value, bool no_index = false) - : name(std::move(name)), value(std::move(value)), no_index(no_index) {} + Header(std::string name, std::string value, bool no_index = false, + int16_t token = -1) + : name(std::move(name)), value(std::move(value)), token(token), + no_index(no_index) {} - Header() : no_index(false) {} + Header() : token(-1), no_index(false) {} bool operator==(const Header &other) const { return name == other.name && value == other.value; @@ -55,6 +57,7 @@ struct Header { std::string name; std::string value; + int16_t token; bool no_index; }; @@ -77,13 +80,14 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field, Headers::value_type to_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, - bool no_index); + bool no_index, int16_t token); // Add name/value pairs to |nva|. If |no_index| is true, this // name/value pair won't be indexed when it is forwarded to the next // hop. This function strips white spaces around |value|. void add_header(Headers &nva, const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, bool no_index); + const uint8_t *value, size_t valuelen, bool no_index, + int16_t token); // Returns pointer to the entry in |nva| which has name |name|. If // more than one entries which have the name |name|, last occurrence @@ -125,14 +129,16 @@ nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) { NGHTTP2_NV_FLAG_NONE}; } -// Appends headers in |headers| to |nv|. Certain headers, including -// disallowed headers in HTTP/2 spec and headers which require -// special handling (i.e. via), are not copied. +// Appends headers in |headers| to |nv|. |headers| must be indexed +// before this call (its element's token field is assigned). Certain +// headers, including disallowed headers in HTTP/2 spec and headers +// which require special handling (i.e. via), are not copied. void copy_headers_to_nva(std::vector &nva, const Headers &headers); // Appends HTTP/1.1 style header lines to |hdrs| from headers in -// |headers|. Certain headers, which requires special handling -// (i.e. via and cookie), are not appended. +// |headers|. |headers| must be indexed before this call (its +// element's token field is assigned). Certain headers, which +// requires special handling (i.e. via and cookie), are not appended. void build_http1_headers_from_headers(std::string &hdrs, const Headers &headers); @@ -226,28 +232,27 @@ int lookup_token(const std::string &name); // array containing at least HD_MAXIDX elements. void init_hdidx(HeaderIndex &hdidx); // Indexes header |token| using index |idx|. -void index_header(HeaderIndex &hdidx, int token, size_t idx); -// Iterates |headers| and for each element, call index_header. -void index_headers(HeaderIndex &hdidx, const Headers &headers); +void index_header(HeaderIndex &hdidx, int16_t token, size_t idx); // Returns true if HTTP/2 request pseudo header |token| is not indexed // yet and not -1. -bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token); +bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token); // Returns true if HTTP/2 response pseudo header |token| is not // indexed yet and not -1. -bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token); +bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, + int16_t token); // Returns true if header field denoted by |token| is allowed for // HTTP/2. -bool http2_header_allowed(int token); +bool http2_header_allowed(int16_t token); // Returns true that |hdidx| contains mandatory HTTP/2 request // headers. bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx); // Returns header denoted by |token| using index |hdidx|. -const Headers::value_type *get_header(const HeaderIndex &hdidx, int token, +const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token, const Headers &nva); struct LinkHeader { diff --git a/src/http2_test.cc b/src/http2_test.cc index 0216152b..d5975582 100644 --- a/src/http2_test.cc +++ b/src/http2_test.cc @@ -58,46 +58,52 @@ void test_http2_add_header(void) { auto nva = Headers(); http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3, - false); + false, -1); CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]); CU_ASSERT(!nva[0].no_index); nva.clear(); http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0, - true); + true, -1); CU_ASSERT(Headers::value_type("alpha", "") == nva[0]); CU_ASSERT(nva[0].no_index); nva.clear(); http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2, - false); + false, -1); CU_ASSERT(Headers::value_type("a", "b") == nva[0]); nva.clear(); http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2, - false); + false, -1); CU_ASSERT(Headers::value_type("a", "b") == nva[0]); nva.clear(); http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b ", 5, - false); + false, -1); CU_ASSERT(Headers::value_type("a", "b") == nva[0]); nva.clear(); http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" bravo ", - 9, false); + 9, false, -1); CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]); nva.clear(); http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" ", 4, - false); + false, -1); CU_ASSERT(Headers::value_type("a", "") == nva[0]); + + nva.clear(); + + http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers", + 8, false, http2::HD_TE); + CU_ASSERT(http2::HD_TE == nva[0].token); } void test_http2_get_header(void) { @@ -128,19 +134,20 @@ void test_http2_get_header(void) { } namespace { -auto headers = Headers{{"alpha", "0", true}, - {"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"}}; +auto headers = + Headers{{"alpha", "0", true}, + {"bravo", "1"}, + {"connection", "2", false, http2::HD_CONNECTION}, + {"connection", "3", false, http2::HD_CONNECTION}, + {"delta", "4"}, + {"expect", "5"}, + {"foxtrot", "6"}, + {"tango", "7"}, + {"te", "8", false, http2::HD_TE}, + {"te", "9", false, http2::HD_TE}, + {"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR}, + {"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR}, + {"zulu", "12"}}; } // namespace void test_http2_copy_headers_to_nva(void) { diff --git a/src/nghttp.cc b/src/nghttp.cc index 80fad936..94150788 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -250,7 +250,7 @@ bool Request::is_ipv6_literal_addr() const { } } -bool Request::response_pseudo_header_allowed(int token) const { +bool Request::response_pseudo_header_allowed(int16_t token) const { if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') { return false; } @@ -262,7 +262,7 @@ bool Request::response_pseudo_header_allowed(int token) const { } } -bool Request::push_request_pseudo_header_allowed(int token) const { +bool Request::push_request_pseudo_header_allowed(int16_t token) const { if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') { return false; } @@ -277,7 +277,7 @@ bool Request::push_request_pseudo_header_allowed(int token) const { } } -Headers::value_type *Request::get_res_header(int token) { +Headers::value_type *Request::get_res_header(int16_t token) { auto idx = res_hdidx[token]; if (idx == -1) { return nullptr; @@ -285,7 +285,7 @@ Headers::value_type *Request::get_res_header(int token) { return &res_nva[idx]; } -Headers::value_type *Request::get_req_header(int token) { +Headers::value_type *Request::get_req_header(int16_t token) { auto idx = req_hdidx[token]; if (idx == -1) { return nullptr; @@ -1688,7 +1688,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, http2::index_header(req->res_hdidx, token, req->res_nva.size()); http2::add_header(req->res_nva, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX); + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); break; } case NGHTTP2_PUSH_PROMISE: { @@ -1712,7 +1712,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, http2::index_header(req->req_hdidx, token, req->req_nva.size()); http2::add_header(req->req_nva, name, namelen, value, valuelen, - flags & NGHTTP2_NV_FLAG_NO_INDEX); + flags & NGHTTP2_NV_FLAG_NO_INDEX, token); break; } } diff --git a/src/nghttp.h b/src/nghttp.h index 14b89f94..14bbb6b2 100644 --- a/src/nghttp.h +++ b/src/nghttp.h @@ -122,11 +122,11 @@ struct Request { bool is_ipv6_literal_addr() const; - bool response_pseudo_header_allowed(int token) const; - bool push_request_pseudo_header_allowed(int token) const; + bool response_pseudo_header_allowed(int16_t token) const; + bool push_request_pseudo_header_allowed(int16_t token) const; - Headers::value_type *get_res_header(int token); - Headers::value_type *get_req_header(int token); + Headers::value_type *get_res_header(int16_t token); + Headers::value_type *get_req_header(int16_t token); void record_request_time(); void record_response_time(); diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index fc0fca8e..86d828dd 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -300,6 +300,7 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers, continue; } + kv.token = token; http2::index_header(hdidx, token, i); if (token == http2::HD_CONTENT_LENGTH) { @@ -322,7 +323,7 @@ int Downstream::index_request_headers() { request_content_length_); } -const Headers::value_type *Downstream::get_request_header(int token) const { +const Headers::value_type *Downstream::get_request_header(int16_t token) const { return http2::get_header(request_hdidx_, token, request_headers_); } @@ -346,10 +347,11 @@ void Downstream::set_last_request_header_value(std::string value) { void Downstream::add_request_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, - bool no_index, int token) { + bool no_index, int16_t token) { http2::index_header(request_hdidx_, token, request_headers_.size()); request_headers_sum_ += namelen + valuelen; - http2::add_header(request_headers_, name, namelen, value, valuelen, no_index); + http2::add_header(request_headers_, name, namelen, value, valuelen, no_index, + token); } bool Downstream::get_request_header_key_prev() const { @@ -525,7 +527,8 @@ int Downstream::index_response_headers() { response_content_length_); } -const Headers::value_type *Downstream::get_response_header(int token) const { +const Headers::value_type * +Downstream::get_response_header(int16_t token) const { return http2::get_header(response_hdidx_, token, response_headers_); } @@ -577,13 +580,21 @@ void Downstream::set_last_response_header_value(std::string value) { item.value = std::move(value); } +void Downstream::add_response_header(std::string name, std::string value, + int16_t token) { + http2::index_header(response_hdidx_, token, response_headers_.size()); + response_headers_sum_ += name.size() + value.size(); + response_headers_.emplace_back(std::move(name), std::move(value), false, + token); +} + void Downstream::add_response_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, - bool no_index, int token) { + bool no_index, int16_t token) { http2::index_header(response_hdidx_, token, response_headers_.size()); response_headers_sum_ += namelen + valuelen; - http2::add_header(response_headers_, name, namelen, value, valuelen, - no_index); + http2::add_header(response_headers_, name, namelen, value, valuelen, no_index, + token); } bool Downstream::get_response_header_key_prev() const { @@ -893,14 +904,14 @@ bool pseudo_header_allowed(const Headers &headers) { } } // namespace -bool Downstream::request_pseudo_header_allowed(int token) const { +bool Downstream::request_pseudo_header_allowed(int16_t token) const { if (!pseudo_header_allowed(request_headers_)) { return false; } return http2::check_http2_request_pseudo_header(request_hdidx_, token); } -bool Downstream::response_pseudo_header_allowed(int token) const { +bool Downstream::response_pseudo_header_allowed(int16_t token) const { if (!pseudo_header_allowed(response_headers_)) { return false; } diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index 83162560..f759041a 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -108,7 +108,7 @@ public: // multiple header have |name| as name, return last occurrence from // the beginning. If no such header is found, returns nullptr. // This function must be called after headers are indexed - const Headers::value_type *get_request_header(int token) const; + const Headers::value_type *get_request_header(int16_t token) const; // Returns pointer to the request header with the name |name|. If // no such header is found, returns nullptr. const Headers::value_type *get_request_header(const std::string &name) const; @@ -117,7 +117,7 @@ public: void add_request_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, bool no_index, - int token); + int16_t token); bool get_request_header_key_prev() const; void append_last_request_header_key(const char *data, size_t len); @@ -165,7 +165,7 @@ public: bool validate_request_bodylen() const; int64_t get_request_content_length() const; void set_request_content_length(int64_t len); - bool request_pseudo_header_allowed(int token) const; + bool request_pseudo_header_allowed(int16_t token) const; bool expect_response_body() const; enum { INITIAL, @@ -192,16 +192,17 @@ public: // multiple header have |name| as name, return last occurrence from // the beginning. If no such header is found, returns nullptr. // This function must be called after response headers are indexed. - const Headers::value_type *get_response_header(int token) const; + const Headers::value_type *get_response_header(int16_t token) const; // Rewrites the location response header field. void rewrite_location_response_header(const std::string &upstream_scheme, uint16_t upstream_port); void add_response_header(std::string name, std::string value); void set_last_response_header_value(std::string value); + void add_response_header(std::string name, std::string value, int16_t token); void add_response_header(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, bool no_index, - int token); + int16_t token); bool get_response_header_key_prev() const; void append_last_response_header_key(const char *data, size_t len); @@ -248,7 +249,7 @@ public: void dec_response_datalen(size_t len); size_t get_response_datalen() const; void reset_response_datalen(); - bool response_pseudo_header_allowed(int token) const; + bool response_pseudo_header_allowed(int16_t token) const; // Call this method when there is incoming data in downstream // connection. diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index c66c57d4..51e84f33 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -823,7 +823,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream, // Otherwise, use chunked encoding to keep upstream connection // open. In HTTP2, we are supporsed not to receive // transfer-encoding. - downstream->add_response_header("transfer-encoding", "chunked"); + downstream->add_response_header("transfer-encoding", "chunked", + http2::HD_TRANSFER_ENCODING); downstream->set_chunked_response(true); } } diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 218daaba..c100266e 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -1521,8 +1521,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) { baselen = 1; } for (auto &kv : downstream->get_response_headers()) { - auto token = http2::lookup_token(kv.name); - if (token != http2::HD_LINK) { + if (kv.token != http2::HD_LINK) { continue; } for (auto &link : @@ -1588,8 +1587,7 @@ int Http2Upstream::submit_push_promise(const std::string &path, std::vector nva; nva.reserve(downstream->get_request_headers().size()); for (auto &kv : downstream->get_request_headers()) { - auto token = http2::lookup_token(kv.name); - switch (token) { + switch (kv.token) { case http2::HD__METHOD: // juse use "GET" for now nva.push_back(http2::make_nv_lc(":method", "GET")); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index ede2625b..89fdcaf7 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -860,8 +860,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { if (hd.name.empty() || hd.name.c_str()[0] == ':') { continue; } - auto token = http2::lookup_token(hd.name); - switch (token) { + switch (hd.token) { case http2::HD_CONNECTION: case http2::HD_KEEP_ALIVE: case http2::HD_PROXY_CONNECTION: