2012-11-18 13:23:13 +01:00
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
* nghttp2 - HTTP/2 C Library
|
2012-11-18 13:23:13 +01:00
|
|
|
*
|
|
|
|
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
2013-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_downstream_connection.h"
|
2012-11-18 13:23:13 +01:00
|
|
|
|
2015-05-13 15:30:35 +02:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2012-11-18 13:23:13 +01:00
|
|
|
#include <unistd.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
#endif // HAVE_UNISTD_H
|
2012-11-18 13:23:13 +01:00
|
|
|
|
2013-01-05 15:21:09 +01:00
|
|
|
#include "http-parser/http_parser.h"
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
#include "shrpx_client_handler.h"
|
|
|
|
#include "shrpx_upstream.h"
|
|
|
|
#include "shrpx_downstream.h"
|
|
|
|
#include "shrpx_config.h"
|
|
|
|
#include "shrpx_error.h"
|
|
|
|
#include "shrpx_http.h"
|
2013-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_session.h"
|
2013-08-27 19:47:22 +02:00
|
|
|
#include "http2.h"
|
2012-11-18 13:23:13 +01:00
|
|
|
#include "util.h"
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
using namespace nghttp2;
|
2012-11-18 13:23:13 +01:00
|
|
|
|
|
|
|
namespace shrpx {
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
Http2DownstreamConnection::Http2DownstreamConnection(
|
|
|
|
DownstreamConnectionPool *dconn_pool, Http2Session *http2session)
|
2015-03-11 13:35:47 +01:00
|
|
|
: DownstreamConnection(dconn_pool), dlnext(nullptr), dlprev(nullptr),
|
|
|
|
http2session_(http2session), sd_(nullptr) {}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
|
|
|
Http2DownstreamConnection::~Http2DownstreamConnection() {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-02-08 13:46:58 +01:00
|
|
|
DCLOG(INFO, this) << "Deleting";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream_->disable_downstream_rtimer();
|
|
|
|
downstream_->disable_downstream_wtimer();
|
|
|
|
|
2014-11-22 15:13:29 +01:00
|
|
|
uint32_t error_code;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_request_state() == Downstream::STREAM_CLOSED &&
|
|
|
|
downstream_->get_upgraded()) {
|
2014-11-22 15:13:29 +01:00
|
|
|
// For upgraded connection, send NO_ERROR. Should we consider
|
|
|
|
// request states other than Downstream::STREAM_CLOSED ?
|
|
|
|
error_code = NGHTTP2_NO_ERROR;
|
|
|
|
} else {
|
|
|
|
error_code = NGHTTP2_INTERNAL_ERROR;
|
|
|
|
}
|
|
|
|
|
2015-11-22 07:23:18 +01:00
|
|
|
if (http2session_->get_state() == Http2Session::CONNECTED &&
|
|
|
|
downstream_->get_downstream_stream_id() != -1) {
|
2014-12-27 18:59:06 +01:00
|
|
|
submit_rst_stream(downstream_, error_code);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2016-01-14 15:49:21 +01:00
|
|
|
auto &resp = downstream_->response();
|
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
http2session_->consume(downstream_->get_downstream_stream_id(),
|
2016-01-14 15:49:21 +01:00
|
|
|
resp.unconsumed_body_length);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2016-01-14 15:49:21 +01:00
|
|
|
resp.unconsumed_body_length = 0;
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
2013-02-07 16:22:22 +01:00
|
|
|
}
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->remove_downstream_connection(this);
|
2015-07-15 16:31:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-02-08 13:46:58 +01:00
|
|
|
DCLOG(INFO, this) << "Deleted";
|
|
|
|
}
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::attach_downstream(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->add_downstream_connection(this);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session_->get_state() == Http2Session::DISCONNECTED) {
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-08-18 17:16:51 +02:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
downstream_ = downstream;
|
2015-01-05 16:30:57 +01:00
|
|
|
downstream_->reset_downstream_rtimer();
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2DownstreamConnection::detach_downstream(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2016-01-14 15:49:21 +01:00
|
|
|
|
|
|
|
auto &resp = downstream_->response();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (submit_rst_stream(downstream) == 0) {
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2013-02-07 16:22:22 +01:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_downstream_stream_id() != -1) {
|
2014-07-25 14:26:03 +02:00
|
|
|
http2session_->consume(downstream_->get_downstream_stream_id(),
|
2016-01-14 15:49:21 +01:00
|
|
|
resp.unconsumed_body_length);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2016-01-14 15:49:21 +01:00
|
|
|
resp.unconsumed_body_length = 0;
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_downstream_rtimer();
|
|
|
|
downstream->disable_downstream_wtimer();
|
2013-11-26 13:29:53 +01:00
|
|
|
downstream_ = nullptr;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
uint32_t error_code) {
|
2013-02-07 16:22:22 +01:00
|
|
|
int rv = -1;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session_->get_state() == Http2Session::CONNECTED &&
|
|
|
|
downstream->get_downstream_stream_id() != -1) {
|
|
|
|
switch (downstream->get_response_state()) {
|
2013-02-07 16:22:22 +01:00
|
|
|
case Downstream::MSG_RESET:
|
2015-01-19 15:44:23 +01:00
|
|
|
case Downstream::MSG_BAD_HEADER:
|
2013-02-07 16:22:22 +01:00
|
|
|
case Downstream::MSG_COMPLETE:
|
|
|
|
break;
|
|
|
|
default:
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
|
|
|
|
<< ", stream_id="
|
2015-11-22 07:23:18 +01:00
|
|
|
<< downstream->get_downstream_stream_id()
|
|
|
|
<< ", error_code=" << error_code;
|
2013-02-07 16:22:22 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = http2session_->submit_rst_stream(
|
|
|
|
downstream->get_downstream_stream_id(), error_code);
|
2013-02-07 16:22:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
ssize_t http2_data_read_callback(nghttp2_session *session, int32_t stream_id,
|
2013-11-04 09:53:57 +01:00
|
|
|
uint8_t *buf, size_t length,
|
2014-04-05 10:59:24 +02:00
|
|
|
uint32_t *data_flags,
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_data_source *source, void *user_data) {
|
2015-03-08 08:29:26 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
2013-07-12 17:19:03 +02:00
|
|
|
return NGHTTP2_ERR_DEFERRED;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto dconn = static_cast<Http2DownstreamConnection *>(source->ptr);
|
2013-11-04 09:53:57 +01:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2012-11-18 13:23:13 +01:00
|
|
|
// In this case, RST_STREAM should have been issued. But depending
|
|
|
|
// on the priority, DATA frame may come first.
|
2013-07-12 17:19:03 +02:00
|
|
|
return NGHTTP2_ERR_DEFERRED;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream->request();
|
2014-12-27 18:59:06 +01:00
|
|
|
auto input = downstream->get_request_buf();
|
|
|
|
auto nread = input->remove(buf, length);
|
|
|
|
auto input_empty = input->rleft() == 0;
|
2014-08-19 18:39:07 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread > 0) {
|
2014-11-22 13:11:18 +01:00
|
|
|
// This is important because it will handle flow control
|
|
|
|
// stuff.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
|
|
|
|
nread) != 0) {
|
2014-11-22 13:11:18 +01:00
|
|
|
// In this case, downstream may be deleted.
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
2014-08-21 14:22:16 +02:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-22 13:11:18 +01:00
|
|
|
// Check dconn is still alive because Upstream::resume_read()
|
|
|
|
// may delete downstream which will delete dconn.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (sd->dconn == nullptr) {
|
2014-11-22 13:11:18 +01:00
|
|
|
return NGHTTP2_ERR_DEFERRED;
|
|
|
|
}
|
|
|
|
}
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (input_empty &&
|
2014-11-27 15:39:04 +01:00
|
|
|
downstream->get_request_state() == Downstream::MSG_COMPLETE &&
|
|
|
|
// If connection is upgraded, don't set EOF flag, since HTTP/1
|
|
|
|
// will set MSG_COMPLETE to request state after upgrade response
|
|
|
|
// header is seen.
|
2016-01-13 14:45:52 +01:00
|
|
|
(!req.upgrade_request ||
|
2014-11-27 15:39:04 +01:00
|
|
|
(downstream->get_response_state() == Downstream::HEADER_COMPLETE &&
|
|
|
|
!downstream->get_upgraded()))) {
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-11-22 13:11:18 +01:00
|
|
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
2015-03-08 08:29:26 +01:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &trailers = req.fs.trailers();
|
2015-03-08 08:48:25 +01:00
|
|
|
if (!trailers.empty()) {
|
2015-03-08 08:29:26 +01:00
|
|
|
std::vector<nghttp2_nv> nva;
|
2015-03-08 08:48:25 +01:00
|
|
|
nva.reserve(trailers.size());
|
2015-11-05 14:48:54 +01:00
|
|
|
// We cannot use nocopy version, since nva may be touched after
|
|
|
|
// Downstream object is deleted.
|
2015-03-08 09:32:01 +01:00
|
|
|
http2::copy_headers_to_nva(nva, trailers);
|
|
|
|
if (!nva.empty()) {
|
|
|
|
rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
|
|
|
|
if (rv != 0) {
|
|
|
|
if (nghttp2_is_fatal(rv)) {
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
2015-03-08 08:29:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (!input_empty) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_downstream_wtimer();
|
|
|
|
} else {
|
|
|
|
downstream->disable_downstream_wtimer();
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == 0 && (*data_flags & NGHTTP2_DATA_FLAG_EOF) == 0) {
|
2014-11-22 13:11:18 +01:00
|
|
|
downstream->disable_downstream_wtimer();
|
|
|
|
|
|
|
|
return NGHTTP2_ERR_DEFERRED;
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::push_request_headers() {
|
2012-11-18 13:23:13 +01:00
|
|
|
int rv;
|
2015-02-17 15:15:53 +01:00
|
|
|
if (!downstream_) {
|
|
|
|
return 0;
|
|
|
|
}
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
if (!http2session_->can_push_request()) {
|
|
|
|
// The HTTP2 session to the backend has not been established or
|
|
|
|
// connection is now being checked. This function will be called
|
|
|
|
// again just after it is established.
|
2015-02-17 15:15:53 +01:00
|
|
|
downstream_->set_request_pending(true);
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
http2session_->start_checking_connection();
|
2012-11-18 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2015-02-17 15:15:53 +01:00
|
|
|
|
|
|
|
downstream_->set_request_pending(false);
|
2015-02-03 17:15:56 +01:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream_->request();
|
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &httpconf = get_config()->http;
|
|
|
|
auto &http2conf = get_config()->http2;
|
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto no_host_rewrite =
|
2016-01-18 09:00:20 +01:00
|
|
|
httpconf.no_host_rewrite || get_config()->http2_proxy ||
|
2016-01-13 14:45:52 +01:00
|
|
|
get_config()->client_proxy || req.method == HTTP_CONNECT;
|
2015-03-09 16:44:35 +01:00
|
|
|
|
2015-03-10 13:54:29 +01:00
|
|
|
// http2session_ has already in CONNECTED state, so we can get
|
|
|
|
// addr_idx here.
|
|
|
|
auto addr_idx = http2session_->get_addr_idx();
|
2015-07-12 15:16:20 +02:00
|
|
|
auto group = http2session_->get_group();
|
2016-01-16 16:52:41 +01:00
|
|
|
const auto &downstream_hostport =
|
|
|
|
get_config()->downstream_addr_groups[group].addrs[addr_idx].hostport;
|
2015-03-10 13:54:29 +01:00
|
|
|
|
2015-09-03 16:36:49 +02:00
|
|
|
// For HTTP/1.0 request, there is no authority in request. In that
|
|
|
|
// case, we use backend server's host nonetheless.
|
2016-01-17 03:33:45 +01:00
|
|
|
auto authority = StringRef(downstream_hostport);
|
2016-01-16 16:52:41 +01:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
if (no_host_rewrite && !req.authority.empty()) {
|
2016-01-16 16:52:41 +01:00
|
|
|
authority = req.authority;
|
2015-02-03 17:15:56 +01:00
|
|
|
}
|
|
|
|
|
2016-01-16 16:52:41 +01:00
|
|
|
downstream_->set_request_downstream_host(authority.str());
|
2015-02-08 11:41:45 +01:00
|
|
|
|
2015-11-05 14:48:54 +01:00
|
|
|
size_t num_cookies = 0;
|
2016-01-18 09:00:20 +01:00
|
|
|
if (!http2conf.no_cookie_crumbling) {
|
2015-11-05 14:48:54 +01:00
|
|
|
num_cookies = downstream_->count_crumble_request_cookie();
|
2013-11-17 16:03:55 +01:00
|
|
|
}
|
2014-11-18 16:56:44 +01:00
|
|
|
|
2016-01-15 15:37:28 +01:00
|
|
|
// 9 means:
|
2013-10-25 14:50:56 +02:00
|
|
|
// 1. :method
|
|
|
|
// 2. :scheme
|
|
|
|
// 3. :path
|
2016-01-16 13:12:51 +01:00
|
|
|
// 4. :authority (or host)
|
2015-03-13 14:40:41 +01:00
|
|
|
// 5. via (optional)
|
|
|
|
// 6. x-forwarded-for (optional)
|
|
|
|
// 7. x-forwarded-proto (optional)
|
|
|
|
// 8. te (optional)
|
2016-01-15 15:37:28 +01:00
|
|
|
// 9. forwarded (optional)
|
2013-11-28 13:36:04 +01:00
|
|
|
auto nva = std::vector<nghttp2_nv>();
|
2016-01-15 15:37:28 +01:00
|
|
|
nva.reserve(req.fs.headers().size() + 9 + num_cookies +
|
2016-01-18 09:00:20 +01:00
|
|
|
httpconf.add_request_headers.size());
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2015-11-05 14:48:54 +01:00
|
|
|
nva.push_back(
|
2016-01-13 14:45:52 +01:00
|
|
|
http2::make_nv_lc_nocopy(":method", http2::to_method_string(req.method)));
|
2015-06-16 14:29:47 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.method != HTTP_CONNECT) {
|
|
|
|
assert(!req.scheme.empty());
|
2015-09-03 17:14:09 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
|
2015-09-03 17:14:09 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.method == HTTP_OPTIONS && req.path.empty()) {
|
2015-09-03 17:14:09 +02:00
|
|
|
nva.push_back(http2::make_nv_ll(":path", "*"));
|
|
|
|
} else {
|
2016-01-13 14:45:52 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy(":path", req.path));
|
2015-09-03 17:14:09 +02:00
|
|
|
}
|
2016-01-16 13:12:51 +01:00
|
|
|
|
|
|
|
if (!req.no_authority) {
|
2016-01-16 16:52:41 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
|
2016-01-16 13:12:51 +01:00
|
|
|
} else {
|
2016-01-16 16:52:41 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy("host", authority));
|
2016-01-16 13:12:51 +01:00
|
|
|
}
|
|
|
|
} else {
|
2016-01-16 16:52:41 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy(":authority", authority));
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
http2::copy_headers_to_nva_nocopy(nva, req.fs.headers());
|
2013-08-27 17:09:46 +02:00
|
|
|
|
|
|
|
bool chunked_encoding = false;
|
2016-01-13 14:45:52 +01:00
|
|
|
auto transfer_encoding = req.fs.header(http2::HD_TRANSFER_ENCODING);
|
2015-01-04 15:22:39 +01:00
|
|
|
if (transfer_encoding &&
|
2015-02-22 07:32:48 +01:00
|
|
|
util::strieq_l("chunked", (*transfer_encoding).value)) {
|
2013-08-27 17:09:46 +02:00
|
|
|
chunked_encoding = true;
|
|
|
|
}
|
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
if (!http2conf.no_cookie_crumbling) {
|
2015-11-05 14:48:54 +01:00
|
|
|
downstream_->crumble_request_cookie(nva);
|
2015-01-04 15:22:39 +01:00
|
|
|
}
|
|
|
|
|
2016-01-15 15:37:28 +01:00
|
|
|
auto upstream = downstream_->get_upstream();
|
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
|
|
|
|
std::string forwarded_value;
|
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &fwdconf = httpconf.forwarded;
|
|
|
|
|
|
|
|
auto fwd =
|
|
|
|
fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED);
|
2016-01-15 15:37:28 +01:00
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
if (fwdconf.params) {
|
|
|
|
auto params = fwdconf.params;
|
2016-01-15 15:37:28 +01:00
|
|
|
|
|
|
|
if (get_config()->http2_proxy || get_config()->client_proxy ||
|
|
|
|
req.method == HTTP_CONNECT) {
|
|
|
|
params &= ~FORWARDED_PROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto value = http::create_forwarded(params, handler->get_forwarded_by(),
|
|
|
|
handler->get_forwarded_for(),
|
|
|
|
req.authority, req.scheme);
|
|
|
|
if (fwd || !value.empty()) {
|
|
|
|
if (fwd) {
|
|
|
|
forwarded_value = fwd->value;
|
|
|
|
|
|
|
|
if (!value.empty()) {
|
|
|
|
forwarded_value += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
forwarded_value += value;
|
|
|
|
|
|
|
|
nva.push_back(http2::make_nv_ls("forwarded", forwarded_value));
|
|
|
|
}
|
|
|
|
} else if (fwd) {
|
|
|
|
nva.push_back(http2::make_nv_ls_nocopy("forwarded", fwd->value));
|
|
|
|
forwarded_value = fwd->value;
|
|
|
|
}
|
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &xffconf = httpconf.xff;
|
|
|
|
|
|
|
|
auto xff = xffconf.strip_incoming ? nullptr
|
|
|
|
: req.fs.header(http2::HD_X_FORWARDED_FOR);
|
|
|
|
|
2015-06-16 14:29:47 +02:00
|
|
|
std::string xff_value;
|
2016-01-16 08:48:41 +01:00
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
if (xffconf.add) {
|
2016-01-16 08:48:41 +01:00
|
|
|
if (xff) {
|
2014-04-03 04:22:11 +02:00
|
|
|
xff_value = (*xff).value;
|
2012-11-18 15:04:14 +01:00
|
|
|
xff_value += ", ";
|
|
|
|
}
|
2016-01-15 15:37:28 +01:00
|
|
|
xff_value += upstream->get_client_handler()->get_ipaddr();
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls("x-forwarded-for", xff_value));
|
2016-01-16 08:48:41 +01:00
|
|
|
} else if (xff) {
|
2015-11-05 14:48:54 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", (*xff).value));
|
2012-11-18 15:04:14 +01:00
|
|
|
}
|
2013-08-27 17:09:46 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
2016-01-13 14:45:52 +01:00
|
|
|
req.method != HTTP_CONNECT) {
|
2014-03-21 10:57:57 +01:00
|
|
|
// We use same protocol with :scheme header field
|
2016-01-13 14:45:52 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme));
|
2014-03-21 10:57:57 +01:00
|
|
|
}
|
|
|
|
|
2015-06-16 14:29:47 +02:00
|
|
|
std::string via_value;
|
2016-01-13 14:45:52 +01:00
|
|
|
auto via = req.fs.header(http2::HD_VIA);
|
2016-01-18 09:00:20 +01:00
|
|
|
if (httpconf.no_via) {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
2015-11-05 14:48:54 +01:00
|
|
|
nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value));
|
2013-08-27 17:09:46 +02:00
|
|
|
}
|
|
|
|
} else {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
2014-04-03 04:22:11 +02:00
|
|
|
via_value = (*via).value;
|
2013-01-09 14:01:25 +01:00
|
|
|
via_value += ", ";
|
|
|
|
}
|
2016-01-13 14:45:52 +01:00
|
|
|
via_value += http::create_via_header_value(req.http_major, req.http_minor);
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls("via", via_value));
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2013-08-27 17:09:46 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto te = req.fs.header(http2::HD_TE);
|
2015-03-24 17:20:41 +01:00
|
|
|
// HTTP/1 upstream request can contain keyword other than
|
|
|
|
// "trailers". We just forward "trailers".
|
|
|
|
// TODO more strict handling required here.
|
|
|
|
if (te && util::strifind(te->value.c_str(), "trailers")) {
|
|
|
|
nva.push_back(http2::make_nv_ll("te", "trailers"));
|
2015-03-08 08:29:26 +01:00
|
|
|
}
|
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
for (auto &p : httpconf.add_request_headers) {
|
2015-11-05 14:48:54 +01:00
|
|
|
nva.push_back(http2::make_nv_nocopy(p.first, p.second));
|
2015-06-05 16:04:20 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-11-18 13:23:13 +01:00
|
|
|
std::stringstream ss;
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &nv : nva) {
|
2015-03-23 15:25:57 +01:00
|
|
|
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, this) << "HTTP request headers\n" << ss.str();
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH);
|
2015-01-04 15:22:39 +01:00
|
|
|
// TODO check content-length: 0 case
|
2014-07-03 19:03:09 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.method == HTTP_CONNECT || chunked_encoding || content_length ||
|
|
|
|
req.http2_expect_body) {
|
2012-11-18 13:23:13 +01:00
|
|
|
// Request-body is expected.
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_data_provider data_prd;
|
2012-11-18 13:23:13 +01:00
|
|
|
data_prd.source.ptr = this;
|
2013-11-04 09:53:57 +01:00
|
|
|
data_prd.read_callback = http2_data_read_callback;
|
2016-01-14 16:09:53 +01:00
|
|
|
rv = http2session_->submit_request(this, nva.data(), nva.size(), &data_prd);
|
2012-11-18 13:23:13 +01:00
|
|
|
} else {
|
2016-01-14 16:09:53 +01:00
|
|
|
rv = http2session_->submit_request(this, nva.data(), nva.size(), nullptr);
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2013-07-12 17:19:03 +02:00
|
|
|
DCLOG(FATAL, this) << "nghttp2_submit_request() failed";
|
2012-11-18 13:23:13 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-06-15 09:14:00 +02:00
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream_->reset_downstream_wtimer();
|
2014-06-15 09:14:00 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
|
2014-11-27 15:39:04 +01:00
|
|
|
size_t datalen) {
|
2014-12-27 18:59:06 +01:00
|
|
|
int rv;
|
|
|
|
auto output = downstream_->get_request_buf();
|
|
|
|
output->append(data, datalen);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_downstream_stream_id() != -1) {
|
2013-11-04 09:53:57 +01:00
|
|
|
rv = http2session_->resume_data(this);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-11-18 18:11:46 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream_->ensure_downstream_wtimer();
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2012-11-18 18:11:46 +01:00
|
|
|
}
|
|
|
|
return 0;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::end_upload_data() {
|
2012-11-18 13:23:13 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_downstream_stream_id() != -1) {
|
2013-11-04 09:53:57 +01:00
|
|
|
rv = http2session_->resume_data(this);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream_->ensure_downstream_wtimer();
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::resume_read(IOCtrlReason reason,
|
|
|
|
size_t consumed) {
|
2014-07-25 14:26:03 +02:00
|
|
|
int rv;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session_->get_state() != Http2Session::CONNECTED ||
|
|
|
|
!http2session_->get_flow_control()) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return 0;
|
2012-11-21 15:47:48 +01:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream_ || downstream_->get_downstream_stream_id() == -1) {
|
2014-01-18 16:38:11 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (consumed > 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
rv = http2session_->consume(downstream_->get_downstream_stream_id(),
|
|
|
|
consumed);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2016-01-14 15:49:21 +01:00
|
|
|
auto &resp = downstream_->response();
|
|
|
|
|
|
|
|
resp.unconsumed_body_length -= consumed;
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2014-08-21 14:22:16 +02:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-11-21 15:47:48 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::on_read() { return 0; }
|
2013-02-08 13:46:58 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::on_write() { return 0; }
|
2013-02-08 13:46:58 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2DownstreamConnection::attach_stream_data(StreamData *sd) {
|
2013-03-07 13:32:10 +01:00
|
|
|
// It is possible sd->dconn is not NULL. sd is detached when
|
|
|
|
// on_stream_close_callback. Before that, after MSG_COMPLETE is set
|
|
|
|
// to Downstream::set_response_state(), upstream's readcb is called
|
|
|
|
// and execution path eventually could reach here. Since the
|
|
|
|
// response was already handled, we just detach sd.
|
|
|
|
detach_stream_data();
|
2012-11-20 17:29:39 +01:00
|
|
|
sd_ = sd;
|
|
|
|
sd_->dconn = this;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
StreamData *Http2DownstreamConnection::detach_stream_data() {
|
|
|
|
if (sd_) {
|
2013-11-26 13:29:53 +01:00
|
|
|
auto sd = sd_;
|
|
|
|
sd_ = nullptr;
|
|
|
|
sd->dconn = nullptr;
|
2012-11-20 17:29:39 +01:00
|
|
|
return sd;
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
return nullptr;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2DownstreamConnection::on_timeout() {
|
|
|
|
if (!downstream_) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return submit_rst_stream(downstream_, NGHTTP2_NO_ERROR);
|
|
|
|
}
|
|
|
|
|
2015-07-12 15:16:20 +02:00
|
|
|
size_t Http2DownstreamConnection::get_group() const {
|
|
|
|
// HTTP/2 backend connections are managed by Http2Session object,
|
|
|
|
// and it stores group index.
|
|
|
|
return http2session_->get_group();
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
} // namespace shrpx
|