src: Store token in Header object to avoid additional lookups

This commit is contained in:
Tatsuhiro Tsujikawa 2015-02-08 14:07:01 +09:00
parent 8dd8d68b83
commit b14cfaf308
11 changed files with 107 additions and 93 deletions

View File

@ -85,7 +85,7 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
http2::index_header(stream->hdidx, token, i);
}
http2::add_header(stream->headers, nv.name, nv.namelen, nv.value,
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
}
}
} // namespace
@ -1061,7 +1061,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
http2::index_header(stream->hdidx, token, stream->headers.size());
http2::add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
return 0;
}
} // namespace

View File

@ -165,14 +165,15 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field,
Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index) {
bool no_index, int16_t token) {
return Header(std::string(reinterpret_cast<const char *>(name), namelen),
std::string(reinterpret_cast<const char *>(value), valuelen),
no_index);
no_index, token);
}
void add_header(Headers &nva, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index) {
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token) {
if (valuelen > 0) {
size_t i, j;
for (i = 0; i < valuelen && (value[i] == ' ' || value[i] == '\t'); ++i)
@ -182,7 +183,7 @@ void add_header(Headers &nva, const uint8_t *name, size_t namelen,
value += i;
valuelen -= i + (valuelen - j - 1);
}
nva.push_back(to_header(name, namelen, value, valuelen, no_index));
nva.push_back(to_header(name, namelen, value, valuelen, no_index, token));
}
const Headers::value_type *get_header(const Headers &nva, const char *name) {
@ -221,7 +222,7 @@ void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
if (kv.name.empty() || kv.name[0] == ':') {
continue;
}
switch (lookup_token(kv.name)) {
switch (kv.token) {
case HD_COOKIE:
case HD_CONNECTION:
case HD_HOST:
@ -247,7 +248,7 @@ void build_http1_headers_from_headers(std::string &hdrs,
if (kv.name.empty() || kv.name[0] == ':') {
continue;
}
switch (lookup_token(kv.name)) {
switch (kv.token) {
case HD_CONNECTION:
case HD_COOKIE:
case HD_HOST:
@ -608,18 +609,7 @@ void init_hdidx(HeaderIndex &hdidx) {
std::fill(std::begin(hdidx), std::end(hdidx), -1);
}
void index_headers(HeaderIndex &hdidx, const Headers &headers) {
for (size_t i = 0; i < headers.size(); ++i) {
auto &kv = headers[i];
auto token = lookup_token(
reinterpret_cast<const uint8_t *>(kv.name.c_str()), kv.name.size());
if (token >= 0) {
http2::index_header(hdidx, token, i);
}
}
}
void index_header(HeaderIndex &hdidx, int token, size_t idx) {
void index_header(HeaderIndex &hdidx, int16_t token, size_t idx) {
if (token == -1) {
return;
}
@ -627,7 +617,8 @@ void index_header(HeaderIndex &hdidx, int token, size_t idx) {
hdidx[token] = idx;
}
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token) {
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx,
int16_t token) {
switch (token) {
case HD__AUTHORITY:
case HD__METHOD:
@ -639,7 +630,8 @@ bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token) {
}
}
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token) {
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx,
int16_t token) {
switch (token) {
case HD__STATUS:
return hdidx[token] == -1;
@ -648,7 +640,7 @@ bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token) {
}
}
bool http2_header_allowed(int token) {
bool http2_header_allowed(int16_t token) {
switch (token) {
case HD_CONNECTION:
case HD_KEEP_ALIVE:
@ -670,7 +662,7 @@ bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx) {
return true;
}
const Headers::value_type *get_header(const HeaderIndex &hdidx, int token,
const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
const Headers &nva) {
auto i = hdidx[token];
if (i == -1) {

View File

@ -40,10 +40,12 @@
namespace nghttp2 {
struct Header {
Header(std::string name, std::string value, bool no_index = false)
: name(std::move(name)), value(std::move(value)), no_index(no_index) {}
Header(std::string name, std::string value, bool no_index = false,
int16_t token = -1)
: name(std::move(name)), value(std::move(value)), token(token),
no_index(no_index) {}
Header() : no_index(false) {}
Header() : token(-1), no_index(false) {}
bool operator==(const Header &other) const {
return name == other.name && value == other.value;
@ -55,6 +57,7 @@ struct Header {
std::string name;
std::string value;
int16_t token;
bool no_index;
};
@ -77,13 +80,14 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field,
Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index);
bool no_index, int16_t token);
// Add name/value pairs to |nva|. If |no_index| is true, this
// name/value pair won't be indexed when it is forwarded to the next
// hop. This function strips white spaces around |value|.
void add_header(Headers &nva, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index);
const uint8_t *value, size_t valuelen, bool no_index,
int16_t token);
// Returns pointer to the entry in |nva| which has name |name|. If
// more than one entries which have the name |name|, last occurrence
@ -125,14 +129,16 @@ nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
NGHTTP2_NV_FLAG_NONE};
}
// Appends headers in |headers| to |nv|. Certain headers, including
// disallowed headers in HTTP/2 spec and headers which require
// special handling (i.e. via), are not copied.
// Appends headers in |headers| to |nv|. |headers| must be indexed
// 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);
// Appends HTTP/1.1 style header lines to |hdrs| from headers in
// |headers|. Certain headers, which requires special handling
// (i.e. via and cookie), are not appended.
// |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(std::string &hdrs,
const Headers &headers);
@ -226,28 +232,27 @@ int lookup_token(const std::string &name);
// array containing at least HD_MAXIDX elements.
void init_hdidx(HeaderIndex &hdidx);
// Indexes header |token| using index |idx|.
void index_header(HeaderIndex &hdidx, int token, size_t idx);
// Iterates |headers| and for each element, call index_header.
void index_headers(HeaderIndex &hdidx, const Headers &headers);
void index_header(HeaderIndex &hdidx, int16_t token, size_t idx);
// Returns true if HTTP/2 request pseudo header |token| is not indexed
// yet and not -1.
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int token);
bool check_http2_request_pseudo_header(const HeaderIndex &hdidx, int16_t token);
// Returns true if HTTP/2 response pseudo header |token| is not
// indexed yet and not -1.
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx, int token);
bool check_http2_response_pseudo_header(const HeaderIndex &hdidx,
int16_t token);
// Returns true if header field denoted by |token| is allowed for
// HTTP/2.
bool http2_header_allowed(int token);
bool http2_header_allowed(int16_t token);
// Returns true that |hdidx| contains mandatory HTTP/2 request
// headers.
bool http2_mandatory_request_headers_presence(const HeaderIndex &hdidx);
// Returns header denoted by |token| using index |hdidx|.
const Headers::value_type *get_header(const HeaderIndex &hdidx, int token,
const Headers::value_type *get_header(const HeaderIndex &hdidx, int16_t token,
const Headers &nva);
struct LinkHeader {

View File

@ -58,46 +58,52 @@ void test_http2_add_header(void) {
auto nva = Headers();
http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"123", 3,
false);
false, -1);
CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]);
CU_ASSERT(!nva[0].no_index);
nva.clear();
http2::add_header(nva, (const uint8_t *)"alpha", 5, (const uint8_t *)"", 0,
true);
true, -1);
CU_ASSERT(Headers::value_type("alpha", "") == nva[0]);
CU_ASSERT(nva[0].no_index);
nva.clear();
http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b", 2,
false);
false, -1);
CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
nva.clear();
http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)"b ", 2,
false);
false, -1);
CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
nva.clear();
http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" b ", 5,
false);
false, -1);
CU_ASSERT(Headers::value_type("a", "b") == nva[0]);
nva.clear();
http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" bravo ",
9, false);
9, false, -1);
CU_ASSERT(Headers::value_type("a", "bravo") == nva[0]);
nva.clear();
http2::add_header(nva, (const uint8_t *)"a", 1, (const uint8_t *)" ", 4,
false);
false, -1);
CU_ASSERT(Headers::value_type("a", "") == nva[0]);
nva.clear();
http2::add_header(nva, (const uint8_t *)"te", 2, (const uint8_t *)"trailers",
8, false, http2::HD_TE);
CU_ASSERT(http2::HD_TE == nva[0].token);
}
void test_http2_get_header(void) {
@ -128,19 +134,20 @@ void test_http2_get_header(void) {
}
namespace {
auto headers = Headers{{"alpha", "0", true},
{"bravo", "1"},
{"connection", "2"},
{"connection", "3"},
{"delta", "4"},
{"expect", "5"},
{"foxtrot", "6"},
{"tango", "7"},
{"te", "8"},
{"te", "9"},
{"x-forwarded-proto", "10"},
{"x-forwarded-proto", "11"},
{"zulu", "12"}};
auto headers =
Headers{{"alpha", "0", true},
{"bravo", "1"},
{"connection", "2", false, http2::HD_CONNECTION},
{"connection", "3", false, http2::HD_CONNECTION},
{"delta", "4"},
{"expect", "5"},
{"foxtrot", "6"},
{"tango", "7"},
{"te", "8", false, http2::HD_TE},
{"te", "9", false, http2::HD_TE},
{"x-forwarded-proto", "10", false, http2::HD_X_FORWARDED_FOR},
{"x-forwarded-proto", "11", false, http2::HD_X_FORWARDED_FOR},
{"zulu", "12"}};
} // namespace
void test_http2_copy_headers_to_nva(void) {

View File

@ -250,7 +250,7 @@ bool Request::is_ipv6_literal_addr() const {
}
}
bool Request::response_pseudo_header_allowed(int token) const {
bool Request::response_pseudo_header_allowed(int16_t token) const {
if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') {
return false;
}
@ -262,7 +262,7 @@ bool Request::response_pseudo_header_allowed(int token) const {
}
}
bool Request::push_request_pseudo_header_allowed(int token) const {
bool Request::push_request_pseudo_header_allowed(int16_t token) const {
if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') {
return false;
}
@ -277,7 +277,7 @@ bool Request::push_request_pseudo_header_allowed(int token) const {
}
}
Headers::value_type *Request::get_res_header(int token) {
Headers::value_type *Request::get_res_header(int16_t token) {
auto idx = res_hdidx[token];
if (idx == -1) {
return nullptr;
@ -285,7 +285,7 @@ Headers::value_type *Request::get_res_header(int token) {
return &res_nva[idx];
}
Headers::value_type *Request::get_req_header(int token) {
Headers::value_type *Request::get_req_header(int16_t token) {
auto idx = req_hdidx[token];
if (idx == -1) {
return nullptr;
@ -1688,7 +1688,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
http2::index_header(req->res_hdidx, token, req->res_nva.size());
http2::add_header(req->res_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
break;
}
case NGHTTP2_PUSH_PROMISE: {
@ -1712,7 +1712,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
http2::index_header(req->req_hdidx, token, req->req_nva.size());
http2::add_header(req->req_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
break;
}
}

View File

@ -122,11 +122,11 @@ struct Request {
bool is_ipv6_literal_addr() const;
bool response_pseudo_header_allowed(int token) const;
bool push_request_pseudo_header_allowed(int token) const;
bool response_pseudo_header_allowed(int16_t token) const;
bool push_request_pseudo_header_allowed(int16_t token) const;
Headers::value_type *get_res_header(int token);
Headers::value_type *get_req_header(int token);
Headers::value_type *get_res_header(int16_t token);
Headers::value_type *get_req_header(int16_t token);
void record_request_time();
void record_response_time();

View File

@ -300,6 +300,7 @@ int index_headers(http2::HeaderIndex &hdidx, Headers &headers,
continue;
}
kv.token = token;
http2::index_header(hdidx, token, i);
if (token == http2::HD_CONTENT_LENGTH) {
@ -322,7 +323,7 @@ int Downstream::index_request_headers() {
request_content_length_);
}
const Headers::value_type *Downstream::get_request_header(int token) const {
const Headers::value_type *Downstream::get_request_header(int16_t token) const {
return http2::get_header(request_hdidx_, token, request_headers_);
}
@ -346,10 +347,11 @@ void Downstream::set_last_request_header_value(std::string value) {
void Downstream::add_request_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int token) {
bool no_index, int16_t token) {
http2::index_header(request_hdidx_, token, request_headers_.size());
request_headers_sum_ += namelen + valuelen;
http2::add_header(request_headers_, name, namelen, value, valuelen, no_index);
http2::add_header(request_headers_, name, namelen, value, valuelen, no_index,
token);
}
bool Downstream::get_request_header_key_prev() const {
@ -525,7 +527,8 @@ int Downstream::index_response_headers() {
response_content_length_);
}
const Headers::value_type *Downstream::get_response_header(int token) const {
const Headers::value_type *
Downstream::get_response_header(int16_t token) const {
return http2::get_header(response_hdidx_, token, response_headers_);
}
@ -577,13 +580,21 @@ void Downstream::set_last_response_header_value(std::string value) {
item.value = std::move(value);
}
void Downstream::add_response_header(std::string name, std::string value,
int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
response_headers_sum_ += name.size() + value.size();
response_headers_.emplace_back(std::move(name), std::move(value), false,
token);
}
void Downstream::add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen,
bool no_index, int token) {
bool no_index, int16_t token) {
http2::index_header(response_hdidx_, token, response_headers_.size());
response_headers_sum_ += namelen + valuelen;
http2::add_header(response_headers_, name, namelen, value, valuelen,
no_index);
http2::add_header(response_headers_, name, namelen, value, valuelen, no_index,
token);
}
bool Downstream::get_response_header_key_prev() const {
@ -893,14 +904,14 @@ bool pseudo_header_allowed(const Headers &headers) {
}
} // namespace
bool Downstream::request_pseudo_header_allowed(int token) const {
bool Downstream::request_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(request_headers_)) {
return false;
}
return http2::check_http2_request_pseudo_header(request_hdidx_, token);
}
bool Downstream::response_pseudo_header_allowed(int token) const {
bool Downstream::response_pseudo_header_allowed(int16_t token) const {
if (!pseudo_header_allowed(response_headers_)) {
return false;
}

View File

@ -108,7 +108,7 @@ 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 *get_request_header(int token) const;
const Headers::value_type *get_request_header(int16_t token) const;
// Returns pointer to the request header with the name |name|. If
// no such header is found, returns nullptr.
const Headers::value_type *get_request_header(const std::string &name) const;
@ -117,7 +117,7 @@ public:
void add_request_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int token);
int16_t token);
bool get_request_header_key_prev() const;
void append_last_request_header_key(const char *data, size_t len);
@ -165,7 +165,7 @@ public:
bool validate_request_bodylen() const;
int64_t get_request_content_length() const;
void set_request_content_length(int64_t len);
bool request_pseudo_header_allowed(int token) const;
bool request_pseudo_header_allowed(int16_t token) const;
bool expect_response_body() const;
enum {
INITIAL,
@ -192,16 +192,17 @@ 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 response headers are indexed.
const Headers::value_type *get_response_header(int token) const;
const Headers::value_type *get_response_header(int16_t token) const;
// Rewrites the location response header field.
void rewrite_location_response_header(const std::string &upstream_scheme,
uint16_t upstream_port);
void add_response_header(std::string name, std::string value);
void set_last_response_header_value(std::string value);
void add_response_header(std::string name, std::string value, int16_t token);
void add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, bool no_index,
int token);
int16_t token);
bool get_response_header_key_prev() const;
void append_last_response_header_key(const char *data, size_t len);
@ -248,7 +249,7 @@ public:
void dec_response_datalen(size_t len);
size_t get_response_datalen() const;
void reset_response_datalen();
bool response_pseudo_header_allowed(int token) const;
bool response_pseudo_header_allowed(int16_t token) const;
// Call this method when there is incoming data in downstream
// connection.

View File

@ -823,7 +823,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
// Otherwise, use chunked encoding to keep upstream connection
// open. In HTTP2, we are supporsed not to receive
// transfer-encoding.
downstream->add_response_header("transfer-encoding", "chunked");
downstream->add_response_header("transfer-encoding", "chunked",
http2::HD_TRANSFER_ENCODING);
downstream->set_chunked_response(true);
}
}

View File

@ -1521,8 +1521,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
baselen = 1;
}
for (auto &kv : downstream->get_response_headers()) {
auto token = http2::lookup_token(kv.name);
if (token != http2::HD_LINK) {
if (kv.token != http2::HD_LINK) {
continue;
}
for (auto &link :
@ -1588,8 +1587,7 @@ int Http2Upstream::submit_push_promise(const std::string &path,
std::vector<nghttp2_nv> nva;
nva.reserve(downstream->get_request_headers().size());
for (auto &kv : downstream->get_request_headers()) {
auto token = http2::lookup_token(kv.name);
switch (token) {
switch (kv.token) {
case http2::HD__METHOD:
// juse use "GET" for now
nva.push_back(http2::make_nv_lc(":method", "GET"));

View File

@ -860,8 +860,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
if (hd.name.empty() || hd.name.c_str()[0] == ':') {
continue;
}
auto token = http2::lookup_token(hd.name);
switch (token) {
switch (hd.token) {
case http2::HD_CONNECTION:
case http2::HD_KEEP_ALIVE:
case http2::HD_PROXY_CONNECTION: