nghttpd, nghttpx: Rework incoming header handling
This commit is contained in:
parent
730d47f7ad
commit
3ae44ef2f3
|
@ -5,14 +5,25 @@ HEADERS = [
|
|||
':method',
|
||||
':path',
|
||||
':scheme',
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
':status',
|
||||
':host', # for spdy
|
||||
'expect',
|
||||
'host',
|
||||
'if-modified-since',
|
||||
"te",
|
||||
"cookie",
|
||||
"http2-settings",
|
||||
"server",
|
||||
"via",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-proto",
|
||||
"alt-svc",
|
||||
"content-length",
|
||||
"location",
|
||||
# disallowed h1 headers
|
||||
'connection',
|
||||
'keep-alive',
|
||||
'proxy-connection',
|
||||
'te',
|
||||
'transfer-encoding',
|
||||
'upgrade'
|
||||
]
|
||||
|
@ -20,9 +31,7 @@ HEADERS = [
|
|||
def to_enum_hd(k):
|
||||
res = 'HD_'
|
||||
for c in k.upper():
|
||||
if c == ':':
|
||||
continue
|
||||
if c == '-':
|
||||
if c == ':' or c == '-':
|
||||
res += '_'
|
||||
continue
|
||||
res += c
|
||||
|
@ -54,7 +63,7 @@ enum {'''
|
|||
|
||||
def gen_index_header():
|
||||
print '''\
|
||||
void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {'''
|
||||
b = build_header(HEADERS)
|
||||
for size in sorted(b.keys()):
|
||||
|
@ -70,8 +79,7 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
for k in headers:
|
||||
print '''\
|
||||
if (util::streq("{}", name, {})) {{
|
||||
hdidx[{}] = idx;
|
||||
return;
|
||||
return {};
|
||||
}}'''.format(k[:-1], size - 1, to_enum_hd(k))
|
||||
print '''\
|
||||
break;'''
|
||||
|
@ -80,6 +88,7 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
break;'''
|
||||
print '''\
|
||||
}
|
||||
return -1;
|
||||
}'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -89,9 +89,12 @@ void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
|
|||
|
||||
namespace {
|
||||
void append_nv(Stream *stream, const std::vector<nghttp2_nv> &nva) {
|
||||
size_t idx = 0;
|
||||
for (auto &nv : nva) {
|
||||
http2::index_header(stream->hdidx, nv.name, nv.namelen, idx++);
|
||||
for (size_t i = 0; i < nva.size(); ++i) {
|
||||
auto &nv = nva[i];
|
||||
auto token = http2::lookup_token(nv.name, nv.namelen);
|
||||
if (token != -1) {
|
||||
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);
|
||||
}
|
||||
|
@ -739,7 +742,7 @@ int Http2Handler::submit_non_final_response(const std::string &status,
|
|||
int Http2Handler::submit_push_promise(Stream *stream,
|
||||
const std::string &push_path) {
|
||||
auto authority =
|
||||
http2::get_header(stream->hdidx, http2::HD_AUTHORITY, stream->headers);
|
||||
http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers);
|
||||
|
||||
if (!authority) {
|
||||
authority =
|
||||
|
@ -900,9 +903,9 @@ void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
|||
const std::string &path,
|
||||
const std::string &status) {
|
||||
auto scheme =
|
||||
http2::get_header(stream->hdidx, http2::HD_SCHEME, stream->headers);
|
||||
http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers);
|
||||
auto authority =
|
||||
http2::get_header(stream->hdidx, http2::HD_AUTHORITY, stream->headers);
|
||||
http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers);
|
||||
if (!authority) {
|
||||
authority =
|
||||
http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
|
||||
|
@ -924,7 +927,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
|||
bool allow_push = true) {
|
||||
int rv;
|
||||
auto reqpath =
|
||||
http2::get_header(stream->hdidx, http2::HD_PATH, stream->headers)->value;
|
||||
http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers)->value;
|
||||
auto ims =
|
||||
get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers);
|
||||
|
||||
|
@ -1038,11 +1041,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (namelen > 0 && name[0] == ':') {
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if ((!stream->headers.empty() &&
|
||||
stream->headers.back().name.c_str()[0] != ':') ||
|
||||
!http2::check_http2_request_pseudo_header(stream->hdidx, name,
|
||||
namelen)) {
|
||||
!http2::check_http2_request_pseudo_header(stream->hdidx, token)) {
|
||||
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
|
@ -1050,8 +1054,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
}
|
||||
}
|
||||
|
||||
http2::index_header(stream->hdidx, name, namelen, stream->headers.size());
|
||||
if (!http2::http2_header_allowed(token)) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
http2::index_header(stream->hdidx, token, stream->headers.size());
|
||||
http2::add_header(stream->headers, name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
return 0;
|
||||
|
@ -1113,7 +1122,7 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
|||
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||
|
||||
if (!http2::check_http2_request_headers(stream->hdidx)) {
|
||||
if (!http2::http2_mandatory_request_headers_presence(stream->hdidx)) {
|
||||
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
|
479
src/http2.cc
479
src/http2.cc
|
@ -174,138 +174,6 @@ void copy_url_component(std::string &dest, const http_parser_url *u, int field,
|
|||
}
|
||||
}
|
||||
|
||||
bool check_http2_allowed_header(const char *name) {
|
||||
return check_http2_allowed_header(reinterpret_cast<const uint8_t *>(name),
|
||||
strlen(name));
|
||||
}
|
||||
|
||||
bool check_http2_allowed_header(const uint8_t *name, size_t namelen) {
|
||||
return !util::strieq("connection", name, namelen) &&
|
||||
!util::strieq("host", name, namelen) &&
|
||||
!util::strieq("keep-alive", name, namelen) &&
|
||||
!util::strieq("proxy-connection", name, namelen) &&
|
||||
!util::strieq("te", name, namelen) &&
|
||||
!util::strieq("transfer-encoding", name, namelen) &&
|
||||
!util::strieq("upgrade", name, namelen);
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char *DISALLOWED_HD[] = {
|
||||
"connection", "keep-alive", "proxy-connection",
|
||||
"te", "transfer-encoding", "upgrade",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto DISALLOWED_HDLEN = util::array_size(DISALLOWED_HD);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *REQUEST_PSEUDO_HD[] = {
|
||||
":authority", ":method", ":path", ":scheme",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto REQUEST_PSEUDO_HDLEN = util::array_size(REQUEST_PSEUDO_HD);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *RESPONSE_PSEUDO_HD[] = {
|
||||
":status",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto RESPONSE_PSEUDO_HDLEN = util::array_size(RESPONSE_PSEUDO_HD);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *IGN_HD[] = {
|
||||
"connection", "http2-settings", "keep-alive", "proxy-connection",
|
||||
"server", "te", "transfer-encoding", "upgrade",
|
||||
"via", "x-forwarded-for", "x-forwarded-proto",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto IGN_HDLEN = util::array_size(IGN_HD);
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
const char *HTTP1_IGN_HD[] = {
|
||||
"connection", "cookie", "http2-settings", "keep-alive",
|
||||
"proxy-connection", "server", "upgrade", "via",
|
||||
"x-forwarded-for", "x-forwarded-proto",
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
auto HTTP1_IGN_HDLEN = util::array_size(HTTP1_IGN_HD);
|
||||
} // namespace
|
||||
|
||||
bool name_less(const Headers::value_type &lhs, const Headers::value_type &rhs) {
|
||||
if (lhs.name.c_str()[0] == ':') {
|
||||
if (rhs.name.c_str()[0] != ':') {
|
||||
return true;
|
||||
}
|
||||
} else if (rhs.name.c_str()[0] == ':') {
|
||||
return false;
|
||||
}
|
||||
|
||||
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),
|
||||
Header(DISALLOWED_HD[i], ""), name_less)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_http2_request_headers(const Headers &nva) {
|
||||
return check_http2_headers(nva);
|
||||
}
|
||||
|
||||
bool check_http2_response_headers(const Headers &nva) {
|
||||
return check_http2_headers(nva);
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename InputIterator>
|
||||
bool check_pseudo_header(const uint8_t *name, size_t namelen,
|
||||
InputIterator allowed_first,
|
||||
InputIterator allowed_last) {
|
||||
for (auto i = allowed_first; i != allowed_last; ++i) {
|
||||
if (util::streq(*i, name, namelen)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool check_http2_request_pseudo_header(const uint8_t *name, size_t namelen) {
|
||||
return check_pseudo_header(name, namelen, REQUEST_PSEUDO_HD,
|
||||
REQUEST_PSEUDO_HD + REQUEST_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
bool check_http2_response_pseudo_header(const uint8_t *name, size_t namelen) {
|
||||
return check_pseudo_header(name, namelen, RESPONSE_PSEUDO_HD,
|
||||
RESPONSE_PSEUDO_HD + RESPONSE_PSEUDO_HDLEN);
|
||||
}
|
||||
|
||||
void normalize_headers(Headers &nva) {
|
||||
for (auto &kv : nva) {
|
||||
util::inp_strlower(kv.name);
|
||||
}
|
||||
std::stable_sort(std::begin(nva), std::end(nva), name_less);
|
||||
}
|
||||
|
||||
Headers::value_type to_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index) {
|
||||
|
@ -328,26 +196,14 @@ void add_header(Headers &nva, const uint8_t *name, size_t namelen,
|
|||
nva.push_back(to_header(name, namelen, value, valuelen, no_index));
|
||||
}
|
||||
|
||||
const Headers::value_type *get_unique_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).name == nv.name) {
|
||||
auto j = i + 1;
|
||||
if (j == std::end(nva) || (*j).name != nv.name) {
|
||||
return &(*i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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).name == nv.name) {
|
||||
return &(*i);
|
||||
const Headers::value_type *res = nullptr;
|
||||
for (auto &nv : nva) {
|
||||
if (nv.name == name) {
|
||||
res = &nv;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string value_to_str(const Headers::value_type *nv) {
|
||||
|
@ -371,40 +227,48 @@ nghttp2_nv make_nv(const std::string &name, const std::string &value,
|
|||
value.size(), flags};
|
||||
}
|
||||
|
||||
void copy_norm_headers_to_nva(std::vector<nghttp2_nv> &nva,
|
||||
const Headers &headers) {
|
||||
size_t i, j;
|
||||
for (i = 0, j = 0; i < headers.size() && j < IGN_HDLEN;) {
|
||||
auto &kv = headers[i];
|
||||
int rv = strcmp(kv.name.c_str(), IGN_HD[j]);
|
||||
if (rv < 0) {
|
||||
if (!kv.name.empty() && kv.name.c_str()[0] != ':') {
|
||||
nva.push_back(make_nv(kv.name, kv.value, kv.no_index));
|
||||
void copy_headers_to_nva(std::vector<nghttp2_nv> &nva, const Headers &headers) {
|
||||
for (auto &kv : headers) {
|
||||
if (kv.name.empty() || kv.name[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
} else if (rv > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
switch (lookup_token(kv.name)) {
|
||||
case HD_COOKIE:
|
||||
case HD_CONNECTION:
|
||||
case HD_HTTP2_SETTINGS:
|
||||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_SERVER:
|
||||
case HD_TRANSFER_ENCODING:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
case HD_X_FORWARDED_FOR:
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (; i < headers.size(); ++i) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void build_http1_headers_from_norm_headers(std::string &hdrs,
|
||||
void build_http1_headers_from_headers(std::string &hdrs,
|
||||
const Headers &headers) {
|
||||
size_t i, j;
|
||||
for (i = 0, j = 0; i < headers.size() && j < HTTP1_IGN_HDLEN;) {
|
||||
auto &kv = headers[i];
|
||||
auto rv = strcmp(kv.name.c_str(), HTTP1_IGN_HD[j]);
|
||||
|
||||
if (rv < 0) {
|
||||
if (!kv.name.empty() && kv.name.c_str()[0] != ':') {
|
||||
for (auto &kv : headers) {
|
||||
if (kv.name.empty() || kv.name[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
switch (lookup_token(kv.name)) {
|
||||
case HD_CONNECTION:
|
||||
case HD_COOKIE:
|
||||
case HD_HTTP2_SETTINGS:
|
||||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_SERVER:
|
||||
case HD_UPGRADE:
|
||||
case HD_VIA:
|
||||
case HD_X_FORWARDED_FOR:
|
||||
case HD_X_FORWARDED_PROTO:
|
||||
continue;
|
||||
}
|
||||
hdrs += kv.name;
|
||||
capitalize(hdrs, hdrs.size() - kv.name.size());
|
||||
hdrs += ": ";
|
||||
|
@ -412,25 +276,6 @@ void build_http1_headers_from_norm_headers(std::string &hdrs,
|
|||
sanitize_header_value(hdrs, hdrs.size() - kv.value.size());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
++i;
|
||||
} else if (rv > 0) {
|
||||
++j;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for (; i < headers.size(); ++i) {
|
||||
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 += kv.value;
|
||||
sanitize_header_value(hdrs, hdrs.size() - kv.value.size());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t determine_window_update_transmission(nghttp2_session *session,
|
||||
|
@ -572,18 +417,29 @@ int parse_http_status_code(const std::string &src) {
|
|||
return status;
|
||||
}
|
||||
|
||||
void init_hdidx(int *hdidx) { memset(hdidx, -1, sizeof(hdidx[0]) * HD_MAXIDX); }
|
||||
int lookup_token(const std::string &name) {
|
||||
return lookup_token(reinterpret_cast<const uint8_t *>(name.c_str()),
|
||||
name.size());
|
||||
}
|
||||
|
||||
// This function was generated by genheaderfunc.py. Inspired by h2o
|
||||
// header lookup. https://github.com/h2o/h2o
|
||||
void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
||||
int lookup_token(const uint8_t *name, size_t namelen) {
|
||||
switch (namelen) {
|
||||
case 2:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'e':
|
||||
if (util::streq("t", name, 1)) {
|
||||
hdidx[HD_TE] = idx;
|
||||
return;
|
||||
return HD_TE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'a':
|
||||
if (util::streq("vi", name, 2)) {
|
||||
return HD_VIA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -592,8 +448,7 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 't':
|
||||
if (util::streq("hos", name, 3)) {
|
||||
hdidx[HD_HOST] = idx;
|
||||
return;
|
||||
return HD_HOST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -602,38 +457,67 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'h':
|
||||
if (util::streq(":pat", name, 4)) {
|
||||
hdidx[HD_PATH] = idx;
|
||||
return;
|
||||
return HD__PATH;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::streq(":hos", name, 4)) {
|
||||
return HD__HOST;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'e':
|
||||
if (util::streq("cooki", name, 5)) {
|
||||
return HD_COOKIE;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (util::streq("serve", name, 5)) {
|
||||
return HD_SERVER;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (util::streq("expec", name, 5)) {
|
||||
hdidx[HD_EXPECT] = idx;
|
||||
return;
|
||||
return HD_EXPECT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'c':
|
||||
if (util::streq("alt-sv", name, 6)) {
|
||||
return HD_ALT_SVC;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (util::streq(":metho", name, 6)) {
|
||||
hdidx[HD_METHOD] = idx;
|
||||
return;
|
||||
return HD__METHOD;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::streq(":schem", name, 6)) {
|
||||
hdidx[HD_SCHEME] = idx;
|
||||
return;
|
||||
return HD__SCHEME;
|
||||
}
|
||||
if (util::streq("upgrad", name, 6)) {
|
||||
hdidx[HD_UPGRADE] = idx;
|
||||
return;
|
||||
return HD_UPGRADE;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::streq(":statu", name, 6)) {
|
||||
return HD__STATUS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'n':
|
||||
if (util::streq("locatio", name, 7)) {
|
||||
return HD_LOCATION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -642,20 +526,40 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'e':
|
||||
if (util::streq("keep-aliv", name, 9)) {
|
||||
hdidx[HD_KEEP_ALIVE] = idx;
|
||||
return;
|
||||
return HD_KEEP_ALIVE;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (util::streq("connectio", name, 9)) {
|
||||
hdidx[HD_CONNECTION] = idx;
|
||||
return;
|
||||
return HD_CONNECTION;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
if (util::streq(":authorit", name, 9)) {
|
||||
hdidx[HD_AUTHORITY] = idx;
|
||||
return;
|
||||
return HD__AUTHORITY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'h':
|
||||
if (util::streq("content-lengt", name, 13)) {
|
||||
return HD_CONTENT_LENGTH;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (util::streq("http2-setting", name, 13)) {
|
||||
return HD_HTTP2_SETTINGS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15:
|
||||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'r':
|
||||
if (util::streq("x-forwarded-fo", name, 14)) {
|
||||
return HD_X_FORWARDED_FOR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -664,8 +568,7 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'n':
|
||||
if (util::streq("proxy-connectio", name, 15)) {
|
||||
hdidx[HD_PROXY_CONNECTION] = idx;
|
||||
return;
|
||||
return HD_PROXY_CONNECTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -674,96 +577,92 @@ void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx) {
|
|||
switch (util::lowcase(name[namelen - 1])) {
|
||||
case 'e':
|
||||
if (util::streq("if-modified-sinc", name, 16)) {
|
||||
hdidx[HD_IF_MODIFIED_SINCE] = idx;
|
||||
return;
|
||||
return HD_IF_MODIFIED_SINCE;
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
if (util::streq("transfer-encodin", name, 16)) {
|
||||
hdidx[HD_TRANSFER_ENCODING] = idx;
|
||||
return HD_TRANSFER_ENCODING;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (util::streq("x-forwarded-prot", name, 16)) {
|
||||
return HD_X_FORWARDED_PROTO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void init_hdidx(int *hdidx) { memset(hdidx, -1, sizeof(hdidx[0]) * HD_MAXIDX); }
|
||||
|
||||
void index_headers(int *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(int *hdidx, int token, size_t idx) {
|
||||
if (token == -1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
assert(token < HD_MAXIDX);
|
||||
hdidx[token] = idx;
|
||||
}
|
||||
break;
|
||||
|
||||
bool check_http2_request_pseudo_header(const int *hdidx, int token) {
|
||||
switch (token) {
|
||||
case HD__AUTHORITY:
|
||||
case HD__METHOD:
|
||||
case HD__PATH:
|
||||
case HD__SCHEME:
|
||||
return hdidx[token] == -1;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_http2_request_pseudo_header(int *hdidx, const uint8_t *s,
|
||||
size_t len) {
|
||||
switch (len) {
|
||||
case 5:
|
||||
switch (util::lowcase(s[len - 1])) {
|
||||
case 'h':
|
||||
if (util::streq(":pat", s, 4)) {
|
||||
if (hdidx[HD_PATH] != -1) {
|
||||
bool check_http2_response_pseudo_header(const int *hdidx, int token) {
|
||||
switch (token) {
|
||||
case HD__STATUS:
|
||||
return hdidx[token] == -1;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch (util::lowcase(s[len - 1])) {
|
||||
case 'd':
|
||||
if (util::streq(":metho", s, 6)) {
|
||||
if (hdidx[HD_METHOD] != -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
if (util::streq(":schem", s, 6)) {
|
||||
if (hdidx[HD_SCHEME] != -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
switch (util::lowcase(s[len - 1])) {
|
||||
case 'y':
|
||||
if (util::streq(":authorit", s, 9)) {
|
||||
if (hdidx[HD_AUTHORITY] != -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_http2_headers(int *hdidx) {
|
||||
if (hdidx[HD_CONNECTION] != -1 || hdidx[HD_KEEP_ALIVE] != -1 ||
|
||||
hdidx[HD_PROXY_CONNECTION] != -1 || hdidx[HD_TE] != -1 ||
|
||||
hdidx[HD_TRANSFER_ENCODING] != -1 || hdidx[HD_UPGRADE] != -1) {
|
||||
bool http2_header_allowed(int token) {
|
||||
switch (token) {
|
||||
case HD_CONNECTION:
|
||||
case HD_KEEP_ALIVE:
|
||||
case HD_PROXY_CONNECTION:
|
||||
case HD_TRANSFER_ENCODING:
|
||||
case HD_UPGRADE:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool http2_mandatory_request_headers_presence(const int *hdidx) {
|
||||
if (hdidx[HD__METHOD] == -1 || hdidx[HD__PATH] == -1 ||
|
||||
hdidx[HD__SCHEME] == -1 ||
|
||||
(hdidx[HD__AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_http2_request_headers(int *hdidx) {
|
||||
if (!check_http2_headers(hdidx)) {
|
||||
return false;
|
||||
}
|
||||
if (hdidx[HD_METHOD] == -1 || hdidx[HD_PATH] == -1 ||
|
||||
hdidx[HD_SCHEME] == -1 ||
|
||||
(hdidx[HD_AUTHORITY] == -1 && hdidx[HD_HOST] == -1)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const Headers::value_type *get_header(int *hdidx, int hdkey,
|
||||
const Headers::value_type *get_header(const int *hdidx, int token,
|
||||
const Headers &nva) {
|
||||
auto i = hdidx[hdkey];
|
||||
auto i = hdidx[token];
|
||||
if (i == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
108
src/http2.h
108
src/http2.h
|
@ -76,35 +76,6 @@ void sanitize_header_value(std::string &s, size_t offset);
|
|||
void copy_url_component(std::string &dest, const http_parser_url *u, int field,
|
||||
const char *url);
|
||||
|
||||
// Returns true if the header field |name| with length |namelen| bytes
|
||||
// is valid for HTTP/2.
|
||||
bool check_http2_allowed_header(const uint8_t *name, size_t namelen);
|
||||
|
||||
// Calls check_http2_allowed_header with |name| and strlen(name),
|
||||
// assuming |name| is null-terminated string.
|
||||
bool check_http2_allowed_header(const char *name);
|
||||
|
||||
// Checks that headers |nva| do not contain disallowed header fields
|
||||
// in HTTP/2 spec. This function returns true if |nva| does not
|
||||
// contains such headers.
|
||||
bool check_http2_headers(const Headers &nva);
|
||||
|
||||
// Calls check_http2_headers()
|
||||
bool check_http2_request_headers(const Headers &nva);
|
||||
|
||||
// Calls check_http2_headers()
|
||||
bool check_http2_response_headers(const Headers &nva);
|
||||
|
||||
// Returns true if |name| is allowed pusedo header for request.
|
||||
bool check_http2_request_pseudo_header(const uint8_t *name, size_t namelen);
|
||||
|
||||
// Returns true if |name| is allowed pusedo header for response.
|
||||
bool check_http2_response_pseudo_header(const uint8_t *name, size_t namelen);
|
||||
|
||||
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,
|
||||
bool no_index);
|
||||
|
@ -115,16 +86,9 @@ Headers::value_type to_header(const uint8_t *name, size_t namelen,
|
|||
void add_header(Headers &nva, const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, bool no_index);
|
||||
|
||||
// Returns the iterator to the entry in |nva| which has name |name|
|
||||
// and the |name| is uinque in the |nva|. If no such entry exist,
|
||||
// returns nullptr.
|
||||
const Headers::value_type *get_unique_header(const Headers &nva,
|
||||
const char *name);
|
||||
|
||||
// Returns the iterator to the entry in |nva| which has name
|
||||
// |name|. If more than one entries which have the name |name|, first
|
||||
// occurrence in |nva| is returned. If no such entry exist, returns
|
||||
// nullptr.
|
||||
// Returns pointer to the entry in |nva| which has name |name|. If
|
||||
// more than one entries which have the name |name|, last occurrence
|
||||
// in |nva| is returned. If no such entry exist, returns nullptr.
|
||||
const Headers::value_type *get_header(const Headers &nva, const char *name);
|
||||
|
||||
// Returns nv->second if nv is not nullptr. Otherwise, returns "".
|
||||
|
@ -165,13 +129,12 @@ nghttp2_nv make_nv_ls(const char (&name)[N], const std::string &value) {
|
|||
// 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.
|
||||
void copy_norm_headers_to_nva(std::vector<nghttp2_nv> &nva,
|
||||
const Headers &headers);
|
||||
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.
|
||||
void build_http1_headers_from_norm_headers(std::string &hdrs,
|
||||
void build_http1_headers_from_headers(std::string &hdrs,
|
||||
const Headers &headers);
|
||||
|
||||
// Return positive window_size_increment if WINDOW_UPDATE should be
|
||||
|
@ -218,43 +181,68 @@ int check_nv(const uint8_t *name, size_t namelen, const uint8_t *value,
|
|||
// Returns parsed HTTP status code. Returns -1 on failure.
|
||||
int parse_http_status_code(const std::string &src);
|
||||
|
||||
// Header fields to be indexed, except HD_MAXIDX which is convenient
|
||||
// member to get maximum value.
|
||||
enum {
|
||||
HD_AUTHORITY,
|
||||
HD_METHOD,
|
||||
HD_PATH,
|
||||
HD_SCHEME,
|
||||
HD__AUTHORITY,
|
||||
HD__HOST,
|
||||
HD__METHOD,
|
||||
HD__PATH,
|
||||
HD__SCHEME,
|
||||
HD__STATUS,
|
||||
HD_ALT_SVC,
|
||||
HD_CONNECTION,
|
||||
HD_CONTENT_LENGTH,
|
||||
HD_COOKIE,
|
||||
HD_EXPECT,
|
||||
HD_HOST,
|
||||
HD_HTTP2_SETTINGS,
|
||||
HD_IF_MODIFIED_SINCE,
|
||||
HD_KEEP_ALIVE,
|
||||
HD_LOCATION,
|
||||
HD_PROXY_CONNECTION,
|
||||
HD_SERVER,
|
||||
HD_TE,
|
||||
HD_TRANSFER_ENCODING,
|
||||
HD_UPGRADE,
|
||||
HD_VIA,
|
||||
HD_X_FORWARDED_FOR,
|
||||
HD_X_FORWARDED_PROTO,
|
||||
HD_MAXIDX,
|
||||
};
|
||||
|
||||
// Looks up header token for header name |name| of length |namelen|.
|
||||
// Only headers we are interested in are tokenized. If header name
|
||||
// cannot be tokenized, returns -1.
|
||||
int lookup_token(const uint8_t *name, size_t namelen);
|
||||
int lookup_token(const std::string &name);
|
||||
|
||||
// Initializes |hdidx|, header index. The |hdidx| must point to the
|
||||
// array containing at least HD_MAXIDX elements.
|
||||
void init_hdidx(int *hdidx);
|
||||
// Indexes header |name| of length |namelen| using index |idx|.
|
||||
void index_header(int *hdidx, const uint8_t *name, size_t namelen, size_t idx);
|
||||
// Indexes header |token| using index |idx|.
|
||||
void index_header(int *hdidx, int token, size_t idx);
|
||||
// Iterates |headers| and for each element, call index_header.
|
||||
void index_headers(int *hdidx, const Headers &headers);
|
||||
|
||||
// Checks pseudo header |name| of length |namelen| are unique and
|
||||
// allowed for HTTP/2 request.
|
||||
bool check_http2_request_pseudo_header(int *hdidx, const uint8_t *name,
|
||||
size_t namelen);
|
||||
// Returns true if HTTP/2 request pseudo header |token| is not indexed
|
||||
// yet and not -1.
|
||||
bool check_http2_request_pseudo_header(const int *hdidx, int token);
|
||||
|
||||
// Checks |hdidx| does not contain disallowed headers.
|
||||
bool check_http2_headers(int *hdidx);
|
||||
// Checks |hdidx| does not contain disallowed headers and contains
|
||||
// mandatory headers. This funtions internally calls
|
||||
// check_http2_headers().
|
||||
bool check_http2_request_headers(int *hdidx);
|
||||
// Returns true if HTTP/2 response pseudo header |token| is not
|
||||
// indexed yet and not -1.
|
||||
bool check_http2_response_pseudo_header(const int *hdidx, int token);
|
||||
|
||||
// Returns header denoted by |hdkey| using index |hdidx|.
|
||||
const Headers::value_type *get_header(int *hdidx, int hdkey,
|
||||
// Returns true if header field denoted by |token| is allowed for
|
||||
// HTTP/2.
|
||||
bool http2_header_allowed(int token);
|
||||
|
||||
// Returns true that |hdidx| contains mandatory HTTP/2 request
|
||||
// headers.
|
||||
bool http2_mandatory_request_headers_presence(const int *hdidx);
|
||||
|
||||
// Returns header denoted by |token| using index |hdidx|.
|
||||
const Headers::value_type *get_header(const int *hdidx, int token,
|
||||
const Headers &nva);
|
||||
|
||||
} // namespace http2
|
||||
|
|
|
@ -100,55 +100,14 @@ void test_http2_add_header(void) {
|
|||
CU_ASSERT(Headers::value_type("a", "") == nva[0]);
|
||||
}
|
||||
|
||||
void test_http2_check_http2_headers(void) {
|
||||
auto nva1 = Headers{{"alpha", "1"}, {"bravo", "2"}, {"upgrade", "http2"}};
|
||||
CU_ASSERT(!http2::check_http2_headers(nva1));
|
||||
|
||||
auto nva2 = Headers{{"connection", "1"}, {"delta", "2"}, {"echo", "3"}};
|
||||
CU_ASSERT(!http2::check_http2_headers(nva2));
|
||||
|
||||
auto nva3 = Headers{{"alpha", "1"}, {"bravo", "2"}, {"te2", "3"}};
|
||||
CU_ASSERT(http2::check_http2_headers(nva3));
|
||||
|
||||
auto n1 = ":authority";
|
||||
auto n1u8 = reinterpret_cast<const uint8_t *>(n1);
|
||||
|
||||
CU_ASSERT(http2::check_http2_request_pseudo_header(n1u8, strlen(n1)));
|
||||
CU_ASSERT(!http2::check_http2_response_pseudo_header(n1u8, strlen(n1)));
|
||||
|
||||
auto n2 = ":status";
|
||||
auto n2u8 = reinterpret_cast<const uint8_t *>(n2);
|
||||
|
||||
CU_ASSERT(!http2::check_http2_request_pseudo_header(n2u8, strlen(n2)));
|
||||
CU_ASSERT(http2::check_http2_response_pseudo_header(n2u8, strlen(n2)));
|
||||
}
|
||||
|
||||
void test_http2_get_unique_header(void) {
|
||||
auto nva = Headers{{"alpha", "1"},
|
||||
{"bravo", "2"},
|
||||
{"bravo", "3"},
|
||||
{"charlie", "4"},
|
||||
{"delta", "5"},
|
||||
{"echo", "6"}};
|
||||
const Headers::value_type *rv;
|
||||
rv = http2::get_unique_header(nva, "delta");
|
||||
CU_ASSERT(rv != nullptr);
|
||||
CU_ASSERT("delta" == rv->name);
|
||||
|
||||
rv = http2::get_unique_header(nva, "bravo");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
|
||||
rv = http2::get_unique_header(nva, "foxtrot");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
}
|
||||
|
||||
void test_http2_get_header(void) {
|
||||
auto nva = Headers{{"alpha", "1"},
|
||||
{"bravo", "2"},
|
||||
{"bravo", "3"},
|
||||
{"charlie", "4"},
|
||||
{"delta", "5"},
|
||||
{"echo", "6"}};
|
||||
{"echo", "6"},
|
||||
{"content-length", "7"}};
|
||||
const Headers::value_type *rv;
|
||||
rv = http2::get_header(nva, "delta");
|
||||
CU_ASSERT(rv != nullptr);
|
||||
|
@ -160,6 +119,12 @@ void test_http2_get_header(void) {
|
|||
|
||||
rv = http2::get_header(nva, "foxtrot");
|
||||
CU_ASSERT(rv == nullptr);
|
||||
|
||||
int hdidx[http2::HD_MAXIDX];
|
||||
http2::init_hdidx(hdidx);
|
||||
hdidx[http2::HD_CONTENT_LENGTH] = 6;
|
||||
rv = http2::get_header(hdidx, http2::HD_CONTENT_LENGTH, nva);
|
||||
CU_ASSERT("content-length" == rv->name);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -178,11 +143,11 @@ auto headers = Headers{{"alpha", "0", true},
|
|||
{"zulu", "12"}};
|
||||
} // namespace
|
||||
|
||||
void test_http2_copy_norm_headers_to_nva(void) {
|
||||
void test_http2_copy_headers_to_nva(void) {
|
||||
std::vector<nghttp2_nv> nva;
|
||||
http2::copy_norm_headers_to_nva(nva, headers);
|
||||
CU_ASSERT(7 == nva.size());
|
||||
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 12};
|
||||
http2::copy_headers_to_nva(nva, headers);
|
||||
CU_ASSERT(9 == nva.size());
|
||||
auto ans = std::vector<int>{0, 1, 4, 5, 6, 7, 8, 9, 12};
|
||||
for (size_t i = 0; i < ans.size(); ++i) {
|
||||
check_nv(headers[ans[i]], &nva[i]);
|
||||
|
||||
|
@ -194,9 +159,9 @@ void test_http2_copy_norm_headers_to_nva(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void test_http2_build_http1_headers_from_norm_headers(void) {
|
||||
void test_http2_build_http1_headers_from_headers(void) {
|
||||
std::string hdrs;
|
||||
http2::build_http1_headers_from_norm_headers(hdrs, headers);
|
||||
http2::build_http1_headers_from_headers(hdrs, headers);
|
||||
CU_ASSERT(hdrs == "Alpha: 0\r\n"
|
||||
"Bravo: 1\r\n"
|
||||
"Delta: 4\r\n"
|
||||
|
@ -206,15 +171,6 @@ void test_http2_build_http1_headers_from_norm_headers(void) {
|
|||
"Te: 8\r\n"
|
||||
"Te: 9\r\n"
|
||||
"Zulu: 12\r\n");
|
||||
|
||||
hdrs.clear();
|
||||
// Both nghttp2 and spdylay do not allow \r and \n in header value
|
||||
// now.
|
||||
|
||||
// auto hd2 = std::vector<std::pair<std::string, std::string>>
|
||||
// {{"alpha", "bravo\r\ncharlie\r\n"}};
|
||||
// http2::build_http1_headers_from_norm_headers(hdrs, hd2);
|
||||
// CU_ASSERT(hdrs == "Alpha: bravo charlie \r\n");
|
||||
}
|
||||
|
||||
void test_http2_lws(void) {
|
||||
|
@ -274,12 +230,64 @@ void test_http2_index_header(void) {
|
|||
int hdidx[http2::HD_MAXIDX];
|
||||
http2::init_hdidx(hdidx);
|
||||
|
||||
http2::index_header(hdidx, reinterpret_cast<const uint8_t *>(":authority"),
|
||||
10, 0);
|
||||
http2::index_header(hdidx, reinterpret_cast<const uint8_t *>("hos"), 3, 1);
|
||||
http2::index_header(hdidx, http2::HD__AUTHORITY, 0);
|
||||
http2::index_header(hdidx, -1, 1);
|
||||
|
||||
CU_ASSERT(0 == hdidx[http2::HD_AUTHORITY]);
|
||||
CU_ASSERT(-1 == hdidx[http2::HD_HOST]);
|
||||
CU_ASSERT(0 == hdidx[http2::HD__AUTHORITY]);
|
||||
}
|
||||
|
||||
void test_http2_lookup_token(void) {
|
||||
CU_ASSERT(http2::HD__AUTHORITY == http2::lookup_token(":authority"));
|
||||
CU_ASSERT(-1 == http2::lookup_token(":authorit"));
|
||||
CU_ASSERT(-1 == http2::lookup_token(":Authority"));
|
||||
CU_ASSERT(http2::HD_EXPECT == http2::lookup_token("expect"));
|
||||
}
|
||||
|
||||
void test_http2_check_http2_pseudo_header(void) {
|
||||
int hdidx[http2::HD_MAXIDX];
|
||||
http2::init_hdidx(hdidx);
|
||||
|
||||
CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
|
||||
hdidx[http2::HD__PATH] = 0;
|
||||
CU_ASSERT(http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
|
||||
hdidx[http2::HD__METHOD] = 1;
|
||||
CU_ASSERT(
|
||||
!http2::check_http2_request_pseudo_header(hdidx, http2::HD__METHOD));
|
||||
CU_ASSERT(!http2::check_http2_request_pseudo_header(hdidx, http2::HD_VIA));
|
||||
|
||||
http2::init_hdidx(hdidx);
|
||||
|
||||
CU_ASSERT(
|
||||
http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
|
||||
hdidx[http2::HD__STATUS] = 0;
|
||||
CU_ASSERT(
|
||||
!http2::check_http2_response_pseudo_header(hdidx, http2::HD__STATUS));
|
||||
CU_ASSERT(!http2::check_http2_response_pseudo_header(hdidx, http2::HD_VIA));
|
||||
}
|
||||
|
||||
void test_http2_http2_header_allowed(void) {
|
||||
CU_ASSERT(http2::http2_header_allowed(http2::HD__PATH));
|
||||
CU_ASSERT(http2::http2_header_allowed(http2::HD_CONTENT_LENGTH));
|
||||
CU_ASSERT(!http2::http2_header_allowed(http2::HD_CONNECTION));
|
||||
}
|
||||
|
||||
void test_http2_mandatory_request_headers_presence(void) {
|
||||
int hdidx[http2::HD_MAXIDX];
|
||||
http2::init_hdidx(hdidx);
|
||||
|
||||
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
hdidx[http2::HD__AUTHORITY] = 0;
|
||||
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
hdidx[http2::HD__METHOD] = 1;
|
||||
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
hdidx[http2::HD__PATH] = 2;
|
||||
CU_ASSERT(!http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
hdidx[http2::HD__SCHEME] = 3;
|
||||
CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
|
||||
hdidx[http2::HD__AUTHORITY] = -1;
|
||||
hdidx[http2::HD_HOST] = 0;
|
||||
CU_ASSERT(http2::http2_mandatory_request_headers_presence(hdidx));
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -28,15 +28,17 @@
|
|||
namespace shrpx {
|
||||
|
||||
void test_http2_add_header(void);
|
||||
void test_http2_check_http2_headers(void);
|
||||
void test_http2_get_unique_header(void);
|
||||
void test_http2_get_header(void);
|
||||
void test_http2_copy_norm_headers_to_nva(void);
|
||||
void test_http2_build_http1_headers_from_norm_headers(void);
|
||||
void test_http2_copy_headers_to_nva(void);
|
||||
void test_http2_build_http1_headers_from_headers(void);
|
||||
void test_http2_lws(void);
|
||||
void test_http2_rewrite_location_uri(void);
|
||||
void test_http2_parse_http_status_code(void);
|
||||
void test_http2_index_header(void);
|
||||
void test_http2_lookup_token(void);
|
||||
void test_http2_check_http2_pseudo_header(void);
|
||||
void test_http2_http2_header_allowed(void);
|
||||
void test_http2_mandatory_request_headers_presence(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
|
137
src/nghttp.cc
137
src/nghttp.cc
|
@ -224,6 +224,9 @@ struct Request {
|
|||
int level;
|
||||
// RequestPriority value defined in HtmlParser.h
|
||||
int pri;
|
||||
int res_hdidx[http2::HD_MAXIDX];
|
||||
// used for incoming PUSH_PROMISE
|
||||
int req_hdidx[http2::HD_MAXIDX];
|
||||
bool expect_final_response;
|
||||
|
||||
// For pushed request, |uri| is empty and |u| is zero-cleared.
|
||||
|
@ -235,7 +238,10 @@ struct Request {
|
|||
data_length(data_length), data_offset(0), response_len(0),
|
||||
inflater(nullptr), html_parser(nullptr), data_prd(data_prd),
|
||||
stream_id(-1), status(0), level(level), pri(pri),
|
||||
expect_final_response(false) {}
|
||||
expect_final_response(false) {
|
||||
http2::init_hdidx(res_hdidx);
|
||||
http2::init_hdidx(req_hdidx);
|
||||
}
|
||||
|
||||
~Request() {
|
||||
nghttp2_gzip_inflate_del(inflater);
|
||||
|
@ -354,12 +360,47 @@ struct Request {
|
|||
}
|
||||
}
|
||||
|
||||
bool response_pseudo_header_allowed() const {
|
||||
return res_nva.empty() || res_nva.back().name.c_str()[0] == ':';
|
||||
bool response_pseudo_header_allowed(int token) const {
|
||||
if (!res_nva.empty() && res_nva.back().name.c_str()[0] != ':') {
|
||||
return false;
|
||||
}
|
||||
switch (token) {
|
||||
case http2::HD__STATUS:
|
||||
return res_hdidx[token] == -1;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool push_request_pseudo_header_allowed() const {
|
||||
return res_nva.empty() || req_nva.back().name.c_str()[0] == ':';
|
||||
bool push_request_pseudo_header_allowed(int token) const {
|
||||
if (!req_nva.empty() && req_nva.back().name.c_str()[0] != ':') {
|
||||
return false;
|
||||
}
|
||||
switch (token) {
|
||||
case http2::HD__AUTHORITY:
|
||||
case http2::HD__METHOD:
|
||||
case http2::HD__PATH:
|
||||
case http2::HD__SCHEME:
|
||||
return req_hdidx[token] == -1;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Headers::value_type *get_res_header(int token) {
|
||||
auto idx = res_hdidx[token];
|
||||
if (idx == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
return &res_nva[idx];
|
||||
}
|
||||
|
||||
Headers::value_type *get_req_header(int token) {
|
||||
auto idx = req_hdidx[token];
|
||||
if (idx == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
return &req_nva[idx];
|
||||
}
|
||||
|
||||
void record_request_time() {
|
||||
|
@ -1537,8 +1578,6 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
|
|||
|
||||
build_headers.emplace_back(kv.name, kv.value, kv.no_index);
|
||||
}
|
||||
std::stable_sort(std::begin(build_headers), std::end(build_headers),
|
||||
http2::name_less);
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(build_headers.size());
|
||||
|
@ -1690,30 +1729,29 @@ void check_response_header(nghttp2_session *session, Request *req) {
|
|||
|
||||
req->expect_final_response = false;
|
||||
|
||||
for (auto &nv : req->res_nva) {
|
||||
if ("content-encoding" == nv.name) {
|
||||
gzip =
|
||||
util::strieq("gzip", nv.value) || util::strieq("deflate", nv.value);
|
||||
continue;
|
||||
}
|
||||
if (":status" == nv.name) {
|
||||
int status;
|
||||
if (req->status != 0 ||
|
||||
(status = http2::parse_http_status_code(nv.value)) == -1) {
|
||||
auto status_hd = req->get_res_header(http2::HD__STATUS);
|
||||
|
||||
if (!status_hd) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
auto status = http2::parse_http_status_code(status_hd->value);
|
||||
if (status == -1) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
req->status = status;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->status == 0) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return;
|
||||
for (auto &nv : req->res_nva) {
|
||||
if ("content-encoding" == nv.name) {
|
||||
gzip =
|
||||
util::strieq("gzip", nv.value) || util::strieq("deflate", nv.value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->status / 100 == 1) {
|
||||
|
@ -1797,15 +1835,17 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
break;
|
||||
}
|
||||
|
||||
if (namelen > 0 && name[0] == ':') {
|
||||
if (!req->response_pseudo_header_allowed() ||
|
||||
!http2::check_http2_response_pseudo_header(name, namelen)) {
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if (!req->response_pseudo_header_allowed(token)) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
|
@ -1818,9 +1858,10 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
break;
|
||||
}
|
||||
|
||||
if (namelen > 0 && name[0] == ':') {
|
||||
if (!req->push_request_pseudo_header_allowed() ||
|
||||
!http2::check_http2_request_pseudo_header(name, namelen)) {
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if (!req->push_request_pseudo_header_allowed(token)) {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
frame->push_promise.promised_stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
|
@ -1828,6 +1869,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);
|
||||
break;
|
||||
|
@ -1895,36 +1937,27 @@ int on_frame_recv_callback2(nghttp2_session *session,
|
|||
if (!req) {
|
||||
break;
|
||||
}
|
||||
std::string scheme, authority, method, path;
|
||||
for (auto &nv : req->req_nva) {
|
||||
if (nv.name == ":scheme") {
|
||||
scheme = nv.value;
|
||||
continue;
|
||||
auto scheme = req->get_req_header(http2::HD__SCHEME);
|
||||
auto authority = req->get_req_header(http2::HD__AUTHORITY);
|
||||
auto method = req->get_req_header(http2::HD__METHOD);
|
||||
auto path = req->get_req_header(http2::HD__PATH);
|
||||
|
||||
if (!authority) {
|
||||
authority = req->get_req_header(http2::HD_HOST);
|
||||
}
|
||||
if (nv.name == ":authority" || nv.name == "host") {
|
||||
authority = nv.value;
|
||||
continue;
|
||||
}
|
||||
if (nv.name == ":method") {
|
||||
method = nv.value;
|
||||
continue;
|
||||
}
|
||||
if (nv.name == ":path") {
|
||||
path = nv.value;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (scheme.empty() || authority.empty() || method.empty() || path.empty() ||
|
||||
path[0] != '/') {
|
||||
|
||||
if (!scheme || !authority || !method || !path || scheme->value.empty() ||
|
||||
authority->value.empty() || method->value.empty() ||
|
||||
path->value.empty() || path->value[0] != '/') {
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||
frame->push_promise.promised_stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
break;
|
||||
}
|
||||
std::string uri = scheme;
|
||||
std::string uri = scheme->value;
|
||||
uri += "://";
|
||||
uri += authority;
|
||||
uri += path;
|
||||
uri += authority->value;
|
||||
uri += path->value;
|
||||
http_parser_url u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
|
|
|
@ -72,15 +72,11 @@ int main(int argc, char *argv[]) {
|
|||
!CU_add_test(pSuite, "ssl_cert_lookup_tree_add_cert_from_file",
|
||||
shrpx::test_shrpx_ssl_cert_lookup_tree_add_cert_from_file) ||
|
||||
!CU_add_test(pSuite, "http2_add_header", shrpx::test_http2_add_header) ||
|
||||
!CU_add_test(pSuite, "http2_check_http2_headers",
|
||||
shrpx::test_http2_check_http2_headers) ||
|
||||
!CU_add_test(pSuite, "http2_get_unique_header",
|
||||
shrpx::test_http2_get_unique_header) ||
|
||||
!CU_add_test(pSuite, "http2_get_header", shrpx::test_http2_get_header) ||
|
||||
!CU_add_test(pSuite, "http2_copy_norm_headers_to_nva",
|
||||
shrpx::test_http2_copy_norm_headers_to_nva) ||
|
||||
!CU_add_test(pSuite, "http2_build_http1_headers_from_norm_headers",
|
||||
shrpx::test_http2_build_http1_headers_from_norm_headers) ||
|
||||
!CU_add_test(pSuite, "http2_copy_headers_to_nva",
|
||||
shrpx::test_http2_copy_headers_to_nva) ||
|
||||
!CU_add_test(pSuite, "http2_build_http1_headers_from_headers",
|
||||
shrpx::test_http2_build_http1_headers_from_headers) ||
|
||||
!CU_add_test(pSuite, "http2_lws", shrpx::test_http2_lws) ||
|
||||
!CU_add_test(pSuite, "http2_rewrite_location_uri",
|
||||
shrpx::test_http2_rewrite_location_uri) ||
|
||||
|
@ -88,21 +84,28 @@ int main(int argc, char *argv[]) {
|
|||
shrpx::test_http2_parse_http_status_code) ||
|
||||
!CU_add_test(pSuite, "http2_index_header",
|
||||
shrpx::test_http2_index_header) ||
|
||||
!CU_add_test(pSuite, "downstream_normalize_request_headers",
|
||||
shrpx::test_downstream_normalize_request_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_normalize_response_headers",
|
||||
shrpx::test_downstream_normalize_response_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_get_norm_request_header",
|
||||
shrpx::test_downstream_get_norm_request_header) ||
|
||||
!CU_add_test(pSuite, "downstream_get_norm_response_header",
|
||||
shrpx::test_downstream_get_norm_response_header) ||
|
||||
!CU_add_test(pSuite, "http2_lookup_token",
|
||||
shrpx::test_http2_lookup_token) ||
|
||||
!CU_add_test(pSuite, "http2_check_http2_pseudo_header",
|
||||
shrpx::test_http2_check_http2_pseudo_header) ||
|
||||
!CU_add_test(pSuite, "http2_http2_header_allowed",
|
||||
shrpx::test_http2_http2_header_allowed) ||
|
||||
!CU_add_test(pSuite, "http2_mandatory_request_headers_presence",
|
||||
shrpx::test_http2_mandatory_request_headers_presence) ||
|
||||
!CU_add_test(pSuite, "downstream_index_request_headers",
|
||||
shrpx::test_downstream_index_request_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_index_response_headers",
|
||||
shrpx::test_downstream_index_response_headers) ||
|
||||
!CU_add_test(pSuite, "downstream_get_request_header",
|
||||
shrpx::test_downstream_get_request_header) ||
|
||||
!CU_add_test(pSuite, "downstream_get_response_header",
|
||||
shrpx::test_downstream_get_response_header) ||
|
||||
!CU_add_test(pSuite, "downstream_crumble_request_cookie",
|
||||
shrpx::test_downstream_crumble_request_cookie) ||
|
||||
!CU_add_test(pSuite, "downstream_assemble_request_cookie",
|
||||
shrpx::test_downstream_assemble_request_cookie) ||
|
||||
!CU_add_test(
|
||||
pSuite, "downstream_rewrite_norm_location_response_header",
|
||||
shrpx::test_downstream_rewrite_norm_location_response_header) ||
|
||||
!CU_add_test(pSuite, "downstream_rewrite_location_response_header",
|
||||
shrpx::test_downstream_rewrite_location_response_header) ||
|
||||
!CU_add_test(pSuite, "config_parse_config_str_list",
|
||||
shrpx::test_shrpx_config_parse_config_str_list) ||
|
||||
!CU_add_test(pSuite, "config_parse_header",
|
||||
|
|
|
@ -116,12 +116,11 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
|||
request_state_(INITIAL), request_major_(1), request_minor_(1),
|
||||
response_state_(INITIAL), response_http_status_(0), response_major_(1),
|
||||
response_minor_(1), upgrade_request_(false), upgraded_(false),
|
||||
http2_upgrade_seen_(false), http2_settings_seen_(false),
|
||||
chunked_request_(false), request_connection_close_(false),
|
||||
request_header_key_prev_(false), request_http2_expect_body_(false),
|
||||
chunked_response_(false), response_connection_close_(false),
|
||||
response_header_key_prev_(false), expect_final_response_(false),
|
||||
request_headers_normalized_(false) {
|
||||
http2_upgrade_seen_(false), chunked_request_(false),
|
||||
request_connection_close_(false), request_header_key_prev_(false),
|
||||
request_http2_expect_body_(false), chunked_response_(false),
|
||||
response_connection_close_(false), response_header_key_prev_(false),
|
||||
expect_final_response_(false) {
|
||||
|
||||
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
|
||||
get_config()->stream_read_timeout);
|
||||
|
@ -136,6 +135,9 @@ Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
|||
upstream_wtimer_.data = this;
|
||||
downstream_rtimer_.data = this;
|
||||
downstream_wtimer_.data = this;
|
||||
|
||||
http2::init_hdidx(request_hdidx_);
|
||||
http2::init_hdidx(response_hdidx_);
|
||||
}
|
||||
|
||||
Downstream::~Downstream() {
|
||||
|
@ -213,35 +215,15 @@ void Downstream::force_resume_read() {
|
|||
}
|
||||
|
||||
namespace {
|
||||
Headers::const_iterator get_norm_header(const Headers &headers,
|
||||
const Headers::value_type *get_header_linear(const Headers &headers,
|
||||
const std::string &name) {
|
||||
auto i = std::lower_bound(std::begin(headers), std::end(headers),
|
||||
Header(name, ""), http2::name_less);
|
||||
if (i != std::end(headers) && (*i).name == name) {
|
||||
return i;
|
||||
const Headers::value_type *res = nullptr;
|
||||
for (auto &kv : headers) {
|
||||
if (kv.name == name) {
|
||||
res = &kv;
|
||||
}
|
||||
return std::end(headers);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
Headers::iterator get_norm_header(Headers &headers, const std::string &name) {
|
||||
auto i = std::lower_bound(std::begin(headers), std::end(headers),
|
||||
Header(name, ""), http2::name_less);
|
||||
if (i != std::end(headers) && (*i).name == name) {
|
||||
return i;
|
||||
}
|
||||
return std::end(headers);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
Headers::const_iterator get_header_linear(const Headers &headers,
|
||||
const std::string &name) {
|
||||
auto i = std::find_if(
|
||||
std::begin(headers), std::end(headers),
|
||||
[&name](const Header &header) { return header.name == name; });
|
||||
return i;
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
@ -253,7 +235,11 @@ void Downstream::assemble_request_cookie() {
|
|||
std::string &cookie = assembled_request_cookie_;
|
||||
cookie = "";
|
||||
for (auto &kv : request_headers_) {
|
||||
if (util::strieq("cookie", kv.name.c_str())) {
|
||||
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
|
||||
!util::streq("cooki", kv.name.c_str(), 5)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto end = kv.value.find_last_not_of(" ;");
|
||||
if (end == std::string::npos) {
|
||||
cookie += kv.value;
|
||||
|
@ -262,19 +248,19 @@ void Downstream::assemble_request_cookie() {
|
|||
}
|
||||
cookie += "; ";
|
||||
}
|
||||
}
|
||||
if (cookie.size() >= 2) {
|
||||
cookie.erase(cookie.size() - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void Downstream::crumble_request_cookie() {
|
||||
Headers Downstream::crumble_request_cookie() {
|
||||
Headers cookie_hdrs;
|
||||
for (auto &kv : request_headers_) {
|
||||
if (util::strieq("cookie", kv.name.c_str())) {
|
||||
if (kv.name.size() != 6 || kv.name[5] != 'e' ||
|
||||
!util::streq("cooki", kv.name.c_str(), 5)) {
|
||||
continue;
|
||||
}
|
||||
size_t last = kv.value.size();
|
||||
size_t num = 0;
|
||||
std::string rep_cookie;
|
||||
|
||||
for (size_t j = 0; j < last;) {
|
||||
j = kv.value.find_first_not_of("\t ;", j);
|
||||
|
@ -288,57 +274,33 @@ void Downstream::crumble_request_cookie() {
|
|||
j = last;
|
||||
}
|
||||
|
||||
if (num == 0) {
|
||||
if (first == 0 && j == last) {
|
||||
break;
|
||||
}
|
||||
rep_cookie = kv.value.substr(first, j - first);
|
||||
} else {
|
||||
cookie_hdrs.push_back(
|
||||
Header("cookie", kv.value.substr(first, j - first), kv.no_index));
|
||||
}
|
||||
++num;
|
||||
}
|
||||
if (num > 0) {
|
||||
kv.value = std::move(rep_cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
request_headers_.insert(std::end(request_headers_),
|
||||
std::make_move_iterator(std::begin(cookie_hdrs)),
|
||||
std::make_move_iterator(std::end(cookie_hdrs)));
|
||||
if (request_headers_normalized_) {
|
||||
normalize_request_headers();
|
||||
}
|
||||
return cookie_hdrs;
|
||||
}
|
||||
|
||||
const std::string &Downstream::get_assembled_request_cookie() const {
|
||||
return assembled_request_cookie_;
|
||||
}
|
||||
|
||||
void Downstream::normalize_request_headers() {
|
||||
http2::normalize_headers(request_headers_);
|
||||
request_headers_normalized_ = true;
|
||||
void Downstream::index_request_headers() {
|
||||
for (auto &kv : request_headers_) {
|
||||
util::inp_strlower(kv.name);
|
||||
}
|
||||
http2::index_headers(request_hdidx_, request_headers_);
|
||||
}
|
||||
|
||||
Headers::const_iterator
|
||||
Downstream::get_norm_request_header(const std::string &name) const {
|
||||
return get_norm_header(request_headers_, name);
|
||||
const Headers::value_type *Downstream::get_request_header(int token) const {
|
||||
return http2::get_header(request_hdidx_, token, request_headers_);
|
||||
}
|
||||
|
||||
Headers::const_iterator
|
||||
const Headers::value_type *
|
||||
Downstream::get_request_header(const std::string &name) const {
|
||||
if (request_headers_normalized_) {
|
||||
return get_norm_request_header(name);
|
||||
}
|
||||
|
||||
return get_header_linear(request_headers_, name);
|
||||
}
|
||||
|
||||
bool Downstream::get_request_headers_normalized() const {
|
||||
return request_headers_normalized_;
|
||||
}
|
||||
|
||||
void Downstream::add_request_header(std::string name, std::string value) {
|
||||
request_header_key_prev_ = true;
|
||||
request_headers_sum_ += name.size() + value.size();
|
||||
|
@ -352,9 +314,10 @@ void Downstream::set_last_request_header_value(std::string value) {
|
|||
item.value = std::move(value);
|
||||
}
|
||||
|
||||
void Downstream::split_add_request_header(const uint8_t *name, size_t namelen,
|
||||
void Downstream::add_request_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index) {
|
||||
bool no_index, int 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);
|
||||
}
|
||||
|
@ -378,7 +341,10 @@ void Downstream::append_last_request_header_value(const char *data,
|
|||
item.value.append(data, len);
|
||||
}
|
||||
|
||||
void Downstream::clear_request_headers() { Headers().swap(request_headers_); }
|
||||
void Downstream::clear_request_headers() {
|
||||
Headers().swap(request_headers_);
|
||||
http2::init_hdidx(request_hdidx_);
|
||||
}
|
||||
|
||||
size_t Downstream::get_request_headers_sum() const {
|
||||
return request_headers_sum_;
|
||||
|
@ -524,19 +490,23 @@ const Headers &Downstream::get_response_headers() const {
|
|||
return response_headers_;
|
||||
}
|
||||
|
||||
void Downstream::normalize_response_headers() {
|
||||
http2::normalize_headers(response_headers_);
|
||||
void Downstream::index_response_headers() {
|
||||
for (auto &kv : response_headers_) {
|
||||
util::inp_strlower(kv.name);
|
||||
}
|
||||
http2::index_headers(response_hdidx_, response_headers_);
|
||||
}
|
||||
|
||||
Headers::const_iterator
|
||||
Downstream::get_norm_response_header(const std::string &name) const {
|
||||
return get_norm_header(response_headers_, name);
|
||||
const Headers::value_type *Downstream::get_response_header(int token) const {
|
||||
return http2::get_header(response_hdidx_, token, response_headers_);
|
||||
}
|
||||
|
||||
void Downstream::rewrite_norm_location_response_header(
|
||||
const std::string &upstream_scheme, uint16_t upstream_port) {
|
||||
auto hd = get_norm_header(response_headers_, "location");
|
||||
if (hd == std::end(response_headers_)) {
|
||||
void
|
||||
Downstream::rewrite_location_response_header(const std::string &upstream_scheme,
|
||||
uint16_t upstream_port) {
|
||||
auto hd =
|
||||
http2::get_header(response_hdidx_, http2::HD_LOCATION, response_headers_);
|
||||
if (!hd) {
|
||||
return;
|
||||
}
|
||||
http_parser_url u;
|
||||
|
@ -553,15 +523,16 @@ void Downstream::rewrite_norm_location_response_header(
|
|||
upstream_scheme, upstream_port);
|
||||
}
|
||||
if (new_uri.empty()) {
|
||||
auto host = get_norm_request_header("host");
|
||||
if (host == std::end(request_headers_)) {
|
||||
auto host = get_request_header(http2::HD_HOST);
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
new_uri = http2::rewrite_location_uri((*hd).value, u, (*host).value,
|
||||
upstream_scheme, upstream_port);
|
||||
}
|
||||
if (!new_uri.empty()) {
|
||||
(*hd).value = std::move(new_uri);
|
||||
auto idx = response_hdidx_[http2::HD_LOCATION];
|
||||
response_headers_[idx].value = std::move(new_uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,9 +549,10 @@ void Downstream::set_last_response_header_value(std::string value) {
|
|||
item.value = std::move(value);
|
||||
}
|
||||
|
||||
void Downstream::split_add_response_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value,
|
||||
size_t valuelen, bool no_index) {
|
||||
void Downstream::add_response_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index, int 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);
|
||||
|
@ -605,7 +577,10 @@ void Downstream::append_last_response_header_value(const char *data,
|
|||
item.value.append(data, len);
|
||||
}
|
||||
|
||||
void Downstream::clear_response_headers() { Headers().swap(response_headers_); }
|
||||
void Downstream::clear_response_headers() {
|
||||
Headers().swap(response_headers_);
|
||||
http2::init_hdidx(response_hdidx_);
|
||||
}
|
||||
|
||||
size_t Downstream::get_response_headers_sum() const {
|
||||
return response_headers_sum_;
|
||||
|
@ -717,39 +692,33 @@ void Downstream::inspect_http1_request() {
|
|||
upgrade_request_ = true;
|
||||
}
|
||||
|
||||
for (auto &hd : request_headers_) {
|
||||
if (!upgrade_request_ && util::strieq("upgrade", hd.name.c_str())) {
|
||||
// TODO Perform more strict checking for upgrade headers
|
||||
if (!upgrade_request_) {
|
||||
auto idx = request_hdidx_[http2::HD_UPGRADE];
|
||||
if (idx != -1) {
|
||||
upgrade_request_ = true;
|
||||
|
||||
if (util::streq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, hd.value.c_str(),
|
||||
hd.value.size())) {
|
||||
auto &val = request_headers_[idx].value;
|
||||
// TODO Perform more strict checking for upgrade headers
|
||||
if (util::streq(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, val.c_str(),
|
||||
val.size())) {
|
||||
http2_upgrade_seen_ = true;
|
||||
}
|
||||
} else if (!http2_settings_seen_ &&
|
||||
util::strieq(hd.name.c_str(), "http2-settings")) {
|
||||
|
||||
http2_settings_seen_ = true;
|
||||
http2_settings_ = hd.value;
|
||||
} else if (!chunked_request_ &&
|
||||
util::strieq(hd.name.c_str(), "transfer-encoding")) {
|
||||
if (util::strifind(hd.value.c_str(), "chunked")) {
|
||||
}
|
||||
}
|
||||
auto idx = request_hdidx_[http2::HD_TRANSFER_ENCODING];
|
||||
if (idx != -1 &&
|
||||
util::strifind(request_headers_[idx].value.c_str(), "chunked")) {
|
||||
chunked_request_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Downstream::inspect_http1_response() {
|
||||
for (auto &hd : response_headers_) {
|
||||
if (!chunked_response_ &&
|
||||
util::strieq(hd.name.c_str(), "transfer-encoding")) {
|
||||
if (util::strifind(hd.value.c_str(), "chunked")) {
|
||||
auto idx = response_hdidx_[http2::HD_TRANSFER_ENCODING];
|
||||
if (idx != -1 &&
|
||||
util::strifind(response_headers_[idx].value.c_str(), "chunked")) {
|
||||
chunked_response_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Downstream::reset_response() {
|
||||
response_http_status_ = 0;
|
||||
|
@ -766,11 +735,20 @@ bool Downstream::get_upgraded() const { return upgraded_; }
|
|||
bool Downstream::get_upgrade_request() const { return upgrade_request_; }
|
||||
|
||||
bool Downstream::get_http2_upgrade_request() const {
|
||||
return request_bodylen_ == 0 && http2_upgrade_seen_ && http2_settings_seen_;
|
||||
return request_bodylen_ == 0 && http2_upgrade_seen_ &&
|
||||
request_hdidx_[http2::HD_HTTP2_SETTINGS] != -1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
const std::string EMPTY;
|
||||
} // namespace
|
||||
|
||||
const std::string &Downstream::get_http2_settings() const {
|
||||
return http2_settings_;
|
||||
auto idx = request_hdidx_[http2::HD_HTTP2_SETTINGS];
|
||||
if (idx == -1) {
|
||||
return EMPTY;
|
||||
}
|
||||
return request_headers_[idx].value;
|
||||
}
|
||||
|
||||
void Downstream::set_downstream_stream_id(int32_t stream_id) {
|
||||
|
@ -832,12 +810,18 @@ bool pseudo_header_allowed(const Headers &headers) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
bool Downstream::request_pseudo_header_allowed() const {
|
||||
return pseudo_header_allowed(request_headers_);
|
||||
bool Downstream::request_pseudo_header_allowed(int 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() const {
|
||||
return pseudo_header_allowed(response_headers_);
|
||||
bool Downstream::response_pseudo_header_allowed(int token) const {
|
||||
if (!pseudo_header_allowed(response_headers_)) {
|
||||
return false;
|
||||
}
|
||||
return http2::check_http2_response_pseudo_header(response_hdidx_, token);
|
||||
}
|
||||
|
||||
void Downstream::init_upstream_timer() {
|
||||
|
|
|
@ -95,30 +95,27 @@ public:
|
|||
const std::string &get_http2_settings() const;
|
||||
// downstream request API
|
||||
const Headers &get_request_headers() const;
|
||||
void crumble_request_cookie();
|
||||
// Crumbles (split cookie by ";") in request_headers_ and returns
|
||||
// them. Headers::no_index is inherited.
|
||||
Headers crumble_request_cookie();
|
||||
void assemble_request_cookie();
|
||||
const std::string &get_assembled_request_cookie() const;
|
||||
// Makes key lowercase and sort headers by name using <
|
||||
void normalize_request_headers();
|
||||
// Returns iterator pointing to the request header with the name
|
||||
// |name|. If multiple header have |name| as name, return first
|
||||
// occurrence from the beginning. If no such header is found,
|
||||
// returns std::end(get_request_headers()). This function must be
|
||||
// called after calling normalize_request_headers().
|
||||
Headers::const_iterator
|
||||
get_norm_request_header(const std::string &name) const;
|
||||
// Returns iterator pointing to the request header with the name
|
||||
// |name|. This function acts like get_norm_request_header(), but
|
||||
// if request_headers_ was not normalized, use linear search to find
|
||||
// the header. Otherwise, get_norm_request_header() is used.
|
||||
Headers::const_iterator get_request_header(const std::string &name) const;
|
||||
bool get_request_headers_normalized() const;
|
||||
// Lower the request header field names and indexes request headers
|
||||
void index_request_headers();
|
||||
// Returns pointer to the request header with the name |name|. If
|
||||
// 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;
|
||||
// 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;
|
||||
void add_request_header(std::string name, std::string value);
|
||||
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,
|
||||
bool no_index);
|
||||
void add_request_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, bool no_index,
|
||||
int token);
|
||||
|
||||
bool get_request_header_key_prev() const;
|
||||
void append_last_request_header_key(const char *data, size_t len);
|
||||
|
@ -161,7 +158,7 @@ public:
|
|||
size_t get_request_datalen() const;
|
||||
void dec_request_datalen(size_t len);
|
||||
void reset_request_datalen();
|
||||
bool request_pseudo_header_allowed() const;
|
||||
bool request_pseudo_header_allowed(int token) const;
|
||||
bool expect_response_body() const;
|
||||
enum {
|
||||
INITIAL,
|
||||
|
@ -177,26 +174,22 @@ public:
|
|||
Memchunks4K *get_request_buf();
|
||||
// downstream response API
|
||||
const Headers &get_response_headers() const;
|
||||
// Makes key lowercase and sort headers by name using <
|
||||
void normalize_response_headers();
|
||||
// Returns iterator pointing to the response header with the name
|
||||
// |name|. If multiple header have |name| as name, return first
|
||||
// occurrence from the beginning. If no such header is found,
|
||||
// returns std::end(get_response_headers()). This function must be
|
||||
// called after calling normalize_response_headers().
|
||||
Headers::const_iterator
|
||||
get_norm_response_header(const std::string &name) const;
|
||||
// Rewrites the location response header field. This function must
|
||||
// be called after calling normalize_response_headers() and
|
||||
// normalize_request_headers().
|
||||
void rewrite_norm_location_response_header(const std::string &upstream_scheme,
|
||||
// Lower the response header field names and indexes response headers
|
||||
void index_response_headers();
|
||||
// Returns pointer to the response header with the name |name|. If
|
||||
// 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;
|
||||
// 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 split_add_response_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen,
|
||||
bool no_index);
|
||||
void add_response_header(const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, bool no_index,
|
||||
int token);
|
||||
|
||||
bool get_response_header_key_prev() const;
|
||||
void append_last_response_header_key(const char *data, size_t len);
|
||||
|
@ -238,7 +231,7 @@ public:
|
|||
void dec_response_datalen(size_t len);
|
||||
size_t get_response_datalen() const;
|
||||
void reset_response_datalen();
|
||||
bool response_pseudo_header_allowed() const;
|
||||
bool response_pseudo_header_allowed(int token) const;
|
||||
|
||||
// Call this method when there is incoming data in downstream
|
||||
// connection.
|
||||
|
@ -298,7 +291,6 @@ private:
|
|||
std::string request_http2_authority_;
|
||||
std::chrono::high_resolution_clock::time_point request_start_time_;
|
||||
std::string assembled_request_cookie_;
|
||||
std::string http2_settings_;
|
||||
|
||||
Memchunks4K request_buf_;
|
||||
Memchunks4K response_buf_;
|
||||
|
@ -343,6 +335,9 @@ private:
|
|||
int response_major_;
|
||||
int response_minor_;
|
||||
|
||||
int request_hdidx_[http2::HD_MAXIDX];
|
||||
int response_hdidx_[http2::HD_MAXIDX];
|
||||
|
||||
// true if the request contains upgrade token (HTTP Upgrade or
|
||||
// CONNECT)
|
||||
bool upgrade_request_;
|
||||
|
@ -350,7 +345,6 @@ private:
|
|||
bool upgraded_;
|
||||
|
||||
bool http2_upgrade_seen_;
|
||||
bool http2_settings_seen_;
|
||||
|
||||
bool chunked_request_;
|
||||
bool request_connection_close_;
|
||||
|
@ -362,9 +356,6 @@ private:
|
|||
bool response_header_key_prev_;
|
||||
bool expect_final_response_;
|
||||
|
||||
// true if request_headers_ is normalized
|
||||
bool request_headers_normalized_;
|
||||
|
||||
bool use_timer_;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
namespace shrpx {
|
||||
|
||||
void test_downstream_normalize_request_headers(void) {
|
||||
void test_downstream_index_request_headers(void) {
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header("1", "0");
|
||||
d.add_request_header("2", "1");
|
||||
|
@ -42,88 +42,83 @@ void test_downstream_normalize_request_headers(void) {
|
|||
d.add_request_header("BravO", "5");
|
||||
d.add_request_header(":method", "6");
|
||||
d.add_request_header(":authority", "7");
|
||||
d.normalize_request_headers();
|
||||
d.index_request_headers();
|
||||
|
||||
auto ans = Headers{{":authority", "7"},
|
||||
{":method", "6"},
|
||||
{"1", "0"},
|
||||
auto ans = Headers{{"1", "0"},
|
||||
{"2", "1"},
|
||||
{"alpha", "3"},
|
||||
{"bravo", "5"},
|
||||
{"charlie", "2"},
|
||||
{"delta", "4"}};
|
||||
{"alpha", "3"},
|
||||
{"delta", "4"},
|
||||
{"bravo", "5"},
|
||||
{":method", "6"},
|
||||
{":authority", "7"}};
|
||||
CU_ASSERT(ans == d.get_request_headers());
|
||||
}
|
||||
|
||||
void test_downstream_normalize_response_headers(void) {
|
||||
void test_downstream_index_response_headers(void) {
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_response_header("Charlie", "0");
|
||||
d.add_response_header("Alpha", "1");
|
||||
d.add_response_header("Delta", "2");
|
||||
d.add_response_header("BravO", "3");
|
||||
d.normalize_response_headers();
|
||||
d.index_response_headers();
|
||||
|
||||
auto ans =
|
||||
Headers{{"alpha", "1"}, {"bravo", "3"}, {"charlie", "0"}, {"delta", "2"}};
|
||||
Headers{{"charlie", "0"}, {"alpha", "1"}, {"delta", "2"}, {"bravo", "3"}};
|
||||
CU_ASSERT(ans == d.get_response_headers());
|
||||
}
|
||||
|
||||
void test_downstream_get_norm_request_header(void) {
|
||||
void test_downstream_get_request_header(void) {
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header("alpha", "0");
|
||||
d.add_request_header("bravo", "1");
|
||||
d.add_request_header("bravo", "2");
|
||||
d.add_request_header("charlie", "3");
|
||||
d.add_request_header("delta", "4");
|
||||
d.add_request_header("echo", "5");
|
||||
auto i = d.get_norm_request_header("alpha");
|
||||
CU_ASSERT(Header("alpha", "0") == *i);
|
||||
i = d.get_norm_request_header("bravo");
|
||||
CU_ASSERT(Header("bravo", "1") == *i);
|
||||
i = d.get_norm_request_header("delta");
|
||||
CU_ASSERT(Header("delta", "4") == *i);
|
||||
i = d.get_norm_request_header("echo");
|
||||
CU_ASSERT(Header("echo", "5") == *i);
|
||||
i = d.get_norm_request_header("foxtrot");
|
||||
CU_ASSERT(i == std::end(d.get_request_headers()));
|
||||
d.add_request_header(":authority", "1");
|
||||
d.add_request_header("content-length", "2");
|
||||
d.index_request_headers();
|
||||
|
||||
// By token
|
||||
CU_ASSERT(Header(":authority", "1") ==
|
||||
*d.get_request_header(http2::HD__AUTHORITY));
|
||||
CU_ASSERT(nullptr == d.get_request_header(http2::HD__METHOD));
|
||||
|
||||
// By name
|
||||
CU_ASSERT(Header("alpha", "0") == *d.get_request_header("alpha"));
|
||||
CU_ASSERT(nullptr == d.get_request_header("bravo"));
|
||||
}
|
||||
|
||||
void test_downstream_get_norm_response_header(void) {
|
||||
void test_downstream_get_response_header(void) {
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_response_header("alpha", "0");
|
||||
d.add_response_header("bravo", "1");
|
||||
d.add_response_header("bravo", "2");
|
||||
d.add_response_header("charlie", "3");
|
||||
d.add_response_header("delta", "4");
|
||||
d.add_response_header("echo", "5");
|
||||
auto i = d.get_norm_response_header("alpha");
|
||||
CU_ASSERT(Header("alpha", "0") == *i);
|
||||
i = d.get_norm_response_header("bravo");
|
||||
CU_ASSERT(Header("bravo", "1") == *i);
|
||||
i = d.get_norm_response_header("delta");
|
||||
CU_ASSERT(Header("delta", "4") == *i);
|
||||
i = d.get_norm_response_header("echo");
|
||||
CU_ASSERT(Header("echo", "5") == *i);
|
||||
i = d.get_norm_response_header("foxtrot");
|
||||
CU_ASSERT(i == std::end(d.get_response_headers()));
|
||||
d.add_response_header(":status", "1");
|
||||
d.add_response_header("content-length", "2");
|
||||
d.index_response_headers();
|
||||
|
||||
// By token
|
||||
CU_ASSERT(Header(":status", "1") ==
|
||||
*d.get_response_header(http2::HD__STATUS));
|
||||
CU_ASSERT(nullptr == d.get_response_header(http2::HD__METHOD));
|
||||
}
|
||||
|
||||
void test_downstream_crumble_request_cookie(void) {
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header(":method", "get");
|
||||
d.add_request_header(":path", "/");
|
||||
d.add_request_header("cookie", "alpha; bravo; ; ;; charlie;;");
|
||||
auto val = "alpha; bravo; ; ;; charlie;;";
|
||||
d.add_request_header(
|
||||
reinterpret_cast<const uint8_t *>("cookie"), sizeof("cookie") - 1,
|
||||
reinterpret_cast<const uint8_t *>(val), strlen(val), true, -1);
|
||||
d.add_request_header("cookie", ";delta");
|
||||
d.add_request_header("cookie", "echo");
|
||||
d.crumble_request_cookie();
|
||||
Headers ans = {{":method", "get"},
|
||||
{":path", "/"},
|
||||
{"cookie", "alpha"},
|
||||
{"cookie", "delta"},
|
||||
{"cookie", "echo"},
|
||||
auto cookies = d.crumble_request_cookie();
|
||||
|
||||
Headers ans = {{"cookie", "alpha"},
|
||||
{"cookie", "bravo"},
|
||||
{"cookie", "charlie"}};
|
||||
CU_ASSERT(ans == d.get_request_headers());
|
||||
{"cookie", "charlie"},
|
||||
{"cookie", "delta"},
|
||||
{"cookie", "echo"}};
|
||||
CU_ASSERT(ans == cookies);
|
||||
CU_ASSERT(cookies[0].no_index);
|
||||
CU_ASSERT(cookies[1].no_index);
|
||||
CU_ASSERT(cookies[2].no_index);
|
||||
}
|
||||
|
||||
void test_downstream_assemble_request_cookie(void) {
|
||||
|
@ -138,21 +133,24 @@ void test_downstream_assemble_request_cookie(void) {
|
|||
CU_ASSERT("alpha; bravo; charlie; delta" == d.get_assembled_request_cookie());
|
||||
}
|
||||
|
||||
void test_downstream_rewrite_norm_location_response_header(void) {
|
||||
void test_downstream_rewrite_location_response_header(void) {
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.add_request_header("host", "localhost:3000");
|
||||
d.add_response_header("location", "http://localhost:3000/");
|
||||
d.rewrite_norm_location_response_header("https", 443);
|
||||
auto location = d.get_norm_response_header("location");
|
||||
d.index_request_headers();
|
||||
d.index_response_headers();
|
||||
d.rewrite_location_response_header("https", 443);
|
||||
auto location = d.get_response_header(http2::HD_LOCATION);
|
||||
CU_ASSERT("https://localhost/" == (*location).value);
|
||||
}
|
||||
{
|
||||
Downstream d(nullptr, 0, 0);
|
||||
d.set_request_http2_authority("localhost");
|
||||
d.add_response_header("location", "http://localhost/");
|
||||
d.rewrite_norm_location_response_header("https", 443);
|
||||
auto location = d.get_norm_response_header("location");
|
||||
d.index_response_headers();
|
||||
d.rewrite_location_response_header("https", 443);
|
||||
auto location = d.get_response_header(http2::HD_LOCATION);
|
||||
CU_ASSERT("https://localhost/" == (*location).value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,13 @@
|
|||
|
||||
namespace shrpx {
|
||||
|
||||
void test_downstream_normalize_request_headers(void);
|
||||
void test_downstream_normalize_response_headers(void);
|
||||
void test_downstream_get_norm_request_header(void);
|
||||
void test_downstream_get_norm_response_header(void);
|
||||
void test_downstream_index_request_headers(void);
|
||||
void test_downstream_index_response_headers(void);
|
||||
void test_downstream_get_request_header(void);
|
||||
void test_downstream_get_response_header(void);
|
||||
void test_downstream_crumble_request_cookie(void);
|
||||
void test_downstream_assemble_request_cookie(void);
|
||||
void test_downstream_rewrite_norm_location_response_header(void);
|
||||
void test_downstream_rewrite_location_response_header(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
|
|
@ -233,14 +233,12 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
return 0;
|
||||
}
|
||||
size_t nheader = downstream_->get_request_headers().size();
|
||||
|
||||
Headers cookies;
|
||||
if (!get_config()->http2_no_cookie_crumbling) {
|
||||
downstream_->crumble_request_cookie();
|
||||
cookies = downstream_->crumble_request_cookie();
|
||||
}
|
||||
|
||||
assert(downstream_->get_request_headers_normalized());
|
||||
|
||||
auto end_headers = std::end(downstream_->get_request_headers());
|
||||
|
||||
// 7 means:
|
||||
// 1. :method
|
||||
// 2. :scheme
|
||||
|
@ -250,10 +248,12 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
// 6. x-forwarded-for (optional)
|
||||
// 7. x-forwarded-proto (optional)
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(nheader + 7);
|
||||
nva.reserve(nheader + 7 + cookies.size());
|
||||
|
||||
std::string via_value;
|
||||
std::string xff_value;
|
||||
std::string scheme, authority, path, query;
|
||||
|
||||
// To reconstruct HTTP/1 status line and headers, proxy should
|
||||
// preserve host header field. See draft-09 section 8.1.3.1.
|
||||
if (downstream_->get_request_method() == "CONNECT") {
|
||||
|
@ -273,7 +273,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
if (!downstream_->get_request_http2_authority().empty()) {
|
||||
nva.push_back(http2::make_nv_ls(
|
||||
":authority", downstream_->get_request_http2_authority()));
|
||||
} else if (downstream_->get_norm_request_header("host") == end_headers) {
|
||||
} else if (!downstream_->get_request_header(http2::HD_HOST)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "host header field missing";
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
authority += util::utos(u.port);
|
||||
}
|
||||
nva.push_back(http2::make_nv_ls(":authority", authority));
|
||||
} else if (downstream_->get_norm_request_header("host") == end_headers) {
|
||||
} else if (!downstream_->get_request_header(http2::HD_HOST)) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
DCLOG(INFO, this) << "host header field missing";
|
||||
}
|
||||
|
@ -341,27 +341,30 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
nva.push_back(
|
||||
http2::make_nv_ls(":method", downstream_->get_request_method()));
|
||||
|
||||
http2::copy_norm_headers_to_nva(nva, downstream_->get_request_headers());
|
||||
http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
|
||||
|
||||
bool chunked_encoding = false;
|
||||
auto transfer_encoding =
|
||||
downstream_->get_norm_request_header("transfer-encoding");
|
||||
if (transfer_encoding != end_headers &&
|
||||
downstream_->get_request_header(http2::HD_TRANSFER_ENCODING);
|
||||
if (transfer_encoding &&
|
||||
util::strieq((*transfer_encoding).value.c_str(), "chunked")) {
|
||||
chunked_encoding = true;
|
||||
}
|
||||
|
||||
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
|
||||
for (auto &nv : cookies) {
|
||||
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
||||
}
|
||||
|
||||
auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
|
||||
if (get_config()->add_x_forwarded_for) {
|
||||
if (xff != end_headers && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
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 &&
|
||||
!get_config()->strip_incoming_x_forwarded_for) {
|
||||
} else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value));
|
||||
}
|
||||
|
||||
|
@ -379,13 +382,13 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
}
|
||||
}
|
||||
|
||||
auto via = downstream_->get_norm_request_header("via");
|
||||
auto via = downstream_->get_request_header(http2::HD_VIA);
|
||||
if (get_config()->no_via) {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
nva.push_back(http2::make_nv_ls("via", (*via).value));
|
||||
}
|
||||
} else {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
via_value = (*via).value;
|
||||
via_value += ", ";
|
||||
}
|
||||
|
@ -407,7 +410,8 @@ int Http2DownstreamConnection::push_request_headers() {
|
|||
}
|
||||
|
||||
auto content_length =
|
||||
downstream_->get_norm_request_header("content-length") != end_headers;
|
||||
downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
|
||||
// TODO check content-length: 0 case
|
||||
|
||||
if (downstream_->get_request_method() == "CONNECT" || chunked_encoding ||
|
||||
content_length || downstream_->get_request_http2_expect_body()) {
|
||||
|
|
|
@ -709,18 +709,24 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (namelen > 0 && name[0] == ':') {
|
||||
if (!downstream->response_pseudo_header_allowed() ||
|
||||
!http2::check_http2_response_pseudo_header(name, namelen)) {
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if (!downstream->response_pseudo_header_allowed(token)) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
downstream->split_add_response_header(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
if (!http2::http2_header_allowed(token)) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
downstream->add_response_header(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -763,20 +769,11 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
|||
|
||||
auto upstream = downstream->get_upstream();
|
||||
|
||||
downstream->normalize_response_headers();
|
||||
auto &nva = downstream->get_response_headers();
|
||||
|
||||
downstream->set_expect_final_response(false);
|
||||
|
||||
if (!http2::check_http2_response_headers(nva)) {
|
||||
http2session->submit_rst_stream(frame->hd.stream_id,
|
||||
NGHTTP2_PROTOCOL_ERROR);
|
||||
downstream->set_response_state(Downstream::MSG_RESET);
|
||||
call_downstream_readcb(http2session, downstream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto status = http2::get_unique_header(nva, ":status");
|
||||
auto status = downstream->get_response_header(http2::HD__STATUS);
|
||||
int status_code;
|
||||
|
||||
if (!http2::non_empty_value(status) ||
|
||||
|
@ -824,7 +821,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto content_length = http2::get_header(nva, "content-length");
|
||||
auto content_length =
|
||||
downstream->get_response_header(http2::HD_CONTENT_LENGTH);
|
||||
if (!content_length && downstream->get_request_method() != "HEAD" &&
|
||||
downstream->get_request_method() != "CONNECT") {
|
||||
unsigned int status;
|
||||
|
|
|
@ -185,17 +185,22 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (namelen > 0 && name[0] == ':') {
|
||||
if (!downstream->request_pseudo_header_allowed() ||
|
||||
!http2::check_http2_request_pseudo_header(name, namelen)) {
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
if (name[0] == ':') {
|
||||
if (!downstream->request_pseudo_header_allowed(token)) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
downstream->split_add_request_header(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
||||
if (!http2::http2_header_allowed(token)) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
downstream->add_request_header(name, namelen, value, valuelen,
|
||||
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
@ -238,7 +243,6 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
downstream->normalize_request_headers();
|
||||
auto &nva = downstream->get_request_headers();
|
||||
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -254,17 +258,11 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
|||
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
|
||||
}
|
||||
|
||||
if (!http2::check_http2_request_headers(nva)) {
|
||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto host = http2::get_unique_header(nva, "host");
|
||||
auto authority = http2::get_unique_header(nva, ":authority");
|
||||
auto path = http2::get_unique_header(nva, ":path");
|
||||
auto method = http2::get_unique_header(nva, ":method");
|
||||
auto scheme = http2::get_unique_header(nva, ":scheme");
|
||||
auto host = downstream->get_request_header(http2::HD_HOST);
|
||||
auto authority = downstream->get_request_header(http2::HD__AUTHORITY);
|
||||
auto path = downstream->get_request_header(http2::HD__PATH);
|
||||
auto method = downstream->get_request_header(http2::HD__METHOD);
|
||||
auto scheme = downstream->get_request_header(http2::HD__SCHEME);
|
||||
|
||||
bool is_connect = method && "CONNECT" == method->value;
|
||||
bool having_host = http2::non_empty_value(host);
|
||||
|
@ -1101,14 +1099,12 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
}
|
||||
}
|
||||
|
||||
downstream->normalize_response_headers();
|
||||
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
||||
!get_config()->no_location_rewrite) {
|
||||
downstream->rewrite_norm_location_response_header(
|
||||
downstream->rewrite_location_response_header(
|
||||
get_client_handler()->get_upstream_scheme(), get_config()->port);
|
||||
}
|
||||
|
||||
auto end_headers = std::end(downstream->get_response_headers());
|
||||
size_t nheader = downstream->get_response_headers().size();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
// 3 means :status and possible server and via header field.
|
||||
|
@ -1117,7 +1113,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
auto response_status = util::utos(downstream->get_response_http_status());
|
||||
nva.push_back(http2::make_nv_ls(":status", response_status));
|
||||
|
||||
http2::copy_norm_headers_to_nva(nva, downstream->get_response_headers());
|
||||
http2::copy_headers_to_nva(nva, downstream->get_response_headers());
|
||||
|
||||
if (downstream->get_non_final_response()) {
|
||||
if (LOG_ENABLED(INFO)) {
|
||||
|
@ -1141,19 +1137,19 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
||||
nva.push_back(http2::make_nv_lc("server", get_config()->server_name));
|
||||
} else {
|
||||
auto server = downstream->get_norm_response_header("server");
|
||||
if (server != end_headers) {
|
||||
auto server = downstream->get_response_header(http2::HD_SERVER);
|
||||
if (server) {
|
||||
nva.push_back(http2::make_nv_ls("server", (*server).value));
|
||||
}
|
||||
}
|
||||
|
||||
auto via = downstream->get_norm_response_header("via");
|
||||
auto via = downstream->get_response_header(http2::HD_VIA);
|
||||
if (get_config()->no_via) {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
nva.push_back(http2::make_nv_ls("via", (*via).value));
|
||||
}
|
||||
} else {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
via_value = (*via).value;
|
||||
via_value += ", ";
|
||||
}
|
||||
|
|
|
@ -213,11 +213,8 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
|
|||
}
|
||||
|
||||
int HttpDownstreamConnection::push_request_headers() {
|
||||
assert(downstream_->get_request_headers_normalized());
|
||||
|
||||
downstream_->assemble_request_cookie();
|
||||
|
||||
auto end_headers = std::end(downstream_->get_request_headers());
|
||||
// Assume that method and request path do not contain \r\n.
|
||||
std::string hdrs = downstream_->get_request_method();
|
||||
hdrs += " ";
|
||||
|
@ -253,14 +250,14 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
hdrs += downstream_->get_request_path();
|
||||
}
|
||||
hdrs += " HTTP/1.1\r\n";
|
||||
if (downstream_->get_norm_request_header("host") == end_headers &&
|
||||
if (!downstream_->get_request_header(http2::HD_HOST) &&
|
||||
!downstream_->get_request_http2_authority().empty()) {
|
||||
hdrs += "Host: ";
|
||||
hdrs += downstream_->get_request_http2_authority();
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
http2::build_http1_headers_from_norm_headers(
|
||||
hdrs, downstream_->get_request_headers());
|
||||
http2::build_http1_headers_from_headers(hdrs,
|
||||
downstream_->get_request_headers());
|
||||
|
||||
if (!downstream_->get_assembled_request_cookie().empty()) {
|
||||
hdrs += "Cookie: ";
|
||||
|
@ -270,7 +267,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
|
||||
if (downstream_->get_request_method() != "CONNECT" &&
|
||||
downstream_->get_request_http2_expect_body() &&
|
||||
downstream_->get_norm_request_header("content-length") == end_headers) {
|
||||
!downstream_->get_request_header(http2::HD_CONTENT_LENGTH)) {
|
||||
|
||||
downstream_->set_chunked_request(true);
|
||||
hdrs += "Transfer-Encoding: chunked\r\n";
|
||||
|
@ -279,18 +276,17 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
if (downstream_->get_request_connection_close()) {
|
||||
hdrs += "Connection: close\r\n";
|
||||
}
|
||||
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
|
||||
auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
|
||||
if (get_config()->add_x_forwarded_for) {
|
||||
hdrs += "X-Forwarded-For: ";
|
||||
if (xff != end_headers && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
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 &&
|
||||
!get_config()->strip_incoming_x_forwarded_for) {
|
||||
} else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
||||
hdrs += "X-Forwarded-For: ";
|
||||
hdrs += (*xff).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*xff).value.size());
|
||||
|
@ -308,17 +304,16 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
hdrs += "http\r\n";
|
||||
}
|
||||
}
|
||||
auto expect = downstream_->get_norm_request_header("expect");
|
||||
if (expect != end_headers &&
|
||||
!util::strifind((*expect).value.c_str(), "100-continue")) {
|
||||
auto expect = downstream_->get_request_header(http2::HD_EXPECT);
|
||||
if (expect && !util::strifind((*expect).value.c_str(), "100-continue")) {
|
||||
hdrs += "Expect: ";
|
||||
hdrs += (*expect).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*expect).value.size());
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
auto via = downstream_->get_norm_request_header("via");
|
||||
auto via = downstream_->get_request_header(http2::HD_VIA);
|
||||
if (get_config()->no_via) {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
hdrs += "Via: ";
|
||||
hdrs += (*via).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
||||
|
@ -326,7 +321,7 @@ int HttpDownstreamConnection::push_request_headers() {
|
|||
}
|
||||
} else {
|
||||
hdrs += "Via: ";
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
hdrs += (*via).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
||||
hdrs += ", ";
|
||||
|
@ -473,6 +468,8 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
downstream->set_response_major(htp->http_major);
|
||||
downstream->set_response_minor(htp->http_minor);
|
||||
|
||||
downstream->index_response_headers();
|
||||
|
||||
if (downstream->get_non_final_response()) {
|
||||
// For non-final response code, we just call
|
||||
// on_downstream_header_complete() without changing response
|
||||
|
|
|
@ -149,7 +149,7 @@ int htp_hdrs_completecb(http_parser *htp) {
|
|||
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
|
||||
}
|
||||
|
||||
downstream->normalize_request_headers();
|
||||
downstream->index_request_headers();
|
||||
|
||||
downstream->inspect_http1_request();
|
||||
|
||||
|
@ -620,15 +620,15 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
hdrs += " ";
|
||||
hdrs += http2::get_status_string(downstream->get_response_http_status());
|
||||
hdrs += "\r\n";
|
||||
downstream->normalize_response_headers();
|
||||
|
||||
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
||||
!get_config()->no_location_rewrite) {
|
||||
downstream->rewrite_norm_location_response_header(
|
||||
downstream->rewrite_location_response_header(
|
||||
get_client_handler()->get_upstream_scheme(), get_config()->port);
|
||||
}
|
||||
auto end_headers = std::end(downstream->get_response_headers());
|
||||
http2::build_http1_headers_from_norm_headers(
|
||||
hdrs, downstream->get_response_headers());
|
||||
|
||||
http2::build_http1_headers_from_headers(hdrs,
|
||||
downstream->get_response_headers());
|
||||
|
||||
auto output = downstream->get_response_buf();
|
||||
|
||||
|
@ -660,8 +660,8 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
hdrs += "Connection: close\r\n";
|
||||
}
|
||||
|
||||
if (downstream->get_norm_response_header("alt-svc") == end_headers) {
|
||||
// We won't change or alter alt-svc from backend at the moment.
|
||||
if (!downstream->get_response_header(http2::HD_ALT_SVC)) {
|
||||
// We won't change or alter alt-svc from backend for now
|
||||
if (!get_config()->altsvcs.empty()) {
|
||||
hdrs += "Alt-Svc: ";
|
||||
|
||||
|
@ -684,17 +684,17 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
hdrs += get_config()->server_name;
|
||||
hdrs += "\r\n";
|
||||
} else {
|
||||
auto server = downstream->get_norm_response_header("server");
|
||||
if (server != end_headers) {
|
||||
auto server = downstream->get_response_header(http2::HD_SERVER);
|
||||
if (server) {
|
||||
hdrs += "Server: ";
|
||||
hdrs += (*server).value;
|
||||
hdrs += "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto via = downstream->get_norm_response_header("via");
|
||||
auto via = downstream->get_response_header(http2::HD_VIA);
|
||||
if (get_config()->no_via) {
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
hdrs += "Via: ";
|
||||
hdrs += (*via).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
||||
|
@ -702,7 +702,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
}
|
||||
} else {
|
||||
hdrs += "Via: ";
|
||||
if (via != end_headers) {
|
||||
if (via) {
|
||||
hdrs += (*via).value;
|
||||
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
||||
hdrs += ", ";
|
||||
|
|
|
@ -206,7 +206,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv, LogSpec *lgsp) {
|
|||
case SHRPX_LOGF_HTTP:
|
||||
if (downstream) {
|
||||
auto hd = downstream->get_request_header(lf.value.get());
|
||||
if (hd != std::end(downstream->get_request_headers())) {
|
||||
if (hd) {
|
||||
std::tie(p, avail) = copy((*hd).value.c_str(), avail, p);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -156,42 +156,33 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
|||
downstream->reset_upstream_rtimer();
|
||||
|
||||
auto nv = frame->syn_stream.nv;
|
||||
const char *path = nullptr;
|
||||
const char *scheme = nullptr;
|
||||
const char *host = nullptr;
|
||||
const char *method = nullptr;
|
||||
|
||||
for (size_t i = 0; nv[i]; i += 2) {
|
||||
if (strcmp(nv[i], ":path") == 0) {
|
||||
path = nv[i + 1];
|
||||
} else if (strcmp(nv[i], ":scheme") == 0) {
|
||||
scheme = nv[i + 1];
|
||||
} else if (strcmp(nv[i], ":method") == 0) {
|
||||
method = nv[i + 1];
|
||||
} else if (strcmp(nv[i], ":host") == 0) {
|
||||
host = nv[i + 1];
|
||||
} else if (nv[i][0] != ':') {
|
||||
downstream->add_request_header(nv[i], nv[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
downstream->normalize_request_headers();
|
||||
downstream->index_request_headers();
|
||||
|
||||
bool is_connect = method && strcmp("CONNECT", method) == 0;
|
||||
if (!path || !host || !method || http2::lws(host) || http2::lws(path) ||
|
||||
http2::lws(method) ||
|
||||
(!is_connect && (!scheme || http2::lws(scheme)))) {
|
||||
auto path = downstream->get_request_header(http2::HD__PATH);
|
||||
auto scheme = downstream->get_request_header(http2::HD__SCHEME);
|
||||
auto host = downstream->get_request_header(http2::HD__HOST);
|
||||
auto method = downstream->get_request_header(http2::HD__METHOD);
|
||||
|
||||
bool is_connect = method && "CONNECT" == method->value;
|
||||
if (!path || !host || !method || !http2::non_empty_value(host) ||
|
||||
!http2::non_empty_value(path) || !http2::non_empty_value(method) ||
|
||||
(!is_connect && (!scheme || !http2::non_empty_value(scheme)))) {
|
||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
downstream->set_request_method(method);
|
||||
downstream->set_request_method(method->value);
|
||||
if (is_connect) {
|
||||
downstream->set_request_http2_authority(path);
|
||||
downstream->set_request_http2_authority(path->value);
|
||||
} else {
|
||||
downstream->set_request_http2_scheme(scheme);
|
||||
downstream->set_request_http2_authority(host);
|
||||
downstream->set_request_path(path);
|
||||
downstream->set_request_http2_scheme(scheme->value);
|
||||
downstream->set_request_http2_authority(host->value);
|
||||
downstream->set_request_path(path->value);
|
||||
}
|
||||
|
||||
downstream->set_request_start_time(
|
||||
|
@ -824,10 +815,10 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|||
if (LOG_ENABLED(INFO)) {
|
||||
DLOG(INFO, downstream) << "HTTP response header completed";
|
||||
}
|
||||
downstream->normalize_response_headers();
|
||||
|
||||
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
||||
!get_config()->no_location_rewrite) {
|
||||
downstream->rewrite_norm_location_response_header(
|
||||
downstream->rewrite_location_response_header(
|
||||
get_client_handler()->get_upstream_scheme(), get_config()->port);
|
||||
}
|
||||
size_t nheader = downstream->get_response_headers().size();
|
||||
|
@ -844,30 +835,39 @@ 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.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.name.c_str(), "via")) {
|
||||
via_value = hd.value;
|
||||
} else if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
||||
util::strieq(hd.name.c_str(), "server")) {
|
||||
// Rewrite server header field later
|
||||
} else {
|
||||
if (hd.name.empty() || hd.name.c_str()[0] == ':') {
|
||||
continue;
|
||||
}
|
||||
auto token = http2::lookup_token(hd.name);
|
||||
switch (token) {
|
||||
case http2::HD_CONNECTION:
|
||||
case http2::HD_KEEP_ALIVE:
|
||||
case http2::HD_PROXY_CONNECTION:
|
||||
case http2::HD_TRANSFER_ENCODING:
|
||||
case http2::HD_VIA:
|
||||
case http2::HD_SERVER:
|
||||
continue;
|
||||
}
|
||||
|
||||
nv[hdidx++] = hd.name.c_str();
|
||||
nv[hdidx++] = hd.value.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
||||
nv[hdidx++] = "server";
|
||||
nv[hdidx++] = get_config()->server_name;
|
||||
} else {
|
||||
auto server = downstream->get_response_header(http2::HD_SERVER);
|
||||
if (server) {
|
||||
nv[hdidx++] = "server";
|
||||
nv[hdidx++] = server->value.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_config()->no_via) {
|
||||
if (!via_value.empty()) {
|
||||
auto via = downstream->get_response_header(http2::HD_VIA);
|
||||
if (via) {
|
||||
via_value = via->value;
|
||||
via_value += ", ";
|
||||
}
|
||||
via_value += http::create_via_header_value(
|
||||
|
|
Loading…
Reference in New Issue