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