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)
|
2016-01-27 13:14:07 +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();
|
|
|
|
|
2016-01-14 16:09:53 +01:00
|
|
|
auto downstream = make_unique<Downstream>(upstream, handler->get_mcpool(), 0);
|
2015-09-03 16:36:49 +02:00
|
|
|
|
|
|
|
upstream->attach_downstream(std::move(downstream));
|
|
|
|
|
2016-06-23 17:04:39 +02:00
|
|
|
handler->stop_read_timer();
|
|
|
|
|
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();
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &req = downstream->request();
|
2015-10-14 17:56:41 +02:00
|
|
|
|
2016-03-10 14:42:07 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
|
|
|
|
2015-10-14 17:56:41 +02:00
|
|
|
// We happen to have the same value for method token.
|
2016-01-13 14:45:52 +01:00
|
|
|
req.method = htp->method;
|
2015-10-14 17:56:41 +02:00
|
|
|
|
2016-02-06 09:22:23 +01:00
|
|
|
if (req.fs.buffer_size() + len >
|
|
|
|
get_config()->http.request_header_field_buffer) {
|
2015-04-29 15:23:25 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large URI size="
|
2016-01-13 14:45:52 +01:00
|
|
|
<< req.fs.buffer_size() + len;
|
2015-04-29 15:23:25 +02:00
|
|
|
}
|
|
|
|
assert(downstream->get_request_state() == Downstream::INITIAL);
|
|
|
|
downstream->set_request_state(Downstream::HTTP1_REQUEST_HEADER_TOO_LARGE);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-13 14:45:52 +01:00
|
|
|
|
|
|
|
req.fs.add_extra_buffer_size(len);
|
|
|
|
|
|
|
|
if (req.method == HTTP_CONNECT) {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.authority =
|
|
|
|
concat_string_ref(balloc, req.authority, StringRef{data, len});
|
2015-09-03 16:36:49 +02:00
|
|
|
} else {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = concat_string_ref(balloc, req.path, StringRef{data, len});
|
2015-09-03 16:36:49 +02:00
|
|
|
}
|
|
|
|
|
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();
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &req = downstream->request();
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &httpconf = get_config()->http;
|
2016-03-10 16:50:27 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
2016-01-13 14:45:52 +01:00
|
|
|
|
2016-02-06 09:22:23 +01:00
|
|
|
if (req.fs.buffer_size() + len > httpconf.request_header_field_buffer) {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
2016-01-13 14:45:52 +01:00
|
|
|
<< req.fs.buffer_size() + len;
|
2015-04-29 14:10:59 +02:00
|
|
|
}
|
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) {
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.fs.header_key_prev()) {
|
|
|
|
req.fs.append_last_header_key(data, len);
|
2015-03-08 10:32:01 +01:00
|
|
|
} else {
|
2016-02-06 09:22:23 +01:00
|
|
|
if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-01-13 14:45:52 +01:00
|
|
|
ULOG(INFO, upstream)
|
|
|
|
<< "Too many header field num=" << req.fs.num_fields() + 1;
|
2015-04-29 14:10:59 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
2016-03-10 16:50:27 +01:00
|
|
|
auto name = http2::copy_lower(balloc, StringRef{data, len});
|
|
|
|
auto token = http2::lookup_token(name);
|
|
|
|
req.fs.add_header_token(name, StringRef{}, false, token);
|
2015-03-08 10:32:01 +01:00
|
|
|
}
|
2012-07-11 11:32:04 +02:00
|
|
|
} else {
|
2015-03-08 10:32:01 +01:00
|
|
|
// trailer part
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.fs.trailer_key_prev()) {
|
|
|
|
req.fs.append_last_trailer_key(data, len);
|
2015-03-08 10:32:01 +01:00
|
|
|
} else {
|
2016-02-06 09:22:23 +01:00
|
|
|
if (req.fs.num_fields() >= httpconf.max_request_header_fields) {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-01-13 14:45:52 +01:00
|
|
|
ULOG(INFO, upstream)
|
|
|
|
<< "Too many header field num=" << req.fs.num_fields() + 1;
|
2015-04-29 14:10:59 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2016-03-10 16:50:27 +01:00
|
|
|
auto name = http2::copy_lower(balloc, StringRef{data, len});
|
|
|
|
auto token = http2::lookup_token(name);
|
|
|
|
req.fs.add_trailer_token(name, StringRef{}, false, token);
|
2015-03-08 10:32:01 +01:00
|
|
|
}
|
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();
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &req = downstream->request();
|
|
|
|
|
2016-02-06 09:22:23 +01:00
|
|
|
if (req.fs.buffer_size() + len >
|
|
|
|
get_config()->http.request_header_field_buffer) {
|
2015-04-29 14:10:59 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
2016-01-13 14:45:52 +01:00
|
|
|
<< req.fs.buffer_size() + len;
|
2015-04-29 14:10:59 +02:00
|
|
|
}
|
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) {
|
2016-01-13 15:01:29 +01:00
|
|
|
req.fs.append_last_header_value(data, len);
|
2012-07-11 11:32:04 +02:00
|
|
|
} else {
|
2016-01-13 15:01:29 +01:00
|
|
|
req.fs.append_last_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 {
|
2016-03-10 14:42:07 +01:00
|
|
|
void rewrite_request_host_path_from_uri(BlockAllocator &balloc, Request &req,
|
|
|
|
const StringRef &uri,
|
2016-08-18 15:31:53 +02:00
|
|
|
http_parser_url &u, bool http2_proxy) {
|
2015-03-13 14:40:41 +01:00
|
|
|
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.
|
2016-03-10 14:42:07 +01:00
|
|
|
auto authority = util::get_uri_field(uri.c_str(), u, UF_HOST);
|
2015-03-13 14:40:41 +01:00
|
|
|
// TODO properly check IPv6 numeric address
|
2016-03-10 14:42:07 +01:00
|
|
|
auto ipv6 = std::find(std::begin(authority), std::end(authority), ':') !=
|
|
|
|
std::end(authority);
|
|
|
|
auto authoritylen = authority.size();
|
|
|
|
if (ipv6) {
|
|
|
|
authoritylen += 2;
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
|
|
|
if (u.field_set & (1 << UF_PORT)) {
|
2016-03-10 14:42:07 +01:00
|
|
|
authoritylen += 1 + str_size("65535");
|
|
|
|
}
|
|
|
|
if (authoritylen > authority.size()) {
|
|
|
|
auto iovec = make_byte_ref(balloc, authoritylen + 1);
|
|
|
|
auto p = iovec.base;
|
|
|
|
if (ipv6) {
|
|
|
|
*p++ = '[';
|
|
|
|
}
|
|
|
|
p = std::copy(std::begin(authority), std::end(authority), p);
|
|
|
|
if (ipv6) {
|
|
|
|
*p++ = ']';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (u.field_set & (1 << UF_PORT)) {
|
|
|
|
*p++ = ':';
|
|
|
|
p = util::utos(p, u.port);
|
|
|
|
}
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
req.authority = StringRef{iovec.base, p};
|
|
|
|
} else {
|
|
|
|
req.authority = authority;
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
|
|
|
|
2016-03-10 14:42:07 +01:00
|
|
|
req.scheme = util::get_uri_field(uri.c_str(), u, UF_SCHEMA);
|
2015-09-03 15:51:24 +02:00
|
|
|
|
2016-03-10 14:42:07 +01:00
|
|
|
StringRef path;
|
2015-03-13 14:40:41 +01:00
|
|
|
if (u.field_set & (1 << UF_PATH)) {
|
2016-03-10 14:42:07 +01:00
|
|
|
path = util::get_uri_field(uri.c_str(), u, UF_PATH);
|
2016-01-13 14:45:52 +01:00
|
|
|
} else if (req.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
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = StringRef::from_lit("");
|
2015-03-13 14:40:41 +01:00
|
|
|
// we ignore query component here
|
|
|
|
return;
|
|
|
|
} else {
|
2016-03-10 14:42:07 +01:00
|
|
|
path = StringRef::from_lit("/");
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
2016-03-10 14:42:07 +01:00
|
|
|
|
2015-03-13 14:40:41 +01:00
|
|
|
if (u.field_set & (1 << UF_QUERY)) {
|
|
|
|
auto &fdata = u.field_data[UF_QUERY];
|
2016-03-10 14:42:07 +01:00
|
|
|
|
|
|
|
if (u.field_set & (1 << UF_PATH)) {
|
|
|
|
auto q = util::get_uri_field(uri.c_str(), u, UF_QUERY);
|
|
|
|
path = StringRef{std::begin(path), std::end(q)};
|
|
|
|
} else {
|
2016-03-20 09:55:17 +01:00
|
|
|
path = concat_string_ref(balloc, path, StringRef::from_lit("?"),
|
|
|
|
StringRef{&uri[fdata.off], fdata.len});
|
2016-03-10 14:42:07 +01:00
|
|
|
}
|
2015-03-13 14:40:41 +01:00
|
|
|
}
|
2016-03-10 14:42:07 +01:00
|
|
|
|
2016-08-18 15:31:53 +02:00
|
|
|
if (http2_proxy) {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = path;
|
2015-07-11 10:50:58 +02:00
|
|
|
} else {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = http2::rewrite_clean_path(balloc, path);
|
2015-07-11 10:50:58 +02:00
|
|
|
}
|
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
|
|
|
}
|
2016-03-05 11:11:36 +01:00
|
|
|
|
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
|
2013-09-24 16:39:36 +02:00
|
|
|
auto downstream = upstream->get_downstream();
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &req = downstream->request();
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
req.http_major = htp->http_major;
|
|
|
|
req.http_minor = htp->http_minor;
|
2012-06-06 17:43:18 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
req.connection_close = !http_should_keep_alive(htp);
|
2012-07-11 09:20:16 +02:00
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto method = req.method;
|
2015-09-03 17:14:09 +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;
|
2015-09-03 17:14:09 +02:00
|
|
|
ss << http2::to_method_string(method) << " "
|
2016-01-13 14:45:52 +01:00
|
|
|
<< (method == HTTP_CONNECT ? req.authority : req.path) << " "
|
|
|
|
<< "HTTP/" << req.http_major << "." << req.http_minor << "\n";
|
|
|
|
|
|
|
|
for (const auto &kv : req.fs.headers()) {
|
|
|
|
ss << TTY_HTTP_HD << kv.name << TTY_RST << ": " << kv.value << "\n";
|
2012-11-23 13:30:17 +01:00
|
|
|
}
|
2016-01-13 14:45:52 +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
|
|
|
}
|
|
|
|
|
2016-05-28 09:44:04 +02:00
|
|
|
// set content-length if no transfer-encoding is given. If
|
|
|
|
// transfer-encoding is given, leave req.fs.content_length to -1.
|
|
|
|
if (!req.fs.header(http2::HD_TRANSFER_ENCODING)) {
|
|
|
|
req.fs.content_length = htp->content_length;
|
2015-01-20 14:55:01 +01:00
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2016-01-16 07:59:24 +01:00
|
|
|
auto host = req.fs.header(http2::HD_HOST);
|
|
|
|
|
|
|
|
if (req.http_major == 1 && req.http_minor == 1 && !host) {
|
2015-02-03 17:15:56 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2016-01-16 07:59:24 +01:00
|
|
|
if (host) {
|
|
|
|
const auto &value = host->value;
|
|
|
|
// Not allow at least '"' or '\' in host. They are illegal in
|
|
|
|
// authority component, also they cause headaches when we put them
|
|
|
|
// in quoted-string.
|
|
|
|
if (std::find_if(std::begin(value), std::end(value), [](char c) {
|
|
|
|
return c == '"' || c == '\\';
|
|
|
|
}) != std::end(value)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-05 16:15:13 +01:00
|
|
|
downstream->inspect_http1_request();
|
|
|
|
|
2016-08-18 15:31:53 +02:00
|
|
|
auto faddr = handler->get_upstream_addr();
|
2016-03-10 14:42:07 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
|
|
|
|
2015-09-03 17:14:09 +02:00
|
|
|
if (method != HTTP_CONNECT) {
|
2015-03-13 14:40:41 +01:00
|
|
|
http_parser_url u{};
|
2016-03-10 14:42:07 +01:00
|
|
|
rv = http_parser_parse_url(req.path.c_str(), req.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))) {
|
2016-08-18 15:31:53 +02:00
|
|
|
if (get_config()->http2_proxy && !faddr->alt_mode) {
|
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
|
|
|
|
2016-01-16 13:12:51 +01:00
|
|
|
req.no_authority = true;
|
|
|
|
|
2016-03-24 16:07:21 +01:00
|
|
|
if (method == HTTP_OPTIONS && req.path == StringRef::from_lit("*")) {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = StringRef{};
|
2015-09-03 17:14:09 +02:00
|
|
|
} else {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.path = http2::rewrite_clean_path(balloc, req.path);
|
2015-09-03 17:14:09 +02:00
|
|
|
}
|
2015-07-11 09:12:35 +02:00
|
|
|
|
2015-09-03 16:36:49 +02:00
|
|
|
if (host) {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.authority = host->value;
|
2015-09-03 16:36:49 +02:00
|
|
|
}
|
|
|
|
|
2016-03-05 11:11:36 +01:00
|
|
|
if (handler->get_ssl()) {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.scheme = StringRef::from_lit("https");
|
2015-05-21 19:19:44 +02:00
|
|
|
} else {
|
2016-03-10 14:42:07 +01:00
|
|
|
req.scheme = StringRef::from_lit("http");
|
2015-05-21 19:19:44 +02:00
|
|
|
}
|
2015-03-13 14:40:41 +01:00
|
|
|
} else {
|
2016-08-18 15:31:53 +02:00
|
|
|
rewrite_request_host_path_from_uri(balloc, req, req.path, u,
|
|
|
|
get_config()->http2_proxy &&
|
|
|
|
!faddr->alt_mode);
|
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 worker = handler->get_worker();
|
|
|
|
auto mruby_ctx = worker->get_mruby_context();
|
|
|
|
|
2016-01-19 11:07:13 +01:00
|
|
|
auto &resp = downstream->response();
|
|
|
|
|
2015-09-02 17:40:14 +02:00
|
|
|
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.http_status = 500;
|
2015-09-02 17:40:14 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-04-02 16:11:03 +02:00
|
|
|
auto dconn = handler->get_downstream_connection(downstream);
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2016-04-02 16:11:03 +02:00
|
|
|
if (!dconn ||
|
|
|
|
(rv = downstream->attach_downstream_connection(std::move(dconn))) != 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;
|
|
|
|
}
|
2016-06-04 10:23:21 +02:00
|
|
|
|
2016-06-16 16:30:35 +02:00
|
|
|
if (faddr->alt_mode) {
|
2016-06-04 10:23:21 +02:00
|
|
|
// Normally, we forward expect: 100-continue to backend server,
|
|
|
|
// and let them decide whether responds with 100 Continue or not.
|
2016-06-16 16:30:35 +02:00
|
|
|
// For alternative mode, we have no backend, so just send 100
|
|
|
|
// Continue here to make the client happy.
|
2016-06-04 10:23:21 +02:00
|
|
|
auto expect = req.fs.header(http2::HD_EXPECT);
|
|
|
|
if (expect &&
|
|
|
|
util::strieq(expect->value, StringRef::from_lit("100-continue"))) {
|
|
|
|
auto output = downstream->get_response_buf();
|
|
|
|
constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n");
|
|
|
|
output->append(res);
|
|
|
|
handler->signal_write();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
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) {
|
2016-06-17 15:32:15 +02:00
|
|
|
// Ignore error if response has been completed. We will end up in
|
|
|
|
// htp_msg_completecb, and request will end gracefully.
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
|
|
|
return 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
|
2016-06-04 10:23:47 +02:00
|
|
|
// first pause parser here just as we normally do, and call
|
2015-09-03 15:41:51 +02:00
|
|
|
// 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() &&
|
|
|
|
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) {
|
2016-01-13 16:37:45 +01:00
|
|
|
status_code = downstream->response().http_status;
|
2015-09-02 17:40:14 +02:00
|
|
|
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 output = downstream->get_response_buf();
|
2016-01-13 16:37:45 +01:00
|
|
|
const auto &resp = downstream->response();
|
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) {
|
2016-05-14 10:16:50 +02:00
|
|
|
if (downstream->can_detach_downstream_connection()) {
|
|
|
|
// Keep-alive
|
|
|
|
downstream->detach_downstream_connection();
|
|
|
|
} else {
|
2014-12-27 18:59:06 +01:00
|
|
|
// Connection close
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-23 17:04:39 +02:00
|
|
|
handler_->repeat_read_timer();
|
|
|
|
|
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
|
|
|
|
2016-01-14 15:49:21 +01:00
|
|
|
return downstream->resume_read(SHRPX_NO_BUFFER, resp.unconsumed_body_length);
|
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) {
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream->request();
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2016-03-19 15:41:21 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
2015-09-03 15:29:16 +02:00
|
|
|
|
|
|
|
auto connection_close = false;
|
2016-05-17 18:21:23 +02:00
|
|
|
|
|
|
|
auto worker = handler_->get_worker();
|
|
|
|
|
|
|
|
if (worker->get_graceful_shutdown()) {
|
|
|
|
resp.fs.add_header_token(StringRef::from_lit("connection"),
|
|
|
|
StringRef::from_lit("close"), false,
|
|
|
|
http2::HD_CONNECTION);
|
|
|
|
connection_close = true;
|
|
|
|
} else if (req.http_major <= 0 ||
|
|
|
|
(req.http_major == 1 && req.http_minor == 0)) {
|
2015-09-03 15:29:16 +02:00
|
|
|
connection_close = true;
|
|
|
|
} else {
|
2016-01-13 16:37:45 +01:00
|
|
|
auto c = resp.fs.header(http2::HD_CONNECTION);
|
2015-09-03 15:29:16 +02:00
|
|
|
if (c && util::strieq_l("close", c->value)) {
|
|
|
|
connection_close = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connection_close) {
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2015-09-03 15:29:16 +02:00
|
|
|
handler_->set_should_close_after_write(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto output = downstream->get_response_buf();
|
|
|
|
|
|
|
|
output->append("HTTP/1.1 ");
|
2016-03-19 15:41:21 +01:00
|
|
|
output->append(http2::get_status_string(balloc, resp.http_status));
|
2015-09-03 19:46:35 +02:00
|
|
|
output->append("\r\n");
|
2015-09-03 15:29:16 +02:00
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
for (auto &kv : resp.fs.headers()) {
|
2015-09-03 15:29:16 +02:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
if (!resp.fs.header(http2::HD_SERVER)) {
|
2015-09-03 15:29:16 +02:00
|
|
|
output->append("Server: ");
|
2016-01-18 09:00:20 +01:00
|
|
|
output->append(get_config()->http.server_name);
|
2015-09-03 15:29:16 +02:00
|
|
|
output->append("\r\n");
|
|
|
|
}
|
|
|
|
|
2016-02-13 14:31:38 +01:00
|
|
|
auto &httpconf = get_config()->http;
|
|
|
|
|
|
|
|
for (auto &p : httpconf.add_response_headers) {
|
|
|
|
output->append(p.name);
|
|
|
|
output->append(": ");
|
|
|
|
output->append(p.value);
|
|
|
|
output->append("\r\n");
|
|
|
|
}
|
|
|
|
|
2015-09-03 15:29:16 +02:00
|
|
|
output->append("\r\n");
|
|
|
|
|
|
|
|
output->append(body, bodylen);
|
|
|
|
|
2016-01-14 15:54:28 +01:00
|
|
|
downstream->response_sent_body_length += bodylen;
|
2015-09-03 15:29:16 +02:00
|
|
|
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) {
|
2014-07-05 11:22:40 +02:00
|
|
|
auto downstream = get_downstream();
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (!downstream) {
|
2016-01-14 16:09:53 +01:00
|
|
|
attach_downstream(make_unique<Downstream>(this, handler_->get_mcpool(), 1));
|
2014-12-27 18:59:06 +01:00
|
|
|
downstream = get_downstream();
|
2014-07-05 11:22:40 +02:00
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2016-03-19 15:41:21 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
|
|
|
|
|
|
|
auto html = http::create_error_html(balloc, status_code);
|
2016-01-13 16:37:45 +01:00
|
|
|
|
|
|
|
resp.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.
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2015-01-21 15:03:39 +01:00
|
|
|
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 ");
|
2016-03-19 15:41:21 +01:00
|
|
|
auto status_str = http2::get_status_string(balloc, status_code);
|
2016-01-17 09:04:16 +01:00
|
|
|
output->append(status_str);
|
2015-01-22 15:54:30 +01:00
|
|
|
output->append("\r\nServer: ");
|
2016-01-18 09:00:20 +01:00
|
|
|
output->append(get_config()->http.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());
|
2016-01-17 09:04:16 +01:00
|
|
|
output->append(cl);
|
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;
|
2016-01-17 09:04:16 +01:00
|
|
|
output->append(date);
|
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");
|
2016-01-17 09:04:16 +01:00
|
|
|
output->append(html);
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2016-01-14 15:54:28 +01:00
|
|
|
downstream->response_sent_body_length += html.size();
|
2014-12-27 18:59:06 +01:00
|
|
|
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
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream->request();
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2016-03-19 15:41:21 +01:00
|
|
|
auto &balloc = downstream->get_block_allocator();
|
2016-01-13 14:45:52 +01: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
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto connect_method = req.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/");
|
2016-01-13 14:45:52 +01:00
|
|
|
buf->append(util::utos(req.http_major));
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append(".");
|
2016-01-13 14:45:52 +01:00
|
|
|
buf->append(util::utos(req.http_minor));
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append(" ");
|
2016-03-19 15:41:21 +01:00
|
|
|
buf->append(http2::get_status_string(balloc, resp.http_status));
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("\r\n");
|
2015-01-04 15:22:39 +01:00
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &httpconf = get_config()->http;
|
|
|
|
|
2016-02-28 13:35:26 +01:00
|
|
|
if (!get_config()->http2_proxy && !httpconf.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
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
http2::build_http1_headers_from_headers(buf, resp.fs.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
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.fs.clear_headers();
|
2014-07-23 16:32:57 +02:00
|
|
|
|
|
|
|
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()) {
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2015-01-22 15:46:35 +01:00
|
|
|
}
|
|
|
|
|
2012-09-13 14:33:35 +02:00
|
|
|
// We check downstream->get_response_connection_close() in case when
|
|
|
|
// the Content-Length is not available.
|
2016-01-13 16:37:45 +01:00
|
|
|
if (!req.connection_close && !resp.connection_close) {
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.http_major <= 0 || req.http_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()) {
|
2016-01-13 16:37:45 +01:00
|
|
|
auto connection = resp.fs.header(http2::HD_CONNECTION);
|
2015-05-26 15:26:17 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto upgrade = resp.fs.header(http2::HD_UPGRADE);
|
2015-05-26 15:26:17 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
if (!resp.fs.header(http2::HD_ALT_SVC)) {
|
2015-01-04 15:22:39 +01:00
|
|
|
// We won't change or alter alt-svc from backend for now
|
2016-01-18 09:00:20 +01:00
|
|
|
if (!httpconf.altsvcs.empty()) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Alt-Svc: ");
|
2014-04-08 15:28:50 +02:00
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
auto &altsvcs = httpconf.altsvcs;
|
2015-09-11 16:07:00 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-28 13:35:26 +01:00
|
|
|
if (!get_config()->http2_proxy) {
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("Server: ");
|
2016-01-18 09:00:20 +01:00
|
|
|
buf->append(httpconf.server_name);
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("\r\n");
|
2014-08-14 15:45:21 +02:00
|
|
|
} else {
|
2016-01-13 16:37:45 +01:00
|
|
|
auto server = resp.fs.header(http2::HD_SERVER);
|
2015-01-04 15:22:39 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto via = resp.fs.header(http2::HD_VIA);
|
2016-01-18 09:00:20 +01:00
|
|
|
if (httpconf.no_via) {
|
2015-01-04 15:22:39 +01:00
|
|
|
if (via) {
|
2015-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
|
|
|
}
|
2016-03-12 10:36:05 +01:00
|
|
|
std::array<char, 16> viabuf;
|
|
|
|
auto end = http::create_via_header_value(viabuf.data(), resp.http_major,
|
|
|
|
resp.http_minor);
|
|
|
|
buf->append(viabuf.data(), end - std::begin(viabuf));
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append("\r\n");
|
2012-06-06 17:43:18 +02:00
|
|
|
}
|
2014-04-26 07:56:08 +02:00
|
|
|
|
2016-01-18 09:00:20 +01:00
|
|
|
for (auto &p : httpconf.add_response_headers) {
|
2016-02-13 14:19:05 +01:00
|
|
|
buf->append(p.name);
|
2015-09-11 16:07:00 +02:00
|
|
|
buf->append(": ");
|
2016-02-13 14:19:05 +01:00
|
|
|
buf->append(p.value);
|
2015-09-11 16:07:00 +02:00
|
|
|
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()) {
|
2016-01-17 09:04:16 +01:00
|
|
|
output->append(util::utox(len));
|
|
|
|
output->append("\r\n");
|
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
|
|
|
|
2016-01-14 15:54:28 +01:00
|
|
|
downstream->response_sent_body_length += len;
|
2014-11-18 16:56:44 +01:00
|
|
|
|
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) {
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream->request();
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2016-01-13 14:45:52 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_chunked_response()) {
|
2014-12-27 18:59:06 +01:00
|
|
|
auto output = downstream->get_response_buf();
|
2016-01-13 16:37:45 +01:00
|
|
|
const auto &trailers = resp.fs.trailers();
|
2015-03-08 09:32:38 +01:00
|
|
|
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
|
|
|
|
2016-01-14 15:20:44 +01:00
|
|
|
if (!downstream->validate_response_recv_body_length()) {
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2015-01-17 11:33:30 +01:00
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
if (req.connection_close || resp.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;
|
2016-04-02 16:11:03 +02:00
|
|
|
std::unique_ptr<DownstreamConnection> dconn;
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-04-02 16:11:03 +02:00
|
|
|
dconn = handler_->get_downstream_connection(downstream_.get());
|
|
|
|
if (!dconn) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = downstream_->attach_downstream_connection(std::move(dconn));
|
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;
|
|
|
|
}
|
|
|
|
|
2016-04-12 16:30:52 +02:00
|
|
|
rv = downstream_->push_request_headers();
|
|
|
|
if (rv != 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-02-20 11:23:52 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-03-03 16:38:43 +01:00
|
|
|
int HttpsUpstream::initiate_push(Downstream *downstream, const StringRef &uri) {
|
2015-09-05 15:47:07 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
Downstream *
|
|
|
|
HttpsUpstream::on_downstream_push_promise(Downstream *downstream,
|
|
|
|
int32_t promised_stream_id) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
int HttpsUpstream::on_downstream_push_promise_complete(
|
|
|
|
Downstream *downstream, Downstream *promised_downstream) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HttpsUpstream::push_enabled() const { return false; }
|
|
|
|
|
|
|
|
void HttpsUpstream::cancel_premature_downstream(
|
|
|
|
Downstream *promised_downstream) {}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
} // namespace shrpx
|