Commit b1edb1f3 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Don't index name/value pair bearing NO_INDEX flag when forwarding it

parent c53c1dc6
......@@ -1342,6 +1342,10 @@ typedef int (*nghttp2_on_begin_headers_callback)
* The |value| of length |valuelen| is header value. The |flags| is
* bitwise OR of one or more of :type:`nghttp2_nv_flag`.
*
* If :enum:`NGHTTP2_NV_FLAG_NO_INDEX` is set in |flags|, the receiver
* must not index this name/value pair when forwarding it to the next
* hop.
*
* When this callback is invoked, ``frame->hd.type`` is either
* :enum:`NGHTTP2_HEADERS` or :enum:`NGHTTP2_PUSH_PROMISE`. After all
* header name/value pairs are processed with this callback, and no
......
......@@ -805,7 +805,7 @@ int Http2Handler::submit_response
http2::make_nv_ls("date", date_str)
};
for(size_t i = 0; i < headers.size(); ++i) {
nva.push_back(http2::make_nv(headers[i].first, headers[i].second));
nva.push_back(http2::make_nv(headers[i].first, headers[i].second, false));
}
int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
data_prd);
......@@ -830,21 +830,21 @@ int Http2Handler::submit_push_promise(Stream *stream,
std::string authority;
auto itr = std::lower_bound(std::begin(stream->headers),
std::end(stream->headers),
std::make_pair(std::string(":authority"),
std::string("")));
if(itr == std::end(stream->headers) || (*itr).first != ":authority") {
Header(":authority", ""));
if(itr == std::end(stream->headers) || (*itr).name != ":authority") {
itr = std::lower_bound(std::begin(stream->headers),
std::end(stream->headers),
std::make_pair(std::string("host"),
std::string("")));
Header("host", ""));
}
auto nva = std::vector<nghttp2_nv>{
http2::make_nv_ll(":method", "GET"),
http2::make_nv_ls(":path", push_path),
get_config()->no_tls ?
http2::make_nv_ll(":scheme", "http") :
http2::make_nv_ll(":scheme", "https"),
http2::make_nv_ls(":authority", (*itr).second)
http2::make_nv_ls(":authority", (*itr).value)
};
return nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_END_HEADERS,
stream->stream_id, nva.data(), nva.size(),
......@@ -1008,18 +1008,17 @@ void prepare_response(Stream *stream, Http2Handler *hd, bool allow_push = true)
int rv;
auto url = (*std::lower_bound(std::begin(stream->headers),
std::end(stream->headers),
std::make_pair(std::string(":path"),
std::string()))).second;
Header(":path", ""))).value;
auto ims = std::lower_bound(std::begin(stream->headers),
std::end(stream->headers),
std::make_pair(std::string("if-modified-since"),
std::string()));
Header("if-modified-since", ""));
time_t last_mod = 0;
bool last_mod_found = false;
if(ims != std::end(stream->headers) &&
(*ims).first == "if-modified-since") {
(*ims).name == "if-modified-since") {
last_mod_found = true;
last_mod = util::parse_http_date((*ims).second);
last_mod = util::parse_http_date((*ims).value);
}
auto query_pos = url.find("?");
if(query_pos != std::string::npos) {
......@@ -1078,7 +1077,8 @@ void append_nv(Stream *stream, const std::vector<nghttp2_nv>& nva)
{
for(auto& nv : nva) {
http2::split_add_header(stream->headers,
nv.name, nv.namelen, nv.value, nv.valuelen);
nv.name, nv.namelen, nv.value, nv.valuelen,
nv.flags & NGHTTP2_NV_FLAG_NO_INDEX);
}
}
} // namespace
......@@ -1114,7 +1114,8 @@ int on_header_callback(nghttp2_session *session,
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
http2::split_add_header(stream->headers, name, namelen, value, valuelen);
http2::split_add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
return 0;
}
} // namespace
......
......@@ -792,7 +792,7 @@ int main(int argc, char **argv)
nva.push_back(http2::make_nv_ls(":path", req));
for(auto& nv : shared_nva) {
nva.push_back(http2::make_nv(nv.first, nv.second));
nva.push_back(http2::make_nv(nv.name, nv.value, false));
}
config.nva.push_back(std::move(nva));
......@@ -804,12 +804,12 @@ int main(int argc, char **argv)
cva.push_back(req.c_str());
for(auto& nv : shared_nva) {
if(nv.first == ":authority") {
if(nv.name == ":authority") {
cva.push_back(":host");
} else {
cva.push_back(nv.first.c_str());
cva.push_back(nv.name.c_str());
}
cva.push_back(nv.second.c_str());
cva.push_back(nv.value.c_str());
}
cva.push_back(":version");
cva.push_back("HTTP/1.1");
......
......@@ -206,14 +206,14 @@ auto nv_name_less = [](const nghttp2_nv& lhs, const nghttp2_nv& rhs)
bool name_less(const Headers::value_type& lhs,
const Headers::value_type& rhs)
{
return lhs.first < rhs.first;
return lhs.name < rhs.name;
}
bool check_http2_headers(const Headers& nva)
{
for(size_t i = 0; i < DISALLOWED_HDLEN; ++i) {
if(std::binary_search(std::begin(nva), std::end(nva),
std::make_pair(DISALLOWED_HD[i], ""), name_less)) {
Header(DISALLOWED_HD[i], ""), name_less)) {
return false;
}
}
......@@ -223,7 +223,7 @@ bool check_http2_headers(const Headers& nva)
void normalize_headers(Headers& nva)
{
for(auto& kv : nva) {
util::inp_strlower(kv.first);
util::inp_strlower(kv.name);
}
std::stable_sort(std::begin(nva), std::end(nva), name_less);
}
......@@ -260,20 +260,23 @@ std::vector<nghttp2_nv> sort_nva(const nghttp2_nv *nva, size_t nvlen)
}
Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
const uint8_t *value, size_t valuelen,
bool no_index)
{
return std::make_pair(std::string(reinterpret_cast<const char*>(name),
return Header(std::string(reinterpret_cast<const char*>(name),
namelen),
std::string(reinterpret_cast<const char*>(value),
valuelen));
valuelen),
no_index);
}
void split_add_header(Headers& nva,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
const uint8_t *value, size_t valuelen,
bool no_index)
{
if(valuelen == 0) {
nva.push_back(to_header(name, namelen, value, valuelen));
nva.push_back(to_header(name, namelen, value, valuelen, no_index));
return;
}
auto j = value;
......@@ -289,7 +292,7 @@ void split_add_header(Headers& nva,
break;
}
auto l = std::find(j, end, '\0');
nva.push_back(to_header(name, namelen, j, l-j));
nva.push_back(to_header(name, namelen, j, l-j, no_index));
j = l;
}
}
......@@ -299,9 +302,9 @@ const Headers::value_type* get_unique_header(const Headers& nva,
{
auto nv = Headers::value_type(name, "");
auto i = std::lower_bound(std::begin(nva), std::end(nva), nv, name_less);
if(i != std::end(nva) && (*i).first == nv.first) {
if(i != std::end(nva) && (*i).name == nv.name) {
auto j = i + 1;
if(j == std::end(nva) || (*j).first != nv.first) {
if(j == std::end(nva) || (*j).name != nv.name) {
return &(*i);
}
}
......@@ -312,7 +315,7 @@ const Headers::value_type* get_header(const Headers& nva, const char *name)
{
auto nv = Headers::value_type(name, "");
auto i = std::lower_bound(std::begin(nva), std::end(nva), nv, name_less);
if(i != std::end(nva) && (*i).first == nv.first) {
if(i != std::end(nva) && (*i).name == nv.name) {
return &(*i);
}
return nullptr;
......@@ -321,14 +324,14 @@ const Headers::value_type* get_header(const Headers& nva, const char *name)
std::string value_to_str(const Headers::value_type *nv)
{
if(nv) {
return nv->second;
return nv->value;
}
return "";
}
bool value_lws(const Headers::value_type *nv)
{
return (*nv).second.find_first_not_of("\t ") == std::string::npos;
return (*nv).value.find_first_not_of("\t ") == std::string::npos;
}
bool non_empty_value(const Headers::value_type *nv)
......@@ -336,13 +339,18 @@ bool non_empty_value(const Headers::value_type *nv)
return nv && !value_lws(nv);
}
nghttp2_nv make_nv(const std::string& name, const std::string& value)
nghttp2_nv make_nv(const std::string& name, const std::string& value,
bool no_index)
{
uint8_t flags;
flags = no_index ? NGHTTP2_NV_FLAG_NO_INDEX : NGHTTP2_NV_FLAG_NONE;
return {
(uint8_t*)name.c_str(),
(uint8_t*)value.c_str(),
(uint16_t)name.size(), (uint16_t)value.size(),
NGHTTP2_NV_FLAG_NONE
flags
};
}
......@@ -351,12 +359,17 @@ Headers concat_norm_headers(Headers headers)
auto res = Headers();
res.reserve(headers.size());
for(auto& kv : headers) {
if(!res.empty() && res.back().first == kv.first &&
kv.first != "cookie" && kv.first != "set-cookie") {
if(!kv.second.empty()) {
res.back().second.append(1, '\0');
res.back().second += kv.second;
if(!res.empty() && res.back().name == kv.name &&
kv.name != "cookie" && kv.name != "set-cookie") {
auto& last = res.back();
if(!kv.value.empty()) {
last.value.append(1, '\0');
last.value += kv.value;
}
// We do ORing nv flags. This is done even if value is empty.
last.no_index |= kv.no_index;
} else {
res.push_back(std::move(kv));
}
......@@ -369,10 +382,11 @@ void copy_norm_headers_to_nva
{
size_t i, j;
for(i = 0, j = 0; i < headers.size() && j < IGN_HDLEN;) {
int rv = strcmp(headers[i].first.c_str(), IGN_HD[j]);
auto& kv = headers[i];
int rv = strcmp(kv.name.c_str(), IGN_HD[j]);
if(rv < 0) {
if(!headers[i].first.empty() && headers[i].first.c_str()[0] != ':') {
nva.push_back(make_nv(headers[i].first, headers[i].second));
if(!kv.name.empty() && kv.name.c_str()[0] != ':') {
nva.push_back(make_nv(kv.name, kv.value, kv.no_index));
}
++i;
} else if(rv > 0) {
......@@ -382,8 +396,9 @@ void copy_norm_headers_to_nva
}
}
for(; i < headers.size(); ++i) {
if(!headers[i].first.empty() && headers[i].first.c_str()[0] != ':') {
nva.push_back(make_nv(headers[i].first, headers[i].second));
auto& kv = headers[i];
if(!kv.name.empty() && kv.name.c_str()[0] != ':') {
nva.push_back(make_nv(kv.name, kv.value, kv.no_index));
}
}
}
......@@ -393,14 +408,16 @@ void build_http1_headers_from_norm_headers
{
size_t i, j;
for(i = 0, j = 0; i < headers.size() && j < HTTP1_IGN_HDLEN;) {
int rv = strcmp(headers[i].first.c_str(), HTTP1_IGN_HD[j]);
auto& kv = headers[i];
auto rv = strcmp(kv.name.c_str(), HTTP1_IGN_HD[j]);
if(rv < 0) {
if(!headers[i].first.empty() && headers[i].first.c_str()[0] != ':') {
hdrs += headers[i].first;
capitalize(hdrs, hdrs.size()-headers[i].first.size());
if(!kv.name.empty() && kv.name.c_str()[0] != ':') {
hdrs += kv.name;
capitalize(hdrs, hdrs.size() - kv.name.size());
hdrs += ": ";
hdrs += headers[i].second;
sanitize_header_value(hdrs, hdrs.size() - headers[i].second.size());
hdrs += kv.value;
sanitize_header_value(hdrs, hdrs.size() - kv.value.size());
hdrs += "\r\n";
}
++i;
......@@ -411,12 +428,14 @@ void build_http1_headers_from_norm_headers
}
}
for(; i < headers.size(); ++i) {
if(!headers[i].first.empty() && headers[i].first.c_str()[0] != ':') {
hdrs += headers[i].first;
capitalize(hdrs, hdrs.size()-headers[i].first.size());
auto& kv = headers[i];
if(!kv.name.empty() && kv.name.c_str()[0] != ':') {
hdrs += kv.name;
capitalize(hdrs, hdrs.size() - kv.name.size());
hdrs += ": ";
hdrs += headers[i].second;
sanitize_header_value(hdrs, hdrs.size() - headers[i].second.size());
hdrs += kv.value;
sanitize_header_value(hdrs, hdrs.size() - kv.value.size());
hdrs += "\r\n";
}
}
......@@ -472,9 +491,9 @@ void dump_nv(FILE *out, const nghttp2_nv *nva, size_t nvlen)
void dump_nv(FILE *out, const Headers& nva)
{
for(auto& nv : nva) {
fwrite(nv.first.c_str(), nv.first.size(), 1, out);
fwrite(nv.name.c_str(), nv.name.size(), 1, out);
fwrite(": ", 2, 1, out);
fwrite(nv.second.c_str(), nv.second.size(), 1, out);
fwrite(nv.value.c_str(), nv.value.size(), 1, out);
fwrite("\n", 1, 1, out);
}
fwrite("\n", 1, 1, out);
......
......@@ -38,7 +38,33 @@
namespace nghttp2 {
typedef std::vector<std::pair<std::string, std::string>> Headers;
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()
: no_index(false)
{}
bool operator==(const Header& other) const
{
return name == other.name && value == other.value;
}
bool operator<(const Header& rhs) const
{
return name < rhs.name || (name == rhs.name && value < rhs.value);
}
std::string name;
std::string value;
bool no_index;
};
typedef std::vector<Header> Headers;
namespace http2 {
......@@ -75,15 +101,18 @@ bool name_less(const Headers::value_type& lhs, const Headers::value_type& rhs);
void normalize_headers(Headers& nva);
Headers::value_type to_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
const uint8_t *value, size_t valuelen,
bool no_index);
// Add name/value pairs to |nva|. The name is given in the |name| with
// |namelen| bytes. This function inspects the |value| and split it
// using '\0' as delimiter. Each token is added to the |nva| with the
// name |name|.
// name |name|. If |no_index| is true, this name/value pair won't be
// indexed when it is forwarded to the next hop.
void split_add_header(Headers& nva,
const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
const uint8_t *value, size_t valuelen,
bool no_index);
// Returns sorted |nva| with |nvlen| elements. The headers are sorted
// by name only and not necessarily stable. In addition to the
......@@ -122,8 +151,10 @@ Headers concat_norm_headers(Headers headers);
// Creates nghttp2_nv using |name| and |value| and returns it. The
// returned value only references the data pointer to name.c_str() and
// value.c_str().
nghttp2_nv make_nv(const std::string& name, const std::string& value);
// value.c_str(). If |no_index| is true, nghttp2_nv flags member has
// NGHTTP2_NV_FLAG_NO_INDEX flag set.
nghttp2_nv make_nv(const std::string& name, const std::string& value,
bool no_index);
// Create nghttp2_nv from string literal |name| and |value|.
template<size_t N, size_t M>
......
......@@ -44,13 +44,12 @@ using namespace nghttp2;
namespace shrpx {
namespace {
void check_nv(const std::pair<std::string, std::string>& a,
const nghttp2_nv *b)
void check_nv(const Header& a, const nghttp2_nv *b)
{
CU_ASSERT(a.first.size() == b->namelen);
CU_ASSERT(a.second.size() == b->valuelen);
CU_ASSERT(memcmp(a.first.c_str(), b->name, b->namelen) == 0);
CU_ASSERT(memcmp(a.second.c_str(), b->value, b->valuelen) == 0);
CU_ASSERT(a.name.size() == b->namelen);
CU_ASSERT(a.value.size() == b->valuelen);
CU_ASSERT(memcmp(a.name.c_str(), b->name, b->namelen) == 0);
CU_ASSERT(memcmp(a.value.c_str(), b->value, b->valuelen) == 0);
}
} // namespace
......@@ -78,7 +77,7 @@ void test_http2_split_add_header(void)
const uint8_t concatval[] = { '4', 0x00, 0x00, '6', 0x00, '5', '9', 0x00 };
auto nva = Headers();
http2::split_add_header(nva, (const uint8_t*)"delta", 5,
concatval, sizeof(concatval));
concatval, sizeof(concatval), false);
CU_ASSERT(Headers::value_type("delta", "4") == nva[0]);
CU_ASSERT(Headers::value_type("delta", "6") == nva[1]);
CU_ASSERT(Headers::value_type("delta", "59") == nva[2]);
......@@ -86,14 +85,16 @@ void test_http2_split_add_header(void)
nva.clear();
http2::split_add_header(nva, (const uint8_t*)"alpha", 5,
(const uint8_t*)"123", 3);
(const uint8_t*)"123", 3, false);
CU_ASSERT(Headers::value_type("alpha", "123") == nva[0]);
CU_ASSERT(!nva[0].no_index);
nva.clear();
http2::split_add_header(nva, (const uint8_t*)"alpha", 5,
(const uint8_t*)"", 0);
(const uint8_t*)"", 0, true);
CU_ASSERT(Headers::value_type("alpha", "") == nva[0]);
CU_ASSERT(nva[0].no_index);
}
void test_http2_check_http2_headers(void)
......@@ -133,7 +134,7 @@ void test_http2_get_unique_header(void)
const Headers::value_type *rv;
rv = http2::get_unique_header(nva, "delta");
CU_ASSERT(rv != nullptr);
CU_ASSERT("delta" == rv->first);
CU_ASSERT("delta" == rv->name);
rv = http2::get_unique_header(nva, "bravo");
CU_ASSERT(rv == nullptr);
......@@ -155,11 +156,11 @@ void test_http2_get_header(void)
const Headers::value_type *rv;
rv = http2::get_header(nva, "delta");
CU_ASSERT(rv != nullptr);
CU_ASSERT("delta" == rv->first);
CU_ASSERT("delta" == rv->name);
rv = http2::get_header(nva, "bravo");
CU_ASSERT(rv != nullptr);
CU_ASSERT("bravo" == rv->first);
CU_ASSERT("bravo" == rv->name);
rv = http2::get_header(nva, "foxtrot");
CU_ASSERT(rv == nullptr);
......@@ -182,8 +183,8 @@ void test_http2_value_lws(void)
}
namespace {
auto headers = std::vector<std::pair<std::string, std::string>>
{{"alpha", "0"},
auto headers = Headers
{{"alpha", "0", true},
{"bravo", "1"},
{"connection", "2"},
{"connection", "3"},
......@@ -207,7 +208,7 @@ void test_http2_concat_norm_headers(void)
hds.emplace_back("set-cookie", "buzz");
auto res = http2::concat_norm_headers(hds);
CU_ASSERT(14 == res.size());
CU_ASSERT(std::string("2") + '\0' + std::string("3") == res[2].second);
CU_ASSERT(std::string("2") + '\0' + std::string("3") == res[2].value);
}
void test_http2_copy_norm_headers_to_nva(void)
......@@ -218,6 +219,12 @@ void test_http2_copy_norm_headers_to_nva(void)
auto ans = std::vector<int>{0, 1, 4, 6, 7, 12};
for(size_t i = 0; i < ans.size(); ++i) {
check_nv(headers[ans[i]], &nva[i]);
if(ans[i] == 0) {
CU_ASSERT(nva[i].flags & NGHTTP2_NV_FLAG_NO_INDEX);
} else {
CU_ASSERT(NGHTTP2_NV_FLAG_NONE == nva[i].flags);
}
}
}
......
......@@ -921,7 +921,7 @@ int submit_request
{
auto path = req->make_reqpath();
auto scheme = util::get_uri_field(req->uri.c_str(), req->u, UF_SCHEMA);
auto build_headers = std::vector<std::pair<std::string, std::string>>
auto build_headers = Headers
{{":method", req->data_prd ? "POST" : "GET"},
{":path", path},
{":scheme", scheme},
......@@ -942,27 +942,30 @@ int submit_request
for(auto& kv : headers) {
size_t i;
for(i = 0; i < num_initial_headers; ++i) {
if(util::strieq(kv.first, build_headers[i].first)) {
build_headers[i].second = kv.second;
if(util::strieq(kv.first, build_headers[i].name)) {
build_headers[i].value = kv.second;
break;
}
}
if(i < num_initial_headers) {
continue;
}
build_headers.push_back(kv);
build_headers.emplace_back(kv.first, kv.second);
}
std::stable_sort(std::begin(build_headers), std::end(build_headers),
[](const std::pair<std::string, std::string>& lhs,
const std::pair<std::string, std::string>& rhs)
[](const Headers::value_type& lhs,
const Headers::value_type& rhs)
{
return lhs.first < rhs.first;
return lhs.name < rhs.name;
});
build_headers = http2::concat_norm_headers(std::move(build_headers));
auto nva = std::vector<nghttp2_nv>();
nva.reserve(build_headers.size());
for(auto& kv : build_headers) {
nva.push_back(http2::make_nv(kv.first, kv.second));
nva.push_back(http2::make_nv(kv.name, kv.value, false));
}
auto rv = nghttp2_submit_request(client->session, &req->pri_spec,
......@@ -1157,13 +1160,13 @@ void check_response_header(nghttp2_session *session, Request* req)
{
bool gzip = false;
for(auto& nv : req->res_nva) {
if("content-encoding" == nv.first) {
gzip = util::strieq("gzip", nv.second) ||
util::strieq("deflate", nv.second);
if("content-encoding" == nv.name) {
gzip = util::strieq("gzip", nv.value) ||
util::strieq("deflate", nv.value);
continue;
}
if(":status" == nv.first) {
req->status.assign(nv.second);
if(":status" == nv.name) {
req->status.assign(nv.value);
}
}
if(gzip) {
......@@ -1228,7 +1231,8 @@ int on_header_callback(nghttp2_session *session,
if(!req) {
break;
}
http2::split_add_header(req->res_nva, name, namelen, value, valuelen);
http2::split_add_header(req->res_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
break;
}
case NGHTTP2_PUSH_PROMISE: {
......@@ -1237,7 +1241,8 @@ int on_header_callback(nghttp2_session *session,
if(!req) {
break;
}
http2::split_add_header(req->push_req_nva, name, namelen, value, valuelen);
http2::split_add_header(req->push_req_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
break;
}
}
......@@ -1284,20 +1289,20 @@ int on_frame_recv_callback2
}
std::string scheme, authority, method, path;
for(auto& nv : req->push_req_nva) {
if(nv.first == ":scheme") {
scheme = nv.second;
if(nv.name == ":scheme") {
scheme = nv.value;
continue;
}
if(nv.first == ":authority" || nv.first == "host") {
authority = nv.second;
if(nv.name == ":authority" || nv.name == "host") {
authority = nv.value;
continue;
}
if(nv.first == ":method") {
method = nv.second;
if(nv.name == ":method") {
method = nv.value;
continue;
}
if(nv.first == ":path") {
path = nv.second;
if(nv.name == ":path") {
path = nv.value;
continue;
}
}
......
......@@ -121,8 +121,8 @@ namespace {
void check_header_field(bool *result, const Headers::value_type &item,
const char *name, const char *value)
{
if(util::strieq(item.first.c_str(), name)) {
if(util::strifind(item.second.c_str(), value)) {
if(util::strieq(item.name.c_str(), name)) {
if(util::strifind(item.value.c_str(), value)) {
*result = true;
}
}
......@@ -150,8 +150,8 @@ Headers::const_iterator get_norm_header(const Headers& headers,
const std::string& name)
{
auto i = std::lower_bound(std::begin(headers), std::end(headers),
std::make_pair(name, ""), http2::name_less);
if(i != std::end(headers) && (*i).first == name) {
Header(name, ""), http2::name_less);
if(i != std::end(headers) && (*i).name == name) {
return i;
}
return std::end(headers);
......@@ -162,8 +162,8 @@ namespace {
Headers::iterator get_norm_header(Headers& headers, const std::string& name)
{
auto i = std::lower_bound(std::begin(headers), std::end(headers),
std::make_pair(name, ""), http2::name_less);
if(i != std::end(headers) && (*i).first == name) {
Header(name, ""), http2::name_less);
if(i != std::end(headers) && (*i).name == name) {
return i;
}
return std::end(headers);
......@@ -180,12 +180,12 @@ void Downstream::assemble_request_cookie()
std::string& cookie = assembled_request_cookie_;
cookie = "";
for(auto& kv : request_headers_) {
if(util::strieq("cookie", kv.first.c_str())) {
auto end = kv.second.find_last_not_of(" ;");
if(util::strieq("cookie", kv.name.c_str())) {
auto end = kv.value.find_last_not_of(" ;");
if(end == std::string::npos) {
cookie += kv.second;
cookie += kv.value;
} else {
cookie.append(std::begin(kv.second), std::begin(kv.second) + end + 1);
cookie.append(std::begin(kv.value), std::begin(kv.value) + end + 1);
}
cookie += "; ";
}
......@@ -199,19 +199,19 @@ void Downstream::crumble_request_cookie()
{
Headers cookie_hdrs;
for(auto& kv : request_headers_) {
if(util::strieq("cookie", kv.first.c_str())) {
size_t last = kv.second.size();
if(util::strieq("cookie", kv.name.c_str())) {
size_t last = kv.value.size();
size_t num = 0;
std::string rep_cookie;
for(size_t j = 0; j < last;) {
j = kv.second.find_first_not_of("\t ;", j);
j = kv.value.find_first_not_of("\t ;", j);
if(j == std::string::npos) {
break;
}
auto first = j;
j = kv.second.find(';', j);
j = kv.value.find(';', j);
if(j == std::string::npos) {
j = last;
}
......@@ -220,15 +220,16 @@ void Downstream::crumble_request_cookie()
if(first == 0 && j == last) {
break;
}
rep_cookie = kv.second.substr(first, j - first);
rep_cookie = kv.value.substr(first, j - first);
} else {
cookie_hdrs.push_back
(std::make_pair("cookie", kv.second.substr(first, j - first)));
(Header("cookie", kv.value.substr(first, j - first),
kv.no_index));
}
++num;
}
if(num > 0) {
kv.second = std::move(rep_cookie);
kv.value = std::move(rep_cookie);
}
}
}
......@@ -269,17 +270,19 @@ void Downstream::set_last_request_header_value(std::string value)
request_header_key_prev_ = false;
request_headers_sum_ += value.size();
Headers::value_type &item = request_headers_.back();
item.second = std::move(value);
item.value = std::move(value);
check_transfer_encoding_chunked(&chunked_request_, item);
check_expect_100_continue(&request_expect_100_continue_, item);
}
void Downstream::split_add_request_header
(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
const uint8_t *value, size_t valuelen,
bool no_index)
{
request_headers_sum_ += namelen + valuelen;
http2::split_add_header(request_headers_, name, namelen, value, valuelen);
http2::split_add_header(request_headers_, name, namelen, value, valuelen,
no_index);
}
bool Downstream::get_request_header_key_prev() const
......@@ -292,7 +295,7 @@ void Downstream::append_last_request_header_key(const char *data, size_t len)
assert(request_header_key_prev_);
request_headers_sum_ += len;
auto& item = request_headers_.back();
item.first.append(data, len);
item.name.append(data, len);
}
void Downstream::append_last_request_header_value(const char *data, size_t len)
......@@ -300,7 +303,7 @@ void Downstream::append_last_request_header_value(const char *data, size_t len)
assert(!request_header_key_prev_);
request_headers_sum_ += len;
auto& item = request_headers_.back();
item.second.append(data, len);
item.value.append(data, len);
}
size_t Downstream::get_request_headers_sum() const
......@@ -498,14 +501,14 @@ void Downstream::rewrite_norm_location_response_header
}
http_parser_url u;
memset(&u, 0, sizeof(u));
int rv = http_parser_parse_url((*hd).second.c_str(), (*hd).second.size(),
int rv = http_parser_parse_url((*hd).value.c_str(), (*hd).value.size(),
0, &u);
if(rv != 0) {
return;
}
std::string new_uri;
if(!request_http2_authority_.empty()) {
new_uri = http2::rewrite_location_uri((*hd).second, u,
new_uri = http2::rewrite_location_uri((*hd).value, u,
request_http2_authority_,
upstream_scheme, upstream_port);
}
......@@ -514,11 +517,11 @@ void Downstream::rewrite_norm_location_response_header
if(host == std::end(request_headers_)) {
return;
}
new_uri = http2::rewrite_location_uri((*hd).second, u, (*host).second,
new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value,
upstream_scheme, upstream_port);
}
if(!new_uri.empty()) {
(*hd).second = std::move(new_uri);
(*hd).value = std::move(new_uri);
}
}
......@@ -536,16 +539,18 @@ void Downstream::set_last_response_header_value(std::string value)
response_header_key_prev_ = false;
response_headers_sum_ += value.size();
auto& item = response_headers_.back();
item.second = std::move(value);
item.value = std::move(value);
check_transfer_encoding_chunked(&chunked_response_, item);
}
void Downstream::split_add_response_header
(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen)
const uint8_t *value, size_t valuelen,
bool no_index)
{
response_headers_sum_ += namelen + valuelen;
http2::split_add_header(response_headers_, name, namelen, value, valuelen);
http2::split_add_header(response_headers_, name, namelen, value, valuelen,
no_index);
}
bool Downstream::get_response_header_key_prev() const
......@@ -558,7 +563,7 @@ void Downstream::append_last_response_header_key(const char *data, size_t len)
assert(response_header_key_prev_);
response_headers_sum_ += len;
auto& item = response_headers_.back();
item.first.append(data, len);
item.name.append(data, len);
}
void Downstream::append_last_response_header_value(const char *data,
......@@ -567,7 +572,7 @@ void Downstream::append_last_response_header_value(const char *data,
assert(!response_header_key_prev_);
response_headers_sum_ += len;
auto& item = response_headers_.back();
item.second.append(data, len);
item.value.append(data, len);
}
size_t Downstream::get_response_headers_sum() const
......@@ -692,7 +697,7 @@ void Downstream::check_upgrade_fulfilled()
// TODO Do more strict checking for upgrade headers
if(response_http_status_ == 101) {
for(auto& hd : request_headers_) {
if(util::strieq("upgrade", hd.first.c_str())) {
if(util::strieq("upgrade", hd.name.c_str())) {
upgraded_ = true;
break;
}
......@@ -713,7 +718,7 @@ void Downstream::check_upgrade_request()
} else {
// TODO Do more strict checking for upgrade headers
for(auto& hd : request_headers_) {
if(util::strieq("upgrade", hd.first.c_str())) {
if(util::strieq("upgrade", hd.name.c_str())) {
upgrade_request_ = true;
break;
}
......@@ -737,12 +742,12 @@ bool Downstream::http2_upgrade_request() const
// For now just check NGHTTP2_CLEARTEXT_PROTO_VERSION_ID in
// Upgrade header field and existence of HTTP2-Settings header
// field.
if(util::strieq(hd.first.c_str(), "upgrade")) {
if(util::strieq(hd.second.c_str(),
if(util::strieq(hd.name.c_str(), "upgrade")) {
if(util::strieq(hd.value.c_str(),
NGHTTP2_CLEARTEXT_PROTO_VERSION_ID)) {
upgrade_seen = true;
}
} else if(util::strieq(hd.first.c_str(), "http2-settings")) {
} else if(util::strieq(hd.name.c_str(), "http2-settings")) {
http2_settings_seen = true;
}
}
......
......@@ -103,7 +103,8 @@ public:
void set_last_request_header_value(std::string value);
void split_add_request_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
const uint8_t *value, size_t valuelen,
bool no_index);
bool get_request_header_key_prev() const;
void append_last_request_header_key(const char *data, size_t len);
......@@ -171,7 +172,8 @@ public:
void set_last_response_header_value(std::string value);
void split_add_response_header(const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
const uint8_t *value, size_t valuelen,
bool no_index);
bool get_response_header_key_prev() const;
void append_last_response_header_key(const char *data, size_t len);
......
......@@ -78,13 +78,13 @@ void test_downstream_get_norm_request_header(void)
d.add_request_header("delta", "4");
d.add_request_header("echo", "5");
auto i = d.get_norm_request_header("alpha");
CU_ASSERT(std::make_pair(std::string("alpha"), std::string("0")) == *i);
CU_ASSERT(Header("alpha", "0") == *i);
i = d.get_norm_request_header("bravo");
CU_ASSERT(std::make_pair(std::string("bravo"), std::string("1")) == *i);
CU_ASSERT(Header("bravo", "1") == *i);
i = d.get_norm_request_header("delta");
CU_ASSERT(std::make_pair(std::string("delta"), std::string("4")) == *i);
CU_ASSERT(Header("delta", "4") == *i);
i = d.get_norm_request_header("echo");
CU_ASSERT(std::make_pair(std::string("echo"), std::string("5")) == *i);
CU_ASSERT(Header("echo", "5") == *i);
i = d.get_norm_request_header("foxtrot");
CU_ASSERT(i == std::end(d.get_request_headers()));
}
......@@ -99,13 +99,13 @@ void test_downstream_get_norm_response_header(void)
d.add_response_header("delta", "4");
d.add_response_header("echo", "5");
auto i = d.get_norm_response_header("alpha");
CU_ASSERT(std::make_pair(std::string("alpha"), std::string("0")) == *i);
CU_ASSERT(Header("alpha", "0") == *i);
i = d.get_norm_response_header("bravo");
CU_ASSERT(std::make_pair(std::string("bravo"), std::string("1")) == *i);
CU_ASSERT(Header("bravo", "1") == *i);
i = d.get_norm_response_header("delta");
CU_ASSERT(std::make_pair(std::string("delta"), std::string("4")) == *i);
CU_ASSERT(Header("delta", "4") == *i);
i = d.get_norm_response_header("echo");
CU_ASSERT(std::make_pair(std::string("echo"), std::string("5")) == *i);
CU_ASSERT(Header("echo", "5") == *i);
i = d.get_norm_response_header("foxtrot");
CU_ASSERT(i == std::end(d.get_response_headers()));
}
......@@ -120,13 +120,13 @@ void test_downstream_crumble_request_cookie(void)
d.add_request_header("cookie", "echo");
d.crumble_request_cookie();
Headers ans = {
std::make_pair(":method", "get"),
std::make_pair(":path", "/"),
std::make_pair("cookie", "alpha"),
std::make_pair("cookie", "delta"),
std::make_pair("cookie", "echo"),
std::make_pair("cookie", "bravo"),
std::make_pair("cookie", "charlie")
{":method", "get"},
{":path", "/"},
{"cookie", "alpha"},
{"cookie", "delta"},
{"cookie", "echo"},
{"cookie", "bravo"},
{"cookie", "charlie"}
};
CU_ASSERT(ans == d.get_request_headers());
}
......@@ -154,7 +154,7 @@ void test_downstream_rewrite_norm_location_response_header(void)
d.add_response_header("location", "http://localhost:3000/");
d.rewrite_norm_location_response_header("https", 443);
auto location = d.get_norm_response_header("location");
CU_ASSERT("https://localhost/" == (*location).second);
CU_ASSERT("https://localhost/" == (*location).value);
}
{
Downstream d(nullptr, 0, 0);
......@@ -162,7 +162,7 @@ void test_downstream_rewrite_norm_location_response_header(void)
d.add_response_header("location", "http://localhost/");
d.rewrite_norm_location_response_header("https", 443);
auto location = d.get_norm_response_header("location");
CU_ASSERT("https://localhost/" == (*location).second);
CU_ASSERT("https://localhost/" == (*location).value);
}
}
......
......@@ -356,21 +356,21 @@ int Http2DownstreamConnection::push_request_headers()
auto transfer_encoding =
downstream_->get_norm_request_header("transfer-encoding");
if(transfer_encoding != end_headers &&
util::strieq((*transfer_encoding).second.c_str(), "chunked")) {
util::strieq((*transfer_encoding).value.c_str(), "chunked")) {
chunked_encoding = true;
}
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
if(get_config()->add_x_forwarded_for) {
if(xff != end_headers) {
xff_value = (*xff).second;
xff_value = (*xff).value;
xff_value += ", ";
}
xff_value += downstream_->get_upstream()->get_client_handler()->
get_ipaddr();
nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
} else if(xff != end_headers) {
nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).second));
nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value));
}
if(downstream_->get_request_method() != "CONNECT") {
......@@ -389,11 +389,11 @@ int Http2DownstreamConnection::push_request_headers()
auto via = downstream_->get_norm_request_header("via");
if(get_config()->no_via) {
if(via != end_headers) {
nva.push_back(http2::make_nv_ls("via", (*via).second));
nva.push_back(http2::make_nv_ls("via", (*via).value));
}
} else {
if(via != end_headers) {
via_value = (*via).second;
via_value = (*via).value;
via_value += ", ";
}
via_value += http::create_via_header_value
......
......@@ -814,7 +814,8 @@ int on_header_callback(nghttp2_session *session,
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
downstream->split_add_response_header(name, namelen, value, valuelen);
downstream->split_add_response_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
return 0;
}
} // namespace
......@@ -886,7 +887,7 @@ int on_response_headers(Http2Session *http2session,
call_downstream_readcb(http2session, downstream);
return 0;
}
downstream->set_response_http_status(strtoul(status->second.c_str(),
downstream->set_response_http_status(strtoul(status->value.c_str(),
nullptr, 10));
downstream->set_response_major(2);
downstream->set_response_minor(0);
......@@ -916,7 +917,7 @@ int on_response_headers(Http2Session *http2session,
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
for(auto& nv : nva) {
ss << TTY_HTTP_HD << nv.first << TTY_RST << ": " << nv.second << "\n";
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
}
SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
<< frame->hd.stream_id
......
......@@ -101,8 +101,8 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http)
std::string settings_payload;
auto downstream = http->get_downstream();
for(auto& hd : downstream->get_request_headers()) {
if(util::strieq(hd.first.c_str(), "http2-settings")) {
auto val = hd.second;
if(util::strieq(hd.name.c_str(), "http2-settings")) {
auto val = hd.value;
util::to_base64(val);
settings_payload = base64::decode(std::begin(val), std::end(val));
break;
......@@ -205,7 +205,8 @@ int on_header_callback(nghttp2_session *session,
if(!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
downstream->split_add_request_header(name, namelen, value, valuelen);
downstream->split_add_request_header(name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
return 0;
}
} // namespace
......@@ -259,7 +260,7 @@ int on_request_headers(Http2Upstream *upstream,
if(LOG_ENABLED(INFO)) {
std::stringstream ss;
for(auto& nv : nva) {
ss << TTY_HTTP_HD << nv.first << TTY_RST << ": " << nv.second << "\n";
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
}
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
<< downstream->get_stream_id()
......@@ -280,7 +281,7 @@ int on_request_headers(Http2Upstream *upstream,
auto path = http2::get_unique_header(nva, ":path");
auto method = http2::get_unique_header(nva, ":method");
auto scheme = http2::get_unique_header(nva, ":scheme");
bool is_connect = method && "CONNECT" == method->second;
bool is_connect = method && "CONNECT" == method->value;
bool having_host = http2::non_empty_value(host);
bool having_authority = http2::non_empty_value(authority);
......@@ -1014,11 +1015,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream)
auto via = downstream->get_norm_response_header("via");
if(get_config()->no_via) {
if(via != end_headers) {
nva.push_back(http2::make_nv_ls("via", (*via).second));
nva.push_back(http2::make_nv_ls("via", (*via).value));
}
} else {
if(via != end_headers) {
via_value = (*via).second;
via_value = (*via).value;
via_value += ", ";
}
via_value += http::create_via_header_value
......
......@@ -171,16 +171,16 @@ int HttpDownstreamConnection::push_request_headers()
if(get_config()->add_x_forwarded_for) {
hdrs += "X-Forwarded-For: ";
if(xff != end_headers) {
hdrs += (*xff).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*xff).second.size());
hdrs += (*xff).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*xff).value.size());
hdrs += ", ";
}
hdrs += client_handler_->get_ipaddr();
hdrs += "\r\n";
} else if(xff != end_headers) {
hdrs += "X-Forwarded-For: ";
hdrs += (*xff).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*xff).second.size());
hdrs += (*xff).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*xff).value.size());
hdrs += "\r\n";
}
if(downstream_->get_request_method() != "CONNECT") {
......@@ -196,25 +196,25 @@ int HttpDownstreamConnection::push_request_headers()
}
auto expect = downstream_->get_norm_request_header("expect");
if(expect != end_headers &&
!util::strifind((*expect).second.c_str(), "100-continue")) {
!util::strifind((*expect).value.c_str(), "100-continue")) {
hdrs += "Expect: ";
hdrs += (*expect).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*expect).second.size());
hdrs += (*expect).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*expect).value.size());
hdrs += "\r\n";
}
auto via = downstream_->get_norm_request_header("via");
if(get_config()->no_via) {
if(via != end_headers) {
hdrs += "Via: ";
hdrs += (*via).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).second.size());
hdrs += (*via).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
hdrs += "\r\n";
}
} else {
hdrs += "Via: ";
if(via != end_headers) {
hdrs += (*via).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).second.size());
hdrs += (*via).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
hdrs += ", ";
}
hdrs += http::create_via_header_value(downstream_->get_request_major(),
......
......@@ -159,8 +159,8 @@ int htp_hdrs_completecb(http_parser *htp)
<< downstream->get_request_minor() << "\n";
const auto& headers = downstream->get_request_headers();
for(size_t i = 0; i < headers.size(); ++i) {
ss << TTY_HTTP_HD << headers[i].first << TTY_RST << ": "
<< headers[i].second << "\n";
ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": "
<< headers[i].value << "\n";
}
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
}
......@@ -682,15 +682,15 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
if(get_config()->no_via) {
if(via != end_headers) {
hdrs += "Via: ";
hdrs += (*via).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).second.size());
hdrs += (*via).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
hdrs += "\r\n";
}
} else {
hdrs += "Via: ";
if(via != end_headers) {
hdrs += (*via).second;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).second.size());
hdrs += (*via).value;
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
hdrs += ", ";
}
hdrs += http::create_via_header_value
......
......@@ -864,18 +864,18 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream)
nv[hdidx++] = ":version";
nv[hdidx++] = "HTTP/1.1";
for(auto& hd : downstream->get_response_headers()) {
if(hd.first.empty() || hd.first.c_str()[0] == ':' ||
util::strieq(hd.first.c_str(), "transfer-encoding") ||
util::strieq(hd.first.c_str(), "keep-alive") || // HTTP/1.0?
util::strieq(hd.first.c_str(), "connection") ||
util::strieq(hd.first.c_str(), "proxy-connection")) {
if(hd.name.empty() || hd.name.c_str()[0] == ':' ||
util::strieq(hd.name.c_str(), "transfer-encoding") ||
util::strieq(hd.name.c_str(), "keep-alive") || // HTTP/1.0?
util::strieq(hd.name.c_str(), "connection") ||
util::strieq(hd.name.c_str(), "proxy-connection")) {
// These are ignored
} else if(!get_config()->no_via &&
util::strieq(hd.first.c_str(), "via")) {
via_value = hd.second;
util::strieq(hd.name.c_str(), "via")) {
via_value = hd.value;
} else {
nv[hdidx++] = hd.first.c_str();
nv[hdidx++] = hd.second.c_str();
nv[hdidx++] = hd.name.c_str();
nv[hdidx++] = hd.value.c_str();
}
}
if(!get_config()->no_via) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment