• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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