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 #endif // HAVE_UNISTD_H
31 #include "http-parser/http_parser.h"
33 #include "shrpx_client_handler.h"
34 #include "shrpx_upstream.h"
35 #include "shrpx_downstream.h"
36 #include "shrpx_config.h"
37 #include "shrpx_error.h"
38 #include "shrpx_http.h"
39 #include "shrpx_http2_session.h"
43 using namespace nghttp2;
47 Http2DownstreamConnection::Http2DownstreamConnection(
48 DownstreamConnectionPool *dconn_pool, Http2Session *http2session)
49 : DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr),
50 http2session_(http2session), sd_(nullptr) {}
52 Http2DownstreamConnection::~Http2DownstreamConnection() {
53 if (LOG_ENABLED(INFO)) {
54 DCLOG(INFO, this) << "Deleting";
57 downstream_->disable_downstream_rtimer();
58 downstream_->disable_downstream_wtimer();
61 if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
62 downstream_->get_upgraded()) {
63 // For upgraded connection, send NO_ERROR. Should we consider
64 // request states other than Downstream::STREAM_CLOSED ?
65 error_code = NGHTTP2_NO_ERROR;
67 error_code = NGHTTP2_INTERNAL_ERROR;
70 if (downstream_->get_downstream_stream_id() != -1) {
71 if (LOG_ENABLED(INFO)) {
72 DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_
74 << downstream_->get_downstream_stream_id()
75 << ", error_code=" << error_code;
78 submit_rst_stream(downstream_, error_code);
80 http2session_->consume(downstream_->get_downstream_stream_id(),
81 downstream_->get_response_datalen());
83 downstream_->reset_response_datalen();
85 http2session_->signal_write();
88 http2session_->remove_downstream_connection(this);
89 // Downstream and DownstreamConnection may be deleted
92 downstream_->release_downstream_connection();
94 if (LOG_ENABLED(INFO)) {
95 DCLOG(INFO, this) << "Deleted";
99 int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
100 if (LOG_ENABLED(INFO)) {
101 DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
103 http2session_->add_downstream_connection(this);
104 if (http2session_->get_state() == Http2Session::DISCONNECTED) {
105 http2session_->signal_write();
108 downstream_ = downstream;
109 downstream_->reset_downstream_rtimer();
114 void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
115 if (LOG_ENABLED(INFO)) {
116 DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
118 if (submit_rst_stream(downstream) == 0) {
119 http2session_->signal_write();
122 if (downstream_->get_downstream_stream_id() != -1) {
123 http2session_->consume(downstream_->get_downstream_stream_id(),
124 downstream_->get_response_datalen());
126 downstream_->reset_response_datalen();
128 http2session_->signal_write();
131 downstream->disable_downstream_rtimer();
132 downstream->disable_downstream_wtimer();
133 downstream_ = nullptr;
136 int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
137 uint32_t error_code) {
139 if (http2session_->get_state() == Http2Session::CONNECTED &&
140 downstream->get_downstream_stream_id() != -1) {
141 switch (downstream->get_response_state()) {
142 case Downstream::MSG_RESET:
143 case Downstream::MSG_BAD_HEADER:
144 case Downstream::MSG_COMPLETE:
147 if (LOG_ENABLED(INFO)) {
148 DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
150 << downstream->get_downstream_stream_id();
152 rv = http2session_->submit_rst_stream(
153 downstream->get_downstream_stream_id(), error_code);
160 ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
161 uint8_t *buf, size_t length,
162 uint32_t *data_flags,
163 nghttp2_data_source *source, void *user_data) {
165 auto sd = static_cast<StreamData *>(
166 nghttp2_session_get_stream_user_data(session, stream_id));
167 if (!sd || !sd->dconn) {
168 return NGHTTP2_ERR_DEFERRED;
170 auto dconn = static_cast<Http2DownstreamConnection *>(source->ptr);
171 auto downstream = dconn->get_downstream();
173 // In this case, RST_STREAM should have been issued. But depending
174 // on the priority, DATA frame may come first.
175 return NGHTTP2_ERR_DEFERRED;
177 auto input = downstream->get_request_buf();
178 auto nread = input->remove(buf, length);
179 auto input_empty = input->rleft() == 0;
182 // This is important because it will handle flow control
184 if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
186 // In this case, downstream may be deleted.
187 return NGHTTP2_ERR_CALLBACK_FAILURE;
190 // Check dconn is still alive because Upstream::resume_read()
191 // may delete downstream which will delete dconn.
192 if (sd->dconn == nullptr) {
193 return NGHTTP2_ERR_DEFERRED;
198 downstream->get_request_state() == Downstream::MSG_COMPLETE &&
199 // If connection is upgraded, don't set EOF flag, since HTTP/1
200 // will set MSG_COMPLETE to request state after upgrade response
202 (!downstream->get_upgrade_request() ||
203 (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
204 !downstream->get_upgraded()))) {
206 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
208 auto &trailers = downstream->get_request_trailers();
209 if (!trailers.empty()) {
210 std::vector<nghttp2_nv> nva;
211 nva.reserve(trailers.size());
212 http2::copy_headers_to_nva(nva, trailers);
214 rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
216 if (nghttp2_is_fatal(rv)) {
217 return NGHTTP2_ERR_CALLBACK_FAILURE;
220 *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
227 downstream->reset_downstream_wtimer();
229 downstream->disable_downstream_wtimer();
232 if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
233 downstream->disable_downstream_wtimer();
235 return NGHTTP2_ERR_DEFERRED;
242 int Http2DownstreamConnection::push_request_headers() {
247 if (!http2session_->can_push_request()) {
248 // The HTTP2 session to the backend has not been established or
249 // connection is now being checked. This function will be called
250 // again just after it is established.
251 downstream_->set_request_pending(true);
252 http2session_->start_checking_connection();
256 downstream_->set_request_pending(false);
258 auto no_host_rewrite = get_config()->no_host_rewrite ||
259 get_config()->http2_proxy ||
260 get_config()->client_proxy ||
261 downstream_->get_request_method() == "CONNECT";
263 // http2session_ has already in CONNECTED state, so we can get
265 auto addr_idx = http2session_->get_addr_idx();
266 auto downstream_hostport =
267 get_config()->downstream_addrs[addr_idx].hostport.get();
269 const char *authority = nullptr, *host = nullptr;
270 if (!no_host_rewrite) {
271 if (!downstream_->get_request_http2_authority().empty()) {
272 authority = downstream_hostport;
274 if (downstream_->get_request_header(http2::HD_HOST)) {
275 host = downstream_hostport;
278 if (!downstream_->get_request_http2_authority().empty()) {
279 authority = downstream_->get_request_http2_authority().c_str();
281 auto h = downstream_->get_request_header(http2::HD_HOST);
283 host = h->value.c_str();
287 if (!authority && !host) {
288 // upstream is HTTP/1.0. We use backend server's host
290 host = downstream_hostport;
294 downstream_->set_request_downstream_host(authority);
296 downstream_->set_request_downstream_host(host);
299 size_t nheader = downstream_->get_request_headers().size();
302 if (!get_config()->http2_no_cookie_crumbling) {
303 cookies = downstream_->crumble_request_cookie();
310 // 4. :authority or host (at least either of them exists)
312 // 6. x-forwarded-for (optional)
313 // 7. x-forwarded-proto (optional)
315 auto nva = std::vector<nghttp2_nv>();
316 nva.reserve(nheader + 8 + cookies.size());
318 std::string via_value;
319 std::string xff_value;
320 std::string scheme, uri_authority, path, query;
323 http2::make_nv_ls(":method", downstream_->get_request_method()));
325 if (downstream_->get_request_method() == "CONNECT") {
327 nva.push_back(http2::make_nv_lc(":authority", authority));
330 http2::make_nv_ls(":authority", downstream_->get_request_path()));
333 if (!downstream_->get_request_http2_scheme().empty()) {
334 nva.push_back(http2::make_nv_ls(":scheme",
335 downstream_->get_request_http2_scheme()));
336 } else 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"));
343 nva.push_back(http2::make_nv_lc(":authority", authority));
346 nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
349 // only emit host header field if :authority is not emitted. They
350 // both must be the same value.
351 if (!authority && host) {
352 nva.push_back(http2::make_nv_lc("host", host));
355 http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
357 bool chunked_encoding = false;
358 auto transfer_encoding =
359 downstream_->get_request_header(http2::HD_TRANSFER_ENCODING);
360 if (transfer_encoding &&
361 util::strieq_l("chunked", (*transfer_encoding).value)) {
362 chunked_encoding = true;
365 for (auto &nv : cookies) {
366 nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
369 auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
370 if (get_config()->add_x_forwarded_for) {
371 if (xff && !get_config()->strip_incoming_x_forwarded_for) {
372 xff_value = (*xff).value;
376 downstream_->get_upstream()->get_client_handler()->get_ipaddr();
377 nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
378 } else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
379 nva.push_back(http2::make_nv_ls("x-forwarded-for", (*xff).value));
382 if (!get_config()->http2_proxy && !get_config()->client_proxy &&
383 downstream_->get_request_method() != "CONNECT") {
384 // We use same protocol with :scheme header field
385 if (scheme.empty()) {
386 if (client_handler_->get_ssl()) {
387 nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https"));
389 nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http"));
392 nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme));
396 auto via = downstream_->get_request_header(http2::HD_VIA);
397 if (get_config()->no_via) {
399 nva.push_back(http2::make_nv_ls("via", (*via).value));
403 via_value = (*via).value;
406 via_value += http::create_via_header_value(
407 downstream_->get_request_major(), downstream_->get_request_minor());
408 nva.push_back(http2::make_nv_ls("via", via_value));
411 auto te = downstream_->get_request_header(http2::HD_TE);
412 // HTTP/1 upstream request can contain keyword other than
413 // "trailers". We just forward "trailers".
414 // TODO more strict handling required here.
415 if (te && util::strifind(te->value.c_str(), "trailers")) {
416 nva.push_back(http2::make_nv_ll("te", "trailers"));
419 if (LOG_ENABLED(INFO)) {
420 std::stringstream ss;
421 for (auto &nv : nva) {
422 ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
424 DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
427 auto content_length =
428 downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
429 // TODO check content-length: 0 case
431 if (downstream_->get_request_method() == "CONNECT" || chunked_encoding ||
432 content_length || downstream_->get_request_http2_expect_body()) {
433 // Request-body is expected.
434 nghttp2_data_provider data_prd;
435 data_prd.source.ptr = this;
436 data_prd.read_callback = http2_data_read_callback;
437 rv = http2session_->submit_request(this, downstream_->get_priority(),
438 nva.data(), nva.size(), &data_prd);
440 rv = http2session_->submit_request(this, downstream_->get_priority(),
441 nva.data(), nva.size(), nullptr);
444 DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
448 downstream_->reset_downstream_wtimer();
450 http2session_->signal_write();
454 int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
457 auto output = downstream_->get_request_buf();
458 output->append(data, datalen);
459 if (downstream_->get_downstream_stream_id() != -1) {
460 rv = http2session_->resume_data(this);
465 downstream_->ensure_downstream_wtimer();
467 http2session_->signal_write();
472 int Http2DownstreamConnection::end_upload_data() {
474 if (downstream_->get_downstream_stream_id() != -1) {
475 rv = http2session_->resume_data(this);
480 downstream_->ensure_downstream_wtimer();
482 http2session_->signal_write();
487 int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
491 if (http2session_->get_state() != Http2Session::CONNECTED ||
492 !http2session_->get_flow_control()) {
496 if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
501 assert(downstream_->get_response_datalen() >= consumed);
503 rv = http2session_->consume(downstream_->get_downstream_stream_id(),
510 downstream_->dec_response_datalen(consumed);
512 http2session_->signal_write();
518 int Http2DownstreamConnection::on_read() { return 0; }
520 int Http2DownstreamConnection::on_write() { return 0; }
522 void Http2DownstreamConnection::attach_stream_data(StreamData *sd) {
523 // It is possible sd->dconn is not NULL. sd is detached when
524 // on_stream_close_callback. Before that, after MSG_COMPLETE is set
525 // to Downstream::set_response_state(), upstream's readcb is called
526 // and execution path eventually could reach here. Since the
527 // response was already handled, we just detach sd.
528 detach_stream_data();
533 StreamData *Http2DownstreamConnection::detach_stream_data() {
543 int Http2DownstreamConnection::on_priority_change(int32_t pri) {
545 if (downstream_->get_priority() == pri) {
548 downstream_->set_priority(pri);
549 if (http2session_->get_state() != Http2Session::CONNECTED) {
552 rv = http2session_->submit_priority(this, pri);
554 DLOG(FATAL, this) << "nghttp2_submit_priority() failed";
557 http2session_->signal_write();
561 int Http2DownstreamConnection::on_timeout() {
566 return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);