nghttpx: Add custom memory allocator mainly for header related objects

This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-09 21:15:32 +09:00
parent 907eeeda8a
commit bae37e3e4a
16 changed files with 346 additions and 111 deletions

108
src/allocator.h Normal file
View File

@ -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 <size_t BLOCK_SIZE> struct BlockAllocator {
BlockAllocator() : retain(nullptr), head(nullptr) {}
~BlockAllocator() {
for (auto mb = retain; mb;) {
auto next = mb->next;
delete[] reinterpret_cast<uint8_t *>(mb);
mb = next;
}
}
MemBlock *alloc_mem_block(size_t size) {
auto block = new uint8_t[sizeof(MemBlock) + size];
auto mb = reinterpret_cast<MemBlock *>(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<ssize_t>(size)) {
head = alloc_mem_block(BLOCK_SIZE);
}
auto res = head->last;
head->last = reinterpret_cast<uint8_t *>(
(reinterpret_cast<intptr_t>(head->last + size) + 0xf) & ~0xf);
return res;
}
MemBlock *retain;
MemBlock *head;
};
template <typename BlockAllocator>
StringRef make_string_ref(BlockAllocator &alloc, const StringRef &src) {
auto dst = static_cast<uint8_t *>(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 <typename BlockAllocator>
StringRef concat_string_ref(BlockAllocator &alloc, const StringRef &a,
const StringRef &b) {
auto dst = static_cast<uint8_t *>(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

View File

@ -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])); buf->append(util::upcase(s[0]));
for (size_t i = 1; i < s.size(); ++i) { for (size_t i = 1; i < s.size(); ++i) {
if (s[i - 1] == '-') { if (s[i - 1] == '-') {
@ -302,14 +302,14 @@ const Headers::value_type *get_header(const Headers &nva, const char *name) {
return res; return res;
} }
std::string value_to_str(const Headers::value_type *nv) { std::string value_to_str(const HeaderRefs::value_type *nv) {
if (nv) { if (nv) {
return nv->value; return nv->value.str();
} }
return ""; return "";
} }
bool non_empty_value(const Headers::value_type *nv) { bool non_empty_value(const HeaderRefs::value_type *nv) {
return nv && !nv->value.empty(); return nv && !nv->value.empty();
} }
@ -326,11 +326,29 @@ nghttp2_nv make_nv_internal(const std::string &name, const std::string &value,
} }
} // namespace } // 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, nghttp2_nv make_nv(const std::string &name, const std::string &value,
bool no_index) { bool no_index) {
return make_nv_internal(name, value, no_index, NGHTTP2_NV_FLAG_NONE); 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, nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
bool no_index) { bool no_index) {
return make_nv_internal(name, value, 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_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 { namespace {
void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva, void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
const Headers &headers, uint8_t nv_flags) { const HeaderRefs &headers, uint8_t nv_flags) {
for (auto &kv : headers) { for (auto &kv : headers) {
if (kv.name.empty() || kv.name[0] == ':') { if (kv.name.empty() || kv.name[0] == ':') {
continue; continue;
@ -367,18 +392,19 @@ void copy_headers_to_nva_internal(std::vector<nghttp2_nv> &nva,
} }
} // namespace } // namespace
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) { void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers) {
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE); copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NONE);
} }
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva, void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
const Headers &headers) { const HeaderRefs &headers) {
copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME | copy_headers_to_nva_internal(nva, headers, NGHTTP2_NV_FLAG_NO_COPY_NAME |
NGHTTP2_NV_FLAG_NO_COPY_VALUE); NGHTTP2_NV_FLAG_NO_COPY_VALUE);
} }
void build_http1_headers_from_headers(DefaultMemchunks *buf, void build_http1_headers_from_headers(DefaultMemchunks *buf,
const Headers &headers) { const HeaderRefs &headers) {
for (auto &kv : headers) { for (auto &kv : headers) {
if (kv.name.empty() || kv.name[0] == ':') { if (kv.name.empty() || kv.name[0] == ':') {
continue; continue;
@ -450,8 +476,15 @@ void dump_nv(FILE *out, const Headers &nva) {
fflush(out); fflush(out);
} }
std::string rewrite_location_uri(const std::string &uri, void dump_nv(FILE *out, const HeaderRefs &nva) {
const http_parser_url &u, 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 &match_host,
const std::string &request_authority, const std::string &request_authority,
const std::string &upstream_scheme) { 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; return 1;
} }
int parse_http_status_code(const std::string &src) { int parse_http_status_code(const StringRef &src) {
if (src.size() != 3) { if (src.size() != 3) {
return -1; return -1;
} }
@ -525,6 +558,10 @@ int lookup_token(const std::string &name) {
name.size()); 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 // This function was generated by genheaderfunc.py. Inspired by h2o
// header lookup. https://github.com/h2o/h2o // header lookup. https://github.com/h2o/h2o
int lookup_token(const uint8_t *name, size_t namelen) { int lookup_token(const uint8_t *name, size_t namelen) {
@ -1278,6 +1315,10 @@ int lookup_method_token(const std::string &name) {
name.size()); name.size());
} }
int lookup_method_token(const StringRef &name) {
return lookup_method_token(name.byte(), name.size());
}
// This function was generated by genmethodfunc.py. // This function was generated by genmethodfunc.py.
int lookup_method_token(const uint8_t *name, size_t namelen) { int lookup_method_token(const uint8_t *name, size_t namelen) {
switch (namelen) { switch (namelen) {

View File

@ -39,6 +39,7 @@
#include "util.h" #include "util.h"
#include "memchunk.h" #include "memchunk.h"
#include "template.h"
namespace nghttp2 { namespace nghttp2 {
@ -66,7 +67,29 @@ struct Header {
bool no_index; 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<Header>; using Headers = std::vector<Header>;
using HeaderRefs = std::vector<HeaderRef>;
namespace http2 { namespace http2 {
@ -76,7 +99,7 @@ std::string get_status_string(unsigned int status_code);
// only predefined status code. Otherwise, returns nullptr. // only predefined status code. Otherwise, returns nullptr.
const char *stringify_status(unsigned int status_code); 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 // Returns true if |value| is LWS
bool lws(const char *value); 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); const Headers::value_type *get_header(const Headers &nva, const char *name);
// Returns nv->second if nv is not nullptr. Otherwise, returns "". // 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. // 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 // Creates nghttp2_nv using |name| and |value| and returns it. The
// returned value only references the data pointer to name.c_str() and // 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, nghttp2_nv make_nv(const std::string &name, const std::string &value,
bool no_index = false); 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, nghttp2_nv make_nv_nocopy(const std::string &name, const std::string &value,
bool no_index = false); 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|. // Create nghttp2_nv from string literal |name| and |value|.
template <size_t N, size_t M> template <size_t N, size_t M>
constexpr nghttp2_nv make_nv_ll(const char(&name)[N], const char(&value)[M]) { 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 // before this call (its element's token field is assigned). Certain
// headers, including disallowed headers in HTTP/2 spec and headers // headers, including disallowed headers in HTTP/2 spec and headers
// which require special handling (i.e. via), are not copied. // which require special handling (i.e. via), are not copied.
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers); void copy_headers_to_nva(std::vector<nghttp2_nv> &nva,
const HeaderRefs &headers);
// Just like copy_headers_to_nva(), but this adds // Just like copy_headers_to_nva(), but this adds
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE. // NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva, void copy_headers_to_nva_nocopy(std::vector<nghttp2_nv> &nva,
const Headers &headers); const HeaderRefs &headers);
// Appends HTTP/1.1 style header lines to |buf| from headers in // Appends HTTP/1.1 style header lines to |buf| from headers in
// |headers|. |headers| must be indexed before this call (its // |headers|. |headers| must be indexed before this call (its
// element's token field is assigned). Certain headers, which // element's token field is assigned). Certain headers, which
// requires special handling (i.e. via and cookie), are not appended. // requires special handling (i.e. via and cookie), are not appended.
void build_http1_headers_from_headers(DefaultMemchunks *buf, void build_http1_headers_from_headers(DefaultMemchunks *buf,
const Headers &headers); const HeaderRefs &headers);
// Return positive window_size_increment if WINDOW_UPDATE should be // Return positive window_size_increment if WINDOW_UPDATE should be
// sent for the stream |stream_id|. If |stream_id| == 0, this function // 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|. // Dumps name/value pairs in |nva| to |out|.
void dump_nv(FILE *out, const Headers &nva); 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 // Rewrites redirection URI which usually appears in location header
// field. The |uri| is the URI in the location header field. The |u| // field. The |uri| is the URI in the location header field. The |u|
// stores the result of parsed |uri|. The |request_authority| is the // 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 // This function returns the new rewritten URI on success. If the
// location URI is not subject to the rewrite, this function returns // location URI is not subject to the rewrite, this function returns
// emtpy string. // emtpy string.
std::string rewrite_location_uri(const std::string &uri, std::string rewrite_location_uri(const StringRef &uri, const http_parser_url &u,
const http_parser_url &u,
const std::string &match_host, const std::string &match_host,
const std::string &request_authority, const std::string &request_authority,
const std::string &upstream_scheme); 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); size_t valuelen);
// Returns parsed HTTP status code. Returns -1 on failure. // 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 // Header fields to be indexed, except HD_MAXIDX which is convenient
// member to get maximum value. // member to get maximum value.
@ -272,6 +303,7 @@ using HeaderIndex = std::array<int16_t, HD_MAXIDX>;
// cannot be tokenized, returns -1. // cannot be tokenized, returns -1.
int lookup_token(const uint8_t *name, size_t namelen); int lookup_token(const uint8_t *name, size_t namelen);
int lookup_token(const std::string &name); int lookup_token(const std::string &name);
int lookup_token(const StringRef &name);
// Initializes |hdidx|, header index. The |hdidx| must point to the // Initializes |hdidx|, header index. The |hdidx| must point to the
// array containing at least HD_MAXIDX elements. // 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. // 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 uint8_t *name, size_t namelen);
int lookup_method_token(const std::string &name); int lookup_method_token(const std::string &name);
int lookup_method_token(const StringRef &name);
// Returns string representation of |method_token|. This is wrapper // Returns string representation of |method_token|. This is wrapper
// function over http_method_str from http-parser. If |method_token| // function over http_method_str from http-parser. If |method_token|

View File

@ -1600,7 +1600,7 @@ void check_response_header(nghttp2_session *session, Request *req) {
return; 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) { if (status == -1) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id, nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);

View File

@ -116,6 +116,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
: dlnext(nullptr), : dlnext(nullptr),
dlprev(nullptr), dlprev(nullptr),
response_sent_body_length(0), response_sent_body_length(0),
req_(balloc_),
resp_(balloc_),
request_start_time_(std::chrono::high_resolution_clock::now()), request_start_time_(std::chrono::high_resolution_clock::now()),
request_buf_(mcpool), request_buf_(mcpool),
response_buf_(mcpool), response_buf_(mcpool),
@ -237,8 +239,9 @@ void Downstream::force_resume_read() {
} }
namespace { namespace {
const Headers::value_type * const HeaderRefs::value_type *
search_header_linear_backwards(const Headers &headers, const StringRef &name) { search_header_linear_backwards(const HeaderRefs &headers,
const StringRef &name) {
for (auto it = headers.rbegin(); it != headers.rend(); ++it) { for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
auto &kv = *it; auto &kv = *it;
if (kv.name == name) { if (kv.name == name) {
@ -258,11 +261,25 @@ std::string Downstream::assemble_request_cookie() const {
continue; continue;
} }
auto end = kv.value.find_last_not_of(" ;"); if (kv.value.empty()) {
if (end == std::string::npos) { 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; cookie += kv.value;
} else { } else {
cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1); cookie.append(std::begin(kv.value), end);
} }
cookie += "; "; cookie += "; ";
} }
@ -280,18 +297,14 @@ size_t Downstream::count_crumble_request_cookie() {
!util::streq_l("cooki", kv.name.c_str(), 5)) { !util::streq_l("cooki", kv.name.c_str(), 5)) {
continue; continue;
} }
size_t last = kv.value.size();
for (size_t j = 0; j < last;) { for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
j = kv.value.find_first_not_of("\t ;", j); if (*it == '\t' || *it == ' ' || *it == ';') {
if (j == std::string::npos) { ++it;
break; continue;
} }
j = kv.value.find(';', j); it = std::find(it, std::end(kv.value), ';');
if (j == std::string::npos) {
j = last;
}
++n; ++n;
} }
@ -305,22 +318,19 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
!util::streq_l("cooki", kv.name.c_str(), 5)) { !util::streq_l("cooki", kv.name.c_str(), 5)) {
continue; continue;
} }
size_t last = kv.value.size();
for (size_t j = 0; j < last;) { for (auto it = std::begin(kv.value); it != std::end(kv.value);) {
j = kv.value.find_first_not_of("\t ;", j); if (*it == '\t' || *it == ' ' || *it == ';') {
if (j == std::string::npos) { ++it;
break; continue;
}
auto first = j;
j = kv.value.find(';', j);
if (j == std::string::npos) {
j = last;
} }
nva.push_back({(uint8_t *)"cookie", (uint8_t *)kv.value.c_str() + first, auto first = it;
str_size("cookie"), j - first,
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 | (uint8_t)(NGHTTP2_NV_FLAG_NO_COPY_NAME |
NGHTTP2_NV_FLAG_NO_COPY_VALUE | NGHTTP2_NV_FLAG_NO_COPY_VALUE |
(kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))}); (kv.no_index ? NGHTTP2_NV_FLAG_NO_INDEX : 0))});
@ -329,42 +339,45 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
} }
namespace { 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, const StringRef &name, const StringRef &value, bool no_index,
int32_t token) { int32_t token) {
key_prev = true; key_prev = true;
sum += name.size() + value.size(); 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
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) { const StringRef &value, bool no_index, int32_t token) {
sum += name.size() + value.size(); 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
namespace { namespace {
void append_last_header_key(bool &key_prev, size_t &sum, Headers &headers, void append_last_header_key(DefaultBlockAllocator &balloc, bool &key_prev,
const char *data, size_t len) { size_t &sum, HeaderRefs &headers, const char *data,
size_t len) {
assert(key_prev); assert(key_prev);
sum += len; sum += len;
auto &item = headers.back(); auto &item = headers.back();
item.name.append(data, len); item.name = concat_string_ref(balloc, item.name, StringRef{data, len});
util::inp_strlower(item.name); 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); item.token = http2::lookup_token(item.name);
} }
} // namespace } // namespace
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) { const char *data, size_t len) {
key_prev = false; key_prev = false;
sum += len; sum += len;
auto &item = headers.back(); auto &item = headers.back();
item.value.append(data, len); item.value = concat_string_ref(balloc, item.value, StringRef{data, len});
} }
} // namespace } // namespace
@ -388,7 +401,7 @@ int FieldStore::parse_content_length() {
return 0; 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) { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
auto &kv = *it; auto &kv = *it;
if (kv.token == token) { if (kv.token == token) {
@ -398,7 +411,7 @@ const Headers::value_type *FieldStore::header(int32_t token) const {
return nullptr; 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) { for (auto it = headers_.rbegin(); it != headers_.rend(); ++it) {
auto &kv = *it; auto &kv = *it;
if (kv.token == token) { if (kv.token == token) {
@ -408,43 +421,47 @@ Headers::value_type *FieldStore::header(int32_t token) {
return nullptr; 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); return search_header_linear_backwards(headers_, name);
} }
void FieldStore::add_header_lower(const StringRef &name, const StringRef &value, void FieldStore::add_header_lower(const StringRef &name, const StringRef &value,
bool no_index) { bool no_index) {
auto low_name = name.str(); auto low_name = make_string_ref(balloc_, name);
util::inp_strlower(low_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); auto token = http2::lookup_token(low_name);
shrpx::add_header(header_key_prev_, buffer_size_, headers_, shrpx::add_header(header_key_prev_, buffer_size_, headers_, low_name,
StringRef{low_name}, value, no_index, token); make_string_ref(balloc_, value), no_index, token);
} }
void FieldStore::add_header_token(const StringRef &name, const StringRef &value, void FieldStore::add_header_token(const StringRef &name, const StringRef &value,
bool no_index, int32_t token) { 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) { void FieldStore::append_last_header_key(const char *data, size_t len) {
shrpx::append_last_header_key(header_key_prev_, buffer_size_, headers_, data, shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_,
len); headers_, data, len);
} }
void FieldStore::append_last_header_value(const char *data, size_t len) { void FieldStore::append_last_header_value(const char *data, size_t len) {
shrpx::append_last_header_value(header_key_prev_, buffer_size_, headers_, shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_,
data, len); headers_, data, len);
} }
void FieldStore::clear_headers() { headers_.clear(); } void FieldStore::clear_headers() { headers_.clear(); }
void FieldStore::add_trailer_lower(const StringRef &name, void FieldStore::add_trailer_lower(const StringRef &name,
const StringRef &value, bool no_index) { const StringRef &value, bool no_index) {
auto low_name = name.str(); auto low_name = make_string_ref(balloc_, name);
util::inp_strlower(low_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); auto token = http2::lookup_token(low_name);
shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, shrpx::add_header(trailer_key_prev_, buffer_size_, trailers_, low_name,
StringRef{low_name}, value, no_index, token); make_string_ref(balloc_, value), no_index, token);
} }
void FieldStore::add_trailer_token(const StringRef &name, void FieldStore::add_trailer_token(const StringRef &name,
@ -452,17 +469,18 @@ void FieldStore::add_trailer_token(const StringRef &name,
int32_t token) { int32_t token) {
// Header size limit should be applied to all header and trailer // Header size limit should be applied to all header and trailer
// fields combined. // 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) { void FieldStore::append_last_trailer_key(const char *data, size_t len) {
shrpx::append_last_header_key(trailer_key_prev_, buffer_size_, trailers_, shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_,
data, len); trailers_, data, len);
} }
void FieldStore::append_last_trailer_value(const char *data, size_t len) { void FieldStore::append_last_trailer_value(const char *data, size_t len) {
shrpx::append_last_header_value(trailer_key_prev_, buffer_size_, trailers_, shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_,
data, len); trailers_, data, len);
} }
void Downstream::set_request_start_time( void Downstream::set_request_start_time(
@ -566,7 +584,7 @@ void Downstream::rewrite_location_response_header(
return; 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_; } bool Downstream::get_chunked_response() const { return chunked_response_; }
@ -703,10 +721,10 @@ bool Downstream::get_http2_upgrade_request() const {
response_state_ == INITIAL; 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); auto http2_settings = req_.fs.header(http2::HD_HTTP2_SETTINGS);
if (!http2_settings) { if (!http2_settings) {
return EMPTY_STRING; return StringRef{};
} }
return http2_settings->value; 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_; } int32_t Downstream::get_assoc_stream_id() const { return assoc_stream_id_; }
DefaultBlockAllocator &Downstream::get_block_allocator() { return balloc_; }
} // namespace shrpx } // namespace shrpx

View File

@ -40,6 +40,7 @@
#include "shrpx_io_control.h" #include "shrpx_io_control.h"
#include "http2.h" #include "http2.h"
#include "memchunk.h" #include "memchunk.h"
#include "allocator.h"
using namespace nghttp2; using namespace nghttp2;
@ -51,18 +52,19 @@ struct BlockedLink;
class FieldStore { class FieldStore {
public: public:
FieldStore(size_t headers_initial_capacity) FieldStore(DefaultBlockAllocator &balloc, size_t headers_initial_capacity)
: content_length(-1), : content_length(-1),
balloc_(balloc),
buffer_size_(0), buffer_size_(0),
header_key_prev_(false), header_key_prev_(false),
trailer_key_prev_(false) { trailer_key_prev_(false) {
headers_.reserve(headers_initial_capacity); headers_.reserve(headers_initial_capacity);
} }
const Headers &headers() const { return headers_; } const HeaderRefs &headers() const { return headers_; }
const Headers &trailers() const { return trailers_; } 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; } const void add_extra_buffer_size(size_t n) { buffer_size_ += n; }
size_t buffer_size() const { return buffer_size_; } size_t buffer_size() const { return buffer_size_; }
@ -73,11 +75,11 @@ public:
// multiple header have |name| as name, return last occurrence from // multiple header have |name| as name, return last occurrence from
// the beginning. If no such header is found, returns nullptr. // the beginning. If no such header is found, returns nullptr.
// This function must be called after headers are indexed // This function must be called after headers are indexed
const Headers::value_type *header(int32_t token) const; const HeaderRefs::value_type *header(int32_t token) const;
Headers::value_type *header(int32_t token); HeaderRefs::value_type *header(int32_t token);
// Returns pointer to the header field with the name |name|. If no // Returns pointer to the header field with the name |name|. If no
// such header is found, returns nullptr. // 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, void add_header_lower(const StringRef &name, const StringRef &value,
bool no_index); bool no_index);
@ -110,10 +112,11 @@ public:
int64_t content_length; int64_t content_length;
private: private:
Headers headers_; DefaultBlockAllocator &balloc_;
HeaderRefs headers_;
// trailer fields. For HTTP/1.1, trailer fields are only included // trailer fields. For HTTP/1.1, trailer fields are only included
// with chunked encoding. For HTTP/2, there is no such limit. // 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_. // Sum of the length of name and value in headers_ and trailers_.
// This could also be increased by add_extra_buffer_size() to take // This could also be increased by add_extra_buffer_size() to take
// into account for request URI in case of HTTP/1.x request. // into account for request URI in case of HTTP/1.x request.
@ -123,8 +126,8 @@ private:
}; };
struct Request { struct Request {
Request() Request(DefaultBlockAllocator &balloc)
: fs(16), : fs(balloc, 16),
recv_body_length(0), recv_body_length(0),
unconsumed_body_length(0), unconsumed_body_length(0),
method(-1), method(-1),
@ -179,8 +182,8 @@ struct Request {
}; };
struct Response { struct Response {
Response() Response(DefaultBlockAllocator &balloc)
: fs(32), : fs(balloc, 32),
recv_body_length(0), recv_body_length(0),
unconsumed_body_length(0), unconsumed_body_length(0),
http_status(0), http_status(0),
@ -245,7 +248,7 @@ public:
// Returns true if the request is HTTP Upgrade for HTTP/2 // Returns true if the request is HTTP Upgrade for HTTP/2
bool get_http2_upgrade_request() const; bool get_http2_upgrade_request() const;
// Returns the value of HTTP2-Settings request header field. // Returns the value of HTTP2-Settings request header field.
const std::string &get_http2_settings() const; StringRef get_http2_settings() const;
// downstream request API // downstream request API
const Request &request() const { return req_; } const Request &request() const { return req_; }
@ -373,6 +376,8 @@ public:
DefaultMemchunks pop_response_buf(); DefaultMemchunks pop_response_buf();
DefaultBlockAllocator &get_block_allocator();
enum { enum {
EVENT_ERROR = 0x1, EVENT_ERROR = 0x1,
EVENT_TIMEOUT = 0x2, EVENT_TIMEOUT = 0x2,
@ -392,6 +397,8 @@ public:
int64_t response_sent_body_length; int64_t response_sent_body_length;
private: private:
DefaultBlockAllocator balloc_;
Request req_; Request req_;
Response resp_; Response resp_;

View File

@ -364,7 +364,7 @@ int Http2DownstreamConnection::push_request_headers() {
StringRef{req.authority}, StringRef{req.scheme}); StringRef{req.authority}, StringRef{req.scheme});
if (fwd || !value.empty()) { if (fwd || !value.empty()) {
if (fwd) { if (fwd) {
forwarded_value = fwd->value; forwarded_value = fwd->value.str();
if (!value.empty()) { if (!value.empty()) {
forwarded_value += ", "; forwarded_value += ", ";
@ -377,7 +377,7 @@ int Http2DownstreamConnection::push_request_headers() {
} }
} else if (fwd) { } else if (fwd) {
nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value)); nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value));
forwarded_value = fwd->value; forwarded_value = fwd->value.str();
} }
auto &xffconf = httpconf.xff; auto &xffconf = httpconf.xff;
@ -389,7 +389,7 @@ int Http2DownstreamConnection::push_request_headers() {
if (xffconf.add) { if (xffconf.add) {
if (xff) { if (xff) {
xff_value = (*xff).value; xff_value = (*xff).value.str();
xff_value += ", "; xff_value += ", ";
} }
xff_value += upstream->get_client_handler()->get_ipaddr(); xff_value += upstream->get_client_handler()->get_ipaddr();
@ -411,7 +411,7 @@ int Http2DownstreamConnection::push_request_headers() {
} }
} else { } else {
if (via) { if (via) {
via_value = (*via).value; via_value = (*via).value.str();
via_value += ", "; via_value += ", ";
} }
via_value += http::create_via_header_value(req.http_major, req.http_minor); via_value += http::create_via_header_value(req.http_major, req.http_minor);

View File

@ -108,7 +108,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
int rv; 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); util::to_base64(http2_settings);
auto settings_payload = auto settings_payload =
@ -1434,7 +1434,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
} }
} else { } else {
if (via) { if (via) {
via_value = (*via).value; via_value = (*via).value.str();
via_value += ", "; via_value += ", ";
} }
via_value += via_value +=

View File

@ -325,7 +325,7 @@ int htp_hdrs_completecb(http_parser *htp) {
} }
if (host) { if (host) {
req.authority = host->value; req.authority = host->value.str();
} }
if (handler->get_ssl()) { if (handler->get_ssl()) {

View File

@ -85,7 +85,7 @@ mrb_value init_module(mrb_state *mrb) {
return create_env(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); auto hash = mrb_hash_new(mrb);
for (auto &hd : headers) { for (auto &hd : headers) {

View File

@ -43,7 +43,7 @@ mrb_value init_module(mrb_state *mrb);
void delete_downstream_from_module(mrb_state *mrb, Downstream *downstream); 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 } // namespace mruby

View File

@ -209,7 +209,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH); auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
if (cl) { if (cl) {
cl->value = util::utos(bodylen); cl->value = make_string_ref(downstream->get_block_allocator(),
StringRef{util::utos(bodylen)});
} else { } else {
resp.fs.add_header_token(StringRef::from_lit("content-length"), resp.fs.add_header_token(StringRef::from_lit("content-length"),
StringRef{util::utos(bodylen)}, false, StringRef{util::utos(bodylen)}, false,

View File

@ -262,12 +262,12 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
req.method = method_token; req.method = method_token;
if (is_connect) { if (is_connect) {
req.authority = path->value; req.authority = path->value.str();
} else { } else {
req.scheme = scheme->value; req.scheme = scheme->value.str();
req.authority = host->value; req.authority = host->value.str();
if (get_config()->http2_proxy) { if (get_config()->http2_proxy) {
req.path = path->value; req.path = path->value.str();
} else if (method_token == HTTP_OPTIONS && path->value == "*") { } else if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty. // Server-wide OPTIONS request. Path is empty.
} else { } else {
@ -1069,7 +1069,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
} }
} else { } else {
if (via) { if (via) {
via_value = via->value; via_value = via->value.str();
via_value += ", "; via_value += ", ";
} }
via_value += via_value +=

View File

@ -443,6 +443,11 @@ private:
size_type len; 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) { inline bool operator==(const StringRef &lhs, const std::string &rhs) {
return lhs.size() == rhs.size() && return lhs.size() == rhs.size() &&
std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); 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); 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) { inline std::ostream &operator<<(std::ostream &o, const StringRef &s) {
return o.write(s.c_str(), s.size()); return o.write(s.c_str(), s.size());
} }

View File

@ -1056,6 +1056,10 @@ int64_t parse_uint(const std::string &s) {
return parse_uint(reinterpret_cast<const uint8_t *>(s.c_str()), s.size()); return parse_uint(reinterpret_cast<const uint8_t *>(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 parse_uint(const uint8_t *s, size_t len) {
int64_t n; int64_t n;
size_t i; size_t i;

View File

@ -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); return iends_with(std::begin(a), std::end(a), b, b + N - 1);
} }
template <typename CharT, size_t N>
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); int strcompare(const char *a, const uint8_t *b, size_t n);
template <typename InputIt> bool strieq(const char *a, InputIt b, size_t bn) { template <typename InputIt> 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()); return strieq(a, N - 1, std::begin(b), b.size());
} }
template <typename CharT, size_t N>
bool strieq_l(const CharT(&a)[N], const StringRef &b) {
return strieq(a, N - 1, std::begin(b), b.size());
}
template <typename InputIt> bool streq(const char *a, InputIt b, size_t bn) { template <typename InputIt> bool streq(const char *a, InputIt b, size_t bn) {
if (!a) { if (!a) {
return false; 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 char *s);
int64_t parse_uint(const uint8_t *s, size_t len); int64_t parse_uint(const uint8_t *s, size_t len);
int64_t parse_uint(const std::string &s); 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 // Parses NULL terminated string |s| as unsigned integer and returns
// the parsed integer casted to double. If |s| ends with "s", the // the parsed integer casted to double. If |s| ends with "s", the