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;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream_->get_downstream_stream_id() != -1) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_
|
|
|
|
<< ", stream_id="
|
|
|
|
<< downstream_->get_downstream_stream_id()
|
|
|
|
<< ", error_code=" << error_code;
|
|
|
|
}
|
2014-11-22 15:13:29 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
submit_rst_stream(downstream_, error_code);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
|
|
|
http2session_->consume(downstream_->get_downstream_stream_id(),
|
|
|
|
downstream_->get_response_datalen());
|
|
|
|
|
|
|
|
downstream_->reset_response_datalen();
|
|
|
|
|
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);
|
2012-11-20 17:29:39 +01:00
|
|
|
// Downstream and DownstreamConnection may be deleted
|
|
|
|
// asynchronously.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_) {
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream_->release_downstream_connection();
|
2012-11-20 17:29:39 +01: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
|
|
|
}
|
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(),
|
|
|
|
downstream_->get_response_datalen());
|
|
|
|
|
|
|
|
downstream_->reset_response_datalen();
|
|
|
|
|
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="
|
2013-02-09 09:56:44 +01:00
|
|
|
<< downstream->get_downstream_stream_id();
|
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
|
|
|
}
|
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.
|
|
|
|
(!downstream->get_upgrade_request() ||
|
|
|
|
(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
|
|
|
|
2015-03-08 08:48:25 +01:00
|
|
|
auto &trailers = downstream->get_request_trailers();
|
|
|
|
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-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
|
|
|
|
2015-03-09 16:44:35 +01:00
|
|
|
auto no_host_rewrite = get_config()->no_host_rewrite ||
|
|
|
|
get_config()->http2_proxy ||
|
|
|
|
get_config()->client_proxy ||
|
|
|
|
downstream_->get_request_method() == "CONNECT";
|
|
|
|
|
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-03-13 14:52:09 +01:00
|
|
|
auto downstream_hostport =
|
|
|
|
get_config()->downstream_addrs[addr_idx].hostport.get();
|
2015-03-10 13:54:29 +01:00
|
|
|
|
2015-02-03 17:15:56 +01:00
|
|
|
const char *authority = nullptr, *host = nullptr;
|
2015-03-09 16:44:35 +01:00
|
|
|
if (!no_host_rewrite) {
|
2015-02-03 17:15:56 +01:00
|
|
|
if (!downstream_->get_request_http2_authority().empty()) {
|
2015-03-13 14:52:09 +01:00
|
|
|
authority = downstream_hostport;
|
2015-02-03 17:15:56 +01:00
|
|
|
}
|
|
|
|
if (downstream_->get_request_header(http2::HD_HOST)) {
|
2015-03-13 14:52:09 +01:00
|
|
|
host = downstream_hostport;
|
2015-02-03 17:15:56 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!downstream_->get_request_http2_authority().empty()) {
|
|
|
|
authority = downstream_->get_request_http2_authority().c_str();
|
|
|
|
}
|
|
|
|
auto h = downstream_->get_request_header(http2::HD_HOST);
|
|
|
|
if (h) {
|
|
|
|
host = h->value.c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!authority && !host) {
|
|
|
|
// upstream is HTTP/1.0. We use backend server's host
|
|
|
|
// nonetheless.
|
2015-03-13 14:52:09 +01:00
|
|
|
host = downstream_hostport;
|
2015-02-03 17:15:56 +01:00
|
|
|
}
|
|
|
|
|
2015-02-08 11:41:45 +01:00
|
|
|
if (authority) {
|
|
|
|
downstream_->set_request_downstream_host(authority);
|
|
|
|
} else {
|
|
|
|
downstream_->set_request_downstream_host(host);
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
size_t nheader = downstream_->get_request_headers().size();
|
2015-01-04 15:22:39 +01:00
|
|
|
|
|
|
|
Headers cookies;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_no_cookie_crumbling) {
|
2015-01-04 15:22:39 +01:00
|
|
|
cookies = downstream_->crumble_request_cookie();
|
2013-11-17 16:03:55 +01:00
|
|
|
}
|
2014-11-18 16:56:44 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
// 8 means:
|
2013-10-25 14:50:56 +02:00
|
|
|
// 1. :method
|
|
|
|
// 2. :scheme
|
|
|
|
// 3. :path
|
2015-03-13 14:40:41 +01:00
|
|
|
// 4. :authority or host (at least either of them exists)
|
|
|
|
// 5. via (optional)
|
|
|
|
// 6. x-forwarded-for (optional)
|
|
|
|
// 7. x-forwarded-proto (optional)
|
|
|
|
// 8. te (optional)
|
2013-11-28 13:36:04 +01:00
|
|
|
auto nva = std::vector<nghttp2_nv>();
|
2015-03-13 14:40:41 +01:00
|
|
|
nva.reserve(nheader + 8 + cookies.size());
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
std::string via_value;
|
2012-11-18 15:04:14 +01:00
|
|
|
std::string xff_value;
|
2015-02-03 17:15:56 +01:00
|
|
|
std::string scheme, uri_authority, path, query;
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":method", downstream_->get_request_method()));
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_request_method() == "CONNECT") {
|
2015-02-03 17:15:56 +01:00
|
|
|
if (authority) {
|
|
|
|
nva.push_back(http2::make_nv_lc(":authority", authority));
|
2013-10-25 14:50:56 +02:00
|
|
|
} else {
|
2014-11-27 15:39:04 +01:00
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":authority", downstream_->get_request_path()));
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
} else {
|
2015-05-21 19:19:44 +02:00
|
|
|
assert(!downstream_->get_request_http2_scheme().empty());
|
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":scheme", downstream_->get_request_http2_scheme()));
|
2015-02-03 17:15:56 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
if (authority) {
|
|
|
|
nva.push_back(http2::make_nv_lc(":authority", authority));
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2012-11-19 13:40:59 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
|
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
// only emit host header field if :authority is not emitted. They
|
|
|
|
// both must be the same value.
|
|
|
|
if (!authority && host) {
|
2015-02-03 17:15:56 +01:00
|
|
|
nva.push_back(http2::make_nv_lc("host", host));
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:22:39 +01:00
|
|
|
http2::copy_headers_to_nva(nva, downstream_->get_request_headers());
|
2013-08-27 17:09:46 +02:00
|
|
|
|
|
|
|
bool chunked_encoding = false;
|
|
|
|
auto transfer_encoding =
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream_->get_request_header(http2::HD_TRANSFER_ENCODING);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:22:39 +01:00
|
|
|
for (auto &nv : cookies) {
|
|
|
|
nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto xff = downstream_->get_request_header(http2::HD_X_FORWARDED_FOR);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->add_x_forwarded_for) {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
2014-04-03 04:22:11 +02:00
|
|
|
xff_value = (*xff).value;
|
2012-11-18 15:04:14 +01:00
|
|
|
xff_value += ", ";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
xff_value +=
|
|
|
|
downstream_->get_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));
|
2015-01-04 15:22:39 +01:00
|
|
|
} else if (xff && !get_config()->strip_incoming_x_forwarded_for) {
|
2014-04-03 04:22:11 +02:00
|
|
|
nva.push_back(http2::make_nv_ls("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 &&
|
|
|
|
downstream_->get_request_method() != "CONNECT") {
|
2014-03-21 10:57:57 +01:00
|
|
|
// We use same protocol with :scheme header field
|
2014-11-27 15:39:04 +01:00
|
|
|
if (scheme.empty()) {
|
|
|
|
if (client_handler_->get_ssl()) {
|
2014-03-21 10:57:57 +01:00
|
|
|
nva.push_back(http2::make_nv_ll("x-forwarded-proto", "https"));
|
|
|
|
} else {
|
|
|
|
nva.push_back(http2::make_nv_ll("x-forwarded-proto", "http"));
|
|
|
|
}
|
|
|
|
} else {
|
2014-04-03 12:14:05 +02:00
|
|
|
nva.push_back(http2::make_nv_ls("x-forwarded-proto", scheme));
|
2014-03-21 10:57:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:22:39 +01:00
|
|
|
auto via = downstream_->get_request_header(http2::HD_VIA);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->no_via) {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
2014-04-03 04:22:11 +02:00
|
|
|
nva.push_back(http2::make_nv_ls("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 += ", ";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
via_value += http::create_via_header_value(
|
|
|
|
downstream_->get_request_major(), downstream_->get_request_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
|
|
|
|
2015-03-08 08:29:26 +01:00
|
|
|
auto te = downstream_->get_request_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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-07-03 19:03:09 +02:00
|
|
|
auto content_length =
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream_->get_request_header(http2::HD_CONTENT_LENGTH);
|
|
|
|
// TODO check content-length: 0 case
|
2014-07-03 19:03:09 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_request_method() == "CONNECT" || chunked_encoding ||
|
|
|
|
content_length || downstream_->get_request_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;
|
2014-02-19 14:53:33 +01:00
|
|
|
rv = http2session_->submit_request(this, downstream_->get_priority(),
|
2013-11-28 13:36:04 +01:00
|
|
|
nva.data(), nva.size(), &data_prd);
|
2012-11-18 13:23:13 +01:00
|
|
|
} else {
|
2014-02-19 14:53:33 +01:00
|
|
|
rv = http2session_->submit_request(this, downstream_->get_priority(),
|
2013-11-28 13:36:04 +01:00
|
|
|
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
|
|
|
assert(downstream_->get_response_datalen() >= consumed);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
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
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
downstream_->dec_response_datalen(consumed);
|
|
|
|
|
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_priority_change(int32_t pri) {
|
2014-01-18 08:12:03 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_priority() == pri) {
|
2014-01-18 08:12:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
downstream_->set_priority(pri);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session_->get_state() != Http2Session::CONNECTED) {
|
2014-01-18 08:12:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rv = http2session_->submit_priority(this, pri);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-01-18 08:12:03 +01:00
|
|
|
DLOG(FATAL, this) << "nghttp2_submit_priority() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session_->signal_write();
|
2014-01-18 08:12:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
} // namespace shrpx
|