diff --git a/src/allocator.h b/src/allocator.h new file mode 100644 index 00000000..2f48f38b --- /dev/null +++ b/src/allocator.h @@ -0,0 +1,108 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 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 ALLOCATOR_H +#define ALLOCATOR_H + +#include "nghttp2_config.h" + +#include "template.h" + +namespace nghttp2 { + +struct MemBlock { + MemBlock *next; + uint8_t *begin, *last, *end; +}; + +template struct BlockAllocator { + BlockAllocator() : retain(nullptr), head(nullptr) {} + + ~BlockAllocator() { + for (auto mb = retain; mb;) { + auto next = mb->next; + delete[] reinterpret_cast(mb); + mb = next; + } + } + + MemBlock *alloc_mem_block(size_t size) { + auto block = new uint8_t[sizeof(MemBlock) + size]; + auto mb = reinterpret_cast(block); + + mb->next = retain; + mb->begin = mb->last = block + sizeof(MemBlock); + mb->end = mb->begin + size; + retain = mb; + return mb; + } + + void *alloc(size_t size) { + if (size >= BLOCK_SIZE) { + auto mb = alloc_mem_block(size); + mb->last = mb->end; + return mb->begin; + } + + if (!head || head->end - head->last < static_cast(size)) { + head = alloc_mem_block(BLOCK_SIZE); + } + + auto res = head->last; + + head->last = reinterpret_cast( + (reinterpret_cast(head->last + size) + 0xf) & ~0xf); + + return res; + } + + MemBlock *retain; + MemBlock *head; +}; + +template +StringRef make_string_ref(BlockAllocator &alloc, const StringRef &src) { + auto dst = static_cast(alloc.alloc(src.size() + 1)); + auto p = dst; + p = std::copy(std::begin(src), std::end(src), p); + *p = '\0'; + return StringRef{dst, src.size()}; +} + +template +StringRef concat_string_ref(BlockAllocator &alloc, const StringRef &a, + const StringRef &b) { + auto dst = static_cast(alloc.alloc(a.size() + b.size() + 1)); + auto p = dst; + p = std::copy(std::begin(a), std::end(a), p); + p = std::copy(std::begin(b), std::end(b), p); + *p = '\0'; + return StringRef{dst, a.size() + b.size()}; +} + +using DefaultBlockAllocator = BlockAllocator<1024>; + +} // namespace aria2 + +#endif // ALLOCATOR_H diff --git a/src/http2.cc b/src/http2.cc index bfad624e..17de9b05 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -238,7 +238,7 @@ const char *stringify_status(unsigned int status_code) { } } -void capitalize(DefaultMemchunks *buf, const std::string &s) { +void capitalize(DefaultMemchunks *buf, const StringRef &s) { buf->append(util::upcase(s[0])); for (size_t i = 1; i < s.size(); ++i) { if (s[i - 1] == '-') { @@ -302,14 +302,14 @@ const Headers::value_type *get_header(const Headers &nva, const char *name) { return res; } -std::string value_to_str(const Headers::value_type *nv) { +std::string value_to_str(const HeaderRefs::value_type *nv) { if (nv) { - return nv->value; + return nv->value.str(); } return ""; } -bool non_empty_value(const Headers::value_type *nv) { +bool non_empty_value(const HeaderRefs::value_type *nv) { return nv && !nv->value.empty(); } @@ -326,11 +326,29 @@ nghttp2_nv make_nv_internal(const std::string &name, const std::string &value, } } // namespace +namespace { +nghttp2_nv make_nv_internal(const StringRef &name, const StringRef &value, + bool no_index, uint8_t nv_flags) { + uint8_t flags; + + flags = + nv_flags | (no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE); + + return {(uint8_t *)name.c_str(), (uint8_t *)value.c_str(), name.size(), + value.size(), flags}; +} +} // namespace + nghttp2_nv make_nv(const std::string &name, const std::string &value, bool no_index) { return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE); } +nghttp2_nv make_nv(const StringRef &name, const StringRef &value, + bool no_index) { + return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE); +} + nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value, bool no_index) { return make_nv_internal(name, value, no_index, @@ -338,9 +356,16 @@ nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value, NGHTTP2_NV_FLAG_NO_COPY_VALUE); } +nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value, + bool no_index) { + return make_nv_internal(name, value, no_index, + NGHTTP2_NV_FLAG_NO_COPY_NAME | + NGHTTP2_NV_FLAG_NO_COPY_VALUE); +} + namespace { void copy_headers_to_nva_internal(std::vector &nva, - const Headers &headers, uint8_t nv_flags) { + const HeaderRefs &headers, uint8_t nv_flags) { for (auto &kv : headers) { if (kv.name.empty() || kv.name[0] == ':') { continue; @@ -367,18 +392,19 @@ void copy_headers_to_nva_internal(std::vector &nva, } } // namespace -void copy_headers_to_nva(std::vector &nva, const Headers &headers) { +void copy_headers_to_nva(std::vector &nva, + const HeaderRefs &headers) { copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE); } void copy_headers_to_nva_nocopy(std::vector &nva, - const Headers &headers) { + const HeaderRefs &headers) { copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE); } void build_http1_headers_from_headers(DefaultMemchunks *buf, - const Headers &headers) { + const HeaderRefs &headers) { for (auto &kv : headers) { if (kv.name.empty() || kv.name[0] == ':') { continue; @@ -450,8 +476,15 @@ void dump_nv(FILE *out, const Headers &nva) { fflush(out); } -std::string rewrite_location_uri(const std::string &uri, - const http_parser_url &u, +void dump_nv(FILE *out, const HeaderRefs &nva) { + for (auto &nv : nva) { + fprintf(out, "%s: %s\n", nv.name.c_str(), nv.value.c_str()); + } + fputc('\n', out); + fflush(out); +} + +std::string rewrite_location_uri(const StringRef &uri, const http_parser_url &u, const std::string &match_host, const std::string &request_authority, const std::string &upstream_scheme) { @@ -499,7 +532,7 @@ int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, return 1; } -int parse_http_status_code(const std::string &src) { +int parse_http_status_code(const StringRef &src) { if (src.size() != 3) { return -1; } @@ -525,6 +558,10 @@ int lookup_token(const std::string &name) { name.size()); } +int lookup_token(const StringRef &name) { + return lookup_token(name.byte(), name.size()); +} + // This function was generated by genheaderfunc.py. Inspired by h2o // header lookup. https://github.com/h2o/h2o int lookup_token(const uint8_t *name, size_t namelen) { @@ -1278,6 +1315,10 @@ int lookup_method_token(const std::string &name) { name.size()); } +int lookup_method_token(const StringRef &name) { + return lookup_method_token(name.byte(), name.size()); +} + // This function was generated by genmethodfunc.py. int lookup_method_token(const uint8_t *name, size_t namelen) { switch (namelen) { diff --git a/src/http2.h b/src/http2.h index 7a05dd45..b4e84e8b 100644 --- a/src/http2.h +++ b/src/http2.h @@ -39,6 +39,7 @@ #include "util.h" #include "memchunk.h" +#include "template.h" namespace nghttp2 { @@ -66,7 +67,29 @@ struct Header { bool no_index; }; +struct HeaderRef { + HeaderRef(const StringRef &name, const StringRef &value, + bool no_index = false, int32_t token = -1) + : name(name), value(value), token(token), no_index(no_index) {} + + HeaderRef() : token(-1), no_index(false) {} + + bool operator==(const HeaderRef &other) const { + return name == other.name && value == other.value; + } + + bool operator<(const HeaderRef &rhs) const { + return name < rhs.name || (name == rhs.name && value < rhs.value); + } + + StringRef name; + StringRef value; + int32_t token; + bool no_index; +}; + using Headers = std::vector
; +using HeaderRefs = std::vector; namespace http2 { @@ -76,7 +99,7 @@ std::string get_status_string(unsigned int status_code); // only predefined status code. Otherwise, returns nullptr. const char *stringify_status(unsigned int status_code); -void capitalize(DefaultMemchunks *buf, const std::string &s); +void capitalize(DefaultMemchunks *buf, const StringRef &s); // Returns true if |value| is LWS bool lws(const char *value); @@ -104,10 +127,10 @@ void add_header(Headers &nva, const uint8_t *name, size_t namelen, const Headers::value_type *get_header(const Headers &nva, const char *name); // Returns nv->second if nv is not nullptr. Otherwise, returns "". -std::string value_to_str(const Headers::value_type *nv); +std::string value_to_str(const HeaderRefs::value_type *nv); // Returns true if the value of |nv| is not empty. -bool non_empty_value(const Headers::value_type *nv); +bool non_empty_value(const HeaderRefs::value_type *nv); // Creates nghttp2_nv using |name| and |value| and returns it. The // returned value only references the data pointer to name.c_str() and @@ -116,9 +139,15 @@ bool non_empty_value(const Headers::value_type *nv); nghttp2_nv make_nv(const std::string &name, const std::string &value, bool no_index = false); +nghttp2_nv make_nv(const StringRef &name, const StringRef &value, + bool no_index = false); + nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value, bool no_index = false); +nghttp2_nv make_nv_nocopy(const StringRef &name, const StringRef &value, + bool no_index = false); + // Create nghttp2_nv from string literal |name| and |value|. template constexpr nghttp2_nv make_nv_ll(const char(&name)[N], const char(&value)[M]) { @@ -163,19 +192,20 @@ nghttp2_nv make_nv_ls_nocopy(const char(&name)[N], const StringRef &value) { // 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); +void copy_headers_to_nva(std::vector &nva, + const HeaderRefs &headers); // Just like copy_headers_to_nva(), but this adds // NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE. void copy_headers_to_nva_nocopy(std::vector &nva, - const Headers &headers); + const HeaderRefs &headers); // Appends HTTP/1.1 style header lines to |buf| from headers in // |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(DefaultMemchunks *buf, - const Headers &headers); + const HeaderRefs &headers); // Return positive window_size_increment if WINDOW_UPDATE should be // sent for the stream |stream_id|. If |stream_id| == 0, this function @@ -196,6 +226,8 @@ void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen); // Dumps name/value pairs in |nva| to |out|. void dump_nv(FILE *out, const Headers &nva); +void dump_nv(FILE *out, const HeaderRefs &nva); + // Rewrites redirection URI which usually appears in location header // field. The |uri| is the URI in the location header field. The |u| // stores the result of parsed |uri|. The |request_authority| is the @@ -209,8 +241,7 @@ void dump_nv(FILE *out, const Headers &nva); // This function returns the new rewritten URI on success. If the // location URI is not subject to the rewrite, this function returns // emtpy string. -std::string rewrite_location_uri(const std::string &uri, - const http_parser_url &u, +std::string rewrite_location_uri(const StringRef &uri, const http_parser_url &u, const std::string &match_host, const std::string &request_authority, const std::string &upstream_scheme); @@ -222,7 +253,7 @@ int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen); // Returns parsed HTTP status code. Returns -1 on failure. -int parse_http_status_code(const std::string &src); +int parse_http_status_code(const StringRef &src); // Header fields to be indexed, except HD_MAXIDX which is convenient // member to get maximum value. @@ -272,6 +303,7 @@ using HeaderIndex = std::array; // cannot be tokenized, returns -1. int lookup_token(const uint8_t *name, size_t namelen); int lookup_token(const std::string &name); +int lookup_token(const StringRef &name); // Initializes |hdidx|, header index. The |hdidx| must point to the // array containing at least HD_MAXIDX elements. @@ -318,6 +350,7 @@ bool expect_response_body(int status_code); // tokenized. If method name cannot be tokenized, returns -1. int lookup_method_token(const uint8_t *name, size_t namelen); int lookup_method_token(const std::string &name); +int lookup_method_token(const StringRef &name); // Returns string representation of |method_token|. This is wrapper // function over http_method_str from http-parser. If |method_token| diff --git a/src/nghttp.cc b/src/nghttp.cc index af349519..d0fc8ece 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -1600,7 +1600,7 @@ void check_response_header(nghttp2_session *session, Request *req) { return; } - auto status = http2::parse_http_status_code(status_hd->value); + auto status = http2::parse_http_status_code(StringRef{status_hd->value}); if (status == -1) { nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, NGHTTP2_PROTOCOL_ERROR); diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index 0a8da837..11462d7c 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -116,6 +116,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, : dlnext(nullptr), dlprev(nullptr), response_sent_body_length(0), + req_(balloc_), + resp_(balloc_), request_start_time_(std::chrono::high_resolution_clock::now()), request_buf_(mcpool), response_buf_(mcpool), @@ -237,8 +239,9 @@ void Downstream::force_resume_read() { } namespace { -const Headers::value_type * -search_header_linear_backwards(const Headers &headers, const StringRef &name) { +const HeaderRefs::value_type * +search_header_linear_backwards(const HeaderRefs &headers, + const StringRef &name) { for (auto it = headers.rbegin(); it != headers.rend(); ++it) { auto &kv = *it; if (kv.name == name) { @@ -258,11 +261,25 @@ std::string Downstream::assemble_request_cookie() const { continue; } - auto end = kv.value.find_last_not_of(" ;"); - if (end == std::string::npos) { + if (kv.value.empty()) { + continue; + } + + auto end = std::end(kv.value); + for (auto it = std::begin(kv.value) + kv.value.size(); + it != std::begin(kv.value); --it) { + auto c = *(it - 1); + if (c == ' ' || c == ';') { + continue; + } + end = it + 1; + break; + } + + if (end == std::end(kv.value)) { cookie += kv.value; } else { - cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1); + cookie.append(std::begin(kv.value), end); } cookie += "; "; } @@ -280,18 +297,14 @@ size_t Downstream::count_crumble_request_cookie() { !util::streq_l("cooki", kv.name.c_str(), 5)) { continue; } - size_t last = kv.value.size(); - for (size_t j = 0; j < last;) { - j = kv.value.find_first_not_of("\t ;", j); - if (j == std::string::npos) { - break; + for (auto it = std::begin(kv.value); it != std::end(kv.value);) { + if (*it == '\t' || *it == ' ' || *it == ';') { + ++it; + continue; } - j = kv.value.find(';', j); - if (j == std::string::npos) { - j = last; - } + it = std::find(it, std::end(kv.value), ';'); ++n; } @@ -305,22 +318,19 @@ void Downstream::crumble_request_cookie(std::vector &nva) { !util::streq_l("cooki", kv.name.c_str(), 5)) { continue; } - size_t last = kv.value.size(); - for (size_t j = 0; j < last;) { - j = kv.value.find_first_not_of("\t ;", j); - if (j == std::string::npos) { - break; - } - auto first = j; - - j = kv.value.find(';', j); - if (j == std::string::npos) { - j = last; + for (auto it = std::begin(kv.value); it != std::end(kv.value);) { + if (*it == '\t' || *it == ' ' || *it == ';') { + ++it; + continue; } - nva.push_back({(uint8_t *)"cookie", (uint8_t *)kv.value.c_str() + first, - str_size("cookie"), j - first, + auto first = it; + + it = std::find(it, std::end(kv.value), ';'); + + nva.push_back({(uint8_t *)"cookie", (uint8_t *)first, str_size("cookie"), + (size_t)(it - first), (uint8_t)(NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE | (kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))}); @@ -329,42 +339,45 @@ void Downstream::crumble_request_cookie(std::vector &nva) { } namespace { -void add_header(bool &key_prev, size_t &sum, Headers &headers, +void add_header(bool &key_prev, size_t &sum, HeaderRefs &headers, const StringRef &name, const StringRef &value, bool no_index, int32_t token) { key_prev = true; sum += name.size() + value.size(); - headers.emplace_back(name.str(), value.str(), no_index, token); + headers.emplace_back(name, value, no_index, token); } } // namespace namespace { -void add_header(size_t &sum, Headers &headers, const StringRef &name, +void add_header(size_t &sum, HeaderRefs &headers, const StringRef &name, const StringRef &value, bool no_index, int32_t token) { sum += name.size() + value.size(); - headers.emplace_back(name.str(), value.str(), no_index, token); + headers.emplace_back(name, value, no_index, token); } } // namespace namespace { -void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers, - const char *data, size_t len) { +void append_last_header_key(DefaultBlockAllocator &balloc, bool &key_prev, + size_t &sum, HeaderRefs &headers, const char *data, + size_t len) { assert(key_prev); sum += len; auto &item = headers.back(); - item.name.append(data, len); - util::inp_strlower(item.name); + item.name = concat_string_ref(balloc, item.name, StringRef{data, len}); + util::inp_strlower((uint8_t *)item.name.c_str(), + (uint8_t *)item.name.c_str() + item.name.size()); item.token = http2::lookup_token(item.name); } } // namespace namespace { -void append_last_header_value(bool &key_prev, size_t &sum, Headers &headers, +void append_last_header_value(DefaultBlockAllocator &balloc, bool &key_prev, + size_t &sum, HeaderRefs &headers, const char *data, size_t len) { key_prev = false; sum += len; auto &item = headers.back(); - item.value.append(data, len); + item.value = concat_string_ref(balloc, item.value, StringRef{data, len}); } } // namespace @@ -388,7 +401,7 @@ int FieldStore::parse_content_length() { return 0; } -const Headers::value_type *FieldStore::header(int32_t token) const { +const HeaderRefs::value_type *FieldStore::header(int32_t token) const { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) { auto &kv = *it; if (kv.token == token) { @@ -398,7 +411,7 @@ const Headers::value_type *FieldStore::header(int32_t token) const { return nullptr; } -Headers::value_type *FieldStore::header(int32_t token) { +HeaderRefs::value_type *FieldStore::header(int32_t token) { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) { auto &kv = *it; if (kv.token == token) { @@ -408,43 +421,47 @@ Headers::value_type *FieldStore::header(int32_t token) { return nullptr; } -const Headers::value_type *FieldStore::header(const StringRef &name) const { +const HeaderRefs::value_type *FieldStore::header(const StringRef &name) const { return search_header_linear_backwards(headers_, name); } void FieldStore::add_header_lower(const StringRef &name, const StringRef &value, bool no_index) { - auto low_name = name.str(); - util::inp_strlower(low_name); + auto low_name = make_string_ref(balloc_, name); + util::inp_strlower((uint8_t *)low_name.c_str(), + (uint8_t *)low_name.c_str() + low_name.size()); auto token = http2::lookup_token(low_name); - shrpx::add_header(header_key_prev_, buffer_size_, headers_, - StringRef{low_name}, value, no_index, token); + shrpx::add_header(header_key_prev_, buffer_size_, headers_, low_name, + make_string_ref(balloc_, value), no_index, token); } void FieldStore::add_header_token(const StringRef &name, const StringRef &value, bool no_index, int32_t token) { - shrpx::add_header(buffer_size_, headers_, name, value, no_index, token); + shrpx::add_header(buffer_size_, headers_, make_string_ref(balloc_, name), + make_string_ref(balloc_, value), no_index, token); + make_string_ref(balloc_, name); } void FieldStore::append_last_header_key(const char *data, size_t len) { - shrpx::append_last_header_key(header_key_prev_, buffer_size_, headers_, data, - len); + shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_, + headers_, data, len); } void FieldStore::append_last_header_value(const char *data, size_t len) { - shrpx::append_last_header_value(header_key_prev_, buffer_size_, headers_, - data, len); + shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_, + headers_, data, len); } void FieldStore::clear_headers() { headers_.clear(); } void FieldStore::add_trailer_lower(const StringRef &name, const StringRef &value, bool no_index) { - auto low_name = name.str(); - util::inp_strlower(low_name); + auto low_name = make_string_ref(balloc_, name); + util::inp_strlower((uint8_t *)low_name.c_str(), + (uint8_t *)low_name.c_str() + low_name.size()); auto token = http2::lookup_token(low_name); - shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, - StringRef{low_name}, value, no_index, token); + shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, low_name, + make_string_ref(balloc_, value), no_index, token); } void FieldStore::add_trailer_token(const StringRef &name, @@ -452,17 +469,18 @@ void FieldStore::add_trailer_token(const StringRef &name, int32_t token) { // Header size limit should be applied to all header and trailer // fields combined. - shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token); + shrpx::add_header(buffer_size_, trailers_, make_string_ref(balloc_, name), + make_string_ref(balloc_, value), no_index, token); } void FieldStore::append_last_trailer_key(const char *data, size_t len) { - shrpx::append_last_header_key(trailer_key_prev_, buffer_size_, trailers_, - data, len); + shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_, + trailers_, data, len); } void FieldStore::append_last_trailer_value(const char *data, size_t len) { - shrpx::append_last_header_value(trailer_key_prev_, buffer_size_, trailers_, - data, len); + shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_, + trailers_, data, len); } void Downstream::set_request_start_time( @@ -566,7 +584,7 @@ void Downstream::rewrite_location_response_header( return; } - hd->value = std::move(new_uri); + hd->value = make_string_ref(balloc_, StringRef{new_uri}); } bool Downstream::get_chunked_response() const { return chunked_response_; } @@ -703,10 +721,10 @@ bool Downstream::get_http2_upgrade_request() const { response_state_ == INITIAL; } -const std::string &Downstream::get_http2_settings() const { +StringRef Downstream::get_http2_settings() const { auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS); if (!http2_settings) { - return EMPTY_STRING; + return StringRef{}; } return http2_settings->value; } @@ -908,4 +926,6 @@ void Downstream::set_assoc_stream_id(int32_t stream_id) { int32_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; } +DefaultBlockAllocator &Downstream::get_block_allocator() { return balloc_; } + } // namespace shrpx diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index 3864674f..7b9dfa83 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -40,6 +40,7 @@ #include "shrpx_io_control.h" #include "http2.h" #include "memchunk.h" +#include "allocator.h" using namespace nghttp2; @@ -51,18 +52,19 @@ struct BlockedLink; class FieldStore { public: - FieldStore(size_t headers_initial_capacity) + FieldStore(DefaultBlockAllocator &balloc, size_t headers_initial_capacity) : content_length(-1), + balloc_(balloc), buffer_size_(0), header_key_prev_(false), trailer_key_prev_(false) { headers_.reserve(headers_initial_capacity); } - const Headers &headers() const { return headers_; } - const Headers &trailers() const { return trailers_; } + const HeaderRefs &headers() const { return headers_; } + const HeaderRefs &trailers() const { return trailers_; } - Headers &headers() { return headers_; } + HeaderRefs &headers() { return headers_; } const void add_extra_buffer_size(size_t n) { buffer_size_ += n; } size_t buffer_size() const { return buffer_size_; } @@ -73,11 +75,11 @@ 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 *header(int32_t token) const; - Headers::value_type *header(int32_t token); + const HeaderRefs::value_type *header(int32_t token) const; + HeaderRefs::value_type *header(int32_t token); // Returns pointer to the header field with the name |name|. If no // such header is found, returns nullptr. - const Headers::value_type *header(const StringRef &name) const; + const HeaderRefs::value_type *header(const StringRef &name) const; void add_header_lower(const StringRef &name, const StringRef &value, bool no_index); @@ -110,10 +112,11 @@ public: int64_t content_length; private: - Headers headers_; + DefaultBlockAllocator &balloc_; + HeaderRefs headers_; // trailer fields. For HTTP/1.1, trailer fields are only included // with chunked encoding. For HTTP/2, there is no such limit. - Headers trailers_; + HeaderRefs trailers_; // Sum of the length of name and value in headers_ and trailers_. // This could also be increased by add_extra_buffer_size() to take // into account for request URI in case of HTTP/1.x request. @@ -123,8 +126,8 @@ private: }; struct Request { - Request() - : fs(16), + Request(DefaultBlockAllocator &balloc) + : fs(balloc, 16), recv_body_length(0), unconsumed_body_length(0), method(-1), @@ -179,8 +182,8 @@ struct Request { }; struct Response { - Response() - : fs(32), + Response(DefaultBlockAllocator &balloc) + : fs(balloc, 32), recv_body_length(0), unconsumed_body_length(0), http_status(0), @@ -245,7 +248,7 @@ public: // Returns true if the request is HTTP Upgrade for HTTP/2 bool get_http2_upgrade_request() const; // Returns the value of HTTP2-Settings request header field. - const std::string &get_http2_settings() const; + StringRef get_http2_settings() const; // downstream request API const Request &request() const { return req_; } @@ -373,6 +376,8 @@ public: DefaultMemchunks pop_response_buf(); + DefaultBlockAllocator &get_block_allocator(); + enum { EVENT_ERROR = 0x1, EVENT_TIMEOUT = 0x2, @@ -392,6 +397,8 @@ public: int64_t response_sent_body_length; private: + DefaultBlockAllocator balloc_; + Request req_; Response resp_; diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 028b6e41..357bd664 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -364,7 +364,7 @@ int Http2DownstreamConnection::push_request_headers() { StringRef{req.authority}, StringRef{req.scheme}); if (fwd || !value.empty()) { if (fwd) { - forwarded_value = fwd->value; + forwarded_value = fwd->value.str(); if (!value.empty()) { forwarded_value += ", "; @@ -377,7 +377,7 @@ int Http2DownstreamConnection::push_request_headers() { } } else if (fwd) { nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value)); - forwarded_value = fwd->value; + forwarded_value = fwd->value.str(); } auto &xffconf = httpconf.xff; @@ -389,7 +389,7 @@ int Http2DownstreamConnection::push_request_headers() { if (xffconf.add) { if (xff) { - xff_value = (*xff).value; + xff_value = (*xff).value.str(); xff_value += ", "; } xff_value += upstream->get_client_handler()->get_ipaddr(); @@ -411,7 +411,7 @@ int Http2DownstreamConnection::push_request_headers() { } } else { if (via) { - via_value = (*via).value; + via_value = (*via).value.str(); via_value += ", "; } via_value += http::create_via_header_value(req.http_major, req.http_minor); diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 9d42fe3a..cf89bf3e 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -108,7 +108,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { int rv; - auto http2_settings = http->get_downstream()->get_http2_settings(); + auto http2_settings = http->get_downstream()->get_http2_settings().str(); util::to_base64(http2_settings); auto settings_payload = @@ -1434,7 +1434,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { } } else { if (via) { - via_value = (*via).value; + via_value = (*via).value.str(); via_value += ", "; } via_value += diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 49c9848e..92994207 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -325,7 +325,7 @@ int htp_hdrs_completecb(http_parser *htp) { } if (host) { - req.authority = host->value; + req.authority = host->value.str(); } if (handler->get_ssl()) { diff --git a/src/shrpx_mruby_module.cc b/src/shrpx_mruby_module.cc index e2f17fb1..27b7769c 100644 --- a/src/shrpx_mruby_module.cc +++ b/src/shrpx_mruby_module.cc @@ -85,7 +85,7 @@ mrb_value init_module(mrb_state *mrb) { return create_env(mrb); } -mrb_value create_headers_hash(mrb_state *mrb, const Headers &headers) { +mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers) { auto hash = mrb_hash_new(mrb); for (auto &hd : headers) { diff --git a/src/shrpx_mruby_module.h b/src/shrpx_mruby_module.h index 3e417f88..a426bead 100644 --- a/src/shrpx_mruby_module.h +++ b/src/shrpx_mruby_module.h @@ -43,7 +43,7 @@ mrb_value init_module(mrb_state *mrb); void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream); -mrb_value create_headers_hash(mrb_state *mrb, const Headers &headers); +mrb_value create_headers_hash(mrb_state *mrb, const HeaderRefs &headers); } // namespace mruby diff --git a/src/shrpx_mruby_module_response.cc b/src/shrpx_mruby_module_response.cc index 73dacccc..c50391e1 100644 --- a/src/shrpx_mruby_module_response.cc +++ b/src/shrpx_mruby_module_response.cc @@ -209,7 +209,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) { auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH); if (cl) { - cl->value = util::utos(bodylen); + cl->value = make_string_ref(downstream->get_block_allocator(), + StringRef{util::utos(bodylen)}); } else { resp.fs.add_header_token(StringRef::from_lit("content-length"), StringRef{util::utos(bodylen)}, false, diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index e15444a4..e5e1a096 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -262,12 +262,12 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, req.method = method_token; if (is_connect) { - req.authority = path->value; + req.authority = path->value.str(); } else { - req.scheme = scheme->value; - req.authority = host->value; + req.scheme = scheme->value.str(); + req.authority = host->value.str(); if (get_config()->http2_proxy) { - req.path = path->value; + req.path = path->value.str(); } else if (method_token == HTTP_OPTIONS && path->value == "*") { // Server-wide OPTIONS request. Path is empty. } else { @@ -1069,7 +1069,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { } } else { if (via) { - via_value = via->value; + via_value = via->value.str(); via_value += ", "; } via_value += diff --git a/src/template.h b/src/template.h index 6adcf0d5..9c52387f 100644 --- a/src/template.h +++ b/src/template.h @@ -443,6 +443,11 @@ private: size_type len; }; +inline bool operator==(const StringRef &lhs, const StringRef &rhs) { + return lhs.size() == rhs.size() && + std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); +} + inline bool operator==(const StringRef &lhs, const std::string &rhs) { return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); @@ -477,6 +482,11 @@ inline bool operator!=(const char *lhs, const StringRef &rhs) { return !(rhs == lhs); } +inline bool operator<(const StringRef &lhs, const StringRef &rhs) { + return std::lexicographical_compare(std::begin(lhs), std::end(lhs), + std::begin(rhs), std::end(rhs)); +} + inline std::ostream &operator<<(std::ostream &o, const StringRef &s) { return o.write(s.c_str(), s.size()); } diff --git a/src/util.cc b/src/util.cc index 784806be..90304315 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1056,6 +1056,10 @@ int64_t parse_uint(const std::string &s) { return parse_uint(reinterpret_cast(s.c_str()), s.size()); } +int64_t parse_uint(const StringRef &s) { + return parse_uint(s.byte(), s.size()); +} + int64_t parse_uint(const uint8_t *s, size_t len) { int64_t n; size_t i; diff --git a/src/util.h b/src/util.h index d03ffd8e..ba3e1277 100644 --- a/src/util.h +++ b/src/util.h @@ -261,6 +261,11 @@ bool iends_with_l(const std::string &a, const CharT(&b)[N]) { return iends_with(std::begin(a), std::end(a), b, b + N - 1); } +template +bool iends_with_l(const StringRef &a, const CharT(&b)[N]) { + return iends_with(std::begin(a), std::end(a), b, b + N - 1); +} + int strcompare(const char *a, const uint8_t *b, size_t n); template bool strieq(const char *a, InputIt b, size_t bn) { @@ -310,6 +315,11 @@ bool strieq_l(const CharT(&a)[N], const std::string &b) { return strieq(a, N - 1, std::begin(b), b.size()); } +template +bool strieq_l(const CharT(&a)[N], const StringRef &b) { + return strieq(a, N - 1, std::begin(b), b.size()); +} + template bool streq(const char *a, InputIt b, size_t bn) { if (!a) { return false; @@ -609,6 +619,7 @@ int64_t parse_uint_with_unit(const char *s); int64_t parse_uint(const char *s); int64_t parse_uint(const uint8_t *s, size_t len); int64_t parse_uint(const std::string &s); +int64_t parse_uint(const StringRef &s); // Parses NULL terminated string |s| as unsigned integer and returns // the parsed integer casted to double. If |s| ends with "s", the