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.
|
|
|
|
*/
|
2013-07-26 12:38:54 +02:00
|
|
|
#include "shrpx_http2_upstream.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2013-01-10 16:11:41 +01:00
|
|
|
#include <netinet/tcp.h>
|
2012-06-04 16:48:31 +02:00
|
|
|
#include <assert.h>
|
2012-07-15 14:15:28 +02:00
|
|
|
#include <cerrno>
|
2012-06-04 16:48:31 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "shrpx_client_handler.h"
|
2013-08-03 11:51:01 +02:00
|
|
|
#include "shrpx_https_upstream.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
#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_config.h"
|
|
|
|
#include "shrpx_http.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-08-03 11:51:01 +02:00
|
|
|
#include "base64.h"
|
2014-02-09 10:47:26 +01:00
|
|
|
#include "app_helper.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 {
|
|
|
|
|
2012-06-04 20:11:43 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
const size_t OUTBUF_MAX_THRES = 16 * 1024;
|
|
|
|
const size_t INBUF_MAX_THRES = 16 * 1024;
|
2012-06-04 20:11:43 +02:00
|
|
|
} // namespace
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
|
|
|
uint32_t error_code, void *user_data) {
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
|
|
|
|
<< " is being closed";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2013-08-31 17:23:07 +02:00
|
|
|
auto downstream = upstream->find_downstream(stream_id);
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-06-01 16:44:32 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
upstream->consume(stream_id, downstream->get_request_datalen());
|
|
|
|
|
|
|
|
downstream->reset_request_datalen();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// At this point, downstream response was read
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream->get_upgraded() &&
|
|
|
|
!downstream->get_response_connection_close()) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// Keep-alive
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->detach_downstream_connection();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
// At this point, downstream read may be paused.
|
|
|
|
|
|
|
|
// If shrpx_downstream::push_request_headers() failed, the
|
|
|
|
// error is handled here.
|
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
// How to test this case? Request sufficient large download
|
|
|
|
// and make client send RST_STREAM after it gets first DATA
|
|
|
|
// frame chunk.
|
|
|
|
|
2013-08-29 15:58:05 +02:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
|
2013-08-03 11:51:01 +02:00
|
|
|
int rv;
|
2014-06-15 09:14:00 +02:00
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
auto http2_settings = http->get_downstream()->get_http2_settings();
|
2014-06-15 09:14:00 +02:00
|
|
|
util::to_base64(http2_settings);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
auto settings_payload =
|
|
|
|
base64::decode(std::begin(http2_settings), std::end(http2_settings));
|
2014-06-15 09:14:00 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = nghttp2_session_upgrade(
|
|
|
|
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()),
|
|
|
|
settings_payload.size(), nullptr);
|
|
|
|
if (rv != 0) {
|
2014-11-08 02:51:56 +01:00
|
|
|
ULOG(WARN, this) << "nghttp2_session_upgrade() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2013-08-03 11:51:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2013-09-26 14:46:35 +02:00
|
|
|
pre_upstream_.reset(http);
|
2014-08-18 15:59:31 +02:00
|
|
|
auto downstream = http->pop_downstream();
|
2013-08-03 11:51:01 +02:00
|
|
|
downstream->reset_upstream(this);
|
2014-08-18 14:36:55 +02:00
|
|
|
downstream->set_stream_id(1);
|
2014-08-15 03:29:46 +02:00
|
|
|
downstream->init_upstream_timer();
|
|
|
|
downstream->reset_upstream_rtimer();
|
2013-08-03 11:51:01 +02:00
|
|
|
downstream->init_response_body_buf();
|
|
|
|
downstream->set_stream_id(1);
|
|
|
|
downstream->set_priority(0);
|
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_queue_.add_active(std::move(downstream));
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-04-05 11:59:22 +02:00
|
|
|
ULOG(INFO, this) << "Connection upgraded to HTTP/2";
|
|
|
|
}
|
|
|
|
|
2013-08-03 11:51:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-30 16:44:23 +01:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void settings_timeout_cb(evutil_socket_t fd, short what, void *arg) {
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(arg);
|
2013-10-30 16:44:23 +01:00
|
|
|
ULOG(INFO, upstream) << "SETTINGS timeout";
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
|
2013-10-30 16:44:23 +01:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->send() != 0) {
|
2013-10-30 16:44:23 +01:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::start_settings_timer() {
|
2013-10-30 16:44:23 +01:00
|
|
|
int rv;
|
|
|
|
// We submit SETTINGS only once
|
2014-11-27 15:39:04 +01:00
|
|
|
if (settings_timerev_) {
|
2013-10-30 16:44:23 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
settings_timerev_ =
|
|
|
|
evtimer_new(handler_->get_evbase(), settings_timeout_cb, this);
|
|
|
|
if (settings_timerev_ == nullptr) {
|
2013-10-30 16:44:23 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
// SETTINGS ACK timeout is 10 seconds for now
|
2014-11-27 15:39:04 +01:00
|
|
|
timeval settings_timeout = {10, 0};
|
2013-10-30 16:44:23 +01:00
|
|
|
rv = evtimer_add(settings_timerev_, &settings_timeout);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv == -1) {
|
2013-10-30 16:44:23 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Upstream::stop_settings_timer() {
|
|
|
|
if (settings_timerev_ == nullptr) {
|
2013-10-30 16:44:23 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
event_free(settings_timerev_);
|
|
|
|
settings_timerev_ = nullptr;
|
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
2014-01-16 15:41:13 +01:00
|
|
|
const uint8_t *name, size_t namelen,
|
2014-11-27 15:39:04 +01:00
|
|
|
const uint8_t *value, size_t valuelen, uint8_t flags,
|
|
|
|
void *user_data) {
|
|
|
|
if (get_config()->upstream_frame_debug) {
|
2014-02-09 10:47:26 +01:00
|
|
|
verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
|
2014-04-01 19:10:35 +02:00
|
|
|
flags, user_data);
|
2014-02-09 10:47:26 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
|
|
|
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2014-01-16 15:41:13 +01:00
|
|
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
2014-01-26 16:44:08 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2014-06-27 15:53:54 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-01-27 17:17:54 +01:00
|
|
|
ULOG(INFO, upstream) << "Too large header block size="
|
|
|
|
<< downstream->get_request_headers_sum();
|
2014-01-26 16:44:08 +01:00
|
|
|
}
|
2014-06-27 15:53:54 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(downstream, 431) != 0) {
|
2014-06-27 15:53:54 +02:00
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!http2::check_nv(name, namelen, value, valuelen)) {
|
2014-08-08 13:52:32 +02:00
|
|
|
// Simply discard name/value, as if it never happen.
|
2014-01-16 18:16:53 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-08-08 13:52:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (namelen > 0 && name[0] == ':') {
|
|
|
|
if (!downstream->request_pseudo_header_allowed() ||
|
|
|
|
!http2::check_http2_request_pseudo_header(name, namelen)) {
|
2014-08-08 13:52:32 +02:00
|
|
|
|
|
|
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:22:11 +02:00
|
|
|
downstream->split_add_request_header(name, namelen, value, valuelen,
|
|
|
|
flags & NGHTTP2_NV_FLAG_NO_INDEX);
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-01-29 13:23:13 +01:00
|
|
|
int on_begin_headers_callback(nghttp2_session *session,
|
2014-11-27 15:39:04 +01:00
|
|
|
const nghttp2_frame *frame, void *user_data) {
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2014-01-29 13:23:13 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-01-29 13:23:13 +01:00
|
|
|
ULOG(INFO, upstream) << "Received upstream request HEADERS stream_id="
|
|
|
|
<< frame->hd.stream_id;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2014-03-25 18:04:24 +01:00
|
|
|
|
|
|
|
// TODO Use priority 0 for now
|
2014-11-27 15:39:04 +01:00
|
|
|
auto downstream =
|
|
|
|
util::make_unique<Downstream>(upstream, frame->hd.stream_id, 0);
|
2014-03-25 18:04:24 +01:00
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->init_upstream_timer();
|
|
|
|
downstream->reset_upstream_rtimer();
|
2014-01-29 13:23:13 +01:00
|
|
|
downstream->init_response_body_buf();
|
|
|
|
|
2014-03-21 11:25:46 +01:00
|
|
|
// Although, we deprecated minor version from HTTP/2, we supply
|
|
|
|
// minor version 0 to use via header field in a conventional way.
|
|
|
|
downstream->set_request_major(2);
|
|
|
|
downstream->set_request_minor(0);
|
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
upstream->add_pending_downstream(std::move(downstream));
|
|
|
|
|
2014-01-29 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
|
|
|
nghttp2_session *session, const nghttp2_frame *frame) {
|
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2014-06-27 15:53:54 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
downstream->normalize_request_headers();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto &nva = downstream->get_request_headers();
|
2014-01-16 15:41:13 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-01-16 15:41:13 +01:00
|
|
|
std::stringstream ss;
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &nv : nva) {
|
2014-04-03 04:22:11 +02:00
|
|
|
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
|
|
|
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
|
2014-11-27 15:39:04 +01:00
|
|
|
<< downstream->get_stream_id() << "\n" << ss.str();
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->http2_upstream_dump_request_header) {
|
2014-01-16 15:41:13 +01:00
|
|
|
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!http2::check_http2_request_headers(nva)) {
|
2014-08-08 16:11:58 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto host = http2::get_unique_header(nva, "host");
|
|
|
|
auto authority = http2::get_unique_header(nva, ":authority");
|
|
|
|
auto path = http2::get_unique_header(nva, ":path");
|
|
|
|
auto method = http2::get_unique_header(nva, ":method");
|
|
|
|
auto scheme = http2::get_unique_header(nva, ":scheme");
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bool is_connect = method && "CONNECT" == method->value;
|
2014-01-16 15:41:13 +01:00
|
|
|
bool having_host = http2::non_empty_value(host);
|
|
|
|
bool having_authority = http2::non_empty_value(authority);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (is_connect) {
|
2014-01-16 15:41:13 +01:00
|
|
|
// Here we strictly require :authority header field.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (scheme || path || !having_authority) {
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-08-08 16:11:58 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// For proxy, :authority is required. Otherwise, we can accept
|
|
|
|
// :authority or host for methods.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!http2::non_empty_value(method) || !http2::non_empty_value(scheme) ||
|
|
|
|
(get_config()->http2_proxy && !having_authority) ||
|
|
|
|
(!get_config()->http2_proxy && !having_authority && !having_host) ||
|
|
|
|
!http2::non_empty_value(path)) {
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-08-08 16:11:58 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream->set_request_method(http2::value_to_str(method));
|
|
|
|
downstream->set_request_http2_scheme(http2::value_to_str(scheme));
|
|
|
|
downstream->set_request_http2_authority(http2::value_to_str(authority));
|
|
|
|
downstream->set_request_path(http2::value_to_str(path));
|
2014-11-19 17:53:30 +01:00
|
|
|
downstream->set_request_start_time(std::chrono::high_resolution_clock::now());
|
2014-07-03 12:59:10 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
|
2014-07-03 12:59:10 +02:00
|
|
|
downstream->set_request_http2_expect_body(true);
|
|
|
|
}
|
|
|
|
|
2014-06-15 09:14:00 +02:00
|
|
|
downstream->inspect_http2_request();
|
2014-01-16 15:41:13 +01:00
|
|
|
|
2014-08-16 14:29:20 +02:00
|
|
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
2014-08-16 14:29:20 +02:00
|
|
|
downstream->disable_upstream_rtimer();
|
|
|
|
|
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
|
|
|
}
|
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
upstream->start_downstream(downstream);
|
2014-08-16 14:29:20 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
void Http2Upstream::start_downstream(Downstream *downstream) {
|
|
|
|
auto next_downstream =
|
|
|
|
downstream_queue_.pop_pending(downstream->get_stream_id());
|
|
|
|
assert(next_downstream);
|
2014-10-06 17:31:35 +02:00
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
if (downstream_queue_.can_activate(
|
|
|
|
downstream->get_request_http2_authority())) {
|
|
|
|
initiate_downstream(std::move(next_downstream));
|
|
|
|
return;
|
2014-08-16 14:29:20 +02:00
|
|
|
}
|
2014-12-04 17:07:00 +01:00
|
|
|
|
|
|
|
downstream_queue_.add_blocked(std::move(next_downstream));
|
2014-08-16 14:29:20 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void
|
|
|
|
Http2Upstream::initiate_downstream(std::unique_ptr<Downstream> downstream) {
|
2014-08-16 14:29:20 +02:00
|
|
|
int rv;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = downstream->attach_downstream_connection(
|
|
|
|
handler_->get_downstream_connection());
|
|
|
|
if (rv != 0) {
|
2014-07-02 17:12:16 +02:00
|
|
|
// downstream connection fails, send error page
|
2014-11-27 15:39:04 +01:00
|
|
|
if (error_reply(downstream.get(), 503) != 0) {
|
2014-08-18 15:59:31 +02:00
|
|
|
rst_stream(downstream.get(), NGHTTP2_INTERNAL_ERROR);
|
2014-07-02 17:12:16 +02:00
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
2014-07-02 17:12:16 +02:00
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_queue_.add_failure(std::move(downstream));
|
|
|
|
|
2014-08-16 14:29:20 +02:00
|
|
|
return;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
|
|
|
rv = downstream->push_request_headers();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-08-16 14:29:20 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (error_reply(downstream.get(), 503) != 0) {
|
2014-08-18 15:59:31 +02:00
|
|
|
rst_stream(downstream.get(), NGHTTP2_INTERNAL_ERROR);
|
2014-07-02 17:12:16 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_queue_.add_failure(std::move(downstream));
|
|
|
|
|
2014-08-16 14:29:20 +02:00
|
|
|
return;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_queue_.add_active(std::move(downstream));
|
2014-01-16 15:41:13 +01:00
|
|
|
|
2014-08-16 14:29:20 +02:00
|
|
|
return;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|
|
|
void *user_data) {
|
2014-01-09 15:47:21 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->upstream_frame_debug) {
|
2014-02-09 10:47:26 +01:00
|
|
|
verbose_on_frame_recv_callback(session, frame, user_data);
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2014-05-24 08:02:46 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
switch (frame->hd.type) {
|
2014-01-27 14:13:41 +01:00
|
|
|
case NGHTTP2_DATA: {
|
2014-08-09 11:47:45 +02:00
|
|
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_rtimer();
|
2014-05-27 17:26:27 +02:00
|
|
|
|
2014-01-27 14:13:41 +01:00
|
|
|
downstream->end_upload_data();
|
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-01-27 14:13:41 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-05-27 17:26:27 +02:00
|
|
|
case NGHTTP2_HEADERS: {
|
|
|
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream) {
|
2014-05-27 17:26:27 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_upstream_rtimer();
|
|
|
|
|
2014-05-24 08:02:46 +02:00
|
|
|
return on_request_headers(upstream, downstream, session, frame);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_rtimer();
|
|
|
|
|
2014-05-24 08:02:46 +02:00
|
|
|
downstream->end_upload_data();
|
|
|
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
2014-07-31 14:46:50 +02:00
|
|
|
} else {
|
2014-11-27 16:23:46 +01:00
|
|
|
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
|
|
|
frame->hd.stream_id,
|
|
|
|
NGHTTP2_PROTOCOL_ERROR);
|
|
|
|
if (rv != 0) {
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-01-18 08:12:03 +01:00
|
|
|
}
|
2014-05-24 08:02:46 +02:00
|
|
|
|
|
|
|
break;
|
2014-05-27 17:26:27 +02:00
|
|
|
}
|
2014-05-24 08:02:46 +02:00
|
|
|
case NGHTTP2_PRIORITY: {
|
2014-03-25 18:04:24 +01:00
|
|
|
// TODO comment out for now
|
|
|
|
// rv = downstream->change_priority(frame->priority.pri);
|
|
|
|
// if(rv != 0) {
|
|
|
|
// return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
// }
|
2014-01-18 08:12:03 +01:00
|
|
|
break;
|
|
|
|
}
|
2013-10-30 16:44:23 +01:00
|
|
|
case NGHTTP2_SETTINGS:
|
2014-11-27 15:39:04 +01:00
|
|
|
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
|
2013-10-30 16:44:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
upstream->stop_settings_timer();
|
|
|
|
break;
|
2014-01-09 15:47:21 +01:00
|
|
|
case NGHTTP2_PUSH_PROMISE:
|
|
|
|
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
|
|
|
frame->push_promise.promised_stream_id,
|
|
|
|
NGHTTP2_REFUSED_STREAM);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-01-09 15:47:21 +01:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
break;
|
2014-07-12 16:30:13 +02:00
|
|
|
case NGHTTP2_GOAWAY:
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-12 16:30:13 +02:00
|
|
|
auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
|
|
|
|
frame->goaway.opaque_data_len);
|
|
|
|
|
|
|
|
ULOG(INFO, upstream) << "GOAWAY received: last-stream-id="
|
|
|
|
<< frame->goaway.last_stream_id
|
2014-11-27 15:39:04 +01:00
|
|
|
<< ", error_code=" << frame->goaway.error_code
|
|
|
|
<< ", debug_data=" << debug_data;
|
2014-07-12 16:30:13 +02:00
|
|
|
}
|
|
|
|
break;
|
2012-06-04 16:48:31 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-08-29 14:03:39 +02:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
|
|
|
int32_t stream_id, const uint8_t *data,
|
|
|
|
size_t len, void *user_data) {
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2013-08-31 17:23:07 +02:00
|
|
|
auto downstream = upstream->find_downstream(stream_id);
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream || !downstream->get_downstream_connection()) {
|
|
|
|
if (upstream->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-07-02 16:56:26 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_upstream_rtimer();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->push_upload_data_chunk(data, len) != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return 0;
|
2013-10-29 16:00:58 +01:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2013-10-29 16:00:58 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-10-30 16:44:23 +01:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
|
|
|
void *user_data) {
|
|
|
|
if (get_config()->upstream_frame_debug) {
|
2014-02-09 10:47:26 +01:00
|
|
|
verbose_on_frame_send_callback(session, frame, user_data);
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2014-07-12 16:30:13 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
switch (frame->hd.type) {
|
2014-07-12 16:30:13 +02:00
|
|
|
case NGHTTP2_SETTINGS:
|
2014-11-27 15:39:04 +01:00
|
|
|
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
|
|
|
|
upstream->start_settings_timer() != 0) {
|
2014-07-12 16:30:13 +02:00
|
|
|
|
2013-10-30 16:44:23 +01:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-07-12 16:30:13 +02:00
|
|
|
break;
|
|
|
|
case NGHTTP2_GOAWAY:
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-12 16:30:13 +02:00
|
|
|
auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
|
|
|
|
frame->goaway.opaque_data_len);
|
|
|
|
|
|
|
|
ULOG(INFO, upstream) << "Sending GOAWAY: last-stream-id="
|
|
|
|
<< frame->goaway.last_stream_id
|
2014-11-27 15:39:04 +01:00
|
|
|
<< ", error_code=" << frame->goaway.error_code
|
|
|
|
<< ", debug_data=" << debug_data;
|
2014-07-12 16:30:13 +02:00
|
|
|
}
|
|
|
|
break;
|
2013-10-30 16:44:23 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2012-09-15 10:19:58 +02:00
|
|
|
namespace {
|
2013-08-29 14:51:58 +02:00
|
|
|
int on_frame_not_send_callback(nghttp2_session *session,
|
2014-11-27 15:39:04 +01:00
|
|
|
const nghttp2_frame *frame, int lib_error_code,
|
|
|
|
void *user_data) {
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
2014-11-08 02:51:56 +01:00
|
|
|
ULOG(WARN, upstream) << "Failed to send control frame type="
|
|
|
|
<< static_cast<uint32_t>(frame->hd.type)
|
|
|
|
<< ", lib_error_code=" << lib_error_code << ":"
|
|
|
|
<< nghttp2_strerror(lib_error_code);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.type == NGHTTP2_HEADERS &&
|
|
|
|
frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
2012-09-15 10:19:58 +02:00
|
|
|
// To avoid stream hanging around, issue RST_STREAM.
|
2013-07-26 12:33:25 +02:00
|
|
|
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream) {
|
2013-07-12 17:19:03 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
2012-09-15 10:19:58 +02:00
|
|
|
}
|
|
|
|
}
|
2013-08-29 14:51:58 +02:00
|
|
|
return 0;
|
2012-09-15 10:19:58 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-02-27 14:39:44 +01:00
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
uint32_t infer_upstream_rst_stream_error_code(uint32_t downstream_error_code) {
|
2014-07-03 16:00:19 +02:00
|
|
|
// NGHTTP2_REFUSED_STREAM is important because it tells upstream
|
|
|
|
// client to retry.
|
2014-11-27 15:39:04 +01:00
|
|
|
switch (downstream_error_code) {
|
2014-07-03 16:00:19 +02:00
|
|
|
case NGHTTP2_NO_ERROR:
|
|
|
|
case NGHTTP2_REFUSED_STREAM:
|
2014-08-23 10:34:56 +02:00
|
|
|
return downstream_error_code;
|
2014-07-03 16:00:19 +02:00
|
|
|
default:
|
|
|
|
return NGHTTP2_INTERNAL_ERROR;
|
2013-02-27 14:39:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-07-26 12:38:54 +02:00
|
|
|
Http2Upstream::Http2Upstream(ClientHandler *handler)
|
2014-12-04 17:07:00 +01:00
|
|
|
: downstream_queue_(get_config()->http2_proxy
|
|
|
|
? get_config()->downstream_connections_per_host
|
|
|
|
: 0),
|
|
|
|
handler_(handler), session_(nullptr), settings_timerev_(nullptr) {
|
2014-09-18 16:03:36 +02:00
|
|
|
reset_timeouts();
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-08-22 13:59:50 +02:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
nghttp2_session_callbacks *callbacks;
|
|
|
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
|
|
|
|
|
|
|
assert(rv == 0);
|
|
|
|
|
2014-09-16 16:39:38 +02:00
|
|
|
auto callbacks_deleter =
|
2014-11-27 15:39:04 +01:00
|
|
|
util::defer(callbacks, nghttp2_session_callbacks_del);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_stream_close_callback(
|
|
|
|
callbacks, on_stream_close_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
|
|
|
on_frame_recv_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
|
|
|
callbacks, on_data_chunk_recv_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
|
|
|
on_frame_send_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
|
|
|
callbacks, on_frame_not_send_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
|
|
|
on_header_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
|
|
|
callbacks, on_begin_headers_callback);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->padding) {
|
|
|
|
nghttp2_session_callbacks_set_select_padding_callback(
|
|
|
|
callbacks, http::select_padding_callback);
|
2014-02-11 09:23:22 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-08-22 13:59:50 +02:00
|
|
|
rv = nghttp2_session_server_new2(&session_, callbacks, this,
|
2014-04-04 14:57:47 +02:00
|
|
|
get_config()->http2_option);
|
|
|
|
|
2013-07-26 12:33:25 +02:00
|
|
|
assert(rv == 0);
|
|
|
|
|
|
|
|
flow_control_ = true;
|
2012-06-09 18:36:30 +02:00
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
// TODO Maybe call from outside?
|
2014-02-16 08:05:26 +01:00
|
|
|
nghttp2_settings_entry entry[2];
|
2013-07-12 17:19:03 +02:00
|
|
|
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
2013-11-04 10:14:05 +01:00
|
|
|
entry[0].value = get_config()->http2_max_concurrent_streams;
|
2012-06-09 18:36:30 +02:00
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
2013-11-12 03:08:43 +01:00
|
|
|
entry[1].value = (1 << get_config()->http2_upstream_window_bits) - 1;
|
2012-06-09 18:36:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry,
|
|
|
|
util::array_size(entry));
|
|
|
|
if (rv != 0) {
|
2014-04-03 06:20:50 +02:00
|
|
|
ULOG(ERROR, this) << "nghttp2_submit_settings() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
|
|
|
}
|
2013-11-20 16:15:17 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->http2_upstream_connection_window_bits > 16) {
|
|
|
|
int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) -
|
|
|
|
1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
2013-11-20 16:15:17 +01:00
|
|
|
rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta);
|
2014-04-03 06:20:50 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-04-03 06:20:50 +02:00
|
|
|
ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->altsvcs.empty()) {
|
2014-04-03 06:20:50 +02:00
|
|
|
// Set max_age to 24hrs, which is default for alt-svc header
|
|
|
|
// field.
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &altsvc : get_config()->altsvcs) {
|
|
|
|
rv = nghttp2_submit_altsvc(
|
|
|
|
session_, NGHTTP2_FLAG_NONE, 0, 86400, altsvc.port,
|
|
|
|
reinterpret_cast<const uint8_t *>(altsvc.protocol_id),
|
|
|
|
altsvc.protocol_id_len,
|
|
|
|
reinterpret_cast<const uint8_t *>(altsvc.host), altsvc.host_len,
|
|
|
|
reinterpret_cast<const uint8_t *>(altsvc.origin), altsvc.origin_len);
|
|
|
|
|
|
|
|
if (rv != 0) {
|
2014-04-08 15:28:50 +02:00
|
|
|
ULOG(ERROR, this) << "nghttp2_submit_altsvc() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
|
|
|
}
|
2014-04-03 06:20:50 +02:00
|
|
|
}
|
2013-11-20 16:15:17 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
Http2Upstream::~Http2Upstream() {
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_session_del(session_);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (settings_timerev_) {
|
2013-10-30 16:44:23 +01:00
|
|
|
event_free(settings_timerev_);
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_read() {
|
2014-01-18 08:50:52 +01:00
|
|
|
ssize_t rv = 0;
|
|
|
|
auto bev = handler_->get_bev();
|
|
|
|
auto input = bufferevent_get_input(bev);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (;;) {
|
2014-06-01 14:01:01 +02:00
|
|
|
auto inputlen = evbuffer_get_contiguous_space(input);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (inputlen == 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
assert(evbuffer_get_length(input) == 0);
|
|
|
|
|
|
|
|
return send();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto mem = evbuffer_pullup(input, inputlen);
|
|
|
|
|
|
|
|
rv = nghttp2_session_mem_recv(session_, mem, inputlen);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
ULOG(ERROR, this) << "nghttp2_session_recv() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_drain(input, rv) != 0) {
|
2014-06-01 14:01:01 +02:00
|
|
|
DCLOG(FATAL, this) << "evbuffer_drain() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
2014-01-18 08:50:52 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_write() { return send(); }
|
2012-06-04 20:11:43 +02:00
|
|
|
|
2012-06-12 14:56:41 +02:00
|
|
|
// After this function call, downstream may be deleted.
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::send() {
|
2014-02-18 15:23:11 +01:00
|
|
|
int rv;
|
2014-03-15 08:10:42 +01:00
|
|
|
uint8_t buf[16384];
|
2014-02-18 15:23:11 +01:00
|
|
|
auto bev = handler_->get_bev();
|
|
|
|
auto output = bufferevent_get_output(bev);
|
2014-11-05 16:56:07 +01:00
|
|
|
|
|
|
|
sendbuf.reset(output, buf, sizeof(buf), handler_->get_write_limit());
|
2014-11-27 15:39:04 +01:00
|
|
|
for (;;) {
|
2014-05-16 14:42:30 +02:00
|
|
|
// Check buffer length and break if it is large enough.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (handler_->get_outbuf_length() + sendbuf.get_buflen() >=
|
|
|
|
OUTBUF_MAX_THRES) {
|
2014-02-18 15:23:11 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint8_t *data;
|
|
|
|
auto datalen = nghttp2_session_mem_send(session_, &data);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (datalen < 0) {
|
2014-02-18 15:23:11 +01:00
|
|
|
ULOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
|
|
|
|
<< nghttp2_strerror(datalen);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (datalen == 0) {
|
2014-02-18 15:23:11 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-11-05 16:56:07 +01:00
|
|
|
rv = sendbuf.add(data, datalen);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-03-04 16:23:33 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
|
|
|
return -1;
|
2014-02-18 15:23:11 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-03-03 13:18:24 +01:00
|
|
|
|
2014-11-05 16:56:07 +01:00
|
|
|
rv = sendbuf.flush();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-03-03 13:18:24 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-05 16:56:07 +01:00
|
|
|
handler_->update_warmup_writelen(sendbuf.get_writelen());
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nghttp2_session_want_read(session_) == 0 &&
|
|
|
|
nghttp2_session_want_write(session_) == 0 &&
|
|
|
|
handler_->get_outbuf_length() == 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-02-18 15:23:11 +01:00
|
|
|
ULOG(INFO, this) << "No more read/write for this HTTP2 session";
|
2012-07-18 18:59:55 +02:00
|
|
|
}
|
2014-02-18 15:23:11 +01:00
|
|
|
return -1;
|
2012-07-18 18:59:55 +02:00
|
|
|
}
|
2014-02-18 15:23:11 +01:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_event() { return 0; }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
ClientHandler *Http2Upstream::get_client_handler() const { return handler_; }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void downstream_readcb(bufferevent *bev, void *ptr) {
|
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2013-08-31 17:23:07 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<Http2Upstream *>(downstream->get_upstream());
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
2013-11-04 10:22:29 +01:00
|
|
|
// If upstream HTTP2 stream was closed, we just close downstream,
|
2012-06-09 16:14:00 +02:00
|
|
|
// because there is no consumer now. Downstream connection is also
|
|
|
|
// closed in this case.
|
2012-06-05 15:46:47 +02:00
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
|
|
|
|
2012-06-05 15:46:47 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-02-09 15:20:29 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_RESET) {
|
2013-02-09 15:20:29 +01:00
|
|
|
// The downstream stream was reset (canceled). In this case,
|
|
|
|
// RST_STREAM to the upstream and delete downstream connection
|
|
|
|
// here. Deleting downstream will be taken place at
|
|
|
|
// on_stream_close_callback.
|
2014-11-27 15:39:04 +01:00
|
|
|
upstream->rst_stream(downstream,
|
|
|
|
infer_upstream_rst_stream_error_code(
|
|
|
|
downstream->get_response_rst_stream_error_code()));
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
dconn = nullptr;
|
2013-02-27 14:39:44 +01:00
|
|
|
} else {
|
2014-06-01 16:44:32 +02:00
|
|
|
auto rv = downstream->on_read();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-02-27 14:39:44 +01:00
|
|
|
DCLOG(INFO, dconn) << "HTTP parser failure";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
2013-07-12 17:19:03 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
2013-08-20 17:56:08 +02:00
|
|
|
// If response was completed, then don't issue RST_STREAM
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(downstream, 502) != 0) {
|
2013-02-27 14:39:44 +01:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2012-07-16 16:29:48 +02:00
|
|
|
}
|
2013-02-27 14:39:44 +01:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
// Clearly, we have to close downstream connection on http parser
|
|
|
|
// failure.
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
dconn = nullptr;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->send() != 0) {
|
2012-07-16 16:29:48 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2012-06-12 14:56:41 +02:00
|
|
|
// At this point, downstream may be deleted.
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void downstream_writecb(bufferevent *bev, void *ptr) {
|
|
|
|
if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
|
2012-11-21 19:13:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2014-08-21 14:22:16 +02:00
|
|
|
dconn->on_write();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void downstream_eventcb(bufferevent *bev, short events, void *ptr) {
|
|
|
|
auto dconn = static_cast<DownstreamConnection *>(ptr);
|
2013-08-31 17:23:07 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2014-11-27 15:39:04 +01:00
|
|
|
auto upstream = static_cast<Http2Upstream *>(downstream->get_upstream());
|
|
|
|
if (events & BEV_EVENT_CONNECTED) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Connection established. stream_id="
|
|
|
|
<< downstream->get_stream_id();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
auto fd = bufferevent_getfd(bev);
|
2013-01-10 16:11:41 +01:00
|
|
|
int val = 1;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&val),
|
|
|
|
sizeof(val)) == -1) {
|
2014-11-08 02:51:56 +01:00
|
|
|
DCLOG(WARN, dconn) << "Setting option TCP_NODELAY failed: errno="
|
|
|
|
<< errno;
|
2013-01-25 14:00:33 +01:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & BEV_EVENT_EOF) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "EOF. stream_id=" << downstream->get_stream_id();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
2012-06-09 16:14:00 +02:00
|
|
|
// If stream was closed already, we don't need to send reply at
|
2012-06-04 20:11:43 +02:00
|
|
|
// the first place. We can delete downstream.
|
2012-06-04 16:48:31 +02:00
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
2013-02-10 18:05:11 +01:00
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete downstream connection. If we don't delete it here, it
|
|
|
|
// will be pooled in on_stream_close_callback.
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
dconn = nullptr;
|
|
|
|
// downstream wil be deleted in on_stream_close_callback.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// Server may indicate the end of the request by EOF
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-06-01 16:44:32 +02:00
|
|
|
ULOG(INFO, upstream) << "Downstream body was ended by EOF";
|
2012-06-04 20:11:43 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
|
|
|
|
|
|
|
// For tunneled connection, MSG_COMPLETE signals
|
|
|
|
// downstream_data_read_callback to send RST_STREAM after
|
|
|
|
// pending response body is sent. This is needed to ensure
|
|
|
|
// that RST_STREAM is sent after all pending data are sent.
|
|
|
|
upstream->on_downstream_body_complete(downstream);
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// If stream was not closed, then we set MSG_COMPLETE and let
|
|
|
|
// on_stream_close_callback delete downstream.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(downstream, 502) != 0) {
|
2012-07-16 16:29:48 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->send() != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// At this point, downstream may be deleted.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
if (events & BEV_EVENT_ERROR) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Downstream network error: "
|
2014-11-27 15:39:04 +01:00
|
|
|
<< evutil_socket_error_to_string(
|
|
|
|
EVUTIL_SOCKET_ERROR());
|
2012-07-15 14:15:28 +02:00
|
|
|
} else {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Timeout";
|
2012-07-15 14:15:28 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
2012-12-09 11:15:14 +01:00
|
|
|
DCLOG(INFO, dconn) << "Note: this is tunnel connection";
|
2012-07-15 14:15:28 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
2012-06-04 20:11:43 +02:00
|
|
|
upstream->remove_downstream(downstream);
|
2014-08-18 15:59:31 +02:00
|
|
|
// downstream was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete downstream connection. If we don't delete it here, it
|
|
|
|
// will be pooled in on_stream_close_callback.
|
2014-08-18 17:16:51 +02:00
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
// dconn was deleted
|
2014-06-01 16:44:32 +02:00
|
|
|
dconn = nullptr;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2014-06-01 16:44:32 +02:00
|
|
|
// For SSL tunneling, we issue RST_STREAM. For other types of
|
|
|
|
// stream, we don't have to do anything since response was
|
|
|
|
// complete.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
2014-07-03 15:59:49 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
|
2014-06-01 16:44:32 +02:00
|
|
|
}
|
2012-06-04 20:11:43 +02:00
|
|
|
} else {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
|
|
|
if (downstream->get_upgraded()) {
|
2014-11-17 16:03:52 +01:00
|
|
|
upstream->on_downstream_body_complete(downstream);
|
|
|
|
} else {
|
|
|
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
|
|
|
}
|
2012-07-11 09:20:16 +02:00
|
|
|
} else {
|
2014-06-01 16:44:32 +02:00
|
|
|
unsigned int status;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (events & BEV_EVENT_TIMEOUT) {
|
2014-06-01 16:44:32 +02:00
|
|
|
status = 504;
|
2012-06-04 16:48:31 +02:00
|
|
|
} else {
|
2014-06-01 16:44:32 +02:00
|
|
|
status = 502;
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->error_reply(downstream, status) != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2012-07-16 16:29:48 +02:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (upstream->send() != 0) {
|
2014-06-01 16:44:32 +02:00
|
|
|
delete upstream->get_client_handler();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// At this point, downstream may be deleted.
|
|
|
|
return;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::rst_stream(Downstream *downstream, uint32_t error_code) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, this) << "RST_STREAM stream_id=" << downstream->get_stream_id()
|
|
|
|
<< " with error_code=" << error_code;
|
2012-06-09 18:36:30 +02:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
int rv;
|
2013-10-25 15:50:24 +02:00
|
|
|
rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
|
|
|
|
downstream->get_stream_id(), error_code);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < NGHTTP2_ERR_FATAL) {
|
2013-07-12 17:19:03 +02:00
|
|
|
ULOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2012-06-04 16:48:31 +02:00
|
|
|
DIE();
|
|
|
|
}
|
2012-07-27 15:11:13 +02:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::terminate_session(uint32_t error_code) {
|
2013-10-30 16:44:23 +01:00
|
|
|
int rv;
|
2013-12-25 16:23:07 +01:00
|
|
|
rv = nghttp2_session_terminate_session(session_, error_code);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2013-10-30 16:44:23 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
namespace {
|
2013-11-04 09:53:57 +01:00
|
|
|
ssize_t downstream_data_read_callback(nghttp2_session *session,
|
2014-11-27 15:39:04 +01:00
|
|
|
int32_t stream_id, uint8_t *buf,
|
|
|
|
size_t length, uint32_t *data_flags,
|
2013-11-04 09:53:57 +01:00
|
|
|
nghttp2_data_source *source,
|
2014-11-27 15:39:04 +01:00
|
|
|
void *user_data) {
|
|
|
|
auto downstream = static_cast<Downstream *>(source->ptr);
|
|
|
|
auto upstream = static_cast<Http2Upstream *>(downstream->get_upstream());
|
2013-08-31 17:23:07 +02:00
|
|
|
auto body = downstream->get_response_body_buf();
|
2014-11-06 13:14:14 +01:00
|
|
|
auto handler = upstream->get_client_handler();
|
2012-06-04 16:48:31 +02:00
|
|
|
assert(body);
|
2014-04-03 11:54:15 +02:00
|
|
|
|
2014-11-06 13:14:14 +01:00
|
|
|
auto limit = handler->get_write_limit();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (limit != -1) {
|
2014-11-06 13:14:14 +01:00
|
|
|
// 9 is HTTP/2 frame header length. Make DATA frame also under
|
|
|
|
// certain limit, so that application layer can flush at DATA
|
|
|
|
// frame boundary, instead of buffering large frame.
|
|
|
|
assert(limit > 9);
|
|
|
|
length = std::min(length, static_cast<size_t>(limit - 9));
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
int nread = evbuffer_remove(body, buf, length);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == -1) {
|
2013-12-20 15:28:54 +01:00
|
|
|
ULOG(FATAL, upstream) << "evbuffer_remove() failed";
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-04-03 11:54:15 +02:00
|
|
|
|
2014-11-22 09:32:59 +01:00
|
|
|
auto body_empty = evbuffer_get_length(body) == 0;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (body_empty &&
|
|
|
|
downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-17 16:03:52 +01:00
|
|
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!downstream->get_upgraded()) {
|
2014-07-02 16:56:26 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
|
2014-07-02 16:56:26 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
|
|
|
|
}
|
2013-02-10 18:05:11 +01:00
|
|
|
} else {
|
|
|
|
// For tunneling, issue RST_STREAM to finish the stream.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
ULOG(INFO, upstream)
|
|
|
|
<< "RST_STREAM to tunneled stream stream_id=" << stream_id;
|
2013-02-10 18:05:11 +01:00
|
|
|
}
|
2014-07-03 15:59:49 +02:00
|
|
|
upstream->rst_stream(downstream, NGHTTP2_NO_ERROR);
|
2013-02-10 18:05:11 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-05-16 14:42:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (body_empty) {
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_upstream_wtimer();
|
2014-11-22 09:32:59 +01:00
|
|
|
} else {
|
|
|
|
downstream->reset_upstream_wtimer();
|
2014-08-09 11:47:45 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread > 0 && downstream->resume_read(SHRPX_NO_BUFFER, nread) != 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
2014-05-16 14:42:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread == 0 && ((*data_flags) & NGHTTP2_DATA_FLAG_EOF) == 0) {
|
2014-08-21 14:22:16 +02:00
|
|
|
return NGHTTP2_ERR_DEFERRED;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-08-21 14:22:16 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nread > 0) {
|
2014-11-18 16:56:44 +01:00
|
|
|
downstream->add_response_sent_bodylen(nread);
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-09-08 07:29:18 +02:00
|
|
|
int Http2Upstream::error_reply(Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
unsigned int status_code) {
|
2012-06-04 16:48:31 +02:00
|
|
|
int rv;
|
2013-08-31 17:23:07 +02:00
|
|
|
auto html = http::create_error_html(status_code);
|
2014-07-05 11:22:40 +02:00
|
|
|
downstream->set_response_http_status(status_code);
|
2012-06-04 16:48:31 +02:00
|
|
|
downstream->init_response_body_buf();
|
2013-08-31 17:23:07 +02:00
|
|
|
auto body = downstream->get_response_body_buf();
|
2012-06-04 16:48:31 +02:00
|
|
|
rv = evbuffer_add(body, html.c_str(), html.size());
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv == -1) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:29:48 +02:00
|
|
|
return -1;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
2012-07-27 15:11:13 +02:00
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_data_provider data_prd;
|
2012-06-04 16:48:31 +02:00
|
|
|
data_prd.source.ptr = downstream;
|
2013-11-04 09:53:57 +01:00
|
|
|
data_prd.read_callback = downstream_data_read_callback;
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2013-08-31 17:23:07 +02:00
|
|
|
auto content_length = util::utos(html.size());
|
2013-09-08 07:29:18 +02:00
|
|
|
auto status_code_str = util::utos(status_code);
|
2013-12-08 13:19:33 +01:00
|
|
|
auto nva = std::vector<nghttp2_nv>{
|
2014-11-27 15:39:04 +01:00
|
|
|
http2::make_nv_ls(":status", status_code_str),
|
|
|
|
http2::make_nv_ll("content-type", "text/html; charset=UTF-8"),
|
|
|
|
http2::make_nv_lc("server", get_config()->server_name),
|
|
|
|
http2::make_nv_ls("content-length", content_length)};
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2013-12-08 13:19:33 +01:00
|
|
|
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
|
|
|
nva.data(), nva.size(), &data_prd);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv < NGHTTP2_ERR_FATAL) {
|
2013-07-12 17:19:03 +02:00
|
|
|
ULOG(FATAL, this) << "nghttp2_submit_response() failed: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2012-06-04 16:48:31 +02:00
|
|
|
DIE();
|
|
|
|
}
|
2014-07-02 16:56:26 +02:00
|
|
|
|
2012-07-27 15:11:13 +02:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_data_cb Http2Upstream::get_downstream_readcb() {
|
2013-11-04 09:53:57 +01:00
|
|
|
return downstream_readcb;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_data_cb Http2Upstream::get_downstream_writecb() {
|
2013-11-04 09:53:57 +01:00
|
|
|
return downstream_writecb;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bufferevent_event_cb Http2Upstream::get_downstream_eventcb() {
|
2013-11-04 09:53:57 +01:00
|
|
|
return downstream_eventcb;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void
|
|
|
|
Http2Upstream::add_pending_downstream(std::unique_ptr<Downstream> downstream) {
|
2014-08-18 15:59:31 +02:00
|
|
|
downstream_queue_.add_pending(std::move(downstream));
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Upstream::remove_downstream(Downstream *downstream) {
|
|
|
|
if (downstream->accesslog_ready()) {
|
2014-11-18 16:56:44 +01:00
|
|
|
handler_->write_accesslog(downstream);
|
|
|
|
}
|
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
auto next_downstream =
|
|
|
|
downstream_queue_.remove_and_pop_blocked(downstream->get_stream_id());
|
2014-08-16 14:29:20 +02:00
|
|
|
|
2014-12-04 17:07:00 +01:00
|
|
|
if (next_downstream) {
|
|
|
|
initiate_downstream(std::move(next_downstream));
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
Downstream *Http2Upstream::find_downstream(int32_t stream_id) {
|
2012-06-07 17:36:19 +02:00
|
|
|
return downstream_queue_.find(stream_id);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session *Http2Upstream::get_http2_session() { return session_; }
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
// WARNING: Never call directly or indirectly nghttp2_session_send or
|
|
|
|
// nghttp2_session_recv. These calls may delete downstream.
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
2014-07-23 16:32:57 +02:00
|
|
|
int rv;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
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-07-23 16:32:57 +02:00
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
downstream->normalize_response_headers();
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
|
|
|
|
!get_config()->no_location_rewrite) {
|
|
|
|
downstream->rewrite_norm_location_response_header(
|
|
|
|
get_client_handler()->get_upstream_scheme(), get_config()->port);
|
2013-12-28 09:02:43 +01:00
|
|
|
}
|
2014-07-12 11:55:08 +02:00
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
auto end_headers = std::end(downstream->get_response_headers());
|
2012-06-04 16:48:31 +02:00
|
|
|
size_t nheader = downstream->get_response_headers().size();
|
2013-11-28 13:36:04 +01:00
|
|
|
auto nva = std::vector<nghttp2_nv>();
|
2014-08-14 15:45:21 +02:00
|
|
|
// 3 means :status and possible server and via header field.
|
|
|
|
nva.reserve(nheader + 3 + get_config()->add_response_headers.size());
|
2012-06-06 19:29:00 +02:00
|
|
|
std::string via_value;
|
2013-09-08 07:29:18 +02:00
|
|
|
auto response_status = util::utos(downstream->get_response_http_status());
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls(":status", response_status));
|
2013-08-27 17:09:46 +02:00
|
|
|
|
2013-11-28 13:36:04 +01:00
|
|
|
http2::copy_norm_headers_to_nva(nva, downstream->get_response_headers());
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_non_final_response()) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-25 17:40:06 +02:00
|
|
|
log_response_headers(downstream, nva);
|
|
|
|
}
|
|
|
|
|
2014-07-23 16:32:57 +02:00
|
|
|
rv = nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE,
|
|
|
|
downstream->get_stream_id(), nullptr,
|
|
|
|
nva.data(), nva.size(), nullptr);
|
|
|
|
|
|
|
|
downstream->clear_response_headers();
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-07-23 16:32:57 +02:00
|
|
|
ULOG(FATAL, this) << "nghttp2_submit_headers() failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
|
2014-08-14 15:45:21 +02:00
|
|
|
nva.push_back(http2::make_nv_lc("server", get_config()->server_name));
|
|
|
|
} else {
|
|
|
|
auto server = downstream->get_norm_response_header("server");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (server != end_headers) {
|
2014-08-14 15:45:21 +02:00
|
|
|
nva.push_back(http2::make_nv_ls("server", (*server).value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 17:09:46 +02:00
|
|
|
auto via = downstream->get_norm_response_header("via");
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->no_via) {
|
|
|
|
if (via != end_headers) {
|
2014-04-03 04:22:11 +02:00
|
|
|
nva.push_back(http2::make_nv_ls("via", (*via).value));
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2013-08-27 17:09:46 +02:00
|
|
|
} else {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (via != end_headers) {
|
2014-04-03 04:22:11 +02:00
|
|
|
via_value = (*via).value;
|
2013-01-09 14:01:25 +01:00
|
|
|
via_value += ", ";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
via_value += http::create_via_header_value(
|
|
|
|
downstream->get_response_major(), downstream->get_response_minor());
|
2013-12-08 14:31:43 +01:00
|
|
|
nva.push_back(http2::make_nv_ls("via", via_value));
|
2012-06-06 19:29:00 +02:00
|
|
|
}
|
2014-04-26 07:56:08 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &p : get_config()->add_response_headers) {
|
2014-04-26 07:56:08 +02:00
|
|
|
nva.push_back(http2::make_nv(p.first, p.second));
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-25 17:40:06 +02:00
|
|
|
log_response_headers(downstream, nva);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2013-11-17 15:52:19 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (get_config()->http2_upstream_dump_response_header) {
|
2013-11-17 15:52:19 +01:00
|
|
|
http2::dump_nv(get_config()->http2_upstream_dump_response_header,
|
2013-11-28 13:36:04 +01:00
|
|
|
nva.data(), nva.size());
|
2013-11-17 15:52:19 +01:00
|
|
|
}
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_data_provider data_prd;
|
2012-06-04 16:48:31 +02:00
|
|
|
data_prd.source.ptr = downstream;
|
2013-11-04 09:53:57 +01:00
|
|
|
data_prd.read_callback = downstream_data_read_callback;
|
2012-06-04 16:48:31 +02:00
|
|
|
|
2014-07-25 16:13:27 +02:00
|
|
|
nghttp2_data_provider *data_prdptr;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->expect_response_body()) {
|
2014-07-25 16:13:27 +02:00
|
|
|
data_prdptr = &data_prd;
|
|
|
|
} else {
|
|
|
|
data_prdptr = nullptr;
|
|
|
|
}
|
|
|
|
|
2013-12-08 13:19:33 +01:00
|
|
|
rv = nghttp2_submit_response(session_, downstream->get_stream_id(),
|
2014-11-27 15:39:04 +01:00
|
|
|
nva.data(), nva.size(), data_prdptr);
|
|
|
|
if (rv != 0) {
|
2013-07-12 17:19:03 +02:00
|
|
|
ULOG(FATAL, this) << "nghttp2_submit_response() 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;
|
|
|
|
}
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
// WARNING: Never call directly or indirectly nghttp2_session_send or
|
|
|
|
// nghttp2_session_recv. These calls may delete downstream.
|
2013-07-26 12:38:54 +02:00
|
|
|
int Http2Upstream::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) {
|
2013-08-31 17:23:07 +02:00
|
|
|
auto body = downstream->get_response_body_buf();
|
2012-07-16 16:03:07 +02:00
|
|
|
int rv = evbuffer_add(body, data, len);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-12-09 11:15:14 +01:00
|
|
|
ULOG(FATAL, this) << "evbuffer_add() failed";
|
2012-07-16 16:03:07 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-04-03 11:54:15 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (flush) {
|
2014-04-03 11:54:15 +02:00
|
|
|
nghttp2_session_resume_data(session_, downstream->get_stream_id());
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream->ensure_upstream_wtimer();
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
2012-06-04 20:11:43 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (evbuffer_get_length(body) >= INBUF_MAX_THRES) {
|
|
|
|
if (!flush) {
|
2014-04-03 11:54:15 +02:00
|
|
|
nghttp2_session_resume_data(session_, downstream->get_stream_id());
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
downstream->ensure_upstream_wtimer();
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
|
|
|
|
2012-06-04 20:11:43 +02:00
|
|
|
downstream->pause_read(SHRPX_NO_BUFFER);
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
// WARNING: Never call directly or indirectly nghttp2_session_send or
|
|
|
|
// nghttp2_session_recv. These calls may delete downstream.
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_downstream_body_complete(Downstream *downstream) {
|
|
|
|
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
|
|
|
}
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_session_resume_data(session_, downstream->get_stream_id());
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->ensure_upstream_wtimer();
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
bool Http2Upstream::get_flow_control() const { return flow_control_; }
|
2012-06-09 18:36:30 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Upstream::pause_read(IOCtrlReason reason) {}
|
2012-11-18 13:23:13 +01:00
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
int Http2Upstream::resume_read(IOCtrlReason reason, Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
size_t consumed) {
|
|
|
|
if (get_flow_control()) {
|
2014-08-21 14:22:16 +02:00
|
|
|
assert(downstream->get_request_datalen() >= consumed);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (consume(downstream->get_stream_id(), consumed) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return -1;
|
2013-02-08 13:46:58 +01:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-08-21 14:22:16 +02:00
|
|
|
downstream->dec_request_datalen(consumed);
|
2013-02-08 13:46:58 +01:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2013-02-27 14:55:44 +01:00
|
|
|
return send();
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2012-11-18 13:23:13 +01:00
|
|
|
|
2014-06-27 15:34:54 +02:00
|
|
|
int Http2Upstream::on_downstream_abort_request(Downstream *downstream,
|
2014-11-27 15:39:04 +01:00
|
|
|
unsigned int status_code) {
|
2014-06-27 15:34:54 +02:00
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = error_reply(downstream, status_code);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-06-27 15:34:54 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return send();
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::consume(int32_t stream_id, size_t len) {
|
2014-07-25 14:26:03 +02:00
|
|
|
int rv;
|
2014-07-02 16:07:46 +02:00
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
rv = nghttp2_session_consume(session_, stream_id, len);
|
2014-07-02 16:07:46 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-11-08 02:51:56 +01:00
|
|
|
ULOG(WARN, this) << "nghttp2_session_consume() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2014-07-25 14:26:03 +02:00
|
|
|
return -1;
|
2014-07-02 16:07:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void
|
|
|
|
Http2Upstream::log_response_headers(Downstream *downstream,
|
|
|
|
const std::vector<nghttp2_nv> &nva) const {
|
2014-07-25 17:40:06 +02:00
|
|
|
std::stringstream ss;
|
2014-11-27 15:39:04 +01:00
|
|
|
for (auto &nv : nva) {
|
2014-07-25 17:40:06 +02:00
|
|
|
ss << TTY_HTTP_HD;
|
2014-11-27 15:39:04 +01:00
|
|
|
ss.write(reinterpret_cast<const char *>(nv.name), nv.namelen);
|
2014-07-25 17:40:06 +02:00
|
|
|
ss << TTY_RST << ": ";
|
2014-11-27 15:39:04 +01:00
|
|
|
ss.write(reinterpret_cast<const char *>(nv.value), nv.valuelen);
|
2014-07-25 17:40:06 +02:00
|
|
|
ss << "\n";
|
|
|
|
}
|
|
|
|
ULOG(INFO, this) << "HTTP response headers. stream_id="
|
2014-11-27 15:39:04 +01:00
|
|
|
<< downstream->get_stream_id() << "\n" << ss.str();
|
2014-07-25 17:40:06 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Upstream::on_timeout(Downstream *downstream) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-08-09 11:47:45 +02:00
|
|
|
ULOG(INFO, this) << "Stream timeout stream_id="
|
|
|
|
<< downstream->get_stream_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
rst_stream(downstream, NGHTTP2_NO_ERROR);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Upstream::reset_timeouts() {
|
2014-09-18 16:03:36 +02:00
|
|
|
handler_->set_upstream_timeouts(&get_config()->http2_upstream_read_timeout,
|
|
|
|
&get_config()->upstream_write_timeout);
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Upstream::on_handler_delete() {
|
|
|
|
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
|
|
|
if (ent.second->accesslog_ready()) {
|
2014-11-23 09:24:23 +01:00
|
|
|
handler_->write_accesslog(ent.second.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 17:59:09 +01:00
|
|
|
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
int Http2Upstream::on_downstream_reset() {
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
|
|
|
auto downstream = ent.second.get();
|
|
|
|
if ((downstream->get_request_state() != Downstream::HEADER_COMPLETE &&
|
|
|
|
downstream->get_request_state() != Downstream::MSG_COMPLETE) ||
|
|
|
|
downstream->get_response_state() != Downstream::INITIAL) {
|
|
|
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// downstream connection is clean; we can retry with new
|
|
|
|
// downstream connection.
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
|
|
|
|
rv = downstream->attach_downstream_connection(
|
|
|
|
handler_->get_downstream_connection());
|
|
|
|
if (rv != 0) {
|
|
|
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
|
|
|
downstream->pop_downstream_connection();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = send();
|
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
} // namespace shrpx
|