1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
5 *
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:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
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.
24 */
25 #include "shrpx_http2_downstream_connection.h"
26
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif // HAVE_UNISTD_H
30
31 #include "llhttp.h"
32
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"
40 #include "shrpx_worker.h"
41 #include "shrpx_log.h"
42 #include "http2.h"
43 #include "util.h"
44 #include "ssl_compat.h"
45
46 using namespace nghttp2;
47
48 namespace shrpx {
49
Http2DownstreamConnection(Http2Session * http2session)50 Http2DownstreamConnection::Http2DownstreamConnection(Http2Session *http2session)
51 : dlnext(nullptr),
52 dlprev(nullptr),
53 http2session_(http2session),
54 sd_(nullptr) {}
55
~Http2DownstreamConnection()56 Http2DownstreamConnection::~Http2DownstreamConnection() {
57 if (LOG_ENABLED(INFO)) {
58 DCLOG(INFO, this) << "Deleting";
59 }
60 if (downstream_) {
61 downstream_->disable_downstream_rtimer();
62 downstream_->disable_downstream_wtimer();
63
64 uint32_t error_code;
65 if (downstream_->get_request_state() == DownstreamState::STREAM_CLOSED &&
66 downstream_->get_upgraded()) {
67 // For upgraded connection, send NO_ERROR. Should we consider
68 // request states other than DownstreamState::STREAM_CLOSED ?
69 error_code = NGHTTP2_NO_ERROR;
70 } else {
71 error_code = NGHTTP2_INTERNAL_ERROR;
72 }
73
74 if (http2session_->get_state() == Http2SessionState::CONNECTED &&
75 downstream_->get_downstream_stream_id() != -1) {
76 submit_rst_stream(downstream_, error_code);
77
78 auto &resp = downstream_->response();
79
80 http2session_->consume(downstream_->get_downstream_stream_id(),
81 resp.unconsumed_body_length);
82
83 resp.unconsumed_body_length = 0;
84
85 http2session_->signal_write();
86 }
87 }
88 http2session_->remove_downstream_connection(this);
89
90 if (LOG_ENABLED(INFO)) {
91 DCLOG(INFO, this) << "Deleted";
92 }
93 }
94
attach_downstream(Downstream * downstream)95 int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
96 if (LOG_ENABLED(INFO)) {
97 DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
98 }
99 http2session_->add_downstream_connection(this);
100 http2session_->signal_write();
101
102 downstream_ = downstream;
103 downstream_->reset_downstream_rtimer();
104
105 auto &req = downstream_->request();
106
107 // HTTP/2 disables HTTP Upgrade.
108 if (req.method != HTTP_CONNECT && req.connect_proto == ConnectProto::NONE) {
109 req.upgrade_request = false;
110 }
111
112 return 0;
113 }
114
detach_downstream(Downstream * downstream)115 void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
116 if (LOG_ENABLED(INFO)) {
117 DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
118 }
119
120 auto &resp = downstream_->response();
121
122 if (downstream_->get_downstream_stream_id() != -1) {
123 if (submit_rst_stream(downstream) == 0) {
124 http2session_->signal_write();
125 }
126
127 http2session_->consume(downstream_->get_downstream_stream_id(),
128 resp.unconsumed_body_length);
129
130 resp.unconsumed_body_length = 0;
131
132 http2session_->signal_write();
133 }
134
135 downstream->disable_downstream_rtimer();
136 downstream->disable_downstream_wtimer();
137 downstream_ = nullptr;
138 }
139
submit_rst_stream(Downstream * downstream,uint32_t error_code)140 int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
141 uint32_t error_code) {
142 int rv = -1;
143 if (http2session_->get_state() == Http2SessionState::CONNECTED &&
144 downstream->get_downstream_stream_id() != -1) {
145 switch (downstream->get_response_state()) {
146 case DownstreamState::MSG_RESET:
147 case DownstreamState::MSG_BAD_HEADER:
148 case DownstreamState::MSG_COMPLETE:
149 break;
150 default:
151 if (LOG_ENABLED(INFO)) {
152 DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
153 << ", stream_id="
154 << downstream->get_downstream_stream_id()
155 << ", error_code=" << error_code;
156 }
157 rv = http2session_->submit_rst_stream(
158 downstream->get_downstream_stream_id(), error_code);
159 }
160 }
161 return rv;
162 }
163
164 namespace {
http2_data_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)165 nghttp2_ssize http2_data_read_callback(nghttp2_session *session,
166 int32_t stream_id, uint8_t *buf,
167 size_t length, uint32_t *data_flags,
168 nghttp2_data_source *source,
169 void *user_data) {
170 int rv;
171 auto sd = static_cast<StreamData *>(
172 nghttp2_session_get_stream_user_data(session, stream_id));
173 if (!sd || !sd->dconn) {
174 return NGHTTP2_ERR_DEFERRED;
175 }
176 auto dconn = sd->dconn;
177 auto downstream = dconn->get_downstream();
178 if (!downstream) {
179 // In this case, RST_STREAM should have been issued. But depending
180 // on the priority, DATA frame may come first.
181 return NGHTTP2_ERR_DEFERRED;
182 }
183 const auto &req = downstream->request();
184 auto input = downstream->get_request_buf();
185
186 auto nread = std::min(input->rleft(), length);
187 auto input_empty = input->rleft() == nread;
188
189 *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
190
191 if (input_empty &&
192 downstream->get_request_state() == DownstreamState::MSG_COMPLETE &&
193 // If connection is upgraded, don't set EOF flag, since HTTP/1
194 // will set MSG_COMPLETE to request state after upgrade response
195 // header is seen.
196 (!req.upgrade_request ||
197 (downstream->get_response_state() == DownstreamState::HEADER_COMPLETE &&
198 !downstream->get_upgraded()))) {
199 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
200
201 const auto &trailers = req.fs.trailers();
202 if (!trailers.empty()) {
203 std::vector<nghttp2_nv> nva;
204 nva.reserve(trailers.size());
205 http2::copy_headers_to_nva_nocopy(nva, trailers, http2::HDOP_STRIP_ALL);
206 if (!nva.empty()) {
207 rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
208 if (rv != 0) {
209 if (nghttp2_is_fatal(rv)) {
210 return NGHTTP2_ERR_CALLBACK_FAILURE;
211 }
212 } else {
213 *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
214 }
215 }
216 }
217 }
218
219 if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
220 downstream->disable_downstream_wtimer();
221
222 return NGHTTP2_ERR_DEFERRED;
223 }
224
225 return nread;
226 }
227 } // namespace
228
push_request_headers()229 int Http2DownstreamConnection::push_request_headers() {
230 int rv;
231 if (!downstream_) {
232 return 0;
233 }
234 if (!http2session_->can_push_request(downstream_)) {
235 // The HTTP2 session to the backend has not been established or
236 // connection is now being checked. This function will be called
237 // again just after it is established.
238 downstream_->set_request_pending(true);
239 http2session_->start_checking_connection();
240 return 0;
241 }
242
243 downstream_->set_request_pending(false);
244
245 const auto &req = downstream_->request();
246
247 if (req.connect_proto != ConnectProto::NONE &&
248 !http2session_->get_allow_connect_proto()) {
249 return -1;
250 }
251
252 auto &balloc = downstream_->get_block_allocator();
253
254 auto config = get_config();
255 auto &httpconf = config->http;
256 auto &http2conf = config->http2;
257
258 auto no_host_rewrite = httpconf.no_host_rewrite || config->http2_proxy ||
259 req.regular_connect_method();
260
261 // http2session_ has already in CONNECTED state, so we can get
262 // addr_idx here.
263 const auto &downstream_hostport = http2session_->get_addr()->hostport;
264
265 // For HTTP/1.0 request, there is no authority in request. In that
266 // case, we use backend server's host nonetheless.
267 auto authority = StringRef(downstream_hostport);
268
269 if (no_host_rewrite && !req.authority.empty()) {
270 authority = req.authority;
271 }
272
273 downstream_->set_request_downstream_host(authority);
274
275 size_t num_cookies = 0;
276 if (!http2conf.no_cookie_crumbling) {
277 num_cookies = downstream_->count_crumble_request_cookie();
278 }
279
280 // 11 means:
281 // 1. :method
282 // 2. :scheme
283 // 3. :path
284 // 4. :authority (or host)
285 // 5. :protocol (optional)
286 // 6. via (optional)
287 // 7. x-forwarded-for (optional)
288 // 8. x-forwarded-proto (optional)
289 // 9. te (optional)
290 // 10. forwarded (optional)
291 // 11. early-data (optional)
292 auto nva = std::vector<nghttp2_nv>();
293 nva.reserve(req.fs.headers().size() + 11 + num_cookies +
294 httpconf.add_request_headers.size());
295
296 if (req.connect_proto == ConnectProto::WEBSOCKET) {
297 nva.push_back(http2::make_field(":method"_sr, "CONNECT"_sr));
298 nva.push_back(http2::make_field(":protocol"_sr, "websocket"_sr));
299 } else {
300 nva.push_back(
301 http2::make_field(":method"_sr, http2::to_method_string(req.method)));
302 }
303
304 if (!req.regular_connect_method()) {
305 assert(!req.scheme.empty());
306
307 auto addr = http2session_->get_addr();
308 assert(addr);
309 // We will handle more protocol scheme upgrade in the future.
310 if (addr->tls && addr->upgrade_scheme && req.scheme == "http"_sr) {
311 nva.push_back(http2::make_field(":scheme"_sr, "https"_sr));
312 } else {
313 nva.push_back(http2::make_field(":scheme"_sr, req.scheme));
314 }
315
316 if (req.method == HTTP_OPTIONS && req.path.empty()) {
317 nva.push_back(http2::make_field(":path"_sr, "*"_sr));
318 } else {
319 nva.push_back(http2::make_field(":path"_sr, req.path));
320 }
321
322 if (!req.no_authority || req.connect_proto != ConnectProto::NONE) {
323 nva.push_back(http2::make_field(":authority"_sr, authority));
324 } else {
325 nva.push_back(http2::make_field("host"_sr, authority));
326 }
327 } else {
328 nva.push_back(http2::make_field(":authority"_sr, authority));
329 }
330
331 auto &fwdconf = httpconf.forwarded;
332 auto &xffconf = httpconf.xff;
333 auto &xfpconf = httpconf.xfp;
334 auto &earlydataconf = httpconf.early_data;
335
336 uint32_t build_flags =
337 (fwdconf.strip_incoming ? http2::HDOP_STRIP_FORWARDED : 0) |
338 (xffconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_FOR : 0) |
339 (xfpconf.strip_incoming ? http2::HDOP_STRIP_X_FORWARDED_PROTO : 0) |
340 (earlydataconf.strip_incoming ? http2::HDOP_STRIP_EARLY_DATA : 0) |
341 http2::HDOP_STRIP_SEC_WEBSOCKET_KEY;
342
343 http2::copy_headers_to_nva_nocopy(nva, req.fs.headers(), build_flags);
344
345 if (!http2conf.no_cookie_crumbling) {
346 downstream_->crumble_request_cookie(nva);
347 }
348
349 auto upstream = downstream_->get_upstream();
350 auto handler = upstream->get_client_handler();
351
352 #if defined(NGHTTP2_GENUINE_OPENSSL) || \
353 defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL)
354 auto conn = handler->get_connection();
355
356 if (conn->tls.ssl && !SSL_is_init_finished(conn->tls.ssl)) {
357 nva.push_back(http2::make_field("early-data"_sr, "1"_sr));
358 }
359 #endif // NGHTTP2_GENUINE_OPENSSL || NGHTTP2_OPENSSL_IS_BORINGSSL ||
360 // NGHTTP2_OPENSSL_IS_WOLFSSL
361
362 auto fwd =
363 fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
364
365 if (fwdconf.params) {
366 auto params = fwdconf.params;
367
368 if (config->http2_proxy || req.regular_connect_method()) {
369 params &= ~FORWARDED_PROTO;
370 }
371
372 auto value = http::create_forwarded(
373 balloc, params, handler->get_forwarded_by(), handler->get_forwarded_for(),
374 req.authority, req.scheme);
375
376 if (fwd || !value.empty()) {
377 if (fwd) {
378 if (value.empty()) {
379 value = fwd->value;
380 } else {
381 value = concat_string_ref(balloc, fwd->value, ", "_sr, value);
382 }
383 }
384
385 nva.push_back(http2::make_field("forwarded"_sr, value));
386 }
387 } else if (fwd) {
388 nva.push_back(http2::make_field("forwarded"_sr, fwd->value));
389 }
390
391 auto xff =
392 xffconf.strip_incoming ? nullptr : req.fs.header(http2::HD_X_FORWARDED_FOR);
393
394 if (xffconf.add) {
395 StringRef xff_value;
396 const auto &addr = upstream->get_client_handler()->get_ipaddr();
397 if (xff) {
398 xff_value = concat_string_ref(balloc, xff->value, ", "_sr, addr);
399 } else {
400 xff_value = addr;
401 }
402 nva.push_back(http2::make_field("x-forwarded-for"_sr, xff_value));
403 } else if (xff) {
404 nva.push_back(http2::make_field("x-forwarded-for"_sr, xff->value));
405 }
406
407 if (!config->http2_proxy && !req.regular_connect_method()) {
408 auto xfp = xfpconf.strip_incoming
409 ? nullptr
410 : req.fs.header(http2::HD_X_FORWARDED_PROTO);
411
412 if (xfpconf.add) {
413 StringRef xfp_value;
414 // We use same protocol with :scheme header field
415 if (xfp) {
416 xfp_value = concat_string_ref(balloc, xfp->value, ", "_sr, req.scheme);
417 } else {
418 xfp_value = req.scheme;
419 }
420 nva.push_back(http2::make_field("x-forwarded-proto"_sr, xfp_value));
421 } else if (xfp) {
422 nva.push_back(http2::make_field("x-forwarded-proto"_sr, xfp->value));
423 }
424 }
425
426 auto via = req.fs.header(http2::HD_VIA);
427 if (httpconf.no_via) {
428 if (via) {
429 nva.push_back(http2::make_field("via"_sr, (*via).value));
430 }
431 } else {
432 size_t vialen = 16;
433 if (via) {
434 vialen += via->value.size() + 2;
435 }
436
437 auto iov = make_byte_ref(balloc, vialen + 1);
438 auto p = std::begin(iov);
439
440 if (via) {
441 p = std::copy(std::begin(via->value), std::end(via->value), p);
442 p = util::copy_lit(p, ", ");
443 }
444 p = http::create_via_header_value(p, req.http_major, req.http_minor);
445 *p = '\0';
446
447 nva.push_back(
448 http2::make_field("via"_sr, StringRef{std::span{std::begin(iov), p}}));
449 }
450
451 auto te = req.fs.header(http2::HD_TE);
452 // HTTP/1 upstream request can contain keyword other than
453 // "trailers". We just forward "trailers".
454 // TODO more strict handling required here.
455 if (te && http2::contains_trailers(te->value)) {
456 nva.push_back(http2::make_field("te"_sr, "trailers"_sr));
457 }
458
459 for (auto &p : httpconf.add_request_headers) {
460 nva.push_back(http2::make_field(p.name, p.value));
461 }
462
463 if (LOG_ENABLED(INFO)) {
464 std::stringstream ss;
465 for (auto &nv : nva) {
466 auto name = StringRef{nv.name, nv.namelen};
467
468 if ("authorization"_sr == name) {
469 ss << TTY_HTTP_HD << name << TTY_RST << ": <redacted>\n";
470 continue;
471 }
472 ss << TTY_HTTP_HD << name << TTY_RST << ": "
473 << StringRef{nv.value, nv.valuelen} << "\n";
474 }
475 DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
476 }
477
478 auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING);
479
480 nghttp2_data_provider2 *data_prdptr = nullptr;
481 nghttp2_data_provider2 data_prd;
482
483 // Add body as long as transfer-encoding is given even if
484 // req.fs.content_length == 0 to forward trailer fields.
485 if (req.method == HTTP_CONNECT || req.connect_proto != ConnectProto::NONE ||
486 transfer_encoding || req.fs.content_length > 0 || req.http2_expect_body) {
487 // Request-body is expected.
488 data_prd = {{}, http2_data_read_callback};
489 data_prdptr = &data_prd;
490 }
491
492 rv = http2session_->submit_request(this, nva.data(), nva.size(), data_prdptr);
493 if (rv != 0) {
494 DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
495 return -1;
496 }
497
498 if (data_prdptr) {
499 downstream_->reset_downstream_wtimer();
500 }
501
502 http2session_->signal_write();
503 return 0;
504 }
505
push_upload_data_chunk(const uint8_t * data,size_t datalen)506 int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
507 size_t datalen) {
508 if (!downstream_->get_request_header_sent()) {
509 auto output = downstream_->get_blocked_request_buf();
510 auto &req = downstream_->request();
511 output->append(data, datalen);
512 req.unconsumed_body_length += datalen;
513 return 0;
514 }
515
516 int rv;
517 auto output = downstream_->get_request_buf();
518 output->append(data, datalen);
519 if (downstream_->get_downstream_stream_id() != -1) {
520 rv = http2session_->resume_data(this);
521 if (rv != 0) {
522 return -1;
523 }
524
525 downstream_->ensure_downstream_wtimer();
526
527 http2session_->signal_write();
528 }
529 return 0;
530 }
531
end_upload_data()532 int Http2DownstreamConnection::end_upload_data() {
533 if (!downstream_->get_request_header_sent()) {
534 downstream_->set_blocked_request_data_eof(true);
535 return 0;
536 }
537
538 int rv;
539 if (downstream_->get_downstream_stream_id() != -1) {
540 rv = http2session_->resume_data(this);
541 if (rv != 0) {
542 return -1;
543 }
544
545 downstream_->ensure_downstream_wtimer();
546
547 http2session_->signal_write();
548 }
549 return 0;
550 }
551
resume_read(IOCtrlReason reason,size_t consumed)552 int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
553 size_t consumed) {
554 int rv;
555
556 if (http2session_->get_state() != Http2SessionState::CONNECTED) {
557 return 0;
558 }
559
560 if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
561 return 0;
562 }
563
564 if (consumed > 0) {
565 rv =
566 http2session_->consume(downstream_->get_downstream_stream_id(), consumed);
567
568 if (rv != 0) {
569 return -1;
570 }
571
572 auto &resp = downstream_->response();
573
574 resp.unconsumed_body_length -= consumed;
575
576 http2session_->signal_write();
577 }
578
579 return 0;
580 }
581
on_read()582 int Http2DownstreamConnection::on_read() { return 0; }
583
on_write()584 int Http2DownstreamConnection::on_write() { return 0; }
585
attach_stream_data(StreamData * sd)586 void Http2DownstreamConnection::attach_stream_data(StreamData *sd) {
587 // It is possible sd->dconn is not NULL. sd is detached when
588 // on_stream_close_callback. Before that, after MSG_COMPLETE is set
589 // to Downstream::set_response_state(), upstream's readcb is called
590 // and execution path eventually could reach here. Since the
591 // response was already handled, we just detach sd.
592 detach_stream_data();
593 sd_ = sd;
594 sd_->dconn = this;
595 }
596
detach_stream_data()597 StreamData *Http2DownstreamConnection::detach_stream_data() {
598 if (sd_) {
599 auto sd = sd_;
600 sd_ = nullptr;
601 sd->dconn = nullptr;
602 return sd;
603 }
604 return nullptr;
605 }
606
on_timeout()607 int Http2DownstreamConnection::on_timeout() {
608 if (!downstream_) {
609 return 0;
610 }
611
612 return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
613 }
614
615 const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const616 Http2DownstreamConnection::get_downstream_addr_group() const {
617 return http2session_->get_downstream_addr_group();
618 }
619
get_addr() const620 DownstreamAddr *Http2DownstreamConnection::get_addr() const { return nullptr; }
621
622 } // namespace shrpx
623