nghttpx: Use StringRef for authority, scheme and path

This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-10 22:42:07 +09:00
parent fa601e5ba3
commit b1b57cc740
20 changed files with 511 additions and 168 deletions

View File

@ -27,6 +27,8 @@
#include "nghttp2_config.h" #include "nghttp2_config.h"
#include <sys/uio.h>
#include "template.h" #include "template.h"
namespace nghttp2 { namespace nghttp2 {
@ -110,6 +112,17 @@ StringRef concat_string_ref(BlockAllocator &alloc, const StringRef &a,
return StringRef{dst, a.size() + b.size()}; return StringRef{dst, a.size() + b.size()};
} }
struct ByteRef {
uint8_t *base;
size_t len;
};
template <typename BlockAllocator>
ByteRef make_byte_ref(BlockAllocator &alloc, size_t size) {
auto dst = static_cast<uint8_t *>(alloc.alloc(size));
return {dst, size};
}
} // namespace aria2 } // namespace aria2
#endif // ALLOCATOR_H #endif // ALLOCATOR_H

View File

@ -484,41 +484,69 @@ void dump_nv(FILE *out, const HeaderRefs &nva) {
fflush(out); fflush(out);
} }
std::string rewrite_location_uri(const StringRef &uri, const http_parser_url &u, StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
const std::string &match_host, const http_parser_url &u,
const std::string &request_authority, const StringRef &match_host,
const std::string &upstream_scheme) { const StringRef &request_authority,
const StringRef &upstream_scheme) {
// We just rewrite scheme and authority. // We just rewrite scheme and authority.
if ((u.field_set & (1 << UF_HOST)) == 0) { if ((u.field_set & (1 << UF_HOST)) == 0) {
return ""; return StringRef{};
} }
auto field = &u.field_data[UF_HOST]; auto field = &u.field_data[UF_HOST];
if (!util::starts_with(std::begin(match_host), std::end(match_host), if (!util::starts_with(std::begin(match_host), std::end(match_host),
&uri[field->off], &uri[field->off] + field->len) || &uri[field->off], &uri[field->off] + field->len) ||
(match_host.size() != field->len && match_host[field->len] != ':')) { (match_host.size() != field->len && match_host[field->len] != ':')) {
return ""; return StringRef{};
} }
std::string res;
auto len = 0;
if (!request_authority.empty()) { if (!request_authority.empty()) {
res += upstream_scheme; len += upstream_scheme.size() + str_size("://") + request_authority.size();
res += "://"; }
res += request_authority;
if (u.field_set & (1 << UF_PATH)) {
field = &u.field_data[UF_PATH];
len += field->len;
}
if (u.field_set & (1 << UF_QUERY)) {
field = &u.field_data[UF_QUERY];
len += 1 + field->len;
}
if (u.field_set & (1 << UF_FRAGMENT)) {
field = &u.field_data[UF_FRAGMENT];
len += 1 + field->len;
}
auto iov = make_byte_ref(balloc, len + 1);
auto p = iov.base;
if (!request_authority.empty()) {
p = std::copy(std::begin(upstream_scheme), std::end(upstream_scheme), p);
p = util::copy_lit(p, "://");
p = std::copy(std::begin(request_authority), std::end(request_authority),
p);
} }
if (u.field_set & (1 << UF_PATH)) { if (u.field_set & (1 << UF_PATH)) {
field = &u.field_data[UF_PATH]; field = &u.field_data[UF_PATH];
res.append(&uri[field->off], field->len); p = std::copy_n(&uri[field->off], field->len, p);
} }
if (u.field_set & (1 << UF_QUERY)) { if (u.field_set & (1 << UF_QUERY)) {
field = &u.field_data[UF_QUERY]; field = &u.field_data[UF_QUERY];
res += '?'; *p++ = '?';
res.append(&uri[field->off], field->len); p = std::copy_n(&uri[field->off], field->len, p);
} }
if (u.field_set & (1 << UF_FRAGMENT)) { if (u.field_set & (1 << UF_FRAGMENT)) {
field = &u.field_data[UF_FRAGMENT]; field = &u.field_data[UF_FRAGMENT];
res += '#'; *p++ = '#';
res.append(&uri[field->off], field->len); p = std::copy_n(&uri[field->off], field->len, p);
} }
return res;
*p = '\0';
return StringRef{iov.base, p};
} }
int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value, int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
@ -1496,7 +1524,7 @@ StringRef to_method_string(int method_token) {
return StringRef{http_method_str(static_cast<http_method>(method_token))}; return StringRef{http_method_str(static_cast<http_method>(method_token))};
} }
StringRef get_pure_path_component(const std::string &uri) { StringRef get_pure_path_component(const StringRef &uri) {
int rv; int rv;
http_parser_url u{}; http_parser_url u{};
@ -1513,9 +1541,9 @@ StringRef get_pure_path_component(const std::string &uri) {
return StringRef::from_lit("/"); return StringRef::from_lit("/");
} }
int construct_push_component(std::string &scheme, std::string &authority, int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
std::string &path, const StringRef &base, StringRef &authority, StringRef &path,
const StringRef &uri) { const StringRef &base, const StringRef &uri) {
int rv; int rv;
StringRef rel, relq; StringRef rel, relq;
@ -1538,15 +1566,26 @@ int construct_push_component(std::string &scheme, std::string &authority,
} }
} else { } else {
if (u.field_set & (1 << UF_SCHEMA)) { if (u.field_set & (1 << UF_SCHEMA)) {
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str()); scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
} }
if (u.field_set & (1 << UF_HOST)) { if (u.field_set & (1 << UF_HOST)) {
http2::copy_url_component(authority, &u, UF_HOST, uri.c_str()); auto auth = util::get_uri_field(uri.c_str(), u, UF_HOST);
if (u.field_set & (1 << UF_PORT)) { auto len = auth.size();
authority += ':'; auto port_exists = u.field_set & (1 << UF_PORT);
authority += util::utos(u.port); if (port_exists) {
len += 1 + str_size("65535");
} }
auto iov = make_byte_ref(balloc, len + 1);
auto p = iov.base;
p = std::copy(std::begin(auth), std::end(auth), p);
if (port_exists) {
*p++ = ':';
p = util::utos(p, u.port);
}
*p = '\0';
authority = StringRef{iov.base, p};
} }
if (u.field_set & (1 << UF_PATH)) { if (u.field_set & (1 << UF_PATH)) {
@ -1562,11 +1601,192 @@ int construct_push_component(std::string &scheme, std::string &authority,
} }
} }
path = http2::path_join(base, StringRef{}, rel, relq); path = http2::path_join(balloc, base, StringRef{}, rel, relq);
return 0; return 0;
} }
namespace {
template <typename InputIt> InputIt eat_file(InputIt first, InputIt last) {
if (first == last) {
*first++ = '/';
return first;
}
if (*(last - 1) == '/') {
return last;
}
auto p = last;
for (; p != first && *(p - 1) != '/'; --p)
;
if (p == first) {
// this should not happend in normal case, where we expect path
// starts with '/'
*first++ = '/';
return first;
}
return p;
}
} // namespace
namespace {
template <typename InputIt> InputIt eat_dir(InputIt first, InputIt last) {
auto p = eat_file(first, last);
--p;
assert(*p == '/');
return eat_file(first, p);
}
} // namespace
StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
const StringRef &base_query, const StringRef &rel_path,
const StringRef &rel_query) {
auto res = make_byte_ref(
balloc, std::max(static_cast<size_t>(1), base_path.size()) +
rel_path.size() + 1 +
std::max(base_query.size(), rel_query.size()) + 1);
auto p = res.base;
if (rel_path.empty()) {
if (base_path.empty()) {
*p++ = '/';
} else {
p = std::copy(std::begin(base_path), std::end(base_path), p);
}
if (rel_query.empty()) {
if (!base_query.empty()) {
*p++ = '?';
p = std::copy(std::begin(base_query), std::end(base_query), p);
}
*p = '\0';
return StringRef{res.base, p};
}
*p++ = '?';
p = std::copy(std::begin(rel_query), std::end(rel_query), p);
*p = '\0';
return StringRef{res.base, p};
}
auto first = std::begin(rel_path);
auto last = std::end(rel_path);
if (rel_path[0] == '/') {
*p++ = '/';
++first;
} else if (base_path.empty()) {
*p++ = '/';
} else {
p = std::copy(std::begin(base_path), std::end(base_path), p);
}
for (; first != last;) {
if (*first == '.') {
if (first + 1 == last) {
break;
}
if (*(first + 1) == '/') {
first += 2;
continue;
}
if (*(first + 1) == '.') {
if (first + 2 == last) {
p = eat_dir(res.base, p);
break;
}
if (*(first + 2) == '/') {
p = eat_dir(res.base, p);
first += 3;
continue;
}
}
}
if (*(p - 1) != '/') {
p = eat_file(res.base, p);
}
auto slash = std::find(first, last, '/');
if (slash == last) {
p = std::copy(first, last, p);
break;
}
p = std::copy(first, slash + 1, p);
first = slash + 1;
for (; first != last && *first == '/'; ++first)
;
}
if (!rel_query.empty()) {
*p++ = '?';
p = std::copy(std::begin(rel_query), std::end(rel_query), p);
}
*p = '\0';
return StringRef{res.base, p};
}
StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
const StringRef &query) {
// First, decode %XX for unreserved characters, then do
// http2::join_path
// We won't find %XX if length is less than 3.
if (path.size() < 3 ||
std::find(std::begin(path), std::end(path), '%') == std::end(path)) {
return path_join(balloc, StringRef{}, StringRef{}, path, query);
}
// includes last terminal NULL.
auto result = make_byte_ref(balloc, path.size() + 1);
auto p = result.base;
auto it = std::begin(path);
for (; it + 2 != std::end(path);) {
if (*it == '%') {
if (util::is_hex_digit(*(it + 1)) && util::is_hex_digit(*(it + 2))) {
auto c =
(util::hex_to_uint(*(it + 1)) << 4) + util::hex_to_uint(*(it + 2));
if (util::in_rfc3986_unreserved_chars(c)) {
*p++ = c;
it += 3;
continue;
}
*p++ = '%';
*p++ = util::upcase(*(it + 1));
*p++ = util::upcase(*(it + 2));
it += 3;
continue;
}
}
*p++ = *it++;
}
p = std::copy(it, std::end(path), p);
*p = '\0';
return path_join(balloc, StringRef{}, StringRef{}, StringRef{result.base, p},
query);
}
StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src) {
if (src.empty() || src[0] != '/') {
return src;
}
// probably, not necessary most of the case, but just in case.
auto fragment = std::find(std::begin(src), std::end(src), '#');
auto query = std::find(std::begin(src), fragment, '?');
if (query != fragment) {
++query;
}
return normalize_path(balloc, StringRef{std::begin(src), query},
StringRef{query, fragment});
}
} // namespace http2 } // namespace http2
} // namespace nghttp2 } // namespace nghttp2

View File

@ -40,6 +40,7 @@
#include "util.h" #include "util.h"
#include "memchunk.h" #include "memchunk.h"
#include "template.h" #include "template.h"
#include "allocator.h"
namespace nghttp2 { namespace nghttp2 {
@ -241,10 +242,11 @@ void dump_nv(FILE *out, const HeaderRefs &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 StringRef &uri, const http_parser_url &u, StringRef rewrite_location_uri(BlockAllocator &balloc, const StringRef &uri,
const std::string &match_host, const http_parser_url &u,
const std::string &request_authority, const StringRef &match_host,
const std::string &upstream_scheme); const StringRef &request_authority,
const StringRef &upstream_scheme);
// Checks the header name/value pair using nghttp2_check_header_name() // Checks the header name/value pair using nghttp2_check_header_name()
// and nghttp2_check_header_value(). If both function returns nonzero, // and nghttp2_check_header_value(). If both function returns nonzero,
@ -337,6 +339,10 @@ std::vector<LinkHeader> parse_link_header(const char *src, size_t len);
std::string path_join(const StringRef &base, const StringRef &base_query, std::string path_join(const StringRef &base, const StringRef &base_query,
const StringRef &rel_path, const StringRef &rel_query); const StringRef &rel_path, const StringRef &rel_query);
StringRef path_join(BlockAllocator &balloc, const StringRef &base_path,
const StringRef &base_query, const StringRef &rel_path,
const StringRef &rel_query);
// true if response has body, taking into account the request method // true if response has body, taking into account the request method
// and status code. // and status code.
bool expect_response_body(const std::string &method, int status_code); bool expect_response_body(const std::string &method, int status_code);
@ -407,19 +413,24 @@ std::string rewrite_clean_path(InputIt first, InputIt last) {
return path; return path;
} }
StringRef normalize_path(BlockAllocator &balloc, const StringRef &path,
const StringRef &query);
StringRef rewrite_clean_path(BlockAllocator &balloc, const StringRef &src);
// Returns path component of |uri|. The returned path does not // Returns path component of |uri|. The returned path does not
// include query component. This function returns empty string if it // include query component. This function returns empty string if it
// fails. // fails.
StringRef get_pure_path_component(const std::string &uri); StringRef get_pure_path_component(const StringRef &uri);
// Deduces scheme, authority and path from given |uri|, and stores // Deduces scheme, authority and path from given |uri|, and stores
// them in |scheme|, |authority|, and |path| respectively. If |uri| // them in |scheme|, |authority|, and |path| respectively. If |uri|
// is relative path, path resolution takes place using path given in // is relative path, path resolution takes place using path given in
// |base| of length |baselen|. This function returns 0 if it // |base| of length |baselen|. This function returns 0 if it
// succeeds, or -1. // succeeds, or -1.
int construct_push_component(std::string &scheme, std::string &authority, int construct_push_component(BlockAllocator &balloc, StringRef &scheme,
std::string &path, const StringRef &base, StringRef &authority, StringRef &path,
const StringRef &uri); const StringRef &base, const StringRef &uri);
} // namespace http2 } // namespace http2

View File

@ -825,11 +825,11 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; } bool ClientHandler::get_http2_upgrade_allowed() const { return !conn_.tls.ssl; }
std::string ClientHandler::get_upstream_scheme() const { StringRef ClientHandler::get_upstream_scheme() const {
if (conn_.tls.ssl) { if (conn_.tls.ssl) {
return "https"; return StringRef::from_lit("https");
} else { } else {
return "http"; return StringRef::from_lit("http");
} }
} }
@ -843,24 +843,35 @@ namespace {
// is mostly same routine found in // is mostly same routine found in
// HttpDownstreamConnection::push_request_headers(), but vastly // HttpDownstreamConnection::push_request_headers(), but vastly
// simplified since we only care about absolute URI. // simplified since we only care about absolute URI.
std::string construct_absolute_request_uri(const Request &req) { StringRef construct_absolute_request_uri(BlockAllocator &balloc,
const Request &req) {
if (req.authority.empty()) { if (req.authority.empty()) {
return req.path; return req.path;
} }
std::string uri; auto len = req.authority.size() + req.path.size();
if (req.scheme.empty()) {
len += str_size("http://");
} else {
len += req.scheme.size() + str_size("://");
}
auto iov = make_byte_ref(balloc, len + 1);
auto p = iov.base;
if (req.scheme.empty()) { if (req.scheme.empty()) {
// We may have to log the request which lacks scheme (e.g., // We may have to log the request which lacks scheme (e.g.,
// http/1.1 with origin form). // http/1.1 with origin form).
uri += "http://"; p = util::copy_lit(p, "http://");
} else { } else {
uri += req.scheme; p = std::copy(std::begin(req.scheme), std::end(req.scheme), p);
uri += "://"; p = util::copy_lit(p, "://");
} }
uri += req.authority; p = std::copy(std::begin(req.authority), std::end(req.authority), p);
uri += req.path; p = std::copy(std::begin(req.path), std::end(req.path), p);
*p = '\0';
return uri; return StringRef{iov.base, p};
} }
} // namespace } // namespace
@ -869,6 +880,8 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
const auto &req = downstream->request(); const auto &req = downstream->request();
const auto &resp = downstream->response(); const auto &resp = downstream->response();
auto &balloc = downstream->get_block_allocator();
upstream_accesslog( upstream_accesslog(
get_config()->logging.access.format, get_config()->logging.access.format,
LogSpec{ LogSpec{
@ -877,7 +890,7 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
req.method == HTTP_CONNECT req.method == HTTP_CONNECT
? StringRef(req.authority) ? StringRef(req.authority)
: get_config()->http2_proxy : get_config()->http2_proxy
? StringRef(construct_absolute_request_uri(req)) ? StringRef(construct_absolute_request_uri(balloc, req))
: req.path.empty() : req.path.empty()
? req.method == HTTP_OPTIONS ? req.method == HTTP_OPTIONS
? StringRef::from_lit("*") ? StringRef::from_lit("*")

View File

@ -109,7 +109,7 @@ public:
int perform_http2_upgrade(HttpsUpstream *http); int perform_http2_upgrade(HttpsUpstream *http);
bool get_http2_upgrade_allowed() const; bool get_http2_upgrade_allowed() const;
// Returns upstream scheme, either "http" or "https" // Returns upstream scheme, either "http" or "https"
std::string get_upstream_scheme() const; StringRef get_upstream_scheme() const;
void start_immediate_shutdown(); void start_immediate_shutdown();
// Writes upstream accesslog using |downstream|. The |downstream| // Writes upstream accesslog using |downstream|. The |downstream|

View File

@ -561,7 +561,7 @@ int Downstream::end_upload_data() {
} }
void Downstream::rewrite_location_response_header( void Downstream::rewrite_location_response_header(
const std::string &upstream_scheme) { const StringRef &upstream_scheme) {
auto hd = resp_.fs.header(http2::HD_LOCATION); auto hd = resp_.fs.header(http2::HD_LOCATION);
if (!hd) { if (!hd) {
return; return;
@ -577,14 +577,15 @@ void Downstream::rewrite_location_response_header(
return; return;
} }
auto new_uri = http2::rewrite_location_uri( auto new_uri = http2::rewrite_location_uri(balloc_, hd->value, u,
hd->value, u, request_downstream_host_, req_.authority, upstream_scheme); request_downstream_host_,
req_.authority, upstream_scheme);
if (new_uri.empty()) { if (new_uri.empty()) {
return; return;
} }
hd->value = make_string_ref(balloc_, StringRef{new_uri}); hd->value = new_uri;
} }
bool Downstream::get_chunked_response() const { return chunked_response_; } bool Downstream::get_chunked_response() const { return chunked_response_; }
@ -879,8 +880,8 @@ void Downstream::add_retry() { ++num_retry_; }
bool Downstream::no_more_retry() const { return num_retry_ > 5; } bool Downstream::no_more_retry() const { return num_retry_ > 5; }
void Downstream::set_request_downstream_host(std::string host) { void Downstream::set_request_downstream_host(const StringRef &host) {
request_downstream_host_ = std::move(host); request_downstream_host_ = host;
} }
void Downstream::set_request_pending(bool f) { request_pending_ = f; } void Downstream::set_request_pending(bool f) { request_pending_ = f; }

View File

@ -147,17 +147,17 @@ struct Request {
FieldStore fs; FieldStore fs;
// Request scheme. For HTTP/2, this is :scheme header field value. // Request scheme. For HTTP/2, this is :scheme header field value.
// For HTTP/1.1, this is deduced from URI or connection. // For HTTP/1.1, this is deduced from URI or connection.
std::string scheme; StringRef scheme;
// Request authority. This is HTTP/2 :authority header field value // Request authority. This is HTTP/2 :authority header field value
// or host header field value. We may deduce it from absolute-form // or host header field value. We may deduce it from absolute-form
// HTTP/1 request. We also store authority-form HTTP/1 request. // HTTP/1 request. We also store authority-form HTTP/1 request.
// This could be empty if request comes from HTTP/1.0 without Host // This could be empty if request comes from HTTP/1.0 without Host
// header field and origin-form. // header field and origin-form.
std::string authority; StringRef authority;
// Request path, including query component. For HTTP/1.1, this is // Request path, including query component. For HTTP/1.1, this is
// request-target. For HTTP/2, this is :path header field value. // request-target. For HTTP/2, this is :path header field value.
// For CONNECT request, this is empty. // For CONNECT request, this is empty.
std::string path; StringRef path;
// the length of request body received so far // the length of request body received so far
int64_t recv_body_length; int64_t recv_body_length;
// The number of bytes not consumed by the application yet. // The number of bytes not consumed by the application yet.
@ -275,7 +275,7 @@ public:
// Validates that received request body length and content-length // Validates that received request body length and content-length
// matches. // matches.
bool validate_request_recv_body_length() const; bool validate_request_recv_body_length() const;
void set_request_downstream_host(std::string host); void set_request_downstream_host(const StringRef &host);
bool expect_response_body() const; bool expect_response_body() const;
enum { enum {
INITIAL, INITIAL,
@ -306,7 +306,7 @@ public:
Response &response() { return resp_; } Response &response() { return resp_; }
// Rewrites the location response header field. // Rewrites the location response header field.
void rewrite_location_response_header(const std::string &upstream_scheme); void rewrite_location_response_header(const StringRef &upstream_scheme);
bool get_chunked_response() const; bool get_chunked_response() const;
void set_chunked_response(bool f); void set_chunked_response(bool f);
@ -407,7 +407,7 @@ private:
// host we requested to downstream. This is used to rewrite // host we requested to downstream. This is used to rewrite
// location header field to decide the location should be rewritten // location header field to decide the location should be rewritten
// or not. // or not.
std::string request_downstream_host_; StringRef request_downstream_host_;
DefaultMemchunks request_buf_; DefaultMemchunks request_buf_;
DefaultMemchunks response_buf_; DefaultMemchunks response_buf_;

View File

@ -71,14 +71,11 @@ DownstreamQueue::find_host_entry(const std::string &host) {
return (*itr).second; return (*itr).second;
} }
const std::string & std::string DownstreamQueue::make_host_key(const StringRef &host) const {
DownstreamQueue::make_host_key(const std::string &host) const { return unified_host_ ? "" : host.str();
static std::string empty_key;
return unified_host_ ? empty_key : host;
} }
const std::string & std::string DownstreamQueue::make_host_key(Downstream *downstream) const {
DownstreamQueue::make_host_key(Downstream *downstream) const {
return make_host_key(downstream->request().authority); return make_host_key(downstream->request().authority);
} }
@ -99,7 +96,7 @@ void DownstreamQueue::mark_blocked(Downstream *downstream) {
ent.blocked.append(link); ent.blocked.append(link);
} }
bool DownstreamQueue::can_activate(const std::string &host) const { bool DownstreamQueue::can_activate(const StringRef &host) const {
auto itr = host_entries_.find(make_host_key(host)); auto itr = host_entries_.find(make_host_key(host));
if (itr == std::end(host_entries_)) { if (itr == std::end(host_entries_)) {
return true; return true;
@ -127,7 +124,7 @@ Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream,
downstreams_.remove(downstream); downstreams_.remove(downstream);
auto &host = make_host_key(downstream); auto host = make_host_key(downstream);
auto &ent = find_host_entry(host); auto &ent = find_host_entry(host);
if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) { if (downstream->get_dispatch_state() == Downstream::DISPATCH_ACTIVE) {

View File

@ -77,7 +77,7 @@ public:
void mark_blocked(Downstream *downstream); void mark_blocked(Downstream *downstream);
// Returns true if we can make downstream connection to given // Returns true if we can make downstream connection to given
// |host|. // |host|.
bool can_activate(const std::string &host) const; bool can_activate(const StringRef &host) const;
// Removes and frees |downstream| object. If |downstream| is in // Removes and frees |downstream| object. If |downstream| is in
// Downstream::DISPATCH_ACTIVE, and |next_blocked| is true, this // Downstream::DISPATCH_ACTIVE, and |next_blocked| is true, this
// function may return Downstream object with the same target host // function may return Downstream object with the same target host
@ -87,8 +87,8 @@ public:
bool next_blocked = true); bool next_blocked = true);
Downstream *get_downstreams() const; Downstream *get_downstreams() const;
HostEntry &find_host_entry(const std::string &host); HostEntry &find_host_entry(const std::string &host);
const std::string &make_host_key(const std::string &host) const; std::string make_host_key(const StringRef &host) const;
const std::string &make_host_key(Downstream *downstream) const; std::string make_host_key(Downstream *downstream) const;
private: private:
// Per target host structure to keep track of the number of // Per target host structure to keep track of the number of

View File

@ -282,10 +282,10 @@ int Http2DownstreamConnection::push_request_headers() {
auto authority = StringRef(downstream_hostport); auto authority = StringRef(downstream_hostport);
if (no_host_rewrite && !req.authority.empty()) { if (no_host_rewrite && !req.authority.empty()) {
authority = StringRef(req.authority); authority = req.authority;
} }
downstream_->set_request_downstream_host(authority.str()); downstream_->set_request_downstream_host(authority);
size_t num_cookies = 0; size_t num_cookies = 0;
if (!http2conf.no_cookie_crumbling) { if (!http2conf.no_cookie_crumbling) {
@ -359,9 +359,9 @@ int Http2DownstreamConnection::push_request_headers() {
params &= ~FORWARDED_PROTO; params &= ~FORWARDED_PROTO;
} }
auto value = http::create_forwarded( auto value = http::create_forwarded(params, handler->get_forwarded_by(),
params, handler->get_forwarded_by(), handler->get_forwarded_for(), handler->get_forwarded_for(),
StringRef{req.authority}, StringRef{req.scheme}); req.authority, req.scheme);
if (fwd || !value.empty()) { if (fwd || !value.empty()) {
if (fwd) { if (fwd) {
forwarded_value = fwd->value.str(); forwarded_value = fwd->value.str();

View File

@ -1986,6 +1986,8 @@ int Http2Session::handle_downstream_push_promise_complete(
Downstream *downstream, Downstream *promised_downstream) { Downstream *downstream, Downstream *promised_downstream) {
auto &promised_req = promised_downstream->request(); auto &promised_req = promised_downstream->request();
auto &promised_balloc = promised_downstream->get_block_allocator();
auto authority = promised_req.fs.header(http2::HD__AUTHORITY); auto authority = promised_req.fs.header(http2::HD__AUTHORITY);
auto path = promised_req.fs.header(http2::HD__PATH); auto path = promised_req.fs.header(http2::HD__PATH);
auto method = promised_req.fs.header(http2::HD__METHOD); auto method = promised_req.fs.header(http2::HD__METHOD);
@ -2007,16 +2009,19 @@ int Http2Session::handle_downstream_push_promise_complete(
// TODO Rewrite authority if we enabled rewrite host. But we // TODO Rewrite authority if we enabled rewrite host. But we
// really don't know how to rewrite host. Should we use the same // really don't know how to rewrite host. Should we use the same
// host in associated stream? // host in associated stream?
promised_req.authority = http2::value_to_str(authority); if (authority) {
promised_req.authority = authority->value;
}
promised_req.method = method_token; promised_req.method = method_token;
// libnghttp2 ensures that we don't have CONNECT method in // libnghttp2 ensures that we don't have CONNECT method in
// PUSH_PROMISE, and guarantees that :scheme exists. // PUSH_PROMISE, and guarantees that :scheme exists.
promised_req.scheme = http2::value_to_str(scheme); if (scheme) {
promised_req.scheme = scheme->value;
}
// For server-wide OPTIONS request, path is empty. // For server-wide OPTIONS request, path is empty.
if (method_token != HTTP_OPTIONS || path->value != "*") { if (method_token != HTTP_OPTIONS || path->value != "*") {
promised_req.path = http2::rewrite_clean_path(std::begin(path->value), promised_req.path = http2::rewrite_clean_path(promised_balloc, path->value);
std::end(path->value));
} }
promised_downstream->inspect_http2_request(); promised_downstream->inspect_http2_request();

View File

@ -303,7 +303,9 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
} }
req.method = method_token; req.method = method_token;
req.scheme = http2::value_to_str(scheme); if (scheme) {
req.scheme = scheme->value;
}
// nghttp2 library guarantees either :authority or host exist // nghttp2 library guarantees either :authority or host exist
if (!authority) { if (!authority) {
@ -311,16 +313,18 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
authority = req.fs.header(http2::HD_HOST); authority = req.fs.header(http2::HD_HOST);
} }
req.authority = http2::value_to_str(authority); if (authority) {
req.authority = authority->value;
}
if (path) { if (path) {
if (method_token == HTTP_OPTIONS && path->value == "*") { if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty. // Server-wide OPTIONS request. Path is empty.
} else if (get_config()->http2_proxy) { } else if (get_config()->http2_proxy) {
req.path = http2::value_to_str(path); req.path = path->value;
} else { } else {
const auto &value = path->value; req.path = http2::rewrite_clean_path(downstream->get_block_allocator(),
req.path = http2::rewrite_clean_path(std::begin(value), std::end(value)); path->value);
} }
} }
@ -580,6 +584,8 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
req.http_major = 2; req.http_major = 2;
req.http_minor = 0; req.http_minor = 0;
auto &promised_balloc = promised_downstream->get_block_allocator();
for (size_t i = 0; i < frame->push_promise.nvlen; ++i) { for (size_t i = 0; i < frame->push_promise.nvlen; ++i) {
auto &nv = frame->push_promise.nva[i]; auto &nv = frame->push_promise.nva[i];
auto token = http2::lookup_token(nv.name, nv.namelen); auto token = http2::lookup_token(nv.name, nv.namelen);
@ -588,13 +594,16 @@ int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
req.method = http2::lookup_method_token(nv.value, nv.valuelen); req.method = http2::lookup_method_token(nv.value, nv.valuelen);
break; break;
case http2::HD__SCHEME: case http2::HD__SCHEME:
req.scheme.assign(nv.value, nv.value + nv.valuelen); req.scheme =
make_string_ref(promised_balloc, StringRef{nv.value, nv.valuelen});
break; break;
case http2::HD__AUTHORITY: case http2::HD__AUTHORITY:
req.authority.assign(nv.value, nv.value + nv.valuelen); req.authority =
make_string_ref(promised_balloc, StringRef{nv.value, nv.valuelen});
break; break;
case http2::HD__PATH: case http2::HD__PATH:
req.path = http2::rewrite_clean_path(nv.value, nv.value + nv.valuelen); req.path = http2::rewrite_clean_path(promised_balloc,
StringRef{nv.value, nv.valuelen});
break; break;
} }
req.fs.add_header_token(StringRef{nv.name, nv.namelen}, req.fs.add_header_token(StringRef{nv.name, nv.namelen},
@ -1746,6 +1755,8 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
return 0; return 0;
} }
auto &balloc = downstream->get_block_allocator();
for (auto &kv : resp.fs.headers()) { for (auto &kv : resp.fs.headers()) {
if (kv.token != http2::HD_LINK) { if (kv.token != http2::HD_LINK) {
continue; continue;
@ -1753,28 +1764,23 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
for (auto &link : for (auto &link :
http2::parse_link_header(kv.value.c_str(), kv.value.size())) { http2::parse_link_header(kv.value.c_str(), kv.value.size())) {
const std::string *scheme_ptr, *authority_ptr; StringRef scheme, authority, path;
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, rv = http2::construct_push_component(balloc, scheme, authority, path,
link.uri); base, link.uri);
if (rv != 0) { if (rv != 0) {
continue; continue;
} }
if (scheme.empty()) { if (scheme.empty()) {
scheme_ptr = &req.scheme; scheme = req.scheme;
} else {
scheme_ptr = &scheme;
} }
if (authority.empty()) { if (authority.empty()) {
authority_ptr = &req.authority; authority = req.authority;
} else {
authority_ptr = &authority;
} }
rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream); rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
} }
@ -1783,9 +1789,9 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
return 0; return 0;
} }
int Http2Upstream::submit_push_promise(const std::string &scheme, int Http2Upstream::submit_push_promise(const StringRef &scheme,
const std::string &authority, const StringRef &authority,
const std::string &path, const StringRef &path,
Downstream *downstream) { Downstream *downstream) {
const auto &req = downstream->request(); const auto &req = downstream->request();
@ -1795,9 +1801,9 @@ int Http2Upstream::submit_push_promise(const std::string &scheme,
// juse use "GET" for now // juse use "GET" for now
nva.push_back(http2::make_nv_ll(":method", "GET")); nva.push_back(http2::make_nv_ll(":method", "GET"));
nva.push_back(http2::make_nv_ls(":scheme", scheme)); nva.push_back(http2::make_nv_ls_nocopy(":scheme", scheme));
nva.push_back(http2::make_nv_ls(":path", path)); nva.push_back(http2::make_nv_ls_nocopy(":path", path));
nva.push_back(http2::make_nv_ls(":authority", authority)); nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
for (auto &kv : req.fs.headers()) { for (auto &kv : req.fs.headers()) {
switch (kv.token) { switch (kv.token) {
@ -1865,27 +1871,25 @@ int Http2Upstream::initiate_push(Downstream *downstream, const StringRef &uri) {
return -1; return -1;
} }
const std::string *scheme_ptr, *authority_ptr; auto &balloc = downstream->get_block_allocator();
std::string scheme, authority, path;
rv = http2::construct_push_component(scheme, authority, path, base, uri); StringRef scheme, authority, path;
rv = http2::construct_push_component(balloc, scheme, authority, path, base,
uri);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
} }
if (scheme.empty()) { if (scheme.empty()) {
scheme_ptr = &req.scheme; scheme = req.scheme;
} else {
scheme_ptr = &scheme;
} }
if (authority.empty()) { if (authority.empty()) {
authority_ptr = &req.authority; authority = req.authority;
} else {
authority_ptr = &authority;
} }
rv = submit_push_promise(*scheme_ptr, *authority_ptr, path, downstream); rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
@ -1943,7 +1947,7 @@ int Http2Upstream::on_downstream_push_promise_complete(
nva.reserve(headers.size()); nva.reserve(headers.size());
for (auto &kv : headers) { for (auto &kv : headers) {
nva.push_back(http2::make_nv_nocopy(kv.name, kv.value, kv.no_index)); nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
} }
auto promised_stream_id = nghttp2_submit_push_promise( auto promised_stream_id = nghttp2_submit_push_promise(

View File

@ -111,9 +111,8 @@ public:
void check_shutdown(); void check_shutdown();
int prepare_push_promise(Downstream *downstream); int prepare_push_promise(Downstream *downstream);
int submit_push_promise(const std::string &scheme, int submit_push_promise(const StringRef &scheme, const StringRef &authority,
const std::string &authority, const std::string &path, const StringRef &path, Downstream *downstream);
Downstream *downstream);
int on_request_headers(Downstream *downstream, const nghttp2_frame *frame); int on_request_headers(Downstream *downstream, const nghttp2_frame *frame);

View File

@ -283,10 +283,10 @@ int HttpDownstreamConnection::push_request_headers() {
httpconf.no_host_rewrite || get_config()->http2_proxy || connect_method; httpconf.no_host_rewrite || get_config()->http2_proxy || connect_method;
if (no_host_rewrite && !req.authority.empty()) { if (no_host_rewrite && !req.authority.empty()) {
authority = StringRef(req.authority); authority = req.authority;
} }
downstream_->set_request_downstream_host(authority.str()); downstream_->set_request_downstream_host(authority);
auto buf = downstream_->get_request_buf(); auto buf = downstream_->get_request_buf();
@ -366,9 +366,9 @@ int HttpDownstreamConnection::push_request_headers() {
params &= ~FORWARDED_PROTO; params &= ~FORWARDED_PROTO;
} }
auto value = http::create_forwarded( auto value = http::create_forwarded(params, handler->get_forwarded_by(),
params, handler->get_forwarded_by(), handler->get_forwarded_for(), handler->get_forwarded_for(),
StringRef{req.authority}, StringRef{req.scheme}); req.authority, req.scheme);
if (fwd || !value.empty()) { if (fwd || !value.empty()) {
buf->append("Forwarded: "); buf->append("Forwarded: ");
if (fwd) { if (fwd) {

View File

@ -86,6 +86,8 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
auto &req = downstream->request(); auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
// We happen to have the same value for method token. // We happen to have the same value for method token.
req.method = htp->method; req.method = htp->method;
@ -103,9 +105,10 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
req.fs.add_extra_buffer_size(len); req.fs.add_extra_buffer_size(len);
if (req.method == HTTP_CONNECT) { if (req.method == HTTP_CONNECT) {
req.authority.append(data, len); req.authority =
concat_string_ref(balloc, req.authority, StringRef{data, len});
} else { } else {
req.path.append(data, len); req.path = concat_string_ref(balloc, req.path, StringRef{data, len});
} }
return 0; return 0;
@ -190,30 +193,51 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
} // namespace } // namespace
namespace { namespace {
void rewrite_request_host_path_from_uri(Request &req, const char *uri, void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
const StringRef &uri,
http_parser_url &u) { http_parser_url &u) {
assert(u.field_set & (1 << UF_HOST)); assert(u.field_set & (1 << UF_HOST));
auto &authority = req.authority;
authority.clear();
// As per https://tools.ietf.org/html/rfc7230#section-5.4, we // As per https://tools.ietf.org/html/rfc7230#section-5.4, we
// rewrite host header field with authority component. // rewrite host header field with authority component.
http2::copy_url_component(authority, &u, UF_HOST, uri); auto authority = util::get_uri_field(uri.c_str(), u, UF_HOST);
// TODO properly check IPv6 numeric address // TODO properly check IPv6 numeric address
if (authority.find(':') != std::string::npos) { auto ipv6 = std::find(std::begin(authority), std::end(authority), ':') !=
authority = '[' + authority; std::end(authority);
authority += ']'; auto authoritylen = authority.size();
if (ipv6) {
authoritylen += 2;
} }
if (u.field_set & (1 << UF_PORT)) { if (u.field_set & (1 << UF_PORT)) {
authority += ':'; authoritylen += 1 + str_size("65535");
authority += util::utos(u.port); }
if (authoritylen > authority.size()) {
auto iovec = make_byte_ref(balloc, authoritylen + 1);
auto p = iovec.base;
if (ipv6) {
*p++ = '[';
}
p = std::copy(std::begin(authority), std::end(authority), p);
if (ipv6) {
*p++ = ']';
} }
http2::copy_url_component(req.scheme, &u, UF_SCHEMA, uri); if (u.field_set & (1 << UF_PORT)) {
*p++ = ':';
p = util::utos(p, u.port);
}
*p = '\0';
std::string path; req.authority = StringRef{iovec.base, p};
} else {
req.authority = authority;
}
req.scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
StringRef path;
if (u.field_set & (1 << UF_PATH)) { if (u.field_set & (1 << UF_PATH)) {
http2::copy_url_component(path, &u, UF_PATH, uri); path = util::get_uri_field(uri.c_str(), u, UF_PATH);
} else if (req.method == HTTP_OPTIONS) { } else if (req.method == HTTP_OPTIONS) {
// Server-wide OPTIONS takes following form in proxy request: // Server-wide OPTIONS takes following form in proxy request:
// //
@ -221,21 +245,35 @@ void rewrite_request_host_path_from_uri(Request &req, const char *uri,
// //
// Notice that no slash after authority. See // Notice that no slash after authority. See
// http://tools.ietf.org/html/rfc7230#section-5.3.4 // http://tools.ietf.org/html/rfc7230#section-5.3.4
req.path = ""; req.path = StringRef::from_lit("");
// we ignore query component here // we ignore query component here
return; return;
} else { } else {
path = "/"; path = StringRef::from_lit("/");
} }
if (u.field_set & (1 << UF_QUERY)) { if (u.field_set & (1 << UF_QUERY)) {
auto &fdata = u.field_data[UF_QUERY]; auto &fdata = u.field_data[UF_QUERY];
path += '?';
path.append(uri + fdata.off, fdata.len); if (u.field_set & (1 << UF_PATH)) {
} auto q = util::get_uri_field(uri.c_str(), u, UF_QUERY);
if (get_config()->http2_proxy) { path = StringRef{std::begin(path), std::end(q)};
req.path = std::move(path);
} else { } else {
req.path = http2::rewrite_clean_path(std::begin(path), std::end(path)); auto iov = make_byte_ref(balloc, path.size() + 1 + fdata.len + 1);
auto p = iov.base;
p = std::copy(std::begin(path), std::end(path), p);
*p++ = '?';
p = std::copy_n(&uri[fdata.off], fdata.len, p);
*p = '\0';
path = StringRef{iov.base, p};
}
}
if (get_config()->http2_proxy) {
req.path = path;
} else {
req.path = http2::rewrite_clean_path(balloc, path);
} }
} }
} // namespace } // namespace
@ -299,12 +337,11 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->inspect_http1_request(); downstream->inspect_http1_request();
auto &balloc = downstream->get_block_allocator();
if (method != HTTP_CONNECT) { if (method != HTTP_CONNECT) {
http_parser_url u{}; http_parser_url u{};
// make a copy of request path, since we may set request path rv = http_parser_parse_url(req.path.c_str(), req.path.size(), 0, &u);
// while we are refering to original request path.
auto path = req.path;
rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u);
if (rv != 0) { if (rv != 0) {
// Expect to respond with 400 bad request // Expect to respond with 400 bad request
return -1; return -1;
@ -318,23 +355,23 @@ int htp_hdrs_completecb(http_parser *htp) {
req.no_authority = true; req.no_authority = true;
if (method == HTTP_OPTIONS && path == "*") { if (method == HTTP_OPTIONS && req.path == "*") {
req.path = ""; req.path = StringRef{};
} else { } else {
req.path = http2::rewrite_clean_path(std::begin(path), std::end(path)); req.path = http2::rewrite_clean_path(balloc, req.path);
} }
if (host) { if (host) {
req.authority = host->value.str(); req.authority = host->value;
} }
if (handler->get_ssl()) { if (handler->get_ssl()) {
req.scheme = "https"; req.scheme = StringRef::from_lit("https");
} else { } else {
req.scheme = "http"; req.scheme = StringRef::from_lit("http");
} }
} else { } else {
rewrite_request_host_path_from_uri(req, path.c_str(), u); rewrite_request_host_path_from_uri(balloc, req, req.path, u);
} }
} }

View File

@ -116,6 +116,8 @@ mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request(); auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
check_phase(mrb, data->phase, PHASE_REQUEST); check_phase(mrb, data->phase, PHASE_REQUEST);
const char *authority; const char *authority;
@ -125,7 +127,8 @@ mrb_value request_set_authority(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string"); mrb_raise(mrb, E_RUNTIME_ERROR, "authority must not be empty string");
} }
req.authority.assign(authority, n); req.authority =
make_string_ref(balloc, StringRef{authority, static_cast<size_t>(n)});
return self; return self;
} }
@ -147,6 +150,8 @@ mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request(); auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
check_phase(mrb, data->phase, PHASE_REQUEST); check_phase(mrb, data->phase, PHASE_REQUEST);
const char *scheme; const char *scheme;
@ -156,7 +161,8 @@ mrb_value request_set_scheme(mrb_state *mrb, mrb_value self) {
mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string"); mrb_raise(mrb, E_RUNTIME_ERROR, "scheme must not be empty string");
} }
req.scheme.assign(scheme, n); req.scheme =
make_string_ref(balloc, StringRef{scheme, static_cast<size_t>(n)});
return self; return self;
} }
@ -178,13 +184,16 @@ mrb_value request_set_path(mrb_state *mrb, mrb_value self) {
auto downstream = data->downstream; auto downstream = data->downstream;
auto &req = downstream->request(); auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
check_phase(mrb, data->phase, PHASE_REQUEST); check_phase(mrb, data->phase, PHASE_REQUEST);
const char *path; const char *path;
mrb_int pathlen; mrb_int pathlen;
mrb_get_args(mrb, "s", &path, &pathlen); mrb_get_args(mrb, "s", &path, &pathlen);
req.path.assign(path, pathlen); req.path =
make_string_ref(balloc, StringRef{path, static_cast<size_t>(pathlen)});
return self; return self;
} }

View File

@ -187,6 +187,8 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
auto &resp = downstream->response(); auto &resp = downstream->response();
int rv; int rv;
auto &balloc = downstream->get_block_allocator();
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed"); mrb_raise(mrb, E_RUNTIME_ERROR, "response has already been committed");
} }
@ -207,14 +209,22 @@ mrb_value response_return(mrb_state *mrb, mrb_value self) {
bodylen = vallen; bodylen = vallen;
} }
StringRef content_length;
{
auto iov = make_byte_ref(balloc, str_size("18446744073709551615") + 1);
auto p = iov.base;
p = util::utos(p, bodylen);
*p = '\0';
content_length = StringRef{iov.base, p};
}
auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH); auto cl = resp.fs.header(http2::HD_CONTENT_LENGTH);
if (cl) { if (cl) {
cl->value = make_string_ref(downstream->get_block_allocator(), cl->value = content_length;
StringRef{util::utos(bodylen)});
} else { } else {
// TODO we don't have to make a copy here.
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, content_length, false, http2::HD_CONTENT_LENGTH);
http2::HD_CONTENT_LENGTH);
} }
resp.fs.content_length = bodylen; resp.fs.content_length = bodylen;

View File

@ -159,6 +159,8 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
auto &req = downstream->request(); auto &req = downstream->request();
auto &balloc = downstream->get_block_allocator();
downstream->reset_upstream_rtimer(); downstream->reset_upstream_rtimer();
auto nv = frame->syn_stream.nv; auto nv = frame->syn_stream.nv;
@ -262,17 +264,16 @@ 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.str(); req.authority = path->value;
} else { } else {
req.scheme = scheme->value.str(); req.scheme = scheme->value;
req.authority = host->value.str(); req.authority = host->value;
if (get_config()->http2_proxy) { if (get_config()->http2_proxy) {
req.path = path->value.str(); req.path = path->value;
} 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 {
req.path = http2::rewrite_clean_path(std::begin(path->value), req.path = http2::rewrite_clean_path(balloc, path->value);
std::end(path->value));
} }
} }

View File

@ -409,7 +409,8 @@ public:
: base(&*first), len(std::distance(first, last)) {} : base(&*first), len(std::distance(first, last)) {}
template <typename InputIt> template <typename InputIt>
StringRef(InputIt *first, InputIt *last) StringRef(InputIt *first, InputIt *last)
: base(first), len(std::distance(first, last)) {} : base(reinterpret_cast<const char *>(first)),
len(std::distance(first, last)) {}
template <typename CharT, size_t N> template <typename CharT, size_t N>
constexpr static StringRef from_lit(const CharT(&s)[N]) { constexpr static StringRef from_lit(const CharT(&s)[N]) {
return StringRef(s, N - 1); return StringRef(s, N - 1);

View File

@ -387,6 +387,23 @@ template <typename T> std::string utos(T n) {
return res; return res;
} }
template <typename T, typename OutputIt> OutputIt utos(OutputIt dst, T n) {
if (n == 0) {
*dst++ = '0';
return dst;
}
int i = 0;
T t = n;
for (; t; t /= 10, ++i)
;
--i;
auto p = dst + i;
for (; n; --i, n /= 10) {
*p-- = (n % 10) + '0';
}
return dst + i + 1;
}
template <typename T> std::string utos_unit(T n) { template <typename T> std::string utos_unit(T n) {
char u = 0; char u = 0;
if (n >= (1 << 30)) { if (n >= (1 << 30)) {
@ -695,6 +712,11 @@ std::string random_alpha_digit(Generator &gen, size_t len) {
return res; return res;
} }
template <typename OutputIterator, typename CharT, size_t N>
OutputIterator copy_lit(OutputIterator it, CharT(&s)[N]) {
return std::copy_n(s, N - 1, it);
}
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2