2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "shrpx_http2_downstream_connection.h"
29 #include "http-parser/http_parser.h"
31 #include "shrpx_client_handler.h"
32 #include "shrpx_upstream.h"
33 #include "shrpx_downstream.h"
34 #include "shrpx_config.h"
35 #include "shrpx_error.h"
36 #include "shrpx_http.h"
37 #include "shrpx_http2_session.h"
38 #include "shrpx_worker_config.h"
42 using namespace nghttp2;
46 Http2DownstreamConnection::Http2DownstreamConnection(
47 DownstreamConnectionPool *dconn_pool, Http2Session *http2session)
48 : DownstreamConnection(dconn_pool), http2session_(http2session),
51 Http2DownstreamConnection::~Http2DownstreamConnection() {
52 if (LOG_ENABLED(INFO)) {
53 DCLOG(INFO, this) << "Deleting";
56 downstream_->disable_downstream_rtimer();
57 downstream_->disable_downstream_wtimer();
60 if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
61 downstream_->get_upgraded()) {
62 // For upgraded connection, send NO_ERROR. Should we consider
63 // request states other than Downstream::STREAM_CLOSED ?
64 error_code = NGHTTP2_NO_ERROR;
66 error_code = NGHTTP2_INTERNAL_ERROR;
69 if (downstream_->get_downstream_stream_id() != -1) {
70 if (LOG_ENABLED(INFO)) {
71 DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_
73 << downstream_->get_downstream_stream_id()
74 << ", error_code=" << error_code;
77 submit_rst_stream(downstream_, error_code);
79 http2session_->consume(downstream_->get_downstream_stream_id(),
80 downstream_->get_response_datalen());
82 downstream_->reset_response_datalen();
84 http2session_->signal_write();
87 http2session_->remove_downstream_connection(this);
88 // Downstream and DownstreamConnection may be deleted
91 downstream_->release_downstream_connection();
93 if (LOG_ENABLED(INFO)) {
94 DCLOG(INFO, this) << "Deleted";
98 int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
99 if (LOG_ENABLED(INFO)) {
100 DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
102 http2session_->add_downstream_connection(this);
103 if (http2session_->get_state() == Http2Session::DISCONNECTED) {
104 http2session_->signal_write();
107 downstream_ = downstream;
108 downstream_->reset_downstream_rtimer();
113 void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
114 if (LOG_ENABLED(INFO)) {
115 DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
117 if (submit_rst_stream(downstream) == 0) {
118 http2session_->signal_write();
121 if (downstream_->get_downstream_stream_id() != -1) {
122 http2session_->consume(downstream_->get_downstream_stream_id(),
123 downstream_->get_response_datalen());
125 downstream_->reset_response_datalen();
127 http2session_->signal_write();
130 downstream->disable_downstream_rtimer();
131 downstream->disable_downstream_wtimer();
132 downstream_ = nullptr;
135 int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
136 uint32_t error_code) {
138 if (http2session_->get_state() == Http2Session::CONNECTED &&
139 downstream->get_downstream_stream_id() != -1) {
140 switch (downstream->get_response_state()) {
141 case Downstream::MSG_RESET:
142 case Downstream::MSG_BAD_HEADER:
143 case Downstream::MSG_COMPLETE:
146 if (LOG_ENABLED(INFO)) {
147 DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
149 << downstream->get_downstream_stream_id();
151 rv = http2session_->submit_rst_stream(
152 downstream->get_downstream_stream_id(), error_code);
159 ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
160 uint8_t *buf, size_t length,
161 uint32_t *data_flags,
162 nghttp2_data_source *source, void *user_data) {
163 auto sd = static_cast<StreamData *>(
164 nghttp2_session_get_stream_user_data(session, stream_id));
165 if (!sd || !sd->dconn) {
166 return NGHTTP2_ERR_DEFERRED;
168 auto dconn = static_cast<Http2DownstreamConnection *>(source->ptr);
169 auto downstream = dconn->get_downstream();
171 // In this case, RST_STREAM should have been issued. But depending
172 // on the priority, DATA frame may come first.
173 return NGHTTP2_ERR_DEFERRED;
175 auto input = downstream->get_request_buf();
176 auto nread = input->remove(buf, length);
177 auto input_empty = input->rleft() == 0;
180 // This is important because it will handle flow control
182 if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
184 // In this case, downstream may be deleted.
185 return NGHTTP2_ERR_CALLBACK_FAILURE;
188 // Check dconn is still alive because Upstream::resume_read()
189 // may delete downstream which will delete dconn.
190 if (sd->dconn == nullptr) {
191 return NGHTTP2_ERR_DEFERRED;
196 downstream->get_request_state() == Downstream::MSG_COMPLETE &&
197 // If connection is upgraded, don't set EOF flag, since HTTP/1
198 // will set MSG_COMPLETE to request state after upgrade response
200 (!downstream->get_upgrade_request() ||
201 (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
202 !downstream->get_upgraded()))) {
204 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
208 downstream->reset_downstream_wtimer();
210 downstream->disable_downstream_wtimer();
213 if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
214 downstream->disable_downstream_wtimer();
216 return NGHTTP2_ERR_DEFERRED;
223 int Http2DownstreamConnection::push_request_headers() {
225 if (!http2session_->can_push_request()) {
226 // The HTTP2 session to the backend has not been established or
227 // connection is now being checked. This function will be called
228 // again just after it is established.
229 http2session_->start_checking_connection();
236 const char *authority = nullptr, *host = nullptr;
237 if (!get_config()->no_host_rewrite && !get_config()->http2_proxy &&
238 !get_config()->client_proxy) {
239 // HTTP/2 backend does not support multiple address, so we always
241 if (!downstream_->get_request_http2_authority().empty()) {
242 authority = get_config()->downstream_addrs[0].hostport.get();
244 if (downstream_->get_request_header(http2::HD_HOST)) {
245 host = get_config()->downstream_addrs[0].hostport.get();
248 if (!downstream_->get_request_http2_authority().empty()) {
249 authority = downstream_->get_request_http2_authority().c_str();
251 auto h = downstream_->get_request_header(http2::HD_HOST);
253 host = h->value.c_str();
257 if (!authority && !host) {
258 // upstream is HTTP/1.0. We use backend server's host
260 host = get_config()->downstream_addrs[0].hostport.get();
264 downstream_->set_request_downstream_host(authority);
266 downstream_->set_request_downstream_host(host);
269 size_t nheader = downstream_->get_request_headers().size();
272 if (!get_config()->http2_no_cookie_crumbling) {
273 cookies = downstream_->crumble_request_cookie();
280 // 4. :authority (at least either :authority or host exists)
283 // 7. x-forwarded-for (optional)
284 // 8. x-forwarded-proto (optional)
285 auto nva = std::vector<nghttp2_nv>();
286 nva.reserve(nheader + 8 + cookies.size());
288 std::string via_value;
289 std::string xff_value;
290 std::string scheme, uri_authority, path, query;
292 // To reconstruct HTTP/1 status line and headers, proxy should
293 // preserve host header field. See draft-09 section 8.1.3.1.
294 if (downstream_->get_request_method() == "CONNECT") {
295 // The upstream may be HTTP/2 or HTTP/1
297 nva.push_back(http2::make_nv_lc(":authority", authority));
300 http2::make_nv_ls(":authority", downstream_->get_request_path()));
302 } else if (!downstream_->get_request_http2_scheme().empty()) {
303 // Here the upstream is HTTP/2
305 http2::make_nv_ls(":scheme", downstream_->get_request_http2_scheme()));
306 nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
308 nva.push_back(http2::make_nv_lc(":authority", authority));
311 // The upstream is HTTP/1
313 const char *url = downstream_->get_request_path().c_str();
314 memset(&u, 0, sizeof(u));
315 rv = http_parser_parse_url(url, downstream_->get_request_path().size(), 0,
318 http2::copy_url_component(scheme, &u, UF_SCHEMA, url);
319 http2::copy_url_component(uri_authority, &u, UF_HOST, url);
320 http2::copy_url_component(path, &u, UF_PATH, url);
321 http2::copy_url_component(query, &u, UF_QUERY, url);
323 if (!uri_authority.empty() &&
324 downstream_->get_request_method() == "OPTIONS") {
330 if (!query.empty()) {
335 if (scheme.empty()) {
336 if (client_handler_->get_ssl()) {
337 nva.push_back(http2::make_nv_ll(":scheme", "https"));
339 nva.push_back(http2::make_nv_ll(":scheme", "http"));
342 nva.push_back(http2::make_nv_ls(":scheme", scheme));
346 http2::make_nv_ls(":path", downstream_->get_request_path()));
348 nva.push_back(http2::make_nv_ls(":path", path));
351 if (!get_config()->no_host_rewrite && !get_config()->http2_proxy &&
352 !get_config()->client_proxy) {
354 nva.push_back(http2::make_nv_lc(":authority", authority));
356 } else if (!uri_authority.empty()) {
357 // TODO properly check IPv6 numeric address
358 if (uri_authority.find(":") != std::string::npos) {
359 uri_authority = "[" + uri_authority;
360 uri_authority += "]";
362 if (u.field_set & (1 << UF_PORT)) {
363 uri_authority += ":";
364 uri_authority += util::utos(u.port);
366 nva.push_back(http2::make_nv_ls(":authority", uri_authority));
371 http2::make_nv_ls(":method", downstream_->get_request_method()));
374 nva.push_back(http2::make_nv_lc("host", host));
377 http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
379 bool chunked_encoding = false;
380 auto transfer_encoding =
381 downstream_->get_request_header(http2::HD_TRANSFER_ENCODING);
382 if (transfer_encoding &&
383 util::strieq((*transfer_encoding).value.c_str(), "chunked")) {
384 chunked_encoding = true;
387 for (auto &nv : cookies) {
388 nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
391 auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
392 if (get_config()->add_x_forwarded_for) {
393 if (xff && !get_config()->strip_incoming_x_forwarded_for) {
394 xff_value = (*xff).value;
398 downstream_->get_upstream()->get_client_handler()->get_ipaddr();
399 nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
400 } else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
401 nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value));
404 if (!get_config()->http2_proxy && !get_config()->client_proxy &&
405 downstream_->get_request_method() != "CONNECT") {
406 // We use same protocol with :scheme header field
407 if (scheme.empty()) {
408 if (client_handler_->get_ssl()) {
409 nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https"));
411 nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http"));
414 nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme));
418 auto via = downstream_->get_request_header(http2::HD_VIA);
419 if (get_config()->no_via) {
421 nva.push_back(http2::make_nv_ls("via", (*via).value));
425 via_value = (*via).value;
428 via_value += http::create_via_header_value(
429 downstream_->get_request_major(), downstream_->get_request_minor());
430 nva.push_back(http2::make_nv_ls("via", via_value));
433 if (LOG_ENABLED(INFO)) {
434 std::stringstream ss;
435 for (auto &nv : nva) {
437 ss.write(reinterpret_cast<const char *>(nv.name), nv.namelen);
438 ss << TTY_RST << ": ";
439 ss.write(reinterpret_cast<const char *>(nv.value), nv.valuelen);
442 DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
445 auto content_length =
446 downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
447 // TODO check content-length: 0 case
449 if (downstream_->get_request_method() == "CONNECT" || chunked_encoding ||
450 content_length || downstream_->get_request_http2_expect_body()) {
451 // Request-body is expected.
452 nghttp2_data_provider data_prd;
453 data_prd.source.ptr = this;
454 data_prd.read_callback = http2_data_read_callback;
455 rv = http2session_->submit_request(this, downstream_->get_priority(),
456 nva.data(), nva.size(), &data_prd);
458 rv = http2session_->submit_request(this, downstream_->get_priority(),
459 nva.data(), nva.size(), nullptr);
462 DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
466 downstream_->reset_downstream_wtimer();
468 http2session_->signal_write();
472 int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
475 auto output = downstream_->get_request_buf();
476 output->append(data, datalen);
477 if (downstream_->get_downstream_stream_id() != -1) {
478 rv = http2session_->resume_data(this);
483 downstream_->ensure_downstream_wtimer();
485 http2session_->signal_write();
490 int Http2DownstreamConnection::end_upload_data() {
492 if (downstream_->get_downstream_stream_id() != -1) {
493 rv = http2session_->resume_data(this);
498 downstream_->ensure_downstream_wtimer();
500 http2session_->signal_write();
505 int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
509 if (http2session_->get_state() != Http2Session::CONNECTED ||
510 !http2session_->get_flow_control()) {
514 if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
519 assert(downstream_->get_response_datalen() >= consumed);
521 rv = http2session_->consume(downstream_->get_downstream_stream_id(),
528 downstream_->dec_response_datalen(consumed);
530 http2session_->signal_write();
536 int Http2DownstreamConnection::on_read() { return 0; }
538 int Http2DownstreamConnection::on_write() { return 0; }
540 void Http2DownstreamConnection::attach_stream_data(StreamData *sd) {
541 // It is possible sd->dconn is not NULL. sd is detached when
542 // on_stream_close_callback. Before that, after MSG_COMPLETE is set
543 // to Downstream::set_response_state(), upstream's readcb is called
544 // and execution path eventually could reach here. Since the
545 // response was already handled, we just detach sd.
546 detach_stream_data();
551 StreamData *Http2DownstreamConnection::detach_stream_data() {
561 int Http2DownstreamConnection::on_priority_change(int32_t pri) {
563 if (downstream_->get_priority() == pri) {
566 downstream_->set_priority(pri);
567 if (http2session_->get_state() != Http2Session::CONNECTED) {
570 rv = http2session_->submit_priority(this, pri);
572 DLOG(FATAL, this) << "nghttp2_submit_priority() failed";
575 http2session_->signal_write();
579 int Http2DownstreamConnection::on_timeout() {
584 return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);