diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 692b9215..93d759e6 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -192,43 +192,6 @@ void on_frame_recv_callback downstream->init_response_body_buf(); auto nva = frame->headers.nva; - std::string path, scheme, host, method; - for(size_t i = 0; i < frame->headers.nvlen; ++i) { - if(util::strieq(":path", nva[i].name, nva[i].namelen)) { - path.assign(reinterpret_cast(nva[i].value), nva[i].valuelen); - } else if(util::strieq(":scheme", nva[i].name, nva[i].namelen)) { - scheme.assign(reinterpret_cast(nva[i].value), nva[i].valuelen); - } else if(util::strieq(":method", nva[i].name, nva[i].namelen)) { - method.assign(reinterpret_cast(nva[i].value), nva[i].valuelen); - downstream->set_request_method(method); - } else if(util::strieq(":host", nva[i].name, nva[i].namelen)) { - host.assign(reinterpret_cast(nva[i].value), nva[i].valuelen); - } else if(nva[i].namelen > 0 && nva[i].name[0] != ':') { - downstream->add_request_header - (std::string(reinterpret_cast(nva[i].name), nva[i].namelen), - std::string(reinterpret_cast(nva[i].value), nva[i].valuelen)); - } - } - if(path.empty() || host.empty() || method.empty()) { - upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); - return; - } - // SpdyDownstreamConnection examines request path to find - // scheme. We construct abs URI for spdy_bridge mode as well as - // spdy_proxy mode. - if((get_config()->spdy_proxy || get_config()->spdy_bridge) && - !scheme.empty() && path[0] == '/') { - std::string reqpath = scheme; - reqpath += "://"; - reqpath += host; - reqpath += path; - downstream->set_request_path(reqpath); - } else { - downstream->set_request_path(path); - } - - downstream->add_request_header("host", host); - downstream->check_upgrade_request(); if(LOG_ENABLED(INFO)) { std::stringstream ss; @@ -244,6 +207,100 @@ void on_frame_recv_callback << "\n" << ss.str(); } + // Assuming that nva is sorted by name. + const char *req_headers[] = {":host", ":method", ":path", ":scheme" }; + const size_t req_hdlen = sizeof(req_headers)/sizeof(req_headers[0]); + int req_hdidx[req_hdlen]; + memset(req_hdidx, -1, sizeof(req_hdidx)); + bool bad_req = false; + { + size_t i, j; + for(i = 0, j = 0; i < frame->headers.nvlen && j < req_hdlen;) { + int rv = util::strcompare(req_headers[j], nva[i].name, nva[i].namelen); + if(rv > 0) { + if(nva[i].namelen > 0 && nva[i].name[0] != ':') { + downstream->add_request_header + (std::string(reinterpret_cast(nva[i].name), + nva[i].namelen), + std::string(reinterpret_cast(nva[i].value), + nva[i].valuelen)); + } + ++i; + } else if(rv < 0) { + ++j; + } else { + if(req_hdidx[j] != -1) { + if(LOG_ENABLED(INFO)) { + ULOG(INFO, upstream) << "multiple " << req_headers[j] + << " found in the request"; + } + bad_req = true; + break; + } + req_hdidx[j] = i; + ++i; + } + } + if(!bad_req) { + // Here :scheme is optional, because with CONNECT method, it + // is omitted. + for(j = 0; j < 3; ++j) { + if(req_hdidx[j] == -1) { + bad_req = true; + break; + } + } + } + if(!bad_req) { + for(; i < frame->headers.nvlen; ++i) { + if(nva[i].namelen > 0 && nva[i].name[0] != ':') { + downstream->add_request_header + (std::string(reinterpret_cast(nva[i].name), + nva[i].namelen), + std::string(reinterpret_cast(nva[i].value), + nva[i].valuelen)); + } + } + } + } + if(bad_req) { + downstream->set_request_state(Downstream::HEADER_COMPLETE); + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + downstream->set_request_state(Downstream::MSG_COMPLETE); + } + if(upstream->error_reply(downstream, 400) != 0) { + upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); + } + return; + } + + std::string host(reinterpret_cast(nva[req_hdidx[0]].value), + nva[req_hdidx[0]].valuelen); + std::string method(reinterpret_cast(nva[req_hdidx[1]].value), + nva[req_hdidx[1]].valuelen); + std::string path(reinterpret_cast(nva[req_hdidx[2]].value), + nva[req_hdidx[2]].valuelen); + + downstream->set_request_method(method); + + // SpdyDownstreamConnection examines request path to find + // scheme. We construct abs URI for spdy_bridge mode as well as + // spdy_proxy mode. + if((get_config()->spdy_proxy || get_config()->spdy_bridge) && + req_hdidx[3] != -1 && path[0] == '/') { + std::string reqpath(reinterpret_cast(nva[req_hdidx[3]].value), + nva[req_hdidx[3]].valuelen); + reqpath += "://"; + reqpath += host; + reqpath += path; + downstream->set_request_path(reqpath); + } else { + downstream->set_request_path(path); + } + + downstream->add_request_header("host", host); + downstream->check_upgrade_request(); + auto dconn = upstream->get_client_handler()->get_downstream_connection(); int rv = dconn->attach_downstream(downstream); if(rv != 0) { diff --git a/src/util.cc b/src/util.cc index 41215f2b..1caa4e66 100644 --- a/src/util.cc +++ b/src/util.cc @@ -26,6 +26,7 @@ #include +#include #include #include @@ -166,10 +167,30 @@ bool strieq(const char *a, const uint8_t *b, size_t bn) return false; } const uint8_t *blast = b + bn; - for(; *a && lowcase(*a) == lowcase(*b); ++a, ++b); + for(; *a && b != blast && lowcase(*a) == lowcase(*b); ++a, ++b); return !*a && b == blast; } +int strcompare(const char *a, const uint8_t *b, size_t bn) +{ + assert(a && b); + const uint8_t *blast = b + bn; + for(; *a && b != blast; ++a, ++b) { + if(*a < *b) { + return -1; + } else if(*a > *b) { + return 1; + } + } + if(!*a && b == blast) { + return 0; + } else if(b == blast) { + return 1; + } else { + return -1; + } +} + bool strifind(const char *a, const char *b) { if(!a || !b) { diff --git a/src/util.h b/src/util.h index 28094cc5..f90c097a 100644 --- a/src/util.h +++ b/src/util.h @@ -291,6 +291,8 @@ bool iendsWith bool endsWith(const std::string& a, const std::string& b); +int strcompare(const char *a, const uint8_t *b, size_t n); + bool strieq(const std::string& a, const std::string& b); bool strieq(const char *a, const char *b);