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"
|
2012-06-04 16:48:31 +02:00
|
|
|
#include "shrpx_http.h"
|
|
|
|
#include "shrpx_config.h"
|
|
|
|
#include "shrpx_error.h"
|
2015-02-25 16:02:29 +01:00
|
|
|
#include "shrpx_log_config.h"
|
2015-02-25 14:53:23 +01:00
|
|
|
#include "shrpx_worker.h"
|
2015-07-12 15:16:20 +02:00
|
|
|
#include "shrpx_http2_session.h"
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-01 17:19:32 +02:00
|
|
|
#include "shrpx_mruby.h"
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2013-08-27 19:47:22 +02:00
|
|
|
#include "http2.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
#include "util.h"
|
2015-02-05 15:21:53 +01:00
|
|
|
#include "template.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
using namespace nghttp2;
|
2012-06-04 16:48:31 +02:00
|
|
|
|
|
|
|
namespace shrpx {
|
|
|
|
|
|
|
|
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
2014-11-27 15:39:04 +01:00
|
|
|
: handler_(handler), current_header_length_(0),
|
2014-12-27 18:59:06 +01:00
|
|
|
ioctrl_(handler->get_rlimit()) {
|
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();
|
2015-04-07 15:13:01 +02:00
|
|
|
|
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
|
2014-03-25 18:04:24 +01:00
|
|
|
// TODO specify 0 as priority for now
|
2015-09-03 16:36:49 +02:00
|
|
|
auto downstream =
|
|
|
|
make_unique<Downstream>(upstream, handler->get_mcpool(), 0, 0);
|
|
|
|
|
|
|
|
upstream->attach_downstream(std::move(downstream));
|
|
|
|
|
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();
|
2015-10-14 17:56:41 +02:00
|
|
|
|
|
|
|
// We happen to have the same value for method token.
|
|
|
|
downstream->set_request_method(htp->method);
|
|
|
|
|
2015-04-29 15:23:25 +02:00
|
|
|
if (downstream->get_request_headers_sum() + len >
|
|
|
|
get_config()->header_field_buffer) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large URI size="
|
|
|
|
<< downstream->get_request_headers_sum() + len;
|
|
|
|
}
|
|
|
|
assert(downstream->get_request_state() == Downstream::INITIAL);
|
|
|
|
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
downstream->add_request_headers_sum(len);
|
2015-09-03 16:36:49 +02:00
|
|
|
if (downstream->get_request_method() == HTTP_CONNECT) {
|
|
|
|
downstream->append_request_http2_authority(data, len);
|
|
|
|
} else {
|
|
|
|
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();
|
2015-04-29 14:10:59 +02:00
|
|
|
if (downstream->get_request_headers_sum() + len >
|
|
|
|
get_config()->header_field_buffer) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
|
|
|
<< downstream->get_request_headers_sum() + len;
|
|
|
|
}
|
2015-04-29 14:39:46 +02:00
|
|
|
if (downstream->get_request_state() == Downstream::INITIAL) {
|
|
|
|
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
|
|
|
}
|
2015-04-29 14:10:59 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-03-08 10:32:01 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::INITIAL) {
|
|
|
|
if (downstream->get_request_header_key_prev()) {
|
|
|
|
downstream->append_last_request_header_key(data, len);
|
|
|
|
} else {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (downstream->get_request_headers().size() >=
|
|
|
|
get_config()->max_header_fields) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too many header field num="
|
|
|
|
<< downstream->get_request_headers().size() + 1;
|
|
|
|
}
|
2015-04-29 14:39:46 +02:00
|
|
|
downstream->set_request_state(
|
|
|
|
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
2015-04-29 14:10:59 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-03-08 10:32:01 +01:00
|
|
|
downstream->add_request_header(std::string(data, len), "");
|
|
|
|
}
|
2012-07-11 11:32:04 +02:00
|
|
|
} else {
|
2015-03-08 10:32:01 +01:00
|
|
|
// trailer part
|
|
|
|
if (downstream->get_request_trailer_key_prev()) {
|
|
|
|
downstream->append_last_request_trailer_key(data, len);
|
|
|
|
} else {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (downstream->get_request_headers().size() >=
|
|
|
|
get_config()->max_header_fields) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too many header field num="
|
|
|
|
<< downstream->get_request_headers().size() + 1;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2015-03-08 10:32:01 +01:00
|
|
|
downstream->add_request_trailer(std::string(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_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();
|
2015-04-29 14:10:59 +02:00
|
|
|
if (downstream->get_request_headers_sum() + len >
|
|
|
|
get_config()->header_field_buffer) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
|
|
|
<< downstream->get_request_headers_sum() + len;
|
|
|
|
}
|
2015-04-29 14:39:46 +02:00
|
|
|
if (downstream->get_request_state() == Downstream::INITIAL) {
|
|
|
|
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
|
|
|
}
|
2015-04-29 14:10:59 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-03-08 10:32:01 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::INITIAL) {
|
|
|
|
if (downstream->get_request_header_key_prev()) {
|
|
|
|
downstream->set_last_request_header_value(data, len);
|
|
|
|
} else {
|
|
|
|
downstream->append_last_request_header_value(data, len);
|
|
|
|
}
|
2012-07-11 11:32:04 +02:00
|
|
|
} else {
|
2015-03-08 10:32:01 +01:00
|
|
|
if (downstream->get_request_trailer_key_prev()) {
|
|
|
|
downstream->set_last_request_trailer_value(data, len);
|
|
|
|
} else {
|
|
|
|
downstream->append_last_request_trailer_value(data, len);
|
|
|
|
}
|
2012-07-11 11:32:04 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
namespace {
|
|
|
|
void rewrite_request_host_path_from_uri(Downstream *downstream, const char *uri,
|
|
|
|
http_parser_url &u) {
|
|
|
|
assert(u.field_set & (1 << UF_HOST));
|
|
|
|
|
|
|
|
// As per https://tools.ietf.org/html/rfc7230#section-5.4, we
|
|
|
|
// rewrite host header field with authority component.
|
|
|
|
std::string authority;
|
|
|
|
http2::copy_url_component(authority, &u, UF_HOST, uri);
|
|
|
|
// TODO properly check IPv6 numeric address
|
|
|
|
if (authority.find(':') != std::string::npos) {
|
|
|
|
authority = '[' + authority;
|
|
|
|
authority += ']';
|
|
|
|
}
|
|
|
|
if (u.field_set & (1 << UF_PORT)) {
|
|
|
|
authority += ':';
|
|
|
|
authority += util::utos(u.port);
|
|
|
|
}
|
|
|
|
downstream->set_request_http2_authority(authority);
|
|
|
|
|
2015-09-03 15:51:24 +02:00
|
|
|
std::string scheme;
|
|
|
|
http2::copy_url_component(scheme, &u, UF_SCHEMA, uri);
|
|
|
|
downstream->set_request_http2_scheme(std::move(scheme));
|
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
std::string path;
|
|
|
|
if (u.field_set & (1 << UF_PATH)) {
|
|
|
|
http2::copy_url_component(path, &u, UF_PATH, uri);
|
2015-06-09 16:15:02 +02:00
|
|
|
} else if (downstream->get_request_method() == HTTP_OPTIONS) {
|
2015-03-13 14:40:41 +01:00
|
|
|
// Server-wide OPTIONS takes following form in proxy request:
|
|
|
|
//
|
|
|
|
// OPTIONS http://example.org HTTP/1.1
|
|
|
|
//
|
|
|
|
// Notice that no slash after authority. See
|
|
|
|
// http://tools.ietf.org/html/rfc7230#section-5.3.4
|
2015-09-03 17:14:09 +02:00
|
|
|
downstream->set_request_path("");
|
2015-03-13 14:40:41 +01:00
|
|
|
// we ignore query component here
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
path = "/";
|
|
|
|
}
|
|
|
|
if (u.field_set & (1 << UF_QUERY)) {
|
|
|
|
auto &fdata = u.field_data[UF_QUERY];
|
|
|
|
path += '?';
|
|
|
|
path.append(uri + fdata.off, fdata.len);
|
|
|
|
}
|
2015-07-11 10:50:58 +02:00
|
|
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
|
|
|
downstream->set_request_path(std::move(path));
|
|
|
|
} else {
|
|
|
|
downstream->set_request_path(
|
|
|
|
http2::rewrite_clean_path(std::begin(path), std::end(path)));
|
|
|
|
}
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
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
|
|
|
|
2012-07-11 11:32:04 +02:00
|
|
|
downstream->set_request_major(htp->http_major);
|
|
|
|
downstream->set_request_minor(htp->http_minor);
|
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
|
|
|
|
2015-09-03 17:14:09 +02:00
|
|
|
auto method = downstream->get_request_method();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-11-23 13:30:17 +01:00
|
|
|
std::stringstream ss;
|
2015-09-03 17:14:09 +02:00
|
|
|
ss << http2::to_method_string(method) << " "
|
|
|
|
<< (method == HTTP_CONNECT ? downstream->get_request_http2_authority()
|
|
|
|
: downstream->get_request_path()) << " "
|
2012-11-23 13:30:17 +01:00
|
|
|
<< "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
|
|
|
}
|
|
|
|
|
2015-01-20 14:55:01 +01:00
|
|
|
if (downstream->index_request_headers() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2015-02-03 17:15:56 +01:00
|
|
|
if (downstream->get_request_major() == 1 &&
|
|
|
|
downstream->get_request_minor() == 1 &&
|
|
|
|
!downstream->get_request_header(http2::HD_HOST)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2015-02-05 16:15:13 +01:00
|
|
|
downstream->inspect_http1_request();
|
|
|
|
|
2015-09-03 17:14:09 +02:00
|
|
|
if (method != HTTP_CONNECT) {
|
2015-03-13 14:40:41 +01:00
|
|
|
http_parser_url u{};
|
2015-05-05 16:43:56 +02:00
|
|
|
// make a copy of request path, since we may set request path
|
|
|
|
// while we are refering to original request path.
|
2015-09-03 17:14:09 +02:00
|
|
|
auto path = downstream->get_request_path();
|
|
|
|
rv = http_parser_parse_url(path.c_str(), path.size(), 0, &u);
|
2015-03-13 14:40:41 +01:00
|
|
|
if (rv != 0) {
|
2012-11-21 14:10:35 +01:00
|
|
|
// Expect to respond with 400 bad request
|
|
|
|
return -1;
|
|
|
|
}
|
2015-03-13 14:40:41 +01:00
|
|
|
// checking UF_HOST could be redundant, but just in case ...
|
|
|
|
if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) {
|
2015-05-04 16:24:33 +02:00
|
|
|
if (get_config()->http2_proxy || get_config()->client_proxy) {
|
2015-03-13 14:40:41 +01:00
|
|
|
// Request URI should be absolute-form for client proxy mode
|
|
|
|
return -1;
|
|
|
|
}
|
2015-05-21 19:19:44 +02:00
|
|
|
|
2015-09-03 17:14:09 +02:00
|
|
|
if (method == HTTP_OPTIONS && path == "*") {
|
|
|
|
downstream->set_request_path("");
|
|
|
|
} else {
|
|
|
|
downstream->set_request_path(
|
|
|
|
http2::rewrite_clean_path(std::begin(path), std::end(path)));
|
|
|
|
}
|
2015-07-11 09:12:35 +02:00
|
|
|
|
2015-09-03 16:36:49 +02:00
|
|
|
auto host = downstream->get_request_header(http2::HD_HOST);
|
|
|
|
if (host) {
|
|
|
|
downstream->set_request_http2_authority(host->value);
|
|
|
|
}
|
|
|
|
|
2015-05-21 19:19:44 +02:00
|
|
|
if (upstream->get_client_handler()->get_ssl()) {
|
|
|
|
downstream->set_request_http2_scheme("https");
|
|
|
|
} else {
|
|
|
|
downstream->set_request_http2_scheme("http");
|
|
|
|
}
|
2015-03-13 14:40:41 +01:00
|
|
|
} else {
|
2015-09-03 17:14:09 +02:00
|
|
|
rewrite_request_host_path_from_uri(downstream, path.c_str(), u);
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
2012-11-21 14:10:35 +01:00
|
|
|
}
|
|
|
|
|
2015-09-02 16:21:45 +02:00
|
|
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
|
|
|
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
auto worker = handler->get_worker();
|
|
|
|
auto mruby_ctx = worker->get_mruby_context();
|
|
|
|
|
|
|
|
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
|
|
|
downstream->set_response_http_status(500);
|
|
|
|
return -1;
|
|
|
|
}
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2015-09-02 17:40:14 +02:00
|
|
|
|
2015-09-03 17:14:09 +02:00
|
|
|
// mruby hook may change method value
|
|
|
|
|
2015-09-02 16:21:45 +02:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = downstream->attach_downstream_connection(
|
2015-07-09 19:52:11 +02:00
|
|
|
upstream->get_client_handler()->get_downstream_connection(downstream));
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
|
|
|
}
|
2015-02-20 11:24:48 +01:00
|
|
|
auto handler = upstream->get_client_handler();
|
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) {
|
2015-09-03 15:41:51 +02:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
// Here both response and request were completed. One of the
|
|
|
|
// reason why end_upload_data() failed is when we sent response
|
|
|
|
// in request phase hook. We only delete and proceed to the
|
|
|
|
// next request handling (if we don't close the connection). We
|
|
|
|
// first pause parser here jsut as we normally do, and call
|
|
|
|
// signal_write() to run on_write().
|
|
|
|
http_parser_pause(htp, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-16 17:12:31 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-02-20 11:24:48 +01:00
|
|
|
|
|
|
|
if (handler->get_http2_upgrade_allowed() &&
|
2015-03-01 02:11:45 +01:00
|
|
|
downstream->get_http2_upgrade_request() &&
|
2015-05-25 16:59:44 +02:00
|
|
|
// we may write non-final header in response_buf, in this case,
|
|
|
|
// response_state is still INITIAL. So don't upgrade in this
|
|
|
|
// case, otherwise we end up send this non-final header as
|
|
|
|
// response body in HTTP/2 upstream.
|
|
|
|
downstream->get_response_buf()->rleft() == 0 &&
|
2015-03-01 02:11:45 +01:00
|
|
|
handler->perform_http2_upgrade(upstream) != 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "HTTP Upgrade to HTTP/2 failed";
|
2015-02-20 11:24:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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() {
|
2014-12-27 18:59:06 +01:00
|
|
|
auto rb = handler_->get_rb();
|
2015-02-13 14:41:50 +01:00
|
|
|
auto rlimit = handler_->get_rlimit();
|
2013-07-31 14:48:37 +02:00
|
|
|
auto downstream = get_downstream();
|
2015-01-29 14:47:37 +01:00
|
|
|
|
|
|
|
if (rb->rleft() == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
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()) {
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
auto rv = downstream->push_upload_data_chunk(rb->pos, rb->rleft());
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
rb->reset();
|
2015-02-13 14:41:50 +01:00
|
|
|
rlimit->startw();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
if (downstream->request_buf_full()) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, this) << "Downstream request buf is full";
|
2013-07-31 14:48:37 +02:00
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
pause_read(SHRPX_NO_BUFFER);
|
2012-07-11 11:32:04 +02:00
|
|
|
|
2014-06-01 14:01:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-02-13 14:41:50 +01:00
|
|
|
if (downstream) {
|
|
|
|
// To avoid reading next pipelined request
|
|
|
|
switch (downstream->get_request_state()) {
|
|
|
|
case Downstream::INITIAL:
|
|
|
|
case Downstream::HEADER_COMPLETE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 17:24:42 +01:00
|
|
|
// http_parser_execute() does nothing once it entered error state.
|
2015-01-29 14:47:37 +01:00
|
|
|
auto nread = http_parser_execute(
|
|
|
|
&htp_, &htp_hooks, reinterpret_cast<const char *>(rb->pos), rb->rleft());
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
rb->drain(nread);
|
2015-02-13 14:41:50 +01:00
|
|
|
rlimit->startw();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
// Well, actually header length + some body bytes
|
|
|
|
current_header_length_ += nread;
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
// Get downstream again because it may be initialized in http parser
|
|
|
|
// execution
|
|
|
|
downstream = get_downstream();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
auto htperr = HTTP_PARSER_ERRNO(&htp_);
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
if (htperr == HPE_PAUSED) {
|
2015-09-03 15:41:51 +02:00
|
|
|
// We may pause parser in htp_msg_completecb when both side are
|
|
|
|
// completed. Signal write, so that we can run on_write().
|
|
|
|
if (downstream &&
|
|
|
|
downstream->get_request_state() == Downstream::MSG_COMPLETE &&
|
|
|
|
downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
handler_->signal_write();
|
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-08-19 16:41:53 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
if (htperr != HPE_OK) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, this) << "HTTP parse failure: "
|
|
|
|
<< "(" << http_errno_name(htperr) << ") "
|
|
|
|
<< http_errno_description(htperr);
|
|
|
|
}
|
2014-08-19 16:41:53 +02:00
|
|
|
|
2015-02-20 11:24:48 +01:00
|
|
|
if (downstream && downstream->get_response_state() != Downstream::INITIAL) {
|
|
|
|
handler_->set_should_close_after_write(true);
|
|
|
|
handler_->signal_write();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
unsigned int status_code;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-06-09 15:08:49 +02:00
|
|
|
if (htperr == HPE_INVALID_METHOD) {
|
|
|
|
status_code = 501;
|
|
|
|
} else if (downstream) {
|
2015-09-02 17:40:14 +02:00
|
|
|
status_code = downstream->get_response_http_status();
|
|
|
|
if (status_code == 0) {
|
|
|
|
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
|
|
|
status_code = 503;
|
|
|
|
} else if (downstream->get_request_state() ==
|
|
|
|
Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE) {
|
|
|
|
status_code = 431;
|
|
|
|
} else {
|
|
|
|
status_code = 400;
|
|
|
|
}
|
2015-04-29 14:39:46 +02:00
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
} else {
|
|
|
|
status_code = 400;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
error_reply(status_code);
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
handler_->signal_write();
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2015-01-29 14:47:37 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// downstream can be NULL here.
|
|
|
|
if (downstream && downstream->request_buf_full()) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, this) << "Downstream request buffer is full";
|
2012-07-16 16:29:48 +02:00
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
|
|
|
|
pause_read(SHRPX_NO_BUFFER);
|
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int HttpsUpstream::on_write() {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = get_downstream();
|
2014-12-27 18:59:06 +01:00
|
|
|
if (!downstream) {
|
|
|
|
return 0;
|
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
|
|
|
|
auto dconn = downstream->get_downstream_connection();
|
|
|
|
auto output = downstream->get_response_buf();
|
|
|
|
|
|
|
|
if (output->rleft() == 0 && dconn &&
|
2015-01-20 17:41:17 +01:00
|
|
|
downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
2015-01-29 14:47:37 +01:00
|
|
|
if (downstream->resume_read(SHRPX_NO_BUFFER,
|
|
|
|
downstream->get_response_datalen()) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-20 17:41:17 +01:00
|
|
|
if (downstream_read(dconn) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2015-01-29 14:47:37 +01:00
|
|
|
|
2015-10-01 16:10:53 +02:00
|
|
|
if (output->rleft() > 0) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to postpone detachment until all data are sent so that
|
|
|
|
// we can notify nghttp2 library all data consumed.
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2015-07-22 14:41:16 +02:00
|
|
|
if (downstream->get_response_connection_close() ||
|
|
|
|
downstream->get_request_state() != Downstream::MSG_COMPLETE) {
|
2014-12-27 18:59:06 +01:00
|
|
|
// Connection close
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
|
|
|
} else {
|
|
|
|
// Keep-alive
|
|
|
|
downstream->detach_downstream_connection();
|
2014-07-25 14:26:03 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
// We need this if response ends before request.
|
|
|
|
if (downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
delete_downstream();
|
2015-09-03 15:41:51 +02:00
|
|
|
|
|
|
|
if (handler_->get_should_close_after_write()) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-13 14:41:50 +01:00
|
|
|
return resume_read(SHRPX_NO_BUFFER, nullptr, 0);
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return downstream->resume_read(SHRPX_NO_BUFFER,
|
2014-08-21 14:22:16 +02:00
|
|
|
downstream->get_response_datalen());
|
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) {
|
2015-02-13 14:41:50 +01:00
|
|
|
// downstream could be nullptr
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream && downstream->request_buf_full()) {
|
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int HttpsUpstream::downstream_read(DownstreamConnection *dconn) {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
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
|
|
|
|
2015-02-21 13:17:52 +01:00
|
|
|
if (rv == SHRPX_ERR_EOF) {
|
|
|
|
return downstream_eof(dconn);
|
|
|
|
}
|
|
|
|
|
2015-09-02 18:32:15 +02:00
|
|
|
if (rv == SHRPX_ERR_DCONN_CANCELED) {
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2015-02-21 13:17:52 +01:00
|
|
|
if (rv < 0) {
|
|
|
|
return downstream_error(dconn, Downstream::EVENT_ERROR);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
|
2015-01-19 15:44:23 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_BAD_HEADER) {
|
2015-01-21 14:55:00 +01:00
|
|
|
error_reply(502);
|
2015-01-19 15:44:23 +01:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2015-07-22 14:41:16 +02:00
|
|
|
if (downstream->can_detach_downstream_connection()) {
|
2015-01-30 17:11:55 +01:00
|
|
|
// Keep-alive
|
|
|
|
downstream->detach_downstream_connection();
|
|
|
|
}
|
|
|
|
|
2015-01-19 15:44:23 +01:00
|
|
|
end:
|
2014-12-27 18:59:06 +01:00
|
|
|
handler_->signal_write();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int HttpsUpstream::downstream_write(DownstreamConnection *dconn) {
|
|
|
|
int rv;
|
|
|
|
rv = dconn->on_write();
|
2015-02-04 13:15:58 +01:00
|
|
|
if (rv == SHRPX_ERR_NETWORK) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return downstream_error(dconn, Downstream::EVENT_ERROR);
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int HttpsUpstream::downstream_eof(DownstreamConnection *dconn) {
|
|
|
|
auto downstream = dconn->get_downstream();
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, dconn) << "EOF";
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2015-01-20 14:19:28 +01:00
|
|
|
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
|
|
|
// Server may indicate the end of the request by EOF
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DCLOG(INFO, dconn) << "The end of the response body was indicated by "
|
|
|
|
<< "EOF";
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
on_downstream_body_complete(downstream);
|
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
goto end;
|
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2015-01-20 14:19:28 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::INITIAL) {
|
|
|
|
// we did not send any response headers, so we can reply error
|
|
|
|
// message.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2015-01-20 14:19:28 +01:00
|
|
|
DCLOG(INFO, dconn) << "Return error reply";
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2015-01-21 14:55:00 +01:00
|
|
|
error_reply(502);
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
goto end;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-09-17 15:53:29 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
// Otherwise, we don't know how to recover from this situation. Just
|
|
|
|
// drop connection.
|
|
|
|
return -1;
|
|
|
|
end:
|
|
|
|
handler_->signal_write();
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int HttpsUpstream::downstream_error(DownstreamConnection *dconn, int events) {
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
if (events & Downstream::EVENT_ERROR) {
|
|
|
|
DCLOG(INFO, dconn) << "Network error/general error";
|
|
|
|
} else {
|
|
|
|
DCLOG(INFO, dconn) << "Timeout";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
if (downstream->get_response_state() != Downstream::INITIAL) {
|
|
|
|
return -1;
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
unsigned int status;
|
|
|
|
if (events & Downstream::EVENT_TIMEOUT) {
|
|
|
|
status = 504;
|
|
|
|
} else {
|
|
|
|
status = 502;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2015-01-21 14:55:00 +01:00
|
|
|
error_reply(status);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
|
|
|
|
handler_->signal_write();
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2015-09-03 15:29:16 +02:00
|
|
|
int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body,
|
|
|
|
size_t bodylen) {
|
|
|
|
auto major = downstream->get_request_major();
|
|
|
|
auto minor = downstream->get_request_minor();
|
|
|
|
|
|
|
|
auto connection_close = false;
|
|
|
|
if (major <= 0 || (major == 1 && minor == 0)) {
|
|
|
|
connection_close = true;
|
|
|
|
} else {
|
|
|
|
auto c = downstream->get_response_header(http2::HD_CONNECTION);
|
|
|
|
if (c && util::strieq_l("close", c->value)) {
|
|
|
|
connection_close = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connection_close) {
|
|
|
|
downstream->set_response_connection_close(true);
|
|
|
|
handler_->set_should_close_after_write(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto output = downstream->get_response_buf();
|
|
|
|
|
|
|
|
output->append("HTTP/1.1 ");
|
2015-09-11 16:07:00 +02:00
|
|
|
output->append(
|
|
|
|
http2::get_status_string(downstream->get_response_http_status()));
|
2015-09-03 19:46:35 +02:00
|
|
|
output->append("\r\n");
|
2015-09-03 15:29:16 +02:00
|
|
|
|
|
|
|
for (auto &kv : downstream->get_response_headers()) {
|
|
|
|
if (kv.name.empty() || kv.name[0] == ':') {
|
|
|
|
continue;
|
|
|
|
}
|
2015-09-11 16:07:00 +02:00
|
|
|
http2::capitalize(output, kv.name);
|
2015-09-03 15:29:16 +02:00
|
|
|
output->append(": ");
|
2015-09-11 16:07:00 +02:00
|
|
|
output->append(kv.value);
|
2015-09-03 15:29:16 +02:00
|
|
|
output->append("\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!downstream->get_response_header(http2::HD_SERVER)) {
|
|
|
|
output->append("Server: ");
|
|
|
|
output->append(get_config()->server_name,
|
|
|
|
strlen(get_config()->server_name));
|
|
|
|
output->append("\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
output->append("\r\n");
|
|
|
|
|
|
|
|
output->append(body, bodylen);
|
|
|
|
|
|
|
|
downstream->add_response_sent_bodylen(bodylen);
|
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-21 14:55:00 +01:00
|
|
|
void 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-12-27 18:59:06 +01:00
|
|
|
if (!downstream) {
|
2015-04-07 15:13:01 +02:00
|
|
|
attach_downstream(
|
|
|
|
make_unique<Downstream>(this, handler_->get_mcpool(), 1, 1));
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream = get_downstream();
|
2014-07-05 11:22:40 +02:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream->set_response_http_status(status_code);
|
2015-01-21 15:03:39 +01:00
|
|
|
// we are going to close connection for both frontend and backend in
|
|
|
|
// error condition. This is safest option.
|
|
|
|
downstream->set_response_connection_close(true);
|
|
|
|
handler_->set_should_close_after_write(true);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
auto output = downstream->get_response_buf();
|
|
|
|
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("HTTP/1.1 ");
|
2014-12-27 18:59:06 +01:00
|
|
|
auto status_str = http2::get_status_string(status_code);
|
|
|
|
output->append(status_str.c_str(), status_str.size());
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("\r\nServer: ");
|
2014-12-27 18:59:06 +01:00
|
|
|
output->append(get_config()->server_name, strlen(get_config()->server_name));
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("\r\nContent-Length: ");
|
2014-12-27 18:59:06 +01:00
|
|
|
auto cl = util::utos(html.size());
|
|
|
|
output->append(cl.c_str(), cl.size());
|
2015-09-07 16:11:23 +02:00
|
|
|
output->append("\r\nDate: ");
|
|
|
|
auto lgconf = log_config();
|
|
|
|
lgconf->update_tstamp(std::chrono::system_clock::now());
|
|
|
|
auto &date = lgconf->time_http_str;
|
|
|
|
output->append(date.c_str(), date.size());
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("\r\nContent-Type: text/html; "
|
|
|
|
"charset=UTF-8\r\nConnection: close\r\n\r\n");
|
2014-12-27 18:59:06 +01:00
|
|
|
output->append(html.c_str(), html.size());
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream->add_response_sent_bodylen(html.size());
|
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
namespace {
|
|
|
|
void write_altsvc(DefaultMemchunks *buf, const AltSvc &altsvc) {
|
|
|
|
buf->append(util::percent_encode_token(altsvc.protocol_id));
|
|
|
|
buf->append("=\"");
|
|
|
|
buf->append(util::quote_string(altsvc.host));
|
|
|
|
buf->append(":");
|
|
|
|
buf->append(altsvc.service);
|
|
|
|
buf->append("\"");
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
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
|
|
|
|
2015-09-03 17:54:41 +02:00
|
|
|
#ifdef HAVE_MRUBY
|
2015-09-02 18:32:15 +02:00
|
|
|
if (!downstream->get_non_final_response()) {
|
|
|
|
auto worker = handler_->get_worker();
|
|
|
|
auto mruby_ctx = worker->get_mruby_context();
|
|
|
|
|
|
|
|
if (mruby_ctx->run_on_response_proc(downstream) != 0) {
|
|
|
|
error_reply(500);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2015-09-03 17:54:41 +02:00
|
|
|
#endif // HAVE_MRUBY
|
2015-09-02 18:32:15 +02:00
|
|
|
|
2015-06-09 16:15:02 +02:00
|
|
|
auto connect_method = downstream->get_request_method() == HTTP_CONNECT;
|
2015-05-26 15:26:17 +02:00
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
auto buf = downstream->get_response_buf();
|
|
|
|
|
|
|
|
buf->append("HTTP/");
|
|
|
|
buf->append(util::utos(downstream->get_request_major()));
|
|
|
|
buf->append(".");
|
|
|
|
buf->append(util::utos(downstream->get_request_minor()));
|
|
|
|
buf->append(" ");
|
|
|
|
buf->append(http2::get_status_string(downstream->get_response_http_status()));
|
|
|
|
buf->append("\r\n");
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
|
|
|
!get_config()->no_location_rewrite) {
|
2015-01-04 15:22:39 +01:00
|
|
|
downstream->rewrite_location_response_header(
|
2015-02-08 11:41:45 +01:00
|
|
|
get_client_handler()->get_upstream_scheme());
|
2013-12-28 09:02:43 +01:00
|
|
|
}
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
http2::build_http1_headers_from_headers(buf,
|
2015-01-04 15:22:39 +01:00
|
|
|
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()) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("\r\n");
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2015-09-11 16:07:00 +02:00
|
|
|
log_response_headers(buf);
|
2014-07-25 17:40:06 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 16:32:57 +02:00
|
|
|
downstream->clear_response_headers();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-25 14:53:23 +01:00
|
|
|
auto worker = handler_->get_worker();
|
|
|
|
|
2015-01-22 15:46:35 +01:00
|
|
|
// after graceful shutdown commenced, add connection: close header
|
|
|
|
// field.
|
2015-02-25 14:53:23 +01:00
|
|
|
if (worker->get_graceful_shutdown()) {
|
2015-01-22 15:46:35 +01:00
|
|
|
downstream->set_response_connection_close(true);
|
|
|
|
}
|
|
|
|
|
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
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Connection: Keep-Alive\r\n");
|
2012-07-11 09:20:16 +02:00
|
|
|
}
|
2015-05-26 15:26:17 +02:00
|
|
|
} else if (!downstream->get_upgraded()) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Connection: close\r\n");
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-04-03 06:20:50 +02:00
|
|
|
|
2015-05-26 15:26:17 +02:00
|
|
|
if (!connect_method && downstream->get_upgraded()) {
|
|
|
|
auto connection = downstream->get_response_header(http2::HD_CONNECTION);
|
|
|
|
if (connection) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Connection: ");
|
|
|
|
buf->append((*connection).value);
|
|
|
|
buf->append("\r\n");
|
2015-05-26 15:26:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
auto upgrade = downstream->get_response_header(http2::HD_UPGRADE);
|
|
|
|
if (upgrade) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Upgrade: ");
|
|
|
|
buf->append((*upgrade).value);
|
|
|
|
buf->append("\r\n");
|
2015-05-26 15:26:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:22:39 +01:00
|
|
|
if (!downstream->get_response_header(http2::HD_ALT_SVC)) {
|
|
|
|
// We won't change or alter alt-svc from backend for now
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->altsvcs.empty()) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Alt-Svc: ");
|
2014-04-08 15:28:50 +02:00
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
auto &altsvcs = get_config()->altsvcs;
|
|
|
|
write_altsvc(buf, altsvcs[0]);
|
|
|
|
for (size_t i = 1; i < altsvcs.size(); ++i) {
|
|
|
|
buf->append(", ");
|
|
|
|
write_altsvc(buf, altsvcs[i]);
|
|
|
|
}
|
|
|
|
buf->append("\r\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) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Server: ");
|
|
|
|
buf->append(get_config()->server_name, strlen(get_config()->server_name));
|
|
|
|
buf->append("\r\n");
|
2014-08-14 15:45:21 +02:00
|
|
|
} else {
|
2015-01-04 15:22:39 +01:00
|
|
|
auto server = downstream->get_response_header(http2::HD_SERVER);
|
|
|
|
if (server) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Server: ");
|
|
|
|
buf->append((*server).value);
|
|
|
|
buf->append("\r\n");
|
2014-08-14 15:45:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-04 15:22:39 +01:00
|
|
|
auto via = downstream->get_response_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) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Via: ");
|
|
|
|
buf->append((*via).value);
|
|
|
|
buf->append("\r\n");
|
2013-08-27 17:09:46 +02:00
|
|
|
}
|
|
|
|
} else {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Via: ");
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append((*via).value);
|
|
|
|
buf->append(", ");
|
2013-01-09 14:01:25 +01:00
|
|
|
}
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append(http::create_via_header_value(
|
|
|
|
downstream->get_response_major(), downstream->get_response_minor()));
|
|
|
|
buf->append("\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) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append(p.first);
|
|
|
|
buf->append(": ");
|
|
|
|
buf->append(p.second);
|
|
|
|
buf->append("\r\n");
|
2014-04-26 07:56:08 +02:00
|
|
|
}
|
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("\r\n");
|
2014-07-25 17:40:06 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2015-09-11 16:07:00 +02:00
|
|
|
log_response_headers(buf);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-07-25 17:40:06 +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) {
|
|
|
|
if (len == 0) {
|
2013-07-30 14:46:00 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
auto output = downstream->get_response_buf();
|
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";
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
output->append(chunk_size_hex.c_str(), chunk_size_hex.size());
|
2013-01-25 14:00:33 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
output->append(data, len);
|
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()) {
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("\r\n");
|
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()) {
|
2014-12-27 18:59:06 +01:00
|
|
|
auto output = downstream->get_response_buf();
|
2015-03-08 09:32:38 +01:00
|
|
|
auto &trailers = downstream->get_response_trailers();
|
|
|
|
if (trailers.empty()) {
|
|
|
|
output->append("0\r\n\r\n");
|
|
|
|
} else {
|
|
|
|
output->append("0\r\n");
|
2015-09-11 16:07:00 +02:00
|
|
|
http2::build_http1_headers_from_headers(output, trailers);
|
2015-03-08 09:32:38 +01:00
|
|
|
output->append("\r\n");
|
|
|
|
}
|
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
|
|
|
|
2015-01-17 11:33:30 +01:00
|
|
|
if (!downstream->validate_response_bodylen()) {
|
|
|
|
downstream->set_response_connection_close(true);
|
|
|
|
}
|
|
|
|
|
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) {
|
2015-01-21 14:45:52 +01:00
|
|
|
error_reply(status_code);
|
|
|
|
handler_->signal_write();
|
|
|
|
return 0;
|
2014-06-27 15:34:54 +02:00
|
|
|
}
|
|
|
|
|
2015-09-11 16:07:00 +02:00
|
|
|
void HttpsUpstream::log_response_headers(DefaultMemchunks *buf) const {
|
2014-07-25 17:40:06 +02:00
|
|
|
std::string nhdrs;
|
2015-09-11 16:07:00 +02:00
|
|
|
for (auto chunk = buf->head; chunk; chunk = chunk->next) {
|
|
|
|
nhdrs.append(chunk->pos, chunk->last);
|
|
|
|
}
|
2015-03-03 17:09:15 +01:00
|
|
|
if (log_config()->errorlog_tty) {
|
2015-09-11 16:07:00 +02:00
|
|
|
nhdrs = http::colorizeHeaders(nhdrs.c_str());
|
2014-07-25 17:40:06 +02:00
|
|
|
}
|
2015-09-11 16:07:00 +02:00
|
|
|
ULOG(INFO, this) << "HTTP response headers\n" << nhdrs;
|
2014-07-25 17:40:06 +02:00
|
|
|
}
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-21 15:30:48 +01:00
|
|
|
int HttpsUpstream::on_downstream_reset(bool no_retry) {
|
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 rv;
|
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
if (!downstream_->request_submission_ready()) {
|
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
|
|
|
// Return error so that caller can delete handler
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-02-02 17:47:04 +01:00
|
|
|
downstream_->pop_downstream_connection();
|
|
|
|
|
|
|
|
downstream_->add_retry();
|
|
|
|
|
|
|
|
if (no_retry || downstream_->no_more_retry()) {
|
2015-02-20 11:23:52 +01:00
|
|
|
goto fail;
|
2015-01-21 15:30:48 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
rv = downstream_->attach_downstream_connection(
|
2015-07-09 19:52:11 +02:00
|
|
|
handler_->get_downstream_connection(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
|
|
|
if (rv != 0) {
|
2015-02-20 11:23:52 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (on_downstream_abort_request(downstream_.get(), 503) != 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
|
|
|
return -1;
|
|
|
|
}
|
2015-02-20 11:23:52 +01:00
|
|
|
downstream_->pop_downstream_connection();
|
|
|
|
|
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
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-05 15:47:07 +02:00
|
|
|
int HttpsUpstream::initiate_push(Downstream *downstream, const char *uri,
|
|
|
|
size_t len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-02 15:42:46 +02:00
|
|
|
int HttpsUpstream::response_riovec(struct iovec *iov, int iovcnt) const {
|
2015-10-01 16:10:53 +02:00
|
|
|
if (!downstream_) {
|
2015-10-02 15:42:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buf = downstream_->get_response_buf();
|
|
|
|
|
|
|
|
return buf->riovec(iov, iovcnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HttpsUpstream::response_drain(size_t n) {
|
|
|
|
if (!downstream_) {
|
|
|
|
return;
|
2015-10-01 16:10:53 +02:00
|
|
|
}
|
|
|
|
|
2015-10-02 15:42:46 +02:00
|
|
|
auto buf = downstream_->get_response_buf();
|
|
|
|
|
|
|
|
buf->drain(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HttpsUpstream::response_empty() const {
|
|
|
|
if (!downstream_) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buf = downstream_->get_response_buf();
|
|
|
|
|
|
|
|
return buf->rleft() == 0;
|
2015-10-01 16:10:53 +02:00
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
} // namespace shrpx
|