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
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <openssl/err.h>
|
|
|
|
|
|
|
|
#include <event2/bufferevent_ssl.h>
|
|
|
|
|
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"
|
2014-07-05 11:22:40 +02:00
|
|
|
#include "shrpx_worker_config.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)
|
|
|
|
: DownstreamConnection(dconn_pool), http2session_(http2session),
|
|
|
|
request_body_buf_(nullptr), sd_(nullptr) {}
|
|
|
|
|
|
|
|
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 (request_body_buf_) {
|
2012-11-18 13:23:13 +01:00
|
|
|
evbuffer_free(request_body_buf_);
|
|
|
|
}
|
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-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream_
|
|
|
|
<< ", stream_id="
|
2014-11-22 15:13:29 +01:00
|
|
|
<< downstream_->get_downstream_stream_id()
|
|
|
|
<< ", error_code=" << error_code;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (submit_rst_stream(downstream_, error_code) == 0) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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();
|
|
|
|
|
|
|
|
http2session_->notify();
|
|
|
|
}
|
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::init_request_body_buf() {
|
2012-11-18 13:23:13 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (request_body_buf_) {
|
2012-11-18 13:23:13 +01:00
|
|
|
rv = evbuffer_drain(request_body_buf_,
|
|
|
|
evbuffer_get_length(request_body_buf_));
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-11-18 13:23:13 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
request_body_buf_ = evbuffer_new();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (request_body_buf_ == nullptr) {
|
2012-11-18 13:23:13 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (init_request_body_buf() == -1) {
|
2012-11-18 13:23:13 +01:00
|
|
|
return -1;
|
|
|
|
}
|
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) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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;
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream_->init_downstream_timer();
|
|
|
|
|
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) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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();
|
|
|
|
|
|
|
|
http2session_->notify();
|
|
|
|
}
|
|
|
|
|
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:
|
|
|
|
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) {
|
|
|
|
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
|
|
|
}
|
2013-11-04 09:53:57 +01:00
|
|
|
auto body = dconn->get_request_body_buf();
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-11-22 13:11:18 +01:00
|
|
|
auto nread = evbuffer_remove(body, buf, length);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == -1) {
|
2014-11-22 13:11:18 +01:00
|
|
|
DCLOG(FATAL, dconn) << "evbuffer_remove() failed";
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
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-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_get_length(body) == 0 &&
|
|
|
|
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;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_get_length(body) > 0) {
|
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;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session_->get_state() != Http2Session::CONNECTED) {
|
2013-11-04 09:53:57 +01:00
|
|
|
// The HTTP2 session to the backend has not been established.
|
|
|
|
// This function will be called again just after it is
|
|
|
|
// established.
|
2012-11-18 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream_) {
|
2012-11-18 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
size_t nheader = downstream_->get_request_headers().size();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_no_cookie_crumbling) {
|
2013-11-17 16:03:55 +01:00
|
|
|
downstream_->crumble_request_cookie();
|
|
|
|
}
|
2014-11-18 16:56:44 +01:00
|
|
|
|
|
|
|
assert(downstream_->get_request_headers_normalized());
|
2014-07-12 11:55:08 +02:00
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
auto end_headers = std::end(downstream_->get_request_headers());
|
2013-12-06 16:32:14 +01:00
|
|
|
|
2014-03-21 10:57:57 +01:00
|
|
|
// 7 means:
|
2013-10-25 14:50:56 +02:00
|
|
|
// 1. :method
|
|
|
|
// 2. :scheme
|
|
|
|
// 3. :path
|
|
|
|
// 4. :authority (optional)
|
|
|
|
// 5. via (optional)
|
|
|
|
// 6. x-forwarded-for (optional)
|
2014-03-21 10:57:57 +01:00
|
|
|
// 7. x-forwarded-proto (optional)
|
2013-11-28 13:36:04 +01:00
|
|
|
auto nva = std::vector<nghttp2_nv>();
|
2014-03-21 10:57:57 +01:00
|
|
|
nva.reserve(nheader + 7);
|
2012-11-18 13:23:13 +01:00
|
|
|
std::string via_value;
|
2012-11-18 15:04:14 +01:00
|
|
|
std::string xff_value;
|
2013-10-25 14:50:56 +02:00
|
|
|
std::string scheme, authority, path, query;
|
2013-12-16 13:17:25 +01:00
|
|
|
// To reconstruct HTTP/1 status line and headers, proxy should
|
|
|
|
// preserve host header field. See draft-09 section 8.1.3.1.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream_->get_request_method() == "CONNECT") {
|
2013-10-25 14:50:56 +02:00
|
|
|
// The upstream may be HTTP/2 or HTTP/1
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream_->get_request_http2_authority().empty()) {
|
|
|
|
nva.push_back(http2::make_nv_ls(
|
|
|
|
":authority", downstream_->get_request_http2_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
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (!downstream_->get_request_http2_scheme().empty()) {
|
2013-10-25 14:50:56 +02:00
|
|
|
// Here the upstream is HTTP/2
|
2014-11-27 15:39:04 +01:00
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":scheme", downstream_->get_request_http2_scheme()));
|
|
|
|
nva.push_back(http2::make_nv_ls(":path", downstream_->get_request_path()));
|
|
|
|
if (!downstream_->get_request_http2_authority().empty()) {
|
|
|
|
nva.push_back(http2::make_nv_ls(
|
|
|
|
":authority", downstream_->get_request_http2_authority()));
|
|
|
|
} else if (downstream_->get_norm_request_header("host") == end_headers) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-12-16 13:14:31 +01:00
|
|
|
DCLOG(INFO, this) << "host header field missing";
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2013-12-16 13:14:31 +01:00
|
|
|
return -1;
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
} else {
|
2013-10-25 14:50:56 +02:00
|
|
|
// The upstream is HTTP/1
|
2012-11-19 13:40:59 +01:00
|
|
|
http_parser_url u;
|
|
|
|
const char *url = downstream_->get_request_path().c_str();
|
|
|
|
memset(&u, 0, sizeof(u));
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = http_parser_parse_url(url, downstream_->get_request_path().size(), 0,
|
|
|
|
&u);
|
|
|
|
if (rv == 0) {
|
2013-08-27 19:47:22 +02:00
|
|
|
http2::copy_url_component(scheme, &u, UF_SCHEMA, url);
|
2013-10-25 14:50:56 +02:00
|
|
|
http2::copy_url_component(authority, &u, UF_HOST, url);
|
2013-08-27 19:47:22 +02:00
|
|
|
http2::copy_url_component(path, &u, UF_PATH, url);
|
|
|
|
http2::copy_url_component(query, &u, UF_QUERY, url);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (path.empty()) {
|
|
|
|
if (!authority.empty() &&
|
|
|
|
downstream_->get_request_method() == "OPTIONS") {
|
2014-07-25 16:40:25 +02:00
|
|
|
path = "*";
|
|
|
|
} else {
|
|
|
|
path = "/";
|
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!query.empty()) {
|
2012-11-19 13:40:59 +01:00
|
|
|
path += "?";
|
|
|
|
path += query;
|
|
|
|
}
|
|
|
|
}
|
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(":scheme", "https"));
|
|
|
|
} else {
|
|
|
|
nva.push_back(http2::make_nv_ll(":scheme", "http"));
|
|
|
|
}
|
2013-02-07 13:53:20 +01:00
|
|
|
} else {
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls(":scheme", scheme));
|
2013-02-07 13:53:20 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (path.empty()) {
|
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":path", downstream_->get_request_path()));
|
2013-02-07 13:53:20 +01:00
|
|
|
} else {
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls(":path", path));
|
2013-02-07 13:53:20 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!authority.empty()) {
|
2013-10-25 14:50:56 +02:00
|
|
|
// TODO properly check IPv6 numeric address
|
2014-11-27 15:39:04 +01:00
|
|
|
if (authority.find(":") != std::string::npos) {
|
2013-10-25 14:50:56 +02:00
|
|
|
authority = "[" + authority;
|
|
|
|
authority += "]";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (u.field_set & (1 << UF_PORT)) {
|
2013-10-25 14:50:56 +02:00
|
|
|
authority += ":";
|
|
|
|
authority += util::utos(u.port);
|
|
|
|
}
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls(":authority", authority));
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (downstream_->get_norm_request_header("host") == end_headers) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-12-16 13:14:31 +01:00
|
|
|
DCLOG(INFO, this) << "host header field missing";
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2013-12-16 13:14:31 +01:00
|
|
|
return -1;
|
2013-10-25 14:50:56 +02:00
|
|
|
}
|
2012-11-19 13:40:59 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nva.push_back(
|
|
|
|
http2::make_nv_ls(":method", downstream_->get_request_method()));
|
2013-02-07 13:53:20 +01:00
|
|
|
|
2013-11-28 13:36:04 +01:00
|
|
|
http2::copy_norm_headers_to_nva(nva, downstream_->get_request_headers());
|
2013-08-27 17:09:46 +02:00
|
|
|
|
|
|
|
bool chunked_encoding = false;
|
|
|
|
auto transfer_encoding =
|
2014-11-27 15:39:04 +01:00
|
|
|
downstream_->get_norm_request_header("transfer-encoding");
|
|
|
|
if (transfer_encoding != end_headers &&
|
|
|
|
util::strieq((*transfer_encoding).value.c_str(), "chunked")) {
|
2013-08-27 17:09:46 +02:00
|
|
|
chunked_encoding = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto xff = downstream_->get_norm_request_header("x-forwarded-for");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->add_x_forwarded_for) {
|
|
|
|
if (xff != end_headers && !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));
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (xff != end_headers &&
|
|
|
|
!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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
auto via = downstream_->get_norm_request_header("via");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->no_via) {
|
|
|
|
if (via != end_headers) {
|
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 {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (via != end_headers) {
|
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
|
|
|
|
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) {
|
2013-11-28 13:36:04 +01:00
|
|
|
ss << TTY_HTTP_HD;
|
2014-11-27 15:39:04 +01:00
|
|
|
ss.write(reinterpret_cast<const char *>(nv.name), nv.namelen);
|
2013-11-28 13:36:04 +01:00
|
|
|
ss << TTY_RST << ": ";
|
2014-11-27 15:39:04 +01:00
|
|
|
ss.write(reinterpret_cast<const char *>(nv.value), nv.valuelen);
|
2013-11-28 13:36:04 +01:00
|
|
|
ss << "\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 =
|
2014-11-27 15:39:04 +01:00
|
|
|
downstream_->get_norm_request_header("content-length") != end_headers;
|
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
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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) {
|
2012-11-18 13:23:13 +01:00
|
|
|
int rv = evbuffer_add(request_body_buf_, data, datalen);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(FATAL, this) << "evbuffer_add() failed";
|
2012-11-18 13:23:13 +01:00
|
|
|
return -1;
|
|
|
|
}
|
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();
|
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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();
|
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_->notify();
|
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);
|
|
|
|
|
|
|
|
http2session_->notify();
|
|
|
|
}
|
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
|
|
|
evbuffer *Http2DownstreamConnection::get_request_body_buf() const {
|
2012-11-20 17:29:39 +01:00
|
|
|
return request_body_buf_;
|
2012-11-18 13:23:13 +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;
|
|
|
|
} else {
|
2013-11-26 13:29:53 +01:00
|
|
|
return nullptr;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bool Http2DownstreamConnection::get_output_buffer_full() {
|
|
|
|
if (request_body_buf_) {
|
|
|
|
return evbuffer_get_length(request_body_buf_) >=
|
|
|
|
Http2Session::OUTBUF_MAX_THRES;
|
2012-11-20 17:29:39 +01:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
http2session_->notify();
|
|
|
|
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
|