Commit 02bb2c3e authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

nghttpx: Create authority from host or authority-form for CONNECT request

parent 64c754e2
......@@ -515,6 +515,10 @@ void Downstream::set_request_http2_authority(std::string authority) {
request_http2_authority_ = std::move(authority);
}
void Downstream::append_request_http2_authority(const char *data, size_t len) {
request_http2_authority_.append(data, len);
}
void Downstream::set_request_major(int major) { request_major_ = major; }
void Downstream::set_request_minor(int minor) { request_minor_ = minor; }
......
......@@ -150,15 +150,19 @@ public:
get_request_start_time() const;
void append_request_path(const char *data, size_t len);
// Returns request path. For HTTP/1.1, this is request-target. For
// HTTP/2, this is :path header field value.
// HTTP/2, this is :path header field value. For CONNECT request,
// this is empty.
const std::string &get_request_path() const;
// Returns HTTP/2 :scheme header field value.
const std::string &get_request_http2_scheme() const;
void set_request_http2_scheme(std::string scheme);
// Returns HTTP/2 :authority header field value. We also set the
// value retrieved from absolute-form HTTP/1 request.
// Returns :authority or host header field value. We may deduce it
// from absolute-form HTTP/1 request. We also store authority-form
// HTTP/1 request. This could be empty if request comes from
// HTTP/1.0 without Host header field and origin-form.
const std::string &get_request_http2_authority() const;
void set_request_http2_authority(std::string authority);
void append_request_http2_authority(const char *data, size_t len);
void set_request_major(int major);
void set_request_minor(int minor);
int get_request_major() const;
......
......@@ -265,37 +265,21 @@ int Http2DownstreamConnection::push_request_headers() {
.addrs[addr_idx]
.hostport.get();
const char *authority = nullptr, *host = nullptr;
if (!no_host_rewrite) {
if (!downstream_->get_request_http2_authority().empty()) {
authority = downstream_hostport;
}
if (downstream_->get_request_header(http2::HD_HOST)) {
host = downstream_hostport;
}
} else {
if (!downstream_->get_request_http2_authority().empty()) {
authority = downstream_->get_request_http2_authority().c_str();
}
auto h = downstream_->get_request_header(http2::HD_HOST);
if (h) {
host = h->value.c_str();
}
// For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless.
const char *authority = downstream_hostport;
auto &req_authority = downstream_->get_request_http2_authority();
if (no_host_rewrite && !req_authority.empty()) {
authority = req_authority.c_str();
}
if (!authority && !host) {
// upstream is HTTP/1.0. We use backend server's host
// nonetheless.
host = downstream_hostport;
if (!authority) {
authority = downstream_hostport;
}
if (authority) {
downstream_->set_request_downstream_host(authority);
} else {
downstream_->set_request_downstream_host(host);
}
downstream_->set_request_downstream_host(authority);
size_t nheader = downstream_->get_request_headers().size();
auto nheader = downstream_->get_request_headers().size();
Headers cookies;
if (!get_config()->http2_no_cookie_crumbling) {
......@@ -306,7 +290,7 @@ int Http2DownstreamConnection::push_request_headers() {
// 1. :method
// 2. :scheme
// 3. :path
// 4. :authority or host (at least either of them exists)
// 4. :authority
// 5. via (optional)
// 6. x-forwarded-for (optional)
// 7. x-forwarded-proto (optional)
......@@ -320,30 +304,14 @@ int Http2DownstreamConnection::push_request_headers() {
auto &scheme = downstream_->get_request_http2_scheme();
if (downstream_->get_request_method() == HTTP_CONNECT) {
if (authority) {
nva.push_back(http2::make_nv_lc(":authority", authority));
} else {
nva.push_back(
http2::make_nv_ls(":authority", downstream_->get_request_path()));
}
} else {
nva.push_back(http2::make_nv_lc(":authority", authority));
if (downstream_->get_request_method() != HTTP_CONNECT) {
assert(!scheme.empty());
nva.push_back(http2::make_nv_ls(":scheme", scheme));
if (authority) {
nva.push_back(http2::make_nv_lc(":authority", authority));
}
nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
}
// only emit host header field if :authority is not emitted. They
// both must be the same value.
if (!authority && host) {
nva.push_back(http2::make_nv_lc("host", host));
}
http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
bool chunked_encoding = false;
......
......@@ -292,7 +292,12 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
downstream->set_request_method(method_token);
downstream->set_request_http2_scheme(http2::value_to_str(scheme));
// nghttp2 library guarantees either :authority or host exist
if (!authority) {
authority = downstream->get_request_header(http2::HD_HOST);
}
downstream->set_request_http2_authority(http2::value_to_str(authority));
if (path) {
if (get_config()->http2_proxy || get_config()->client_proxy) {
downstream->set_request_path(http2::value_to_str(path));
......
......@@ -209,42 +209,25 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
}
int HttpDownstreamConnection::push_request_headers() {
const char *authority = nullptr, *host = nullptr;
auto downstream_hostport = get_config()
->downstream_addr_groups[group_]
.addrs[addr_idx_]
.hostport.get();
auto connect_method = downstream_->get_request_method() == HTTP_CONNECT;
if (!get_config()->no_host_rewrite && !get_config()->http2_proxy &&
!get_config()->client_proxy && !connect_method) {
if (!downstream_->get_request_http2_authority().empty()) {
authority = downstream_hostport;
}
if (downstream_->get_request_header(http2::HD_HOST)) {
host = downstream_hostport;
}
} else {
if (!downstream_->get_request_http2_authority().empty()) {
authority = downstream_->get_request_http2_authority().c_str();
}
auto h = downstream_->get_request_header(http2::HD_HOST);
if (h) {
host = h->value.c_str();
}
}
// For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless.
const char *authority = downstream_hostport;
auto &req_authority = downstream_->get_request_http2_authority();
auto no_host_rewrite = get_config()->no_host_rewrite ||
get_config()->http2_proxy ||
get_config()->client_proxy || connect_method;
if (!authority && !host) {
// upstream is HTTP/1.0. We use backend server's host
// nonetheless.
host = downstream_hostport;
if (no_host_rewrite && !req_authority.empty()) {
authority = req_authority.c_str();
}
if (authority) {
downstream_->set_request_downstream_host(authority);
} else {
downstream_->set_request_downstream_host(host);
}
downstream_->set_request_downstream_host(authority);
downstream_->assemble_request_cookie();
......@@ -255,23 +238,14 @@ int HttpDownstreamConnection::push_request_headers() {
auto &scheme = downstream_->get_request_http2_scheme();
if (connect_method) {
if (authority) {
hdrs += authority;
} else {
hdrs += downstream_->get_request_path();
}
hdrs += authority;
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
// Construct absolute-form request target because we are going to
// send a request to a HTTP/1 proxy.
assert(!scheme.empty());
hdrs += scheme;
hdrs += "://";
if (authority) {
hdrs += authority;
} else {
hdrs += host;
}
hdrs += authority;
// Server-wide OPTIONS takes following form in proxy request:
//
......@@ -287,11 +261,7 @@ int HttpDownstreamConnection::push_request_headers() {
hdrs += downstream_->get_request_path();
}
hdrs += " HTTP/1.1\r\nHost: ";
if (authority) {
hdrs += authority;
} else {
hdrs += host;
}
hdrs += authority;
hdrs += "\r\n";
http2::build_http1_headers_from_headers(hdrs,
......
......@@ -70,8 +70,14 @@ int htp_msg_begin(http_parser *htp) {
auto handler = upstream->get_client_handler();
// TODO specify 0 as priority for now
upstream->attach_downstream(
make_unique<Downstream>(upstream, handler->get_mcpool(), 0, 0));
auto downstream =
make_unique<Downstream>(upstream, handler->get_mcpool(), 0, 0);
// We happen to have the same value for method token.
downstream->set_request_method(htp->method);
upstream->attach_downstream(std::move(downstream));
return 0;
}
} // namespace
......@@ -91,7 +97,12 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) {
return -1;
}
downstream->add_request_headers_sum(len);
downstream->append_request_path(data, len);
if (downstream->get_request_method() == HTTP_CONNECT) {
downstream->append_request_http2_authority(data, len);
} else {
downstream->append_request_path(data, len);
}
return 0;
}
} // namespace
......@@ -242,8 +253,6 @@ int htp_hdrs_completecb(http_parser *htp) {
}
auto downstream = upstream->get_downstream();
// We happen to have the same value for method token.
downstream->set_request_method(htp->method);
downstream->set_request_major(htp->http_major);
downstream->set_request_minor(htp->http_minor);
......@@ -296,6 +305,11 @@ int htp_hdrs_completecb(http_parser *htp) {
downstream->set_request_path(
http2::rewrite_clean_path(std::begin(uri), std::end(uri)));
auto host = downstream->get_request_header(http2::HD_HOST);
if (host) {
downstream->set_request_http2_authority(host->value);
}
if (upstream->get_client_handler()->get_ssl()) {
downstream->set_request_http2_scheme("https");
} else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment