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]));
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<nghttp2_nv> &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<nghttp2_nv> &nva,
}
} // 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);
}
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 |
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) {

View File

@ -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<Header>;
using HeaderRefs = std::vector<HeaderRef>;
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 <size_t N, size_t 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
// 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<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
// NGHTTP2_NV_FLAG_NO_COPY_NAME and NGHTTP2_NV_FLAG_NO_COPY_VALUE.
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
// |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<int16_t, HD_MAXIDX>;
// 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|

View File

@ -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);

View File

@ -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<nghttp2_nv> &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<nghttp2_nv> &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

View File

@ -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_;

View File

@ -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);

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 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 +=

View File

@ -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()) {

View File

@ -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) {

View File

@ -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

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);
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,

View File

@ -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 +=

View File

@ -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());
}

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());
}
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;

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);
}
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);
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());
}
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) {
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