2012-06-04 16:48:31 +02:00
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
* nghttp2 - HTTP/2 C Library
|
2012-06-04 16:48:31 +02: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.
|
|
|
|
*/
|
|
|
|
#include "shrpx_https_upstream.h"
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <set>
|
2012-11-23 13:30:17 +01:00
|
|
|
#include <sstream>
|
2012-06-04 16:48:31 +02:00
|
|
|
|
|
|
|
#include "shrpx_client_handler.h"
|
|
|
|
#include "shrpx_downstream.h"
|
2012-06-09 16:14:00 +02:00
|
|
|
#include "shrpx_downstream_connection.h"
|
2013-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_downstream_connection.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
#include "shrpx_http.h"
|
|
|
|
#include "shrpx_config.h"
|
|
|
|
#include "shrpx_error.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-06-04 16:48:31 +02:00
|
|
|
#include "util.h"
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
using namespace nghttp2;
|
2012-06-04 16:48:31 +02:00
|
|
|
|
|
|
|
namespace shrpx {
|
|
|
|
|
2012-06-04 20:11:43 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
const size_t OUTBUF_MAX_THRES = 16 * 1024;
|
2012-06-04 20:11:43 +02:00
|
|
|
} // namespace
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
2014-11-27 15:39:04 +01:00
|
|
|
: handler_(handler), current_header_length_(0),
|
|
|
|
ioctrl_(handler->get_bev()) {
|
2013-09-23 17:18:00 +02:00
|
|
|
http_parser_init(&htp_, HTTP_REQUEST);
|
|
|
|
htp_.data = this;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
HttpsUpstream::~HttpsUpstream() {}
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::reset_current_header_length() {
|
2012-06-05 15:13:22 +02:00
|
|
|
current_header_length_ = 0;
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_msg_begin(http_parser *htp) {
|
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(INFO, upstream) << "HTTP request started";
|
2012-07-11 09:20:16 +02:00
|
|
|
}
|
2012-06-05 15:13:22 +02:00
|
|
|
upstream->reset_current_header_length();
|
2014-03-25 18:04:24 +01:00
|
|
|
// TODO specify 0 as priority for now
|
2014-08-18 15:59:31 +02:00
|
|
|
upstream->attach_downstream(util::make_unique<Downstream>(upstream, 0, 0));
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_uricb(http_parser *htp, const char *data, size_t len) {
|
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->append_request_path(data, len);
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_header_key_prev()) {
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->append_last_request_header_key(data, len);
|
|
|
|
} else {
|
|
|
|
downstream->add_request_header(std::string(data, len), "");
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-01-27 17:17:54 +01:00
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
|
|
|
<< downstream->get_request_headers_sum();
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_header_key_prev()) {
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->set_last_request_header_value(std::string(data, len));
|
|
|
|
} else {
|
|
|
|
downstream->append_last_request_header_value(data, len);
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-01-27 17:17:54 +01:00
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
|
|
|
<< downstream->get_request_headers_sum();
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_hdrs_completecb(http_parser *htp) {
|
2012-11-21 14:10:35 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(INFO, upstream) << "HTTP request headers completed";
|
2012-07-11 09:20:16 +02:00
|
|
|
}
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
downstream->set_request_method(
|
|
|
|
http_method_str((enum http_method)htp->method));
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->set_request_major(htp->http_major);
|
|
|
|
downstream->set_request_minor(htp->http_minor);
|
2014-11-19 17:53:30 +01:00
|
|
|
downstream->set_request_start_time(std::chrono::high_resolution_clock::now());
|
2012-06-06 17:43:18 +02:00
|
|
|
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->set_request_connection_close(!http_should_keep_alive(htp));
|
2012-07-11 09:20:16 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-11-23 13:30:17 +01:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << downstream->get_request_method() << " "
|
|
|
|
<< downstream->get_request_path() << " "
|
|
|
|
<< "HTTP/" << downstream->get_request_major() << "."
|
|
|
|
<< downstream->get_request_minor() << "\n";
|
2014-11-27 15:39:04 +01:00
|
|
|
const auto &headers = downstream->get_request_headers();
|
|
|
|
for (size_t i = 0; i < headers.size(); ++i) {
|
2014-04-03 04:22:11 +02:00
|
|
|
ss << TTY_HTTP_HD << headers[i].name << TTY_RST << ": "
|
|
|
|
<< headers[i].value << "\n";
|
2012-11-23 13:30:17 +01:00
|
|
|
}
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(INFO, upstream) << "HTTP request headers\n" << ss.str();
|
2012-11-23 13:30:17 +01:00
|
|
|
}
|
|
|
|
|
2014-07-05 11:22:40 +02:00
|
|
|
downstream->normalize_request_headers();
|
|
|
|
|
|
|
|
downstream->inspect_http1_request();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->client_proxy &&
|
|
|
|
downstream->get_request_method() != "CONNECT") {
|
2012-11-21 14:10:35 +01:00
|
|
|
// Make sure that request path is an absolute URI.
|
|
|
|
http_parser_url u;
|
2013-09-24 16:39:36 +02:00
|
|
|
auto url = downstream->get_request_path().c_str();
|
2012-11-21 14:10:35 +01:00
|
|
|
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 || !(u.field_set & (1 << UF_SCHEMA))) {
|
2012-11-21 14:10:35 +01:00
|
|
|
// Expect to respond with 400 bad request
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = downstream->attach_downstream_connection(
|
|
|
|
upstream->get_client_handler()->get_downstream_connection());
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-06-09 16:14:00 +02:00
|
|
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
2014-08-18 17:16:51 +02:00
|
|
|
|
2012-07-16 16:29:48 +02:00
|
|
|
return -1;
|
2012-06-05 19:23:07 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
rv = downstream->push_request_headers();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_bodycb(http_parser *htp, const char *data, size_t len) {
|
2012-07-16 17:12:31 +02:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = downstream->push_upload_data_chunk(
|
|
|
|
reinterpret_cast<const uint8_t *>(data), len);
|
|
|
|
if (rv != 0) {
|
2012-07-16 17:12:31 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int htp_msg_completecb(http_parser *htp) {
|
2012-07-16 17:12:31 +02:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(htp->data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(INFO, upstream) << "HTTP request completed";
|
|
|
|
}
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2012-11-18 18:11:46 +01:00
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
2012-07-16 17:12:31 +02:00
|
|
|
rv = downstream->end_upload_data();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-07-16 17:12:31 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
// Stop further processing to complete this request
|
2012-07-11 11:32:04 +02:00
|
|
|
http_parser_pause(htp, 1);
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2012-07-11 11:32:04 +02:00
|
|
|
http_parser_settings htp_hooks = {
|
2014-11-27 15:39:04 +01:00
|
|
|
htp_msg_begin, // http_cb on_message_begin;
|
|
|
|
htp_uricb, // http_data_cb on_url;
|
|
|
|
nullptr, // http_data_cb on_status;
|
|
|
|
htp_hdr_keycb, // http_data_cb on_header_field;
|
|
|
|
htp_hdr_valcb, // http_data_cb on_header_value;
|
|
|
|
htp_hdrs_completecb, // http_cb on_headers_complete;
|
|
|
|
htp_bodycb, // http_data_cb on_body;
|
|
|
|
htp_msg_completecb // http_cb on_message_complete;
|
2012-06-04 16:48:31 +02:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// on_read() does not consume all available data in input buffer if
|
|
|
|
// one http request is fully received.
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_read() {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto bev = handler_->get_bev();
|
|
|
|
auto input = bufferevent_get_input(bev);
|
2013-07-31 14:48:37 +02:00
|
|
|
auto downstream = get_downstream();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2013-07-31 14:48:37 +02:00
|
|
|
// downstream can be nullptr here, because it is initialized in the
|
|
|
|
// callback chain called by http_parser_execute()
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream && downstream->get_upgraded()) {
|
|
|
|
for (;;) {
|
2014-06-01 14:01:01 +02:00
|
|
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (inputlen == 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mem = evbuffer_pullup(input, inputlen);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
auto rv = downstream->push_upload_data_chunk(
|
|
|
|
reinterpret_cast<const uint8_t *>(mem), inputlen);
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_drain(input, inputlen) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_output_buffer_full()) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(INFO, this) << "Downstream output buffer is full";
|
|
|
|
}
|
|
|
|
pause_read(SHRPX_NO_BUFFER);
|
|
|
|
|
|
|
|
return 0;
|
2013-07-31 14:48:37 +02:00
|
|
|
}
|
|
|
|
}
|
2012-07-11 11:32:04 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (;;) {
|
2014-06-01 14:01:01 +02:00
|
|
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (inputlen == 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mem = evbuffer_pullup(input, inputlen);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
auto nread = http_parser_execute(
|
|
|
|
&htp_, &htp_hooks, reinterpret_cast<const char *>(mem), inputlen);
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_drain(input, nread) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(FATAL, this) << "evbuffer_drain() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Well, actually header length + some body bytes
|
|
|
|
current_header_length_ += nread;
|
|
|
|
|
|
|
|
// Get downstream again because it may be initialized in http parser
|
|
|
|
// execution
|
|
|
|
downstream = get_downstream();
|
|
|
|
|
|
|
|
auto handler = get_client_handler();
|
|
|
|
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (htperr == HPE_PAUSED) {
|
2014-06-01 14:01:01 +02:00
|
|
|
|
|
|
|
assert(downstream);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
2014-06-01 14:01:01 +02:00
|
|
|
handler->set_should_close_after_write(true);
|
|
|
|
// Following paues_read is needed to avoid reading next data.
|
|
|
|
pause_read(SHRPX_MSG_BLOCK);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (error_reply(503) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// Downstream gets deleted after response body is read.
|
|
|
|
return 0;
|
2012-07-16 16:29:48 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2012-06-09 16:14:00 +02:00
|
|
|
assert(downstream->get_request_state() == Downstream::MSG_COMPLETE);
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_downstream_connection() == nullptr) {
|
2012-11-18 13:23:13 +01:00
|
|
|
// Error response has already be sent
|
2012-06-09 16:14:00 +02:00
|
|
|
assert(downstream->get_response_state() == Downstream::MSG_COMPLETE);
|
2012-09-20 15:28:40 +02:00
|
|
|
delete_downstream();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->get_http2_upgrade_allowed() &&
|
|
|
|
downstream->get_http2_upgrade_request()) {
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->perform_http2_upgrade(this) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return -1;
|
2012-06-09 17:49:33 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-06-09 17:49:33 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
|
|
|
pause_read(SHRPX_MSG_BLOCK);
|
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (htperr != HPE_OK) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(INFO, this) << "HTTP parse failure: "
|
|
|
|
<< "(" << http_errno_name(htperr) << ") "
|
|
|
|
<< http_errno_description(htperr);
|
|
|
|
}
|
|
|
|
|
|
|
|
handler->set_should_close_after_write(true);
|
|
|
|
pause_read(SHRPX_MSG_BLOCK);
|
|
|
|
|
2014-08-19 16:41:53 +02:00
|
|
|
unsigned int status_code;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream &&
|
|
|
|
downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
2014-08-19 16:41:53 +02:00
|
|
|
status_code = 503;
|
|
|
|
} else {
|
|
|
|
status_code = 400;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (error_reply(status_code) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
|
|
|
// downstream can be NULL here.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream && downstream->get_output_buffer_full()) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(INFO, this) << "Downstream output buffer is full";
|
|
|
|
}
|
|
|
|
|
|
|
|
pause_read(SHRPX_NO_BUFFER);
|
|
|
|
|
|
|
|
return 0;
|
2012-07-16 16:29:48 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_write() {
|
2012-11-21 15:47:48 +01:00
|
|
|
int rv = 0;
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream) {
|
2014-07-25 14:26:03 +02:00
|
|
|
// We need to postpone detachment until all data are sent so that
|
|
|
|
// we can notify nghttp2 library all data consumed.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
if (downstream->get_response_connection_close()) {
|
2014-07-25 14:26:03 +02:00
|
|
|
// Connection close
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
2014-07-25 14:26:03 +02:00
|
|
|
} else {
|
|
|
|
// Keep-alive
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->detach_downstream_connection();
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
2014-09-17 15:53:29 +02:00
|
|
|
// We need this if response ends before request.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
2014-09-17 15:53:29 +02:00
|
|
|
delete_downstream();
|
|
|
|
return resume_read(SHRPX_MSG_BLOCK, nullptr, 0);
|
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
rv = downstream->resume_read(SHRPX_NO_BUFFER,
|
|
|
|
downstream->get_response_datalen());
|
2012-06-04 20:11:43 +02:00
|
|
|
}
|
2012-11-21 15:47:48 +01:00
|
|
|
return rv;
|
2012-06-04 20:11:43 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_event() { return 0; }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
ClientHandler *HttpsUpstream::get_client_handler() const { return handler_; }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::pause_read(IOCtrlReason reason) {
|
2012-06-04 20:11:43 +02:00
|
|
|
ioctrl_.pause_read(reason);
|
|
|
|
}
|
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
int HttpsUpstream::resume_read(IOCtrlReason reason, Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
size_t consumed) {
|
|
|
|
if (ioctrl_.resume_read(reason)) {
|
2012-06-04 20:11:43 +02:00
|
|
|
// Process remaining data in input buffer here because these bytes
|
|
|
|
// are not notified by readcb until new data arrive.
|
2013-09-23 17:18:00 +02:00
|
|
|
http_parser_pause(&htp_, 0);
|
2012-11-20 17:29:39 +01:00
|
|
|
return on_read();
|
2012-06-04 20:11:43 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void https_downstream_readcb(bufferevent *bev, void *ptr) {
|
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(downstream->get_upstream());
|
2012-11-18 13:23:13 +01:00
|
|
|
int rv;
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2012-11-20 17:29:39 +01:00
|
|
|
rv = downstream->on_read();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
2012-11-20 17:29:39 +01:00
|
|
|
delete upstream->get_client_handler();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
2012-06-09 16:14:00 +02:00
|
|
|
// We already sent HTTP response headers to upstream
|
|
|
|
// client. Just close the upstream connection.
|
2012-06-04 16:48:31 +02:00
|
|
|
delete upstream->get_client_handler();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We did not sent any HTTP response, so sent error
|
|
|
|
// response. Cannot reuse downstream connection in this case.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(502) != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->delete_downstream();
|
|
|
|
|
|
|
|
// Process next HTTP request
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->resume_read(SHRPX_MSG_BLOCK, nullptr, 0) == -1) {
|
2012-07-16 16:29:48 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
|
|
|
if (handler->get_outbuf_length() >= OUTBUF_MAX_THRES) {
|
2014-06-01 16:44:32 +02:00
|
|
|
downstream->pause_read(SHRPX_NO_BUFFER);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
// If pending data exist, we defer detachment to correctly notify
|
|
|
|
// the all consumed data to nghttp2 library.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->get_outbuf_length() == 0) {
|
|
|
|
if (downstream->get_response_connection_close()) {
|
2014-07-25 14:26:03 +02:00
|
|
|
// Connection close
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
dconn = nullptr;
|
|
|
|
} else {
|
|
|
|
// Keep-alive
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->detach_downstream_connection();
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
if (handler->get_should_close_after_write() &&
|
|
|
|
handler->get_outbuf_length() == 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// If all upstream response body has already written out to
|
|
|
|
// the peer, we cannot use writecb for ClientHandler. In
|
|
|
|
// this case, we just delete handler here.
|
|
|
|
delete handler;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
upstream->delete_downstream();
|
|
|
|
|
|
|
|
// Process next HTTP request
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->resume_read(SHRPX_MSG_BLOCK, nullptr, 0) == -1) {
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// This path is effectively only taken for HTTP2 downstream
|
|
|
|
// because only HTTP2 downstream sets response_state to
|
|
|
|
// MSG_COMPLETE and this function. For HTTP downstream, EOF
|
|
|
|
// from tunnel connection is handled on
|
|
|
|
// https_downstream_eventcb.
|
|
|
|
//
|
|
|
|
// Tunneled connection always indicates connection close.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->get_outbuf_length() == 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// For tunneled connection, if there is no pending data,
|
|
|
|
// delete handler because on_write will not be called.
|
|
|
|
delete handler;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 16:44:32 +02:00
|
|
|
DLOG(INFO, downstream) << "Tunneled connection has pending data";
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-09-17 15:53:29 +02:00
|
|
|
|
|
|
|
// Delete handler here if we have no pending write.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->get_should_close_after_write() &&
|
|
|
|
handler->get_outbuf_length() == 0) {
|
2014-09-17 15:53:29 +02:00
|
|
|
delete handler;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void https_downstream_writecb(bufferevent *bev, void *ptr) {
|
|
|
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
2012-11-21 19:13:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(downstream->get_upstream());
|
2013-02-09 09:03:03 +01:00
|
|
|
// May return -1
|
2014-08-21 14:22:16 +02:00
|
|
|
upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void https_downstream_eventcb(bufferevent *bev, short events, void *ptr) {
|
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<HttpsUpstream *>(downstream->get_upstream());
|
|
|
|
if (events & BEV_EVENT_CONNECTED) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Connection established";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & BEV_EVENT_EOF) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "EOF";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
2012-06-07 17:39:55 +02:00
|
|
|
// Server may indicate the end of the request by EOF
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "The end of the response body was indicated by "
|
|
|
|
<< "EOF";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2012-06-07 17:39:55 +02:00
|
|
|
upstream->on_downstream_body_complete(downstream);
|
2012-07-11 09:20:16 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
2013-09-24 16:39:36 +02:00
|
|
|
auto handler = upstream->get_client_handler();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler->get_should_close_after_write() &&
|
|
|
|
handler->get_outbuf_length() == 0) {
|
2012-07-11 09:20:16 +02:00
|
|
|
// If all upstream response body has already written out to
|
|
|
|
// the peer, we cannot use writecb for ClientHandler. In this
|
|
|
|
// case, we just delete handler here.
|
|
|
|
delete handler;
|
|
|
|
return;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
2012-06-07 17:39:55 +02:00
|
|
|
// error
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Treated as error";
|
2012-06-07 17:39:55 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(502) != 0) {
|
2012-07-16 16:29:48 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
2012-09-20 15:28:40 +02:00
|
|
|
upstream->delete_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->resume_read(SHRPX_MSG_BLOCK, nullptr, 0) == -1) {
|
2013-02-09 09:03:03 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
if (events & BEV_EVENT_ERROR) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Network error";
|
|
|
|
} else {
|
|
|
|
DCLOG(INFO, dconn) << "Timeout";
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::INITIAL) {
|
2013-10-02 16:29:44 +02:00
|
|
|
unsigned int status;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & BEV_EVENT_TIMEOUT) {
|
2012-06-04 16:48:31 +02:00
|
|
|
status = 504;
|
|
|
|
} else {
|
|
|
|
status = 502;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(status) != 0) {
|
2012-07-16 16:29:48 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
2012-09-20 15:28:40 +02:00
|
|
|
upstream->delete_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->resume_read(SHRPX_MSG_BLOCK, nullptr, 0) == -1) {
|
2013-02-09 09:03:03 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::error_reply(unsigned int status_code) {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto html = http::create_error_html(status_code);
|
2014-07-05 11:22:40 +02:00
|
|
|
auto downstream = get_downstream();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream) {
|
2014-07-05 11:22:40 +02:00
|
|
|
downstream->set_response_http_status(status_code);
|
|
|
|
}
|
|
|
|
|
2013-01-16 14:51:33 +01:00
|
|
|
std::string header;
|
|
|
|
header.reserve(512);
|
|
|
|
header += "HTTP/1.1 ";
|
2013-08-27 19:47:22 +02:00
|
|
|
header += http2::get_status_string(status_code);
|
2013-01-16 14:51:33 +01:00
|
|
|
header += "\r\nServer: ";
|
|
|
|
header += get_config()->server_name;
|
|
|
|
header += "\r\nContent-Length: ";
|
|
|
|
header += util::utos(html.size());
|
|
|
|
header += "\r\nContent-Type: text/html; charset=UTF-8\r\n";
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_client_handler()->get_should_close_after_write()) {
|
2013-01-16 14:51:33 +01:00
|
|
|
header += "Connection: close\r\n";
|
2012-06-05 15:13:22 +02:00
|
|
|
}
|
2013-01-16 14:51:33 +01:00
|
|
|
header += "\r\n";
|
2013-09-24 16:39:36 +02:00
|
|
|
auto output = bufferevent_get_output(handler_->get_bev());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_add(output, header.c_str(), header.size()) != 0 ||
|
|
|
|
evbuffer_add(output, html.c_str(), html.size()) != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:29:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream) {
|
2014-11-18 16:56:44 +01:00
|
|
|
downstream->add_response_sent_bodylen(html.size());
|
2012-06-09 16:14:00 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2014-11-18 16:56:44 +01:00
|
|
|
} else {
|
|
|
|
handler_->write_accesslog(1, 1, status_code, html.size());
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2012-07-16 16:29:48 +02:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_data_cb HttpsUpstream::get_downstream_readcb() {
|
2012-06-04 16:48:31 +02:00
|
|
|
return https_downstream_readcb;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_data_cb HttpsUpstream::get_downstream_writecb() {
|
2012-06-04 16:48:31 +02:00
|
|
|
return https_downstream_writecb;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_event_cb HttpsUpstream::get_downstream_eventcb() {
|
2012-06-04 16:48:31 +02:00
|
|
|
return https_downstream_eventcb;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::attach_downstream(std::unique_ptr<Downstream> downstream) {
|
2012-09-20 15:28:40 +02:00
|
|
|
assert(!downstream_);
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_ = std::move(downstream);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::delete_downstream() {
|
|
|
|
if (downstream_ && downstream_->accesslog_ready()) {
|
2014-11-18 16:56:44 +01:00
|
|
|
handler_->write_accesslog(downstream_.get());
|
|
|
|
}
|
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_.reset();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
Downstream *HttpsUpstream::get_downstream() const { return downstream_.get(); }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
std::unique_ptr<Downstream> HttpsUpstream::pop_downstream() {
|
2014-08-18 15:59:31 +02:00
|
|
|
return std::unique_ptr<Downstream>(downstream_.release());
|
2013-08-03 11:51:01 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
if (downstream->get_non_final_response()) {
|
2014-07-23 16:32:57 +02:00
|
|
|
DLOG(INFO, downstream) << "HTTP non-final response header";
|
|
|
|
} else {
|
|
|
|
DLOG(INFO, downstream) << "HTTP response header completed";
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-05-14 15:39:28 +02:00
|
|
|
|
|
|
|
std::string hdrs = "HTTP/";
|
|
|
|
hdrs += util::utos(downstream->get_request_major());
|
|
|
|
hdrs += ".";
|
|
|
|
hdrs += util::utos(downstream->get_request_minor());
|
|
|
|
hdrs += " ";
|
2013-08-27 19:47:22 +02:00
|
|
|
hdrs += http2::get_status_string(downstream->get_response_http_status());
|
2012-06-04 16:48:31 +02:00
|
|
|
hdrs += "\r\n";
|
2013-08-27 17:09:46 +02:00
|
|
|
downstream->normalize_response_headers();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
|
|
|
!get_config()->no_location_rewrite) {
|
|
|
|
downstream->rewrite_norm_location_response_header(
|
|
|
|
get_client_handler()->get_upstream_scheme(), get_config()->port);
|
2013-12-28 09:02:43 +01:00
|
|
|
}
|
2013-08-27 17:09:46 +02:00
|
|
|
auto end_headers = std::end(downstream->get_response_headers());
|
2014-11-27 15:39:04 +01:00
|
|
|
http2::build_http1_headers_from_norm_headers(
|
|
|
|
hdrs, downstream->get_response_headers());
|
2012-07-11 09:20:16 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_non_final_response()) {
|
2014-07-23 16:32:57 +02:00
|
|
|
hdrs += "\r\n";
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-25 17:40:06 +02:00
|
|
|
log_response_headers(hdrs);
|
|
|
|
}
|
|
|
|
|
2014-07-23 16:32:57 +02:00
|
|
|
auto output = bufferevent_get_output(handler_->get_bev());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_add(output, hdrs.c_str(), hdrs.size()) != 0) {
|
2014-07-23 16:32:57 +02:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream->clear_response_headers();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-13 14:33:35 +02:00
|
|
|
// We check downstream->get_response_connection_close() in case when
|
|
|
|
// the Content-Length is not available.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream->get_request_connection_close() &&
|
|
|
|
!downstream->get_response_connection_close()) {
|
|
|
|
if (downstream->get_request_major() <= 0 ||
|
|
|
|
downstream->get_request_minor() <= 0) {
|
2012-09-13 14:33:35 +02:00
|
|
|
// We add this header for HTTP/1.0 or HTTP/0.9 clients
|
2012-07-11 09:20:16 +02:00
|
|
|
hdrs += "Connection: Keep-Alive\r\n";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (!downstream->get_upgraded() ||
|
|
|
|
downstream->get_request_method() != "CONNECT") {
|
2012-09-13 14:33:35 +02:00
|
|
|
hdrs += "Connection: close\r\n";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-04-03 06:20:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_norm_response_header("alt-svc") == end_headers) {
|
2014-04-03 06:20:50 +02:00
|
|
|
// We won't change or alter alt-svc from backend at the moment.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->altsvcs.empty()) {
|
2014-04-03 06:20:50 +02:00
|
|
|
hdrs += "Alt-Svc: ";
|
2014-04-08 15:28:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &altsvc : get_config()->altsvcs) {
|
2014-04-08 15:28:50 +02:00
|
|
|
hdrs += util::percent_encode_token(altsvc.protocol_id);
|
2014-10-27 16:17:32 +01:00
|
|
|
hdrs += "=\"";
|
|
|
|
hdrs += util::quote_string(std::string(altsvc.host, altsvc.host_len));
|
|
|
|
hdrs += ":";
|
2014-04-08 15:28:50 +02:00
|
|
|
hdrs += util::utos(altsvc.port);
|
2014-10-27 16:17:32 +01:00
|
|
|
hdrs += "\", ";
|
2014-04-08 15:28:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hdrs[hdrs.size() - 2] = '\r';
|
|
|
|
hdrs[hdrs.size() - 1] = '\n';
|
2014-04-03 06:20:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
2014-08-14 15:45:21 +02:00
|
|
|
hdrs += "Server: ";
|
|
|
|
hdrs += get_config()->server_name;
|
|
|
|
hdrs += "\r\n";
|
|
|
|
} else {
|
|
|
|
auto server = downstream->get_norm_response_header("server");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (server != end_headers) {
|
2014-08-14 15:45:21 +02:00
|
|
|
hdrs += "Server: ";
|
|
|
|
hdrs += (*server).value;
|
|
|
|
hdrs += "\r\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
auto via = downstream->get_norm_response_header("via");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->no_via) {
|
|
|
|
if (via != end_headers) {
|
2013-08-27 17:09:46 +02:00
|
|
|
hdrs += "Via: ";
|
2014-04-03 04:22:11 +02:00
|
|
|
hdrs += (*via).value;
|
|
|
|
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
2013-08-27 17:09:46 +02:00
|
|
|
hdrs += "\r\n";
|
|
|
|
}
|
|
|
|
} else {
|
2013-01-09 14:01:25 +01:00
|
|
|
hdrs += "Via: ";
|
2014-11-27 15:39:04 +01:00
|
|
|
if (via != end_headers) {
|
2014-04-03 04:22:11 +02:00
|
|
|
hdrs += (*via).value;
|
|
|
|
http2::sanitize_header_value(hdrs, hdrs.size() - (*via).value.size());
|
2013-01-09 14:01:25 +01:00
|
|
|
hdrs += ", ";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
hdrs += http::create_via_header_value(downstream->get_response_major(),
|
|
|
|
downstream->get_response_minor());
|
2013-01-09 14:01:25 +01:00
|
|
|
hdrs += "\r\n";
|
2012-06-06 17:43:18 +02:00
|
|
|
}
|
2014-04-26 07:56:08 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &p : get_config()->add_response_headers) {
|
2014-04-26 07:56:08 +02:00
|
|
|
hdrs += p.first;
|
|
|
|
hdrs += ": ";
|
|
|
|
hdrs += p.second;
|
|
|
|
hdrs += "\r\n";
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
hdrs += "\r\n";
|
2014-07-25 17:40:06 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-25 17:40:06 +02:00
|
|
|
log_response_headers(hdrs);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-07-25 17:40:06 +02:00
|
|
|
|
2013-09-24 16:39:36 +02:00
|
|
|
auto output = bufferevent_get_output(handler_->get_bev());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_add(output, hdrs.c_str(), hdrs.size()) != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:55:08 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int HttpsUpstream::on_downstream_body(Downstream *downstream,
|
2014-04-03 11:54:15 +02:00
|
|
|
const uint8_t *data, size_t len,
|
2014-11-27 15:39:04 +01:00
|
|
|
bool flush) {
|
2012-06-04 16:48:31 +02:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (len == 0) {
|
2013-07-30 14:46:00 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-09-24 16:39:36 +02:00
|
|
|
auto output = bufferevent_get_output(handler_->get_bev());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_chunked_response()) {
|
2014-05-14 15:39:28 +02:00
|
|
|
auto chunk_size_hex = util::utox(len);
|
|
|
|
chunk_size_hex += "\r\n";
|
|
|
|
|
|
|
|
rv = evbuffer_add(output, chunk_size_hex.c_str(), chunk_size_hex.size());
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:55:08 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_add(output, data, len) != 0) {
|
2013-01-25 14:00:33 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-11-18 16:56:44 +01:00
|
|
|
downstream->add_response_sent_bodylen(len);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_chunked_response()) {
|
|
|
|
if (evbuffer_add(output, "\r\n", 2) != 0) {
|
2013-01-25 14:00:33 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
2012-06-09 19:51:42 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_downstream_body_complete(Downstream *downstream) {
|
|
|
|
if (downstream->get_chunked_response()) {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto output = bufferevent_get_output(handler_->get_bev());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_add(output, "0\r\n\r\n", 5) != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:55:08 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DLOG(INFO, downstream) << "HTTP response completed";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_connection_close() ||
|
|
|
|
downstream->get_response_connection_close()) {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto handler = get_client_handler();
|
2012-06-04 16:48:31 +02:00
|
|
|
handler->set_should_close_after_write(true);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-27 15:34:54 +02:00
|
|
|
int HttpsUpstream::on_downstream_abort_request(Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
unsigned int status_code) {
|
2014-06-27 15:34:54 +02:00
|
|
|
return error_reply(status_code);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::log_response_headers(const std::string &hdrs) const {
|
2014-07-25 17:40:06 +02:00
|
|
|
const char *hdrp;
|
|
|
|
std::string nhdrs;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (worker_config->errorlog_tty) {
|
2014-07-25 17:40:06 +02:00
|
|
|
nhdrs = http::colorizeHeaders(hdrs.c_str());
|
|
|
|
hdrp = nhdrs.c_str();
|
|
|
|
} else {
|
|
|
|
hdrp = hdrs.c_str();
|
|
|
|
}
|
|
|
|
ULOG(INFO, this) << "HTTP response headers\n" << hdrp;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::reset_timeouts() {
|
2014-09-18 16:03:36 +02:00
|
|
|
handler_->set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
|
|
|
&get_config()->upstream_write_timeout);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void HttpsUpstream::on_handler_delete() {
|
|
|
|
if (downstream_ && downstream_->accesslog_ready()) {
|
2014-11-18 17:59:09 +01:00
|
|
|
handler_->write_accesslog(downstream_.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
int HttpsUpstream::on_downstream_reset() {
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if ((downstream_->get_request_state() != Downstream::HEADER_COMPLETE &&
|
|
|
|
downstream_->get_request_state() != Downstream::MSG_COMPLETE) ||
|
|
|
|
downstream_->get_response_state() != Downstream::INITIAL) {
|
|
|
|
// Return error so that caller can delete handler
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream_->pop_downstream_connection();
|
|
|
|
|
|
|
|
rv = downstream_->attach_downstream_connection(
|
|
|
|
handler_->get_downstream_connection());
|
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
} // namespace shrpx
|