nghttpx: Add custom memory allocator mainly for header related objects
This commit is contained in:
parent
907eeeda8a
commit
bae37e3e4a
|
@ -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
|
63
src/http2.cc
63
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<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) {
|
||||
|
|
51
src/http2.h
51
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<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|
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 +=
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 +=
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
11
src/util.h
11
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 <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
|
||||
|
|
Loading…
Reference in New Issue