Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / shrpx_http2_downstream_connection.cc
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 "http-parser/http_parser.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 "http2.h"
41 #include "util.h"
42
43 using namespace nghttp2;
44
45 namespace shrpx {
46
47 Http2DownstreamConnection::Http2DownstreamConnection(
48     DownstreamConnectionPool *dconn_pool, Http2Session *http2session)
49     : DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr),
50       http2session_(http2session), sd_(nullptr) {}
51
52 Http2DownstreamConnection::~Http2DownstreamConnection() {
53   if (LOG_ENABLED(INFO)) {
54     DCLOG(INFO, this) << "Deleting";
55   }
56   if (downstream_) {
57     downstream_->disable_downstream_rtimer();
58     downstream_->disable_downstream_wtimer();
59
60     uint32_t error_code;
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;
66     } else {
67       error_code = NGHTTP2_INTERNAL_ERROR;
68     }
69
70     if (downstream_->get_downstream_stream_id() != -1) {
71       if (LOG_ENABLED(INFO)) {
72         DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_
73                           << ", stream_id="
74                           << downstream_->get_downstream_stream_id()
75                           << ", error_code=" << error_code;
76       }
77
78       submit_rst_stream(downstream_, error_code);
79
80       http2session_->consume(downstream_->get_downstream_stream_id(),
81                              downstream_->get_response_datalen());
82
83       downstream_->reset_response_datalen();
84
85       http2session_->signal_write();
86     }
87   }
88   http2session_->remove_downstream_connection(this);
89   // Downstream and DownstreamConnection may be deleted
90   // asynchronously.
91   if (downstream_) {
92     downstream_->release_downstream_connection();
93   }
94   if (LOG_ENABLED(INFO)) {
95     DCLOG(INFO, this) << "Deleted";
96   }
97 }
98
99 int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
100   if (LOG_ENABLED(INFO)) {
101     DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
102   }
103   http2session_->add_downstream_connection(this);
104   if (http2session_->get_state() == Http2Session::DISCONNECTED) {
105     http2session_->signal_write();
106   }
107
108   downstream_ = downstream;
109   downstream_->reset_downstream_rtimer();
110
111   return 0;
112 }
113
114 void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
115   if (LOG_ENABLED(INFO)) {
116     DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
117   }
118   if (submit_rst_stream(downstream) == 0) {
119     http2session_->signal_write();
120   }
121
122   if (downstream_->get_downstream_stream_id() != -1) {
123     http2session_->consume(downstream_->get_downstream_stream_id(),
124                            downstream_->get_response_datalen());
125
126     downstream_->reset_response_datalen();
127
128     http2session_->signal_write();
129   }
130
131   downstream->disable_downstream_rtimer();
132   downstream->disable_downstream_wtimer();
133   downstream_ = nullptr;
134 }
135
136 int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
137                                                  uint32_t error_code) {
138   int rv = -1;
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:
145       break;
146     default:
147       if (LOG_ENABLED(INFO)) {
148         DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
149                           << ", stream_id="
150                           << downstream->get_downstream_stream_id();
151       }
152       rv = http2session_->submit_rst_stream(
153           downstream->get_downstream_stream_id(), error_code);
154     }
155   }
156   return rv;
157 }
158
159 namespace {
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) {
164   int rv;
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;
169   }
170   auto dconn = static_cast<Http2DownstreamConnection *>(source->ptr);
171   auto downstream = dconn->get_downstream();
172   if (!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;
176   }
177   auto input = downstream->get_request_buf();
178   auto nread = input->remove(buf, length);
179   auto input_empty = input->rleft() == 0;
180
181   if (nread > 0) {
182     // This is important because it will handle flow control
183     // stuff.
184     if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
185                                                 nread) != 0) {
186       // In this case, downstream may be deleted.
187       return NGHTTP2_ERR_CALLBACK_FAILURE;
188     }
189
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;
194     }
195   }
196
197   if (input_empty &&
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
201       // header is seen.
202       (!downstream->get_upgrade_request() ||
203        (downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
204         !downstream->get_upgraded()))) {
205
206     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
207
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);
213       if (!nva.empty()) {
214         rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
215         if (rv != 0) {
216           if (nghttp2_is_fatal(rv)) {
217             return NGHTTP2_ERR_CALLBACK_FAILURE;
218           }
219         } else {
220           *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
221         }
222       }
223     }
224   }
225
226   if (!input_empty) {
227     downstream->reset_downstream_wtimer();
228   } else {
229     downstream->disable_downstream_wtimer();
230   }
231
232   if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
233     downstream->disable_downstream_wtimer();
234
235     return NGHTTP2_ERR_DEFERRED;
236   }
237
238   return nread;
239 }
240 } // namespace
241
242 int Http2DownstreamConnection::push_request_headers() {
243   int rv;
244   if (!downstream_) {
245     return 0;
246   }
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();
253     return 0;
254   }
255
256   downstream_->set_request_pending(false);
257
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";
262
263   // http2session_ has already in CONNECTED state, so we can get
264   // addr_idx here.
265   auto addr_idx = http2session_->get_addr_idx();
266   auto downstream_hostport =
267       get_config()->downstream_addrs[addr_idx].hostport.get();
268
269   const char *authority = nullptr, *host = nullptr;
270   if (!no_host_rewrite) {
271     if (!downstream_->get_request_http2_authority().empty()) {
272       authority = downstream_hostport;
273     }
274     if (downstream_->get_request_header(http2::HD_HOST)) {
275       host = downstream_hostport;
276     }
277   } else {
278     if (!downstream_->get_request_http2_authority().empty()) {
279       authority = downstream_->get_request_http2_authority().c_str();
280     }
281     auto h = downstream_->get_request_header(http2::HD_HOST);
282     if (h) {
283       host = h->value.c_str();
284     }
285   }
286
287   if (!authority && !host) {
288     // upstream is HTTP/1.0.  We use backend server's host
289     // nonetheless.
290     host = downstream_hostport;
291   }
292
293   if (authority) {
294     downstream_->set_request_downstream_host(authority);
295   } else {
296     downstream_->set_request_downstream_host(host);
297   }
298
299   size_t nheader = downstream_->get_request_headers().size();
300
301   Headers cookies;
302   if (!get_config()->http2_no_cookie_crumbling) {
303     cookies = downstream_->crumble_request_cookie();
304   }
305
306   // 8 means:
307   // 1. :method
308   // 2. :scheme
309   // 3. :path
310   // 4. :authority or host (at least either of them exists)
311   // 5. via (optional)
312   // 6. x-forwarded-for (optional)
313   // 7. x-forwarded-proto (optional)
314   // 8. te (optional)
315   auto nva = std::vector<nghttp2_nv>();
316   nva.reserve(nheader + 8 + cookies.size());
317
318   std::string via_value;
319   std::string xff_value;
320   std::string scheme, uri_authority, path, query;
321
322   nva.push_back(
323       http2::make_nv_ls(":method", downstream_->get_request_method()));
324
325   if (downstream_->get_request_method() == "CONNECT") {
326     if (authority) {
327       nva.push_back(http2::make_nv_lc(":authority", authority));
328     } else {
329       nva.push_back(
330           http2::make_nv_ls(":authority", downstream_->get_request_path()));
331     }
332   } else {
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"));
338     } else {
339       nva.push_back(http2::make_nv_ll(":scheme", "http"));
340     }
341
342     if (authority) {
343       nva.push_back(http2::make_nv_lc(":authority", authority));
344     }
345
346     nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
347   }
348
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));
353   }
354
355   http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
356
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;
363   }
364
365   for (auto &nv : cookies) {
366     nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
367   }
368
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;
373       xff_value += ", ";
374     }
375     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));
380   }
381
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"));
388       } else {
389         nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http"));
390       }
391     } else {
392       nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme));
393     }
394   }
395
396   auto via = downstream_->get_request_header(http2::HD_VIA);
397   if (get_config()->no_via) {
398     if (via) {
399       nva.push_back(http2::make_nv_ls("via", (*via).value));
400     }
401   } else {
402     if (via) {
403       via_value = (*via).value;
404       via_value += ", ";
405     }
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));
409   }
410
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"));
417   }
418
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";
423     }
424     DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
425   }
426
427   auto content_length =
428       downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
429   // TODO check content-length: 0 case
430
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);
439   } else {
440     rv = http2session_->submit_request(this, downstream_->get_priority(),
441                                        nva.data(), nva.size(), nullptr);
442   }
443   if (rv != 0) {
444     DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
445     return -1;
446   }
447
448   downstream_->reset_downstream_wtimer();
449
450   http2session_->signal_write();
451   return 0;
452 }
453
454 int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
455                                                       size_t datalen) {
456   int rv;
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);
461     if (rv != 0) {
462       return -1;
463     }
464
465     downstream_->ensure_downstream_wtimer();
466
467     http2session_->signal_write();
468   }
469   return 0;
470 }
471
472 int Http2DownstreamConnection::end_upload_data() {
473   int rv;
474   if (downstream_->get_downstream_stream_id() != -1) {
475     rv = http2session_->resume_data(this);
476     if (rv != 0) {
477       return -1;
478     }
479
480     downstream_->ensure_downstream_wtimer();
481
482     http2session_->signal_write();
483   }
484   return 0;
485 }
486
487 int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
488                                            size_t consumed) {
489   int rv;
490
491   if (http2session_->get_state() != Http2Session::CONNECTED ||
492       !http2session_->get_flow_control()) {
493     return 0;
494   }
495
496   if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
497     return 0;
498   }
499
500   if (consumed > 0) {
501     assert(downstream_->get_response_datalen() >= consumed);
502
503     rv = http2session_->consume(downstream_->get_downstream_stream_id(),
504                                 consumed);
505
506     if (rv != 0) {
507       return -1;
508     }
509
510     downstream_->dec_response_datalen(consumed);
511
512     http2session_->signal_write();
513   }
514
515   return 0;
516 }
517
518 int Http2DownstreamConnection::on_read() { return 0; }
519
520 int Http2DownstreamConnection::on_write() { return 0; }
521
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();
529   sd_ = sd;
530   sd_->dconn = this;
531 }
532
533 StreamData *Http2DownstreamConnection::detach_stream_data() {
534   if (sd_) {
535     auto sd = sd_;
536     sd_ = nullptr;
537     sd->dconn = nullptr;
538     return sd;
539   }
540   return nullptr;
541 }
542
543 int Http2DownstreamConnection::on_priority_change(int32_t pri) {
544   int rv;
545   if (downstream_->get_priority() == pri) {
546     return 0;
547   }
548   downstream_->set_priority(pri);
549   if (http2session_->get_state() != Http2Session::CONNECTED) {
550     return 0;
551   }
552   rv = http2session_->submit_priority(this, pri);
553   if (rv != 0) {
554     DLOG(FATAL, this) << "nghttp2_submit_priority() failed";
555     return -1;
556   }
557   http2session_->signal_write();
558   return 0;
559 }
560
561 int Http2DownstreamConnection::on_timeout() {
562   if (!downstream_) {
563     return 0;
564   }
565
566   return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
567 }
568
569 } // namespace shrpx