2012-11-20 17:29:39 +01:00
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
* nghttp2 - HTTP/2 C Library
|
2012-11-20 17:29:39 +01: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-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_session.h"
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2013-01-10 16:11:41 +01:00
|
|
|
#include <netinet/tcp.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2018-06-09 09:21:30 +02:00
|
|
|
# include <unistd.h>
|
2015-05-13 15:30:35 +02:00
|
|
|
#endif // HAVE_UNISTD_H
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2012-11-20 17:29:39 +01:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <openssl/err.h>
|
|
|
|
|
|
|
|
#include "shrpx_upstream.h"
|
|
|
|
#include "shrpx_downstream.h"
|
|
|
|
#include "shrpx_config.h"
|
|
|
|
#include "shrpx_error.h"
|
2013-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_downstream_connection.h"
|
2012-11-20 17:29:39 +01:00
|
|
|
#include "shrpx_client_handler.h"
|
2017-04-01 08:07:32 +02:00
|
|
|
#include "shrpx_tls.h"
|
2013-08-25 18:25:31 +02:00
|
|
|
#include "shrpx_http.h"
|
2015-03-10 13:54:29 +01:00
|
|
|
#include "shrpx_worker.h"
|
2015-03-10 15:11:22 +01:00
|
|
|
#include "shrpx_connect_blocker.h"
|
2017-02-16 14:46:22 +01:00
|
|
|
#include "shrpx_log.h"
|
2013-08-27 19:47:22 +02:00
|
|
|
#include "http2.h"
|
2012-11-20 17:29:39 +01:00
|
|
|
#include "util.h"
|
2013-02-09 08:42:01 +01:00
|
|
|
#include "base64.h"
|
2017-04-01 07:47:36 +02:00
|
|
|
#include "tls.h"
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2013-07-12 17:19:03 +02:00
|
|
|
using namespace nghttp2;
|
2012-11-20 17:29:39 +01:00
|
|
|
|
|
|
|
namespace shrpx {
|
|
|
|
|
2015-03-09 13:22:31 +01:00
|
|
|
namespace {
|
2017-03-15 15:12:50 +01:00
|
|
|
constexpr ev_tstamp CONNCHK_TIMEOUT = 5.;
|
|
|
|
constexpr ev_tstamp CONNCHK_PING_TIMEOUT = 1.;
|
2015-03-09 13:22:31 +01:00
|
|
|
} // namespace
|
|
|
|
|
2016-02-07 10:54:44 +01:00
|
|
|
namespace {
|
|
|
|
constexpr size_t MAX_BUFFER_SIZE = 32_k;
|
|
|
|
} // namespace
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
namespace {
|
|
|
|
void connchk_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(w->data);
|
2015-03-09 13:22:31 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
ev_timer_stop(loop, w);
|
2015-03-09 13:22:31 +01:00
|
|
|
|
|
|
|
switch (http2session->get_connection_check_state()) {
|
2018-10-17 04:08:56 +02:00
|
|
|
case ConnectionCheck::STARTED:
|
2015-03-09 13:22:31 +01:00
|
|
|
// ping timeout; disconnect
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "ping timeout";
|
|
|
|
}
|
2016-02-27 11:39:03 +01:00
|
|
|
|
2016-04-12 16:30:52 +02:00
|
|
|
delete http2session;
|
|
|
|
|
2015-03-09 13:22:31 +01:00
|
|
|
return;
|
|
|
|
default:
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "connection check required";
|
|
|
|
}
|
2018-10-17 04:08:56 +02:00
|
|
|
http2session->set_connection_check_state(ConnectionCheck::REQUIRED);
|
2015-03-09 13:22:31 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(w->data);
|
2016-08-19 09:24:48 +02:00
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "SETTINGS timeout";
|
|
|
|
}
|
2016-05-19 16:12:34 +02:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(http2session->get_addr(), http2session->get_raddr());
|
2016-05-19 16:12:34 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
|
2016-04-12 16:30:52 +02:00
|
|
|
delete http2session;
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
http2session->signal_write();
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
2015-02-04 13:15:58 +01:00
|
|
|
auto conn = static_cast<Connection *>(w->data);
|
|
|
|
auto http2session = static_cast<Http2Session *>(conn->data);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2016-11-25 16:00:32 +01:00
|
|
|
if (w == &conn->rt && !conn->expired_rt()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "Timeout";
|
|
|
|
}
|
|
|
|
|
2016-05-24 14:59:24 +02:00
|
|
|
http2session->on_timeout();
|
|
|
|
|
2016-04-12 16:30:52 +02:00
|
|
|
delete http2session;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void readcb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
|
|
int rv;
|
2015-02-04 13:15:58 +01:00
|
|
|
auto conn = static_cast<Connection *>(w->data);
|
|
|
|
auto http2session = static_cast<Http2Session *>(conn->data);
|
2014-12-27 18:59:06 +01:00
|
|
|
rv = http2session->do_read();
|
|
|
|
if (rv != 0) {
|
2016-04-12 16:30:52 +02:00
|
|
|
delete http2session;
|
|
|
|
|
2015-02-10 16:44:30 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-03-09 13:22:31 +01:00
|
|
|
http2session->connection_alive();
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void writecb(struct ev_loop *loop, ev_io *w, int revents) {
|
|
|
|
int rv;
|
2015-02-04 13:15:58 +01:00
|
|
|
auto conn = static_cast<Connection *>(w->data);
|
|
|
|
auto http2session = static_cast<Http2Session *>(conn->data);
|
2014-12-27 18:59:06 +01:00
|
|
|
rv = http2session->do_write();
|
|
|
|
if (rv != 0) {
|
2016-04-12 16:30:52 +02:00
|
|
|
delete http2session;
|
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
return;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2015-03-10 16:27:51 +01:00
|
|
|
http2session->reset_connection_check_timer_if_not_checking();
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2016-04-02 17:23:44 +02:00
|
|
|
namespace {
|
|
|
|
void initiate_connection_cb(struct ev_loop *loop, ev_timer *w, int revents) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(w->data);
|
|
|
|
ev_timer_stop(loop, w);
|
|
|
|
if (http2session->initiate_connection() != 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "Could not initiate backend connection";
|
|
|
|
}
|
2016-04-12 16:30:52 +02:00
|
|
|
|
2016-04-02 17:23:44 +02:00
|
|
|
delete http2session;
|
2016-04-12 16:30:52 +02:00
|
|
|
|
2016-04-02 17:23:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2016-06-04 05:36:22 +02:00
|
|
|
namespace {
|
|
|
|
void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(w->data);
|
|
|
|
http2session->check_retire();
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2015-03-10 13:54:29 +01:00
|
|
|
Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
|
2016-06-02 18:20:49 +02:00
|
|
|
Worker *worker,
|
|
|
|
const std::shared_ptr<DownstreamAddrGroup> &group,
|
2016-04-02 16:11:03 +02:00
|
|
|
DownstreamAddr *addr)
|
2016-02-27 11:39:03 +01:00
|
|
|
: dlnext(nullptr),
|
|
|
|
dlprev(nullptr),
|
|
|
|
conn_(loop, -1, nullptr, worker->get_mcpool(),
|
2018-09-30 05:23:34 +02:00
|
|
|
group->shared_addr->timeout.write, group->shared_addr->timeout.read,
|
|
|
|
{}, {}, writecb, readcb, timeoutcb, this,
|
|
|
|
get_config()->tls.dyn_rec.warmup_threshold,
|
2018-10-16 15:17:37 +02:00
|
|
|
get_config()->tls.dyn_rec.idle_timeout, Proto::HTTP2),
|
2016-02-07 10:54:44 +01:00
|
|
|
wb_(worker->get_mcpool()),
|
2016-01-27 13:14:07 +01:00
|
|
|
worker_(worker),
|
|
|
|
ssl_ctx_(ssl_ctx),
|
2016-02-27 15:24:14 +01:00
|
|
|
group_(group),
|
2016-04-02 16:11:03 +02:00
|
|
|
addr_(addr),
|
2016-01-27 13:14:07 +01:00
|
|
|
session_(nullptr),
|
2016-12-04 15:43:41 +01:00
|
|
|
raddr_(nullptr),
|
2018-10-17 04:03:49 +02:00
|
|
|
state_(Http2SessionState::DISCONNECTED),
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_(ConnectionCheck::NONE),
|
2018-10-17 03:15:50 +02:00
|
|
|
freelist_zone_(FreelistZone::NONE),
|
2018-03-11 04:02:18 +01:00
|
|
|
settings_recved_(false),
|
|
|
|
allow_connect_proto_(false) {
|
2014-12-27 18:59:06 +01:00
|
|
|
read_ = write_ = &Http2Session::noop;
|
2016-02-07 11:20:17 +01:00
|
|
|
|
|
|
|
on_read_ = &Http2Session::read_noop;
|
|
|
|
on_write_ = &Http2Session::write_noop;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2022-01-13 21:21:56 +01:00
|
|
|
// We will reuse this many times, so use repeat timeout value. The
|
2015-03-09 13:22:31 +01:00
|
|
|
// timeout value is set later.
|
|
|
|
ev_timer_init(&connchk_timer_, connchk_timeout_cb, 0., 0.);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
connchk_timer_.data = this;
|
|
|
|
|
2022-01-13 21:21:56 +01:00
|
|
|
// SETTINGS ACK timeout is 10 seconds for now. We will reuse this
|
2014-12-27 18:59:06 +01:00
|
|
|
// many times, so use repeat timeout value.
|
2016-05-21 06:48:13 +02:00
|
|
|
ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
settings_timer_.data = this;
|
2016-04-02 17:23:44 +02:00
|
|
|
|
|
|
|
ev_timer_init(&initiate_connection_timer_, initiate_connection_cb, 0., 0.);
|
|
|
|
initiate_connection_timer_.data = this;
|
2016-06-04 05:36:22 +02:00
|
|
|
|
|
|
|
ev_prepare_init(&prep_, prepare_cb);
|
|
|
|
prep_.data = this;
|
|
|
|
ev_prepare_start(loop, &prep_);
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
|
2016-02-27 11:39:03 +01:00
|
|
|
Http2Session::~Http2Session() {
|
2016-04-12 16:30:52 +02:00
|
|
|
exclude_from_scheduling();
|
|
|
|
disconnect(should_hard_fail());
|
2016-02-27 11:39:03 +01:00
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2014-12-15 15:34:00 +01:00
|
|
|
int Http2Session::disconnect(bool hard) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
SSLOG(INFO, this) << "Disconnecting";
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2013-07-12 17:19:03 +02:00
|
|
|
nghttp2_session_del(session_);
|
2013-09-24 14:34:04 +02:00
|
|
|
session_ = nullptr;
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
wb_.reset();
|
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
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
if (dns_query_) {
|
|
|
|
auto dns_tracker = worker_->get_dns_tracker();
|
|
|
|
dns_tracker->cancel(dns_query_.get());
|
|
|
|
}
|
|
|
|
|
2015-02-14 16:24:52 +01:00
|
|
|
conn_.rlimit.stopw();
|
|
|
|
conn_.wlimit.stopw();
|
|
|
|
|
2016-06-04 05:36:22 +02:00
|
|
|
ev_prepare_stop(conn_.loop, &prep_);
|
|
|
|
|
2016-04-02 17:23:44 +02:00
|
|
|
ev_timer_stop(conn_.loop, &initiate_connection_timer_);
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_timer_stop(conn_.loop, &settings_timer_);
|
|
|
|
ev_timer_stop(conn_.loop, &connchk_timer_);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
read_ = write_ = &Http2Session::noop;
|
2016-02-07 11:20:17 +01:00
|
|
|
|
|
|
|
on_read_ = &Http2Session::read_noop;
|
|
|
|
on_write_ = &Http2Session::write_noop;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
conn_.disconnect();
|
2013-02-09 08:42:01 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (proxy_htp_) {
|
2013-09-23 17:19:53 +02:00
|
|
|
proxy_htp_.reset();
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_ = ConnectionCheck::NONE;
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::DISCONNECTED;
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2016-03-27 08:53:26 +02:00
|
|
|
// When deleting Http2DownstreamConnection, it calls this object's
|
2016-09-14 15:16:07 +02:00
|
|
|
// remove_downstream_connection(). The multiple
|
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
|
|
|
// Http2DownstreamConnection objects belong to the same
|
2017-12-17 05:23:37 +01:00
|
|
|
// ClientHandler object if upstream is h2. So be careful when you
|
|
|
|
// delete ClientHandler here.
|
2016-03-27 08:53:26 +02:00
|
|
|
//
|
|
|
|
// We allow creating new pending Http2DownstreamConnection with this
|
|
|
|
// object. Upstream::on_downstream_reset() may add
|
2016-09-14 15:16:07 +02:00
|
|
|
// Http2DownstreamConnection to another Http2Session.
|
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
|
|
|
|
2016-09-14 15:16:07 +02:00
|
|
|
for (auto dc = dconns_.head; dc;) {
|
|
|
|
auto next = dc->dlnext;
|
|
|
|
auto downstream = dc->get_downstream();
|
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
|
|
|
|
// Failure is allowed only for HTTP/1 upstream where upstream is
|
|
|
|
// not shared by multiple Downstreams.
|
|
|
|
if (upstream->on_downstream_reset(downstream, hard) != 0) {
|
|
|
|
delete upstream->get_client_handler();
|
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
|
|
|
}
|
2016-09-14 15:16:07 +02:00
|
|
|
|
|
|
|
// dc was deleted
|
|
|
|
dc = next;
|
2013-02-08 13:46:58 +01:00
|
|
|
}
|
|
|
|
|
2016-03-27 08:53:26 +02:00
|
|
|
auto streams = std::move(streams_);
|
2015-03-11 13:35:47 +01:00
|
|
|
for (auto s = streams.head; s;) {
|
|
|
|
auto next = s->dlnext;
|
2013-09-24 14:34:04 +02:00
|
|
|
delete s;
|
2015-03-11 13:35:47 +01:00
|
|
|
s = next;
|
2012-11-20 17:29:39 +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
|
|
|
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
int Http2Session::resolve_name() {
|
2018-10-15 16:02:44 +02:00
|
|
|
auto dns_query = std::make_unique<DNSQuery>(
|
2018-10-17 02:42:43 +02:00
|
|
|
addr_->host, [this](DNSResolverStatus status, const Address *result) {
|
2016-12-04 15:43:41 +01:00
|
|
|
int rv;
|
|
|
|
|
2018-10-17 02:42:43 +02:00
|
|
|
if (status == DNSResolverStatus::OK) {
|
2016-12-04 15:43:41 +01:00
|
|
|
*resolved_addr_ = *result;
|
|
|
|
util::set_port(*this->resolved_addr_, this->addr_->port);
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = this->initiate_connection();
|
|
|
|
if (rv != 0) {
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
});
|
2018-10-15 16:02:44 +02:00
|
|
|
resolved_addr_ = std::make_unique<Address>();
|
2016-12-04 15:43:41 +01:00
|
|
|
auto dns_tracker = worker_->get_dns_tracker();
|
2018-10-17 02:42:43 +02:00
|
|
|
switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
|
|
|
|
case DNSResolverStatus::ERROR:
|
2016-12-04 15:43:41 +01:00
|
|
|
return -1;
|
2018-10-17 02:42:43 +02:00
|
|
|
case DNSResolverStatus::RUNNING:
|
2016-12-04 15:43:41 +01:00
|
|
|
dns_query_ = std::move(dns_query);
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::RESOLVING_NAME;
|
2016-12-04 15:43:41 +01:00
|
|
|
return 0;
|
2018-10-17 02:42:43 +02:00
|
|
|
case DNSResolverStatus::OK:
|
2016-12-04 15:43:41 +01:00
|
|
|
util::set_port(*resolved_addr_, addr_->port);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
assert(0);
|
2017-05-18 16:10:44 +02:00
|
|
|
abort();
|
2016-12-04 15:43:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 12:44:36 +01:00
|
|
|
namespace {
|
|
|
|
int htp_hdrs_completecb(llhttp_t *htp);
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
constexpr llhttp_settings_t htp_hooks = {
|
|
|
|
nullptr, // llhttp_cb on_message_begin;
|
|
|
|
nullptr, // llhttp_data_cb on_url;
|
|
|
|
nullptr, // llhttp_data_cb on_status;
|
2022-11-09 10:00:00 +01:00
|
|
|
nullptr, // llhttp_data_cb on_method;
|
|
|
|
nullptr, // llhttp_data_cb on_version;
|
2018-11-23 12:44:36 +01:00
|
|
|
nullptr, // llhttp_data_cb on_header_field;
|
|
|
|
nullptr, // llhttp_data_cb on_header_value;
|
2022-11-09 10:00:00 +01:00
|
|
|
nullptr, // llhttp_data_cb on_chunk_extension_name;
|
|
|
|
nullptr, // llhttp_data_cb on_chunk_extension_value;
|
2018-11-23 12:44:36 +01:00
|
|
|
htp_hdrs_completecb, // llhttp_cb on_headers_complete;
|
|
|
|
nullptr, // llhttp_data_cb on_body;
|
|
|
|
nullptr, // llhttp_cb on_message_complete;
|
2022-11-09 10:00:00 +01:00
|
|
|
nullptr, // llhttp_cb on_url_complete;
|
|
|
|
nullptr, // llhttp_cb on_status_complete;
|
|
|
|
nullptr, // llhttp_cb on_method_complete;
|
|
|
|
nullptr, // llhttp_cb on_version_complete;
|
|
|
|
nullptr, // llhttp_cb on_header_field_complete;
|
|
|
|
nullptr, // llhttp_cb on_header_value_complete;
|
|
|
|
nullptr, // llhttp_cb on_chunk_extension_name_complete;
|
|
|
|
nullptr, // llhttp_cb on_chunk_extension_value_complete;
|
|
|
|
nullptr, // llhttp_cb on_chunk_header;
|
|
|
|
nullptr, // llhttp_cb on_chunk_complete;
|
|
|
|
nullptr, // llhttp_cb on_reset;
|
2018-11-23 12:44:36 +01:00
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Session::initiate_connection() {
|
2013-02-22 13:54:07 +01:00
|
|
|
int rv = 0;
|
2015-03-10 13:54:29 +01:00
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
auto worker_blocker = worker_->get_connect_blocker();
|
2015-07-09 19:52:11 +02:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::DISCONNECTED ||
|
|
|
|
state_ == Http2SessionState::RESOLVING_NAME) {
|
2016-02-21 07:27:19 +01:00
|
|
|
if (worker_blocker->blocked()) {
|
2016-02-21 08:11:50 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this)
|
|
|
|
<< "Worker wide backend connection was blocked temporarily";
|
|
|
|
}
|
2016-02-21 07:27:19 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2016-02-07 10:22:57 +01:00
|
|
|
}
|
2015-03-10 13:54:29 +01:00
|
|
|
|
2016-10-10 15:50:41 +02:00
|
|
|
auto &downstreamconf = *get_config()->conn.downstream;
|
|
|
|
|
2016-01-17 14:51:40 +01:00
|
|
|
const auto &proxy = get_config()->downstream_http_proxy;
|
2018-10-17 04:03:49 +02:00
|
|
|
if (!proxy.host.empty() && state_ == Http2SessionState::DISCONNECTED) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-01-17 14:51:40 +01:00
|
|
|
SSLOG(INFO, this) << "Connecting to the proxy " << proxy.host << ":"
|
|
|
|
<< proxy.port;
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2014-08-19 14:22:53 +02:00
|
|
|
|
2016-01-17 14:51:40 +01:00
|
|
|
conn_.fd = util::create_nonblock_socket(proxy.addr.su.storage.ss_family);
|
2014-08-19 14:22:53 +02:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (conn_.fd == -1) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
|
|
|
SSLOG(WARN, this) << "Backend proxy socket() failed; addr="
|
|
|
|
<< util::to_numeric_addr(&proxy.addr)
|
|
|
|
<< ", errno=" << error;
|
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
worker_blocker->on_failure();
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2013-09-24 16:17:53 +02:00
|
|
|
}
|
2013-02-09 08:42:01 +01:00
|
|
|
|
2016-01-17 14:51:40 +01:00
|
|
|
rv = connect(conn_.fd, &proxy.addr.su.sa, proxy.addr.len);
|
2014-12-27 18:59:06 +01:00
|
|
|
if (rv != 0 && errno != EINPROGRESS) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
|
|
|
SSLOG(WARN, this) << "Backend proxy connect() failed; addr="
|
|
|
|
<< util::to_numeric_addr(&proxy.addr)
|
|
|
|
<< ", errno=" << error;
|
|
|
|
|
2016-05-21 03:30:09 +02:00
|
|
|
worker_blocker->on_failure();
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
raddr_ = &proxy.addr;
|
|
|
|
|
2016-05-21 03:30:09 +02:00
|
|
|
worker_blocker->on_success();
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
|
|
|
|
ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
conn_.wlimit.startw();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2016-10-10 15:50:41 +02:00
|
|
|
conn_.wt.repeat = downstreamconf.timeout.connect;
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_timer_again(conn_.loop, &conn_.wt);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
write_ = &Http2Session::connected;
|
|
|
|
|
|
|
|
on_read_ = &Http2Session::downstream_read_proxy;
|
|
|
|
on_write_ = &Http2Session::downstream_connect_proxy;
|
|
|
|
|
2018-11-23 12:44:36 +01:00
|
|
|
proxy_htp_ = std::make_unique<llhttp_t>();
|
|
|
|
llhttp_init(proxy_htp_.get(), HTTP_RESPONSE, &htp_hooks);
|
2013-02-09 08:42:01 +01:00
|
|
|
proxy_htp_->data = this;
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::PROXY_CONNECTING;
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::DISCONNECTED ||
|
|
|
|
state_ == Http2SessionState::PROXY_CONNECTED ||
|
|
|
|
state_ == Http2SessionState::RESOLVING_NAME) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ != Http2SessionState::RESOLVING_NAME) {
|
2016-12-04 15:43:41 +01:00
|
|
|
SSLOG(INFO, this) << "Connecting to downstream server";
|
|
|
|
}
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2016-05-24 16:36:43 +02:00
|
|
|
if (addr_->tls) {
|
|
|
|
assert(ssl_ctx_);
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ != Http2SessionState::RESOLVING_NAME) {
|
2017-04-01 08:07:32 +02:00
|
|
|
auto ssl = tls::create_ssl(ssl_ctx_);
|
2016-12-04 15:43:41 +01:00
|
|
|
if (!ssl) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-07-25 15:22:17 +02:00
|
|
|
|
2017-04-01 08:07:32 +02:00
|
|
|
tls::setup_downstream_http2_alpn(ssl);
|
2016-02-28 08:56:14 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
conn_.set_ssl(ssl);
|
2017-03-28 14:06:28 +02:00
|
|
|
conn_.tls.client_session_cache = &addr_->tls_session_cache;
|
2012-11-22 13:51:11 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
auto sni_name =
|
|
|
|
addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
|
2013-03-29 14:06:33 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
if (!util::numeric_host(sni_name.c_str())) {
|
|
|
|
// TLS extensions: SNI. There is no documentation about the return
|
|
|
|
// code for this function (actually this is macro wrapping SSL_ctrl
|
|
|
|
// at the time of this writing).
|
|
|
|
SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
|
|
|
|
}
|
|
|
|
|
2017-04-01 08:07:32 +02:00
|
|
|
auto tls_session = tls::reuse_tls_session(addr_->tls_session_cache);
|
2016-12-04 15:43:41 +01:00
|
|
|
if (tls_session) {
|
|
|
|
SSL_set_session(conn_.tls.ssl, tls_session);
|
|
|
|
SSL_SESSION_free(tls_session);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::DISCONNECTED) {
|
2016-12-04 15:43:41 +01:00
|
|
|
if (addr_->dns) {
|
|
|
|
rv = resolve_name();
|
|
|
|
if (rv != 0) {
|
|
|
|
downstream_failure(addr_, nullptr);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::RESOLVING_NAME) {
|
2016-12-04 15:43:41 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
raddr_ = resolved_addr_.get();
|
|
|
|
} else {
|
|
|
|
raddr_ = &addr_->addr;
|
|
|
|
}
|
2013-02-22 13:54:07 +01:00
|
|
|
}
|
2016-02-27 15:40:04 +01:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::RESOLVING_NAME) {
|
2018-10-17 02:42:43 +02:00
|
|
|
if (dns_query_->status == DNSResolverStatus::ERROR) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, nullptr);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-17 02:42:43 +02:00
|
|
|
assert(dns_query_->status == DNSResolverStatus::OK);
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::DISCONNECTED;
|
2016-12-04 15:43:41 +01:00
|
|
|
dns_query_.reset();
|
|
|
|
raddr_ = resolved_addr_.get();
|
2016-02-27 15:40:04 +01:00
|
|
|
}
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
// If state_ == Http2SessionState::PROXY_CONNECTED, we have
|
|
|
|
// connected to the proxy using conn_.fd and tunnel has been
|
|
|
|
// established.
|
|
|
|
if (state_ == Http2SessionState::DISCONNECTED) {
|
2015-02-04 13:15:58 +01:00
|
|
|
assert(conn_.fd == -1);
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
2015-02-04 13:15:58 +01:00
|
|
|
if (conn_.fd == -1) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
|
|
|
SSLOG(WARN, this)
|
2016-12-04 15:43:41 +01:00
|
|
|
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
2016-02-21 08:11:50 +01:00
|
|
|
<< ", errno=" << error;
|
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
worker_blocker->on_failure();
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
worker_blocker->on_success();
|
|
|
|
|
2015-03-10 13:54:29 +01:00
|
|
|
rv = connect(conn_.fd,
|
|
|
|
// TODO maybe not thread-safe?
|
2016-12-04 15:43:41 +01:00
|
|
|
const_cast<sockaddr *>(&raddr_->su.sa), raddr_->len);
|
2014-12-27 18:59:06 +01:00
|
|
|
if (rv != 0 && errno != EINPROGRESS) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
2016-12-04 15:43:41 +01:00
|
|
|
SSLOG(WARN, this)
|
|
|
|
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
|
|
|
<< ", errno=" << error;
|
2016-02-21 08:11:50 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2015-02-14 16:24:52 +01:00
|
|
|
|
|
|
|
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
|
|
|
|
ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
2015-07-25 15:22:17 +02:00
|
|
|
conn_.prepare_client_handshake();
|
2013-02-22 13:54:07 +01:00
|
|
|
} else {
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::DISCONNECTED) {
|
2016-12-04 15:43:41 +01:00
|
|
|
// Without TLS and proxy.
|
|
|
|
if (addr_->dns) {
|
|
|
|
rv = resolve_name();
|
|
|
|
if (rv != 0) {
|
|
|
|
downstream_failure(addr_, nullptr);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::RESOLVING_NAME) {
|
2016-12-04 15:43:41 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
raddr_ = resolved_addr_.get();
|
|
|
|
} else {
|
|
|
|
raddr_ = &addr_->addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::RESOLVING_NAME) {
|
2018-10-17 02:42:43 +02:00
|
|
|
if (dns_query_->status == DNSResolverStatus::ERROR) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, nullptr);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-17 02:42:43 +02:00
|
|
|
assert(dns_query_->status == DNSResolverStatus::OK);
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::DISCONNECTED;
|
2016-12-04 15:43:41 +01:00
|
|
|
dns_query_.reset();
|
|
|
|
raddr_ = resolved_addr_.get();
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::DISCONNECTED) {
|
2014-08-12 15:22:02 +02:00
|
|
|
// Without TLS and proxy.
|
2015-02-04 13:15:58 +01:00
|
|
|
assert(conn_.fd == -1);
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
|
2014-08-19 14:22:53 +02:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (conn_.fd == -1) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
|
|
|
SSLOG(WARN, this)
|
2016-12-04 15:43:41 +01:00
|
|
|
<< "socket() failed; addr=" << util::to_numeric_addr(raddr_)
|
2016-02-21 08:11:50 +01:00
|
|
|
<< ", errno=" << error;
|
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
worker_blocker->on_failure();
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2014-08-19 14:22:53 +02:00
|
|
|
}
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2016-02-21 07:27:19 +01:00
|
|
|
worker_blocker->on_success();
|
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
rv = connect(conn_.fd, const_cast<sockaddr *>(&raddr_->su.sa),
|
|
|
|
raddr_->len);
|
2014-12-27 18:59:06 +01:00
|
|
|
if (rv != 0 && errno != EINPROGRESS) {
|
2016-02-21 08:11:50 +01:00
|
|
|
auto error = errno;
|
2016-12-04 15:43:41 +01:00
|
|
|
SSLOG(WARN, this)
|
|
|
|
<< "connect() failed; addr=" << util::to_numeric_addr(raddr_)
|
|
|
|
<< ", errno=" << error;
|
2016-02-21 08:11:50 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2015-02-14 16:24:52 +01:00
|
|
|
|
|
|
|
ev_io_set(&conn_.rev, conn_.fd, EV_READ);
|
|
|
|
ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
|
2013-02-22 13:54:07 +01:00
|
|
|
}
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2014-08-12 15:22:02 +02:00
|
|
|
|
2013-02-22 13:54:07 +01:00
|
|
|
// We have been already connected when no TLS and proxy is used.
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::PROXY_CONNECTED) {
|
2017-12-01 14:27:16 +01:00
|
|
|
on_read_ = &Http2Session::read_noop;
|
|
|
|
on_write_ = &Http2Session::write_noop;
|
|
|
|
|
2016-11-26 10:36:56 +01:00
|
|
|
return connected();
|
2013-02-22 13:54:07 +01:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2016-11-26 10:36:56 +01:00
|
|
|
write_ = &Http2Session::connected;
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::CONNECTING;
|
2016-11-26 10:36:56 +01:00
|
|
|
conn_.wlimit.startw();
|
|
|
|
|
|
|
|
conn_.wt.repeat = downstreamconf.timeout.connect;
|
|
|
|
ev_timer_again(conn_.loop, &conn_.wt);
|
|
|
|
|
2014-06-01 16:44:32 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
|
|
|
// Unreachable
|
2017-03-15 15:13:54 +01:00
|
|
|
assert(0);
|
|
|
|
|
2014-07-06 16:32:08 +02:00
|
|
|
return 0;
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2013-02-09 08:42:01 +01:00
|
|
|
namespace {
|
2018-11-23 12:44:36 +01:00
|
|
|
int htp_hdrs_completecb(llhttp_t *htp) {
|
2014-11-27 15:39:04 +01:00
|
|
|
auto http2session = static_cast<Http2Session *>(htp->data);
|
2015-02-14 16:24:52 +01:00
|
|
|
|
|
|
|
// We only read HTTP header part. If tunneling succeeds, response
|
|
|
|
// body is a different protocol (HTTP/2 in this case), we don't read
|
|
|
|
// them here.
|
|
|
|
|
2013-02-09 08:42:01 +01:00
|
|
|
// We just check status code here
|
2018-10-04 05:04:15 +02:00
|
|
|
if (htp->status_code / 100 == 2) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-11-04 09:53:57 +01:00
|
|
|
SSLOG(INFO, http2session) << "Tunneling success";
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2018-10-17 04:03:49 +02:00
|
|
|
http2session->set_state(Http2SessionState::PROXY_CONNECTED);
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2018-11-23 12:44:36 +01:00
|
|
|
return HPE_PAUSED;
|
2013-02-09 08:42:01 +01:00
|
|
|
}
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
SSLOG(WARN, http2session) << "Tunneling failed: " << htp->status_code;
|
2018-10-17 04:03:49 +02:00
|
|
|
http2session->set_state(Http2SessionState::PROXY_FAILED);
|
2014-06-01 16:44:32 +02:00
|
|
|
|
2018-11-23 12:44:36 +01:00
|
|
|
return HPE_PAUSED;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2013-02-09 08:42:01 +01:00
|
|
|
} // namespace
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
int Http2Session::downstream_read_proxy(const uint8_t *data, size_t datalen) {
|
2018-11-23 12:44:36 +01:00
|
|
|
auto htperr = llhttp_execute(proxy_htp_.get(),
|
|
|
|
reinterpret_cast<const char *>(data), datalen);
|
2015-02-22 09:53:12 +01:00
|
|
|
if (htperr == HPE_PAUSED) {
|
|
|
|
switch (state_) {
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::PROXY_CONNECTED:
|
2015-02-22 09:53:12 +01:00
|
|
|
// Initiate SSL/TLS handshake through established tunnel.
|
|
|
|
if (initiate_connection() != 0) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2015-02-22 09:53:12 +01:00
|
|
|
return 0;
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::PROXY_FAILED:
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2018-10-17 04:03:49 +02:00
|
|
|
default:
|
|
|
|
break;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2015-02-22 09:53:12 +01:00
|
|
|
// should not be here
|
|
|
|
assert(0);
|
|
|
|
}
|
2015-02-14 16:24:52 +01:00
|
|
|
|
2015-02-22 09:53:12 +01:00
|
|
|
if (htperr != HPE_OK) {
|
|
|
|
return -1;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2015-02-22 09:53:12 +01:00
|
|
|
|
|
|
|
return 0;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::downstream_connect_proxy() {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Connected to the proxy";
|
|
|
|
}
|
2015-03-10 13:54:29 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
std::string req = "CONNECT ";
|
2016-02-07 10:22:57 +01:00
|
|
|
req.append(addr_->hostport.c_str(), addr_->hostport.size());
|
|
|
|
if (addr_->port == 80 || addr_->port == 443) {
|
2015-11-27 16:03:16 +01:00
|
|
|
req += ':';
|
2016-02-07 10:22:57 +01:00
|
|
|
req += util::utos(addr_->port);
|
2015-03-13 15:01:55 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
req += " HTTP/1.1\r\nHost: ";
|
2016-02-07 10:22:57 +01:00
|
|
|
req += addr_->host;
|
2014-12-27 18:59:06 +01:00
|
|
|
req += "\r\n";
|
2016-01-17 14:51:40 +01:00
|
|
|
const auto &proxy = get_config()->downstream_http_proxy;
|
|
|
|
if (!proxy.userinfo.empty()) {
|
2014-12-27 18:59:06 +01:00
|
|
|
req += "Proxy-Authorization: Basic ";
|
2016-01-17 14:51:40 +01:00
|
|
|
req += base64::encode(std::begin(proxy.userinfo), std::end(proxy.userinfo));
|
2014-12-27 18:59:06 +01:00
|
|
|
req += "\r\n";
|
|
|
|
}
|
|
|
|
req += "\r\n";
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "HTTP proxy request headers\n" << req;
|
|
|
|
}
|
2016-02-07 10:54:44 +01:00
|
|
|
wb_.append(req);
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
on_write_ = &Http2Session::write_noop;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
signal_write();
|
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn) {
|
2015-03-11 13:35:47 +01:00
|
|
|
dconns_.append(dconn);
|
2016-04-02 16:11:03 +02:00
|
|
|
++addr_->num_dconn;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2015-11-12 16:53:29 +01:00
|
|
|
void Http2Session::remove_downstream_connection(
|
|
|
|
Http2DownstreamConnection *dconn) {
|
2016-04-02 16:11:03 +02:00
|
|
|
--addr_->num_dconn;
|
2015-03-11 13:35:47 +01:00
|
|
|
dconns_.remove(dconn);
|
2012-11-20 17:29:39 +01:00
|
|
|
dconn->detach_stream_data();
|
2016-02-27 11:39:03 +01:00
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Remove downstream";
|
|
|
|
}
|
|
|
|
|
2018-10-17 03:15:50 +02:00
|
|
|
if (freelist_zone_ == FreelistZone::NONE && !max_concurrency_reached()) {
|
2016-02-27 11:39:03 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-04-02 16:11:03 +02:00
|
|
|
SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
|
|
|
|
<< ", freelist.size="
|
|
|
|
<< addr_->http2_extra_freelist.size();
|
2016-02-27 11:39:03 +01:00
|
|
|
}
|
2016-04-02 16:11:03 +02:00
|
|
|
|
|
|
|
add_to_extra_freelist();
|
2016-02-27 11:39:03 +01:00
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Session::remove_stream_data(StreamData *sd) {
|
2015-03-11 13:35:47 +01:00
|
|
|
streams_.remove(sd);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (sd->dconn) {
|
2012-11-20 17:29:39 +01:00
|
|
|
sd->dconn->detach_stream_data();
|
|
|
|
}
|
|
|
|
delete sd;
|
|
|
|
}
|
|
|
|
|
2016-01-14 16:09:53 +01:00
|
|
|
int Http2Session::submit_request(Http2DownstreamConnection *dconn,
|
2013-11-28 13:36:04 +01:00
|
|
|
const nghttp2_nv *nva, size_t nvlen,
|
2014-11-27 15:39:04 +01:00
|
|
|
const nghttp2_data_provider *data_prd) {
|
2018-10-17 04:03:49 +02:00
|
|
|
assert(state_ == Http2SessionState::CONNECTED);
|
2018-10-15 16:02:44 +02:00
|
|
|
auto sd = std::make_unique<StreamData>();
|
2015-03-11 13:35:47 +01:00
|
|
|
sd->dlnext = sd->dlprev = nullptr;
|
2014-03-25 18:04:24 +01:00
|
|
|
// TODO Specify nullptr to pri_spec for now
|
2014-11-27 15:39:04 +01:00
|
|
|
auto stream_id =
|
|
|
|
nghttp2_submit_request(session_, nullptr, nva, nvlen, data_prd, sd.get());
|
|
|
|
if (stream_id < 0) {
|
2013-07-12 17:19:03 +02:00
|
|
|
SSLOG(FATAL, this) << "nghttp2_submit_request() failed: "
|
2014-05-07 16:24:07 +02:00
|
|
|
<< nghttp2_strerror(stream_id);
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2014-05-07 16:24:07 +02:00
|
|
|
|
|
|
|
dconn->attach_stream_data(sd.get());
|
|
|
|
dconn->get_downstream()->set_downstream_stream_id(stream_id);
|
2015-03-11 13:35:47 +01:00
|
|
|
streams_.append(sd.release());
|
2014-05-07 16:24:07 +02:00
|
|
|
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) {
|
2018-10-17 04:03:49 +02:00
|
|
|
assert(state_ == Http2SessionState::CONNECTED);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "RST_STREAM stream_id=" << stream_id
|
|
|
|
<< " with error_code=" << error_code;
|
|
|
|
}
|
|
|
|
int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, stream_id,
|
|
|
|
error_code);
|
|
|
|
if (rv != 0) {
|
2013-07-12 17:19:03 +02:00
|
|
|
SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
nghttp2_session *Http2Session::get_session() const { return session_; }
|
2013-10-29 16:07:35 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Session::resume_data(Http2DownstreamConnection *dconn) {
|
2018-10-17 04:03:49 +02:00
|
|
|
assert(state_ == Http2SessionState::CONNECTED);
|
2013-09-24 14:34:04 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2013-07-12 17:19:03 +02:00
|
|
|
int rv = nghttp2_session_resume_data(session_,
|
2012-11-20 17:29:39 +01:00
|
|
|
downstream->get_downstream_stream_id());
|
2014-11-27 15:39:04 +01:00
|
|
|
switch (rv) {
|
2012-11-20 17:29:39 +01:00
|
|
|
case 0:
|
2013-07-12 17:19:03 +02:00
|
|
|
case NGHTTP2_ERR_INVALID_ARGUMENT:
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
|
|
|
default:
|
2013-07-12 17:19:03 +02:00
|
|
|
SSLOG(FATAL, this) << "nghttp2_resume_session() failed: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
void call_downstream_readcb(Http2Session *http2session,
|
|
|
|
Downstream *downstream) {
|
2013-09-24 14:34:04 +02:00
|
|
|
auto upstream = downstream->get_upstream();
|
2014-12-27 18:59:06 +01:00
|
|
|
if (!upstream) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (upstream->downstream_read(downstream->get_downstream_connection()) != 0) {
|
|
|
|
delete upstream->get_client_handler();
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
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 http2session = static_cast<Http2Session *>(user_data);
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2017-04-30 08:45:53 +02:00
|
|
|
SSLOG(INFO, http2session)
|
|
|
|
<< "Stream stream_id=" << stream_id
|
|
|
|
<< " is being closed with error code " << error_code;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
|
|
|
if (sd == 0) {
|
2012-11-20 17:29:39 +01:00
|
|
|
// We might get this close callback when pushed streams are
|
|
|
|
// closed.
|
2013-08-29 15:58:05 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2013-07-26 12:33:25 +02:00
|
|
|
auto dconn = sd->dconn;
|
2014-11-27 15:39:04 +01:00
|
|
|
if (dconn) {
|
2013-07-26 12:33:25 +02:00
|
|
|
auto downstream = dconn->get_downstream();
|
2016-09-15 15:11:26 +02:00
|
|
|
auto upstream = downstream->get_upstream();
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2016-09-15 15:11:26 +02:00
|
|
|
if (downstream->get_downstream_stream_id() % 2 == 0 &&
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->get_request_state() == DownstreamState::INITIAL) {
|
2016-09-15 15:11:26 +02:00
|
|
|
// Downstream is canceled in backend before it is submitted in
|
|
|
|
// frontend session.
|
2015-11-15 16:12:54 +01:00
|
|
|
|
2016-09-15 15:11:26 +02:00
|
|
|
// This will avoid to send RST_STREAM to backend
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2016-09-15 15:11:26 +02:00
|
|
|
upstream->cancel_premature_downstream(downstream);
|
|
|
|
} else {
|
2018-10-17 03:03:32 +02:00
|
|
|
if (downstream->get_upgraded() && downstream->get_response_state() ==
|
|
|
|
DownstreamState::HEADER_COMPLETE) {
|
2016-09-15 15:11:26 +02:00
|
|
|
// For tunneled connection, we have to submit RST_STREAM to
|
|
|
|
// upstream *after* whole response body is sent. We just set
|
|
|
|
// MSG_COMPLETE here. Upstream will take care of that.
|
|
|
|
downstream->get_upstream()->on_downstream_body_complete(downstream);
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
|
2016-09-15 15:11:26 +02:00
|
|
|
} else if (error_code == NGHTTP2_NO_ERROR) {
|
|
|
|
switch (downstream->get_response_state()) {
|
2018-10-17 03:03:32 +02:00
|
|
|
case DownstreamState::MSG_COMPLETE:
|
|
|
|
case DownstreamState::MSG_BAD_HEADER:
|
2016-09-15 15:11:26 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2016-09-15 15:11:26 +02:00
|
|
|
} else if (downstream->get_response_state() !=
|
2018-10-17 03:03:32 +02:00
|
|
|
DownstreamState::MSG_BAD_HEADER) {
|
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2015-02-19 16:01:15 +01:00
|
|
|
}
|
2018-10-17 03:03:32 +02:00
|
|
|
if (downstream->get_response_state() == DownstreamState::MSG_RESET &&
|
2016-09-15 15:11:26 +02:00
|
|
|
downstream->get_response_rst_stream_error_code() ==
|
|
|
|
NGHTTP2_NO_ERROR) {
|
|
|
|
downstream->set_response_rst_stream_error_code(error_code);
|
2015-02-19 16:01:15 +01:00
|
|
|
}
|
2016-09-15 15:11:26 +02:00
|
|
|
call_downstream_readcb(http2session, downstream);
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2016-09-15 15:11:26 +02:00
|
|
|
// dconn may be deleted
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
// The life time of StreamData ends here
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session->remove_stream_data(sd);
|
2013-08-29 15:58:05 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
void Http2Session::start_settings_timer() {
|
2016-05-21 07:13:57 +02:00
|
|
|
auto &downstreamconf = get_config()->http2.downstream;
|
|
|
|
|
|
|
|
ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.);
|
2016-05-21 06:48:13 +02:00
|
|
|
ev_timer_start(conn_.loop, &settings_timer_);
|
2013-10-31 13:45:17 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
void Http2Session::stop_settings_timer() {
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_timer_stop(conn_.loop, &settings_timer_);
|
2013-10-31 13:45:17 +01:00
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
namespace {
|
2016-03-12 07:05:20 +01:00
|
|
|
int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
|
|
|
|
nghttp2_rcbuf *name, nghttp2_rcbuf *value,
|
|
|
|
uint8_t flags, void *user_data) {
|
2015-11-15 16:12:54 +01:00
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
auto namebuf = nghttp2_rcbuf_get_buf(name);
|
|
|
|
auto valuebuf = nghttp2_rcbuf_get_buf(value);
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2016-02-06 04:25:34 +01:00
|
|
|
auto &httpconf = get_config()->http;
|
2016-01-13 16:37:45 +01:00
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
switch (frame->hd.type) {
|
|
|
|
case NGHTTP2_HEADERS: {
|
|
|
|
auto trailer = frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
|
|
|
|
!downstream->get_expect_final_response();
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
if (resp.fs.buffer_size() + namebuf.len + valuebuf.len >
|
2016-02-06 04:25:34 +01:00
|
|
|
httpconf.response_header_field_buffer ||
|
|
|
|
resp.fs.num_fields() >= httpconf.max_response_header_fields) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-03-12 07:05:20 +01:00
|
|
|
DLOG(INFO, downstream)
|
|
|
|
<< "Too large or many header field size="
|
|
|
|
<< resp.fs.buffer_size() + namebuf.len + valuebuf.len
|
|
|
|
<< ", num=" << resp.fs.num_fields() + 1;
|
2016-02-06 04:25:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (trailer) {
|
|
|
|
// We don't care trailer part exceeds header size limit; just
|
|
|
|
// discard it.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
auto token = http2::lookup_token(namebuf.base, namebuf.len);
|
2016-02-20 13:41:23 +01:00
|
|
|
auto no_index = flags & NGHTTP2_NV_FLAG_NO_INDEX;
|
2016-02-20 11:13:39 +01:00
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
downstream->add_rcbuf(name);
|
|
|
|
downstream->add_rcbuf(value);
|
|
|
|
|
2015-03-08 08:48:25 +01:00
|
|
|
if (trailer) {
|
2015-11-15 16:12:54 +01:00
|
|
|
// just store header fields for trailer part
|
2016-03-12 07:05:20 +01:00
|
|
|
resp.fs.add_trailer_token(StringRef{namebuf.base, namebuf.len},
|
|
|
|
StringRef{valuebuf.base, valuebuf.len},
|
|
|
|
no_index, token);
|
2015-03-08 08:48:25 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
resp.fs.add_header_token(StringRef{namebuf.base, namebuf.len},
|
|
|
|
StringRef{valuebuf.base, valuebuf.len}, no_index,
|
|
|
|
token);
|
2015-03-08 08:48:25 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2015-11-15 16:12:54 +01:00
|
|
|
case NGHTTP2_PUSH_PROMISE: {
|
|
|
|
auto promised_stream_id = frame->push_promise.promised_stream_id;
|
|
|
|
auto promised_sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, promised_stream_id));
|
|
|
|
if (!promised_sd || !promised_sd->dconn) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
2016-09-27 16:49:01 +02:00
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auto promised_downstream = promised_sd->dconn->get_downstream();
|
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
auto namebuf = nghttp2_rcbuf_get_buf(name);
|
|
|
|
auto valuebuf = nghttp2_rcbuf_get_buf(value);
|
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
assert(promised_downstream);
|
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &promised_req = promised_downstream->request();
|
|
|
|
|
2016-02-06 04:25:34 +01:00
|
|
|
// We use request header limit for PUSH_PROMISE
|
2016-03-12 07:05:20 +01:00
|
|
|
if (promised_req.fs.buffer_size() + namebuf.len + valuebuf.len >
|
2016-02-06 09:22:23 +01:00
|
|
|
httpconf.request_header_field_buffer ||
|
|
|
|
promised_req.fs.num_fields() >= httpconf.max_request_header_fields) {
|
2016-02-06 04:25:34 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
DLOG(INFO, downstream)
|
|
|
|
<< "Too large or many header field size="
|
2016-03-12 07:05:20 +01:00
|
|
|
<< promised_req.fs.buffer_size() + namebuf.len + valuebuf.len
|
2016-02-06 04:25:34 +01:00
|
|
|
<< ", num=" << promised_req.fs.num_fields() + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
promised_downstream->add_rcbuf(name);
|
|
|
|
promised_downstream->add_rcbuf(value);
|
|
|
|
|
|
|
|
auto token = http2::lookup_token(namebuf.base, namebuf.len);
|
|
|
|
promised_req.fs.add_header_token(StringRef{namebuf.base, namebuf.len},
|
|
|
|
StringRef{valuebuf.base, valuebuf.len},
|
|
|
|
flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
|
2016-03-10 16:50:27 +01:00
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2014-08-08 13:52:32 +02:00
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2016-08-27 17:49:38 +02:00
|
|
|
namespace {
|
|
|
|
int on_invalid_header_callback2(nghttp2_session *session,
|
|
|
|
const nghttp2_frame *frame, nghttp2_rcbuf *name,
|
|
|
|
nghttp2_rcbuf *value, uint8_t flags,
|
|
|
|
void *user_data) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t stream_id;
|
|
|
|
|
|
|
|
if (frame->hd.type == NGHTTP2_PUSH_PROMISE) {
|
|
|
|
stream_id = frame->push_promise.promised_stream_id;
|
|
|
|
} else {
|
|
|
|
stream_id = frame->hd.stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
auto namebuf = nghttp2_rcbuf_get_buf(name);
|
|
|
|
auto valuebuf = nghttp2_rcbuf_get_buf(value);
|
|
|
|
|
|
|
|
SSLOG(INFO, http2session)
|
|
|
|
<< "Invalid header field for stream_id=" << stream_id
|
|
|
|
<< " in frame type=" << static_cast<uint32_t>(frame->hd.type)
|
|
|
|
<< ": name=[" << StringRef{namebuf.base, namebuf.len} << "], value=["
|
|
|
|
<< StringRef{valuebuf.base, valuebuf.len} << "]";
|
|
|
|
}
|
|
|
|
|
|
|
|
http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR);
|
|
|
|
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
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 http2session = static_cast<Http2Session *>(user_data);
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
switch (frame->hd.type) {
|
|
|
|
case NGHTTP2_HEADERS: {
|
|
|
|
if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
|
|
|
|
frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
|
|
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
|
|
|
NGHTTP2_INTERNAL_ERROR);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-01-29 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2015-11-15 16:12:54 +01:00
|
|
|
case NGHTTP2_PUSH_PROMISE: {
|
|
|
|
auto promised_stream_id = frame->push_promise.promised_stream_id;
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
2016-09-27 16:49:01 +02:00
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
|
|
|
|
assert(downstream);
|
|
|
|
assert(downstream->get_downstream_stream_id() == frame->hd.stream_id);
|
|
|
|
|
|
|
|
if (http2session->handle_downstream_push_promise(downstream,
|
|
|
|
promised_stream_id) != 0) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
2016-09-27 16:49:01 +02:00
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-01-29 13:23:13 +01:00
|
|
|
}
|
2015-11-15 16:12:54 +01:00
|
|
|
|
2014-01-29 13:23:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
2014-11-27 15:39:04 +01:00
|
|
|
int on_response_headers(Http2Session *http2session, Downstream *downstream,
|
|
|
|
nghttp2_session *session, const nghttp2_frame *frame) {
|
2014-01-16 15:41:13 +01:00
|
|
|
int rv;
|
2014-07-23 16:32:57 +02:00
|
|
|
|
|
|
|
auto upstream = downstream->get_upstream();
|
2017-01-11 11:27:08 +01:00
|
|
|
auto handler = upstream->get_client_handler();
|
2016-01-13 14:45:52 +01:00
|
|
|
const auto &req = downstream->request();
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &resp = downstream->response();
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto &nva = resp.fs.headers();
|
2014-01-16 15:41:13 +01:00
|
|
|
|
2017-01-11 11:27:08 +01:00
|
|
|
auto config = get_config();
|
|
|
|
auto &loggingconf = config->logging;
|
|
|
|
|
2014-07-31 14:46:50 +02:00
|
|
|
downstream->set_expect_final_response(false);
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
auto status = resp.fs.header(http2::HD__STATUS);
|
2015-02-19 16:58:20 +01:00
|
|
|
// libnghttp2 guarantees this exists and can be parsed
|
2019-09-21 06:45:20 +02:00
|
|
|
assert(status);
|
2015-02-19 16:58:20 +01:00
|
|
|
auto status_code = http2::parse_http_status_code(status->value);
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.http_status = status_code;
|
|
|
|
resp.http_major = 2;
|
|
|
|
resp.http_minor = 0;
|
2014-01-16 15:41:13 +01:00
|
|
|
|
2016-08-04 17:04:47 +02:00
|
|
|
downstream->set_downstream_addr_group(
|
|
|
|
http2session->get_downstream_addr_group());
|
|
|
|
downstream->set_addr(http2session->get_addr());
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
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 << nv.name << TTY_RST << ": " << nv.value << "\n";
|
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
SSLOG(INFO, http2session)
|
|
|
|
<< "HTTP response headers. stream_id=" << frame->hd.stream_id << "\n"
|
|
|
|
<< ss.str();
|
2014-07-25 17:40:06 +02:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_non_final_response()) {
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-07-25 17:40:06 +02:00
|
|
|
SSLOG(INFO, http2session) << "This is non-final response.";
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
downstream->set_expect_final_response(true);
|
|
|
|
rv = upstream->on_downstream_header_complete(downstream);
|
|
|
|
|
|
|
|
// Now Dowstream's response headers are erased.
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-07-23 16:32:57 +02:00
|
|
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
|
|
|
NGHTTP2_PROTOCOL_ERROR);
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::HEADER_COMPLETE);
|
2018-03-11 04:02:18 +01:00
|
|
|
downstream->check_upgrade_fulfilled_http2();
|
2015-04-10 15:23:46 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_upgraded()) {
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2017-10-28 15:25:42 +02:00
|
|
|
// On upgrade success, both ends can send data
|
2015-02-13 14:41:50 +01:00
|
|
|
if (upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0) != 0) {
|
2014-01-16 15:41:13 +01:00
|
|
|
// If resume_read fails, just drop connection. Not ideal.
|
2017-01-11 11:27:08 +01:00
|
|
|
delete handler;
|
2014-08-09 11:47:45 +02:00
|
|
|
return -1;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2017-04-30 08:45:53 +02:00
|
|
|
SSLOG(INFO, http2session)
|
|
|
|
<< "HTTP upgrade success. stream_id=" << frame->hd.stream_id;
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2015-04-10 15:23:46 +02:00
|
|
|
} else {
|
2016-01-13 16:37:45 +01:00
|
|
|
auto content_length = resp.fs.header(http2::HD_CONTENT_LENGTH);
|
2015-04-10 15:23:46 +02:00
|
|
|
if (content_length) {
|
|
|
|
// libnghttp2 guarantees this can be parsed
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.fs.content_length = util::parse_uint(content_length->value);
|
2015-04-10 15:23:46 +02:00
|
|
|
}
|
|
|
|
|
2016-01-13 16:37:45 +01:00
|
|
|
if (resp.fs.content_length == -1 && downstream->expect_response_body()) {
|
2015-04-10 15:23:46 +02:00
|
|
|
// Here we have response body but Content-Length is not known in
|
|
|
|
// advance.
|
2016-01-13 14:45:52 +01:00
|
|
|
if (req.http_major <= 0 || (req.http_major == 1 && req.http_minor == 0)) {
|
2015-04-10 15:23:46 +02:00
|
|
|
// We simply close connection for pre-HTTP/1.1 in this case.
|
2016-01-13 16:37:45 +01:00
|
|
|
resp.connection_close = true;
|
2015-04-10 15:23:46 +02:00
|
|
|
} else {
|
|
|
|
// Otherwise, use chunked encoding to keep upstream connection
|
2018-10-14 15:57:11 +02:00
|
|
|
// open. In HTTP2, we are supposed not to receive
|
2015-04-10 15:23:46 +02:00
|
|
|
// transfer-encoding.
|
2016-02-20 13:41:23 +01:00
|
|
|
resp.fs.add_header_token(StringRef::from_lit("transfer-encoding"),
|
|
|
|
StringRef::from_lit("chunked"), false,
|
|
|
|
http2::HD_TRANSFER_ENCODING);
|
2015-04-10 15:23:46 +02:00
|
|
|
downstream->set_chunked_response(true);
|
|
|
|
}
|
|
|
|
}
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2015-04-10 15:23:46 +02:00
|
|
|
|
2016-04-27 14:19:28 +02:00
|
|
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
|
|
|
resp.headers_only = true;
|
|
|
|
}
|
|
|
|
|
2017-01-11 11:27:08 +01:00
|
|
|
if (loggingconf.access.write_early && downstream->accesslog_ready()) {
|
|
|
|
handler->write_accesslog(downstream);
|
|
|
|
downstream->set_accesslog_written(true);
|
|
|
|
}
|
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
rv = upstream->on_downstream_header_complete(downstream);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2015-09-02 18:32:15 +02:00
|
|
|
// Handling early return (in other words, response was hijacked by
|
|
|
|
// mruby scripting).
|
2018-10-17 03:03:32 +02:00
|
|
|
if (downstream->get_response_state() == DownstreamState::MSG_COMPLETE) {
|
2015-09-02 18:32:15 +02:00
|
|
|
http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL);
|
|
|
|
} else {
|
|
|
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
|
|
|
NGHTTP2_INTERNAL_ERROR);
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2015-09-02 18:32:15 +02:00
|
|
|
}
|
2014-01-16 15:41:13 +01:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-01-16 15:41:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2012-11-20 17:29:39 +01: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-04-03 11:54:15 +02:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
2014-04-03 11:54:15 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
switch (frame->hd.type) {
|
2014-04-03 11:54:15 +02:00
|
|
|
case NGHTTP2_DATA: {
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
rv = upstream->on_downstream_body(downstream, nullptr, 0, true);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-04-03 11:54:15 +02:00
|
|
|
http2session->submit_rst_stream(frame->hd.stream_id,
|
|
|
|
NGHTTP2_INTERNAL_ERROR);
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
} else if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->disable_downstream_rtimer();
|
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
if (downstream->get_response_state() ==
|
|
|
|
DownstreamState::HEADER_COMPLETE) {
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
|
2014-07-23 16:32:57 +02:00
|
|
|
|
|
|
|
rv = upstream->on_downstream_body_complete(downstream);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
|
|
|
}
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
2014-07-23 16:32:57 +02:00
|
|
|
|
2014-04-03 11:54:15 +02:00
|
|
|
call_downstream_readcb(http2session, downstream);
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2014-04-03 11:54:15 +02:00
|
|
|
}
|
2014-07-23 16:32:57 +02:00
|
|
|
case NGHTTP2_HEADERS: {
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE ||
|
|
|
|
frame->headers.cat == NGHTTP2_HCAT_PUSH_RESPONSE) {
|
2014-07-23 16:32:57 +02:00
|
|
|
rv = on_response_headers(http2session, downstream, session, frame);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
2015-01-05 16:51:52 +01:00
|
|
|
} else if (frame->headers.cat == NGHTTP2_HCAT_HEADERS) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_expect_final_response()) {
|
2014-07-31 14:46:50 +02:00
|
|
|
rv = on_response_headers(http2session, downstream, session, frame);
|
2014-07-03 14:18:01 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
2014-07-31 14:46:50 +02:00
|
|
|
}
|
2014-07-03 14:18:01 +02:00
|
|
|
}
|
|
|
|
}
|
2014-07-23 16:32:57 +02:00
|
|
|
|
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_downstream_rtimer();
|
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
if (downstream->get_response_state() ==
|
|
|
|
DownstreamState::HEADER_COMPLETE) {
|
|
|
|
downstream->set_response_state(DownstreamState::MSG_COMPLETE);
|
2014-07-23 16:32:57 +02:00
|
|
|
|
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
|
|
|
|
rv = upstream->on_downstream_body_complete(downstream);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
} else {
|
|
|
|
downstream->reset_downstream_rtimer();
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
2014-08-09 11:47:45 +02:00
|
|
|
|
|
|
|
// This may delete downstream
|
|
|
|
call_downstream_readcb(http2session, downstream);
|
|
|
|
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2014-07-23 16:32:57 +02:00
|
|
|
}
|
2013-07-26 12:33:25 +02:00
|
|
|
case NGHTTP2_RST_STREAM: {
|
2014-11-27 15:39:04 +01:00
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (sd && sd->dconn) {
|
2013-07-26 12:33:25 +02:00
|
|
|
auto downstream = sd->dconn->get_downstream();
|
2016-09-15 15:11:26 +02:00
|
|
|
downstream->set_response_rst_stream_error_code(
|
|
|
|
frame->rst_stream.error_code);
|
|
|
|
call_downstream_readcb(http2session, downstream);
|
2013-07-26 12:33:25 +02:00
|
|
|
}
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2013-07-26 12:33:25 +02:00
|
|
|
}
|
2016-05-21 03:30:09 +02:00
|
|
|
case NGHTTP2_SETTINGS: {
|
2014-11-27 15:39:04 +01:00
|
|
|
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
|
2018-03-11 04:02:18 +01:00
|
|
|
http2session->on_settings_received(frame);
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2013-10-31 13:45:17 +01:00
|
|
|
}
|
2016-05-21 03:30:09 +02:00
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session->stop_settings_timer();
|
2016-05-21 03:30:09 +02:00
|
|
|
|
|
|
|
auto addr = http2session->get_addr();
|
|
|
|
auto &connect_blocker = addr->connect_blocker;
|
|
|
|
|
|
|
|
connect_blocker->on_success();
|
|
|
|
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2016-05-21 03:30:09 +02:00
|
|
|
}
|
2015-03-09 13:22:31 +01:00
|
|
|
case NGHTTP2_PING:
|
|
|
|
if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
LOG(INFO) << "PING ACK received";
|
|
|
|
}
|
|
|
|
http2session->connection_alive();
|
|
|
|
}
|
|
|
|
return 0;
|
2015-11-15 16:12:54 +01:00
|
|
|
case NGHTTP2_PUSH_PROMISE: {
|
|
|
|
auto promised_stream_id = frame->push_promise.promised_stream_id;
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2013-11-04 09:53:57 +01:00
|
|
|
SSLOG(INFO, http2session)
|
2014-11-27 15:39:04 +01:00
|
|
|
<< "Received downstream PUSH_PROMISE stream_id="
|
|
|
|
<< frame->hd.stream_id
|
2015-11-15 16:12:54 +01:00
|
|
|
<< ", promised_stream_id=" << promised_stream_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
|
|
|
return 0;
|
2013-07-26 12:33:25 +02:00
|
|
|
}
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
|
|
|
|
assert(downstream);
|
|
|
|
assert(downstream->get_downstream_stream_id() == frame->hd.stream_id);
|
|
|
|
|
|
|
|
auto promised_sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, promised_stream_id));
|
|
|
|
if (!promised_sd || !promised_sd->dconn) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto promised_downstream = promised_sd->dconn->get_downstream();
|
|
|
|
|
|
|
|
assert(promised_downstream);
|
|
|
|
|
|
|
|
if (http2session->handle_downstream_push_promise_complete(
|
|
|
|
downstream, promised_downstream) != 0) {
|
|
|
|
http2session->submit_rst_stream(promised_stream_id, NGHTTP2_CANCEL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
2016-03-22 15:51:00 +01:00
|
|
|
case NGHTTP2_GOAWAY:
|
2016-03-22 17:16:44 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
auto debug_data = util::ascii_dump(frame->goaway.opaque_data,
|
|
|
|
frame->goaway.opaque_data_len);
|
|
|
|
|
|
|
|
SSLOG(INFO, http2session)
|
|
|
|
<< "GOAWAY received: last-stream-id=" << frame->goaway.last_stream_id
|
|
|
|
<< ", error_code=" << frame->goaway.error_code
|
|
|
|
<< ", debug_data=" << debug_data;
|
|
|
|
}
|
2016-03-22 15:51:00 +01:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
default:
|
2015-02-21 09:08:03 +01:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01: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) {
|
2012-11-20 17:29:39 +01:00
|
|
|
int rv;
|
2014-11-27 15:39:04 +01:00
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
|
|
|
if (!sd || !sd->dconn) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-08-29 14:18:40 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2013-07-26 12:33:25 +02:00
|
|
|
auto downstream = sd->dconn->get_downstream();
|
2016-09-15 15:11:26 +02:00
|
|
|
if (!downstream->expect_response_body()) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2013-08-29 14:18:40 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2012-11-21 15:47:48 +01:00
|
|
|
|
2014-07-23 16:32:57 +02:00
|
|
|
// We don't want DATA after non-final response, which is illegal in
|
|
|
|
// HTTP.
|
2014-11-27 15:39:04 +01:00
|
|
|
if (downstream->get_non_final_response()) {
|
2014-07-23 16:32:57 +02:00
|
|
|
http2session->submit_rst_stream(stream_id, NGHTTP2_PROTOCOL_ERROR);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2014-07-23 16:32:57 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-09 11:47:45 +02:00
|
|
|
downstream->reset_downstream_rtimer();
|
|
|
|
|
2016-01-14 15:20:44 +01:00
|
|
|
auto &resp = downstream->response();
|
|
|
|
|
|
|
|
resp.recv_body_length += len;
|
2016-01-14 15:49:21 +01:00
|
|
|
resp.unconsumed_body_length += len;
|
2014-07-05 11:22:40 +02:00
|
|
|
|
2013-07-26 12:33:25 +02:00
|
|
|
auto upstream = downstream->get_upstream();
|
2014-04-03 11:54:15 +02:00
|
|
|
rv = upstream->on_downstream_body(downstream, data, len, false);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (http2session->consume(stream_id, len) != 0) {
|
2014-07-25 14:26:03 +02:00
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2014-07-25 14:26:03 +02:00
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
call_downstream_readcb(http2session, downstream);
|
2013-08-29 14:18:40 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-10-31 13:45:17 +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) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) {
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
2014-08-09 11:47:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!sd || !sd->dconn) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
|
2016-09-14 17:25:41 +02:00
|
|
|
if (frame->hd.type == NGHTTP2_HEADERS &&
|
|
|
|
frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
|
|
|
downstream->set_request_header_sent(true);
|
2018-08-22 15:32:25 +02:00
|
|
|
auto src = downstream->get_blocked_request_buf();
|
|
|
|
if (src->rleft()) {
|
|
|
|
auto dest = downstream->get_request_buf();
|
|
|
|
src->remove(*dest);
|
|
|
|
if (http2session->resume_data(sd->dconn) != 0) {
|
|
|
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
downstream->ensure_downstream_wtimer();
|
|
|
|
}
|
2016-09-15 15:03:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
|
2014-08-09 11:47:45 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
downstream->reset_downstream_rtimer();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (frame->hd.type == NGHTTP2_SETTINGS &&
|
|
|
|
(frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
|
2014-12-27 18:59:06 +01:00
|
|
|
http2session->start_settings_timer();
|
2013-10-31 13:45:17 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2012-11-20 17:29:39 +01: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 http2session = static_cast<Http2Session *>(user_data);
|
2015-01-21 14:49:00 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, http2session) << "Failed to send control frame type="
|
|
|
|
<< static_cast<uint32_t>(frame->hd.type)
|
2016-09-14 17:25:41 +02:00
|
|
|
<< ", lib_error_code=" << lib_error_code << ": "
|
2015-01-21 14:49:00 +01:00
|
|
|
<< nghttp2_strerror(lib_error_code);
|
|
|
|
}
|
2016-09-14 17:25:41 +02:00
|
|
|
if (frame->hd.type != NGHTTP2_HEADERS ||
|
|
|
|
lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ||
|
|
|
|
lib_error_code == NGHTTP2_ERR_STREAM_CLOSING) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
|
|
|
if (!sd) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!sd->dconn) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
auto downstream = sd->dconn->get_downstream();
|
|
|
|
|
|
|
|
if (lib_error_code == NGHTTP2_ERR_START_STREAM_NOT_ALLOWED) {
|
|
|
|
// Migrate to another downstream connection.
|
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
|
|
|
|
if (upstream->on_downstream_reset(downstream, false)) {
|
|
|
|
// This should be done for h1 upstream only. Deleting
|
2017-12-17 05:23:37 +01:00
|
|
|
// ClientHandler for h2 upstream may lead to crash.
|
2016-09-14 17:25:41 +02:00
|
|
|
delete upstream->get_client_handler();
|
2015-02-12 14:44:29 +01:00
|
|
|
}
|
2016-09-14 17:25:41 +02:00
|
|
|
|
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
2016-09-14 17:25:41 +02:00
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
// To avoid stream hanging around, flag DownstreamState::MSG_RESET.
|
|
|
|
downstream->set_response_state(DownstreamState::MSG_RESET);
|
2016-09-14 17:25:41 +02:00
|
|
|
call_downstream_readcb(http2session, downstream);
|
|
|
|
|
2013-08-29 14:51:58 +02:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2016-05-14 10:17:27 +02:00
|
|
|
namespace {
|
|
|
|
constexpr auto PADDING = std::array<uint8_t, 256>{};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
|
|
|
|
const uint8_t *framehd, size_t length,
|
|
|
|
nghttp2_data_source *source, void *user_data) {
|
|
|
|
auto http2session = static_cast<Http2Session *>(user_data);
|
|
|
|
auto sd = static_cast<StreamData *>(
|
|
|
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
2016-05-22 14:57:24 +02:00
|
|
|
|
|
|
|
if (sd == nullptr) {
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
2016-05-14 10:17:27 +02:00
|
|
|
auto dconn = sd->dconn;
|
|
|
|
auto downstream = dconn->get_downstream();
|
|
|
|
auto input = downstream->get_request_buf();
|
|
|
|
auto wb = http2session->get_request_buf();
|
|
|
|
|
|
|
|
size_t padlen = 0;
|
|
|
|
|
|
|
|
wb->append(framehd, 9);
|
|
|
|
if (frame->data.padlen > 0) {
|
|
|
|
padlen = frame->data.padlen - 1;
|
|
|
|
wb->append(static_cast<uint8_t>(padlen));
|
|
|
|
}
|
|
|
|
|
|
|
|
input->remove(*wb, length);
|
|
|
|
|
|
|
|
wb->append(PADDING.data(), padlen);
|
|
|
|
|
2017-02-20 14:08:39 +01:00
|
|
|
if (input->rleft() == 0) {
|
|
|
|
downstream->disable_downstream_wtimer();
|
|
|
|
} else {
|
|
|
|
downstream->reset_downstream_wtimer();
|
|
|
|
}
|
2016-05-14 10:17:27 +02:00
|
|
|
|
|
|
|
if (length > 0) {
|
|
|
|
// This is important because it will handle flow control
|
|
|
|
// stuff.
|
|
|
|
if (downstream->get_upstream()->resume_read(SHRPX_NO_BUFFER, downstream,
|
|
|
|
length) != 0) {
|
|
|
|
// In this case, downstream may be deleted.
|
|
|
|
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Here sd->dconn could be nullptr, because
|
|
|
|
// Upstream::resume_read() may delete downstream which will delete
|
|
|
|
// dconn. Is this still really true?
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2015-02-24 07:21:10 +01:00
|
|
|
nghttp2_session_callbacks *create_http2_downstream_callbacks() {
|
2012-11-20 17:29:39 +01:00
|
|
|
int rv;
|
2014-08-22 13:59:50 +02:00
|
|
|
nghttp2_session_callbacks *callbacks;
|
2015-02-24 07:21:10 +01:00
|
|
|
|
2014-08-22 13:59:50 +02:00
|
|
|
rv = nghttp2_session_callbacks_new(&callbacks);
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2015-02-24 07:21:10 +01:00
|
|
|
return nullptr;
|
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
|
|
|
|
2016-03-12 07:05:20 +01:00
|
|
|
nghttp2_session_callbacks_set_on_header_callback2(callbacks,
|
|
|
|
on_header_callback2);
|
2014-08-22 13:59:50 +02:00
|
|
|
|
2016-08-27 17:49:38 +02:00
|
|
|
nghttp2_session_callbacks_set_on_invalid_header_callback2(
|
|
|
|
callbacks, on_invalid_header_callback2);
|
|
|
|
|
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
|
|
|
|
2016-05-14 10:17:27 +02:00
|
|
|
nghttp2_session_callbacks_set_send_data_callback(callbacks,
|
|
|
|
send_data_callback);
|
|
|
|
|
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-11-20 17:29:39 +01:00
|
|
|
|
2015-02-24 07:21:10 +01:00
|
|
|
return callbacks;
|
|
|
|
}
|
|
|
|
|
2015-03-28 12:19:17 +01:00
|
|
|
int Http2Session::connection_made() {
|
2015-02-24 07:21:10 +01:00
|
|
|
int rv;
|
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::CONNECTED;
|
2015-02-24 07:21:10 +01:00
|
|
|
|
2017-12-01 14:27:16 +01:00
|
|
|
on_write_ = &Http2Session::downstream_write;
|
|
|
|
on_read_ = &Http2Session::downstream_read;
|
|
|
|
|
2016-05-24 16:36:43 +02:00
|
|
|
if (addr_->tls) {
|
2015-02-24 07:21:10 +01:00
|
|
|
const unsigned char *next_proto = nullptr;
|
2016-04-08 16:03:42 +02:00
|
|
|
unsigned int next_proto_len = 0;
|
2016-04-08 16:06:37 +02:00
|
|
|
|
2018-03-25 18:27:23 +02:00
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
2015-02-24 07:21:10 +01:00
|
|
|
SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
|
2018-04-03 14:39:44 +02:00
|
|
|
#endif // !OPENSSL_NO_NEXTPROTONEG
|
2015-02-24 07:21:10 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
2016-04-08 16:06:37 +02:00
|
|
|
if (!next_proto) {
|
2015-02-24 07:21:10 +01:00
|
|
|
SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
|
|
|
|
}
|
2016-04-08 16:06:37 +02:00
|
|
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
|
2015-02-24 07:21:10 +01:00
|
|
|
if (!next_proto) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2015-02-24 07:21:10 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2016-04-08 16:06:37 +02:00
|
|
|
|
|
|
|
auto proto = StringRef{next_proto, next_proto_len};
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
|
|
|
|
}
|
|
|
|
if (!util::check_h2_is_selected(proto)) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2016-04-08 16:06:37 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-02-24 07:21:10 +01:00
|
|
|
}
|
|
|
|
|
2016-10-08 04:34:23 +02:00
|
|
|
auto config = get_config();
|
|
|
|
auto &http2conf = config->http2;
|
2016-01-18 09:00:20 +01:00
|
|
|
|
|
|
|
rv = nghttp2_session_client_new2(&session_, http2conf.downstream.callbacks,
|
|
|
|
this, http2conf.downstream.option);
|
2014-04-04 14:57:47 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-06-15 16:43:03 +02:00
|
|
|
std::array<nghttp2_settings_entry, 5> entry;
|
|
|
|
size_t nentry = 3;
|
2015-11-15 16:12:54 +01:00
|
|
|
entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
2016-02-27 16:06:40 +01:00
|
|
|
entry[0].value = http2conf.downstream.max_concurrent_streams;
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
2016-09-09 14:05:37 +02:00
|
|
|
entry[1].value = http2conf.downstream.window_size;
|
2013-11-02 08:59:59 +01:00
|
|
|
|
2022-06-15 16:43:03 +02:00
|
|
|
entry[2].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
|
|
|
|
entry[2].value = 1;
|
|
|
|
|
2016-10-08 04:34:23 +02:00
|
|
|
if (http2conf.no_server_push || config->http2_proxy) {
|
2015-11-15 16:12:54 +01:00
|
|
|
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
|
|
|
entry[nentry].value = 0;
|
|
|
|
++nentry;
|
|
|
|
}
|
2012-11-21 15:47:48 +01:00
|
|
|
|
2016-09-12 15:53:02 +02:00
|
|
|
if (http2conf.downstream.decoder_dynamic_table_size !=
|
|
|
|
NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
|
|
|
|
entry[nentry].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
|
|
|
|
entry[nentry].value = http2conf.downstream.decoder_dynamic_table_size;
|
|
|
|
++nentry;
|
|
|
|
}
|
|
|
|
|
2015-02-05 16:06:01 +01:00
|
|
|
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(),
|
2015-11-15 16:12:54 +01:00
|
|
|
nentry);
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2012-11-20 17:29:39 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2013-07-26 12:33:25 +02:00
|
|
|
|
2016-09-09 14:05:37 +02:00
|
|
|
rv = nghttp2_session_set_local_window_size(
|
|
|
|
session_, NGHTTP2_FLAG_NONE, 0,
|
|
|
|
http2conf.downstream.connection_window_size);
|
|
|
|
if (rv != 0) {
|
|
|
|
return -1;
|
2013-11-20 16:15:17 +01:00
|
|
|
}
|
|
|
|
|
2016-05-21 03:30:09 +02:00
|
|
|
reset_connection_check_timer(CONNCHK_TIMEOUT);
|
|
|
|
|
2017-01-08 11:43:24 +01:00
|
|
|
submit_pending_requests();
|
2014-06-10 17:19:54 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
signal_write();
|
2012-11-20 17:29:39 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int Http2Session::do_read() { return read_(*this); }
|
|
|
|
int Http2Session::do_write() { return write_(*this); }
|
2014-01-18 08:50:52 +01:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
int Http2Session::on_read(const uint8_t *data, size_t datalen) {
|
|
|
|
return on_read_(*this, data, datalen);
|
|
|
|
}
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
int Http2Session::on_write() { return on_write_(*this); }
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
int Http2Session::downstream_read(const uint8_t *data, size_t datalen) {
|
|
|
|
ssize_t rv;
|
2014-06-01 14:01:01 +02:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
rv = nghttp2_session_mem_recv(session_, data, datalen);
|
|
|
|
if (rv < 0) {
|
2016-05-21 03:30:09 +02:00
|
|
|
SSLOG(ERROR, this) << "nghttp2_session_mem_recv() returned error: "
|
2016-02-07 11:20:17 +01:00
|
|
|
<< nghttp2_strerror(rv);
|
|
|
|
return -1;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nghttp2_session_want_read(session_) == 0 &&
|
|
|
|
nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "No more read/write for this HTTP2 session";
|
2014-06-01 14:01:01 +02:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2014-01-18 08:50:52 +01:00
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
signal_write();
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int Http2Session::downstream_write() {
|
|
|
|
for (;;) {
|
2014-02-18 15:23:11 +01:00
|
|
|
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
|
|
|
SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
|
|
|
|
<< nghttp2_strerror(datalen);
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
2014-02-18 15:23:11 +01:00
|
|
|
}
|
2014-11-27 15:39:04 +01:00
|
|
|
if (datalen == 0) {
|
2014-02-18 15:23:11 +01:00
|
|
|
break;
|
|
|
|
}
|
2016-02-07 10:54:44 +01:00
|
|
|
wb_.append(data, datalen);
|
|
|
|
|
|
|
|
if (wb_.rleft() >= MAX_BUFFER_SIZE) {
|
|
|
|
break;
|
2014-02-18 15:23:11 +01:00
|
|
|
}
|
2014-03-03 13:18:24 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (nghttp2_session_want_read(session_) == 0 &&
|
2014-12-27 18:59:06 +01:00
|
|
|
nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
|
2014-11-27 15:39:04 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2014-02-18 15:23:11 +01:00
|
|
|
SSLOG(INFO, this) << "No more read/write for this session";
|
2012-12-16 17:10:45 +01:00
|
|
|
}
|
2014-02-18 15:23:11 +01:00
|
|
|
return -1;
|
2012-12-16 17:10:45 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2014-02-18 15:23:11 +01:00
|
|
|
return 0;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2015-02-20 11:48:35 +01:00
|
|
|
void Http2Session::signal_write() {
|
|
|
|
switch (state_) {
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::DISCONNECTED:
|
2016-04-02 17:23:44 +02:00
|
|
|
if (!ev_is_active(&initiate_connection_timer_)) {
|
2015-03-10 15:11:22 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
2016-04-02 17:23:44 +02:00
|
|
|
LOG(INFO) << "Start connecting to backend server";
|
2015-03-10 15:11:22 +01:00
|
|
|
}
|
2016-04-02 17:23:44 +02:00
|
|
|
// Since the timer is set to 0., these will feed 2 events. We
|
|
|
|
// will stop the timer in the initiate_connection_timer_ to void
|
|
|
|
// 2nd event.
|
|
|
|
ev_timer_start(conn_.loop, &initiate_connection_timer_);
|
|
|
|
ev_feed_event(conn_.loop, &initiate_connection_timer_, 0);
|
2015-02-20 11:48:35 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::CONNECTED:
|
2015-02-20 11:48:35 +01:00
|
|
|
conn_.wlimit.startw();
|
|
|
|
break;
|
2018-10-17 04:03:49 +02:00
|
|
|
default:
|
|
|
|
break;
|
2015-02-20 11:48:35 +01:00
|
|
|
}
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
struct ev_loop *Http2Session::get_loop() const {
|
2015-02-04 13:15:58 +01:00
|
|
|
return conn_.loop;
|
2012-11-20 17:29:39 +01:00
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_io *Http2Session::get_wev() { return &conn_.wev; }
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
Http2SessionState Http2Session::get_state() const { return state_; }
|
2012-11-20 17:29:39 +01:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
void Http2Session::set_state(Http2SessionState state) { state_ = state; }
|
2013-02-09 08:42:01 +01:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Session::terminate_session(uint32_t error_code) {
|
2013-10-31 13:45:17 +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-31 13:45:17 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
SSL *Http2Session::get_ssl() const { return conn_.tls.ssl; }
|
2014-04-26 16:00:58 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
int Http2Session::consume(int32_t stream_id, size_t len) {
|
2014-07-25 14:26:03 +02:00
|
|
|
int rv;
|
2014-07-02 16:20:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (!session_) {
|
2014-07-31 16:34:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
rv = nghttp2_session_consume(session_, stream_id, len);
|
2014-07-02 16:20:45 +02:00
|
|
|
|
2014-11-27 15:39:04 +01:00
|
|
|
if (rv != 0) {
|
2014-11-08 02:51:56 +01:00
|
|
|
SSLOG(WARN, this) << "nghttp2_session_consume() returned error: "
|
|
|
|
<< nghttp2_strerror(rv);
|
2014-07-02 16:20:45 +02:00
|
|
|
|
2014-07-25 14:26:03 +02:00
|
|
|
return -1;
|
2014-07-02 16:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-11 04:02:18 +01:00
|
|
|
bool Http2Session::can_push_request(const Downstream *downstream) const {
|
|
|
|
auto &req = downstream->request();
|
2018-10-17 04:03:49 +02:00
|
|
|
return state_ == Http2SessionState::CONNECTED &&
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_ == ConnectionCheck::NONE &&
|
2018-10-17 02:50:29 +02:00
|
|
|
(req.connect_proto == ConnectProto::NONE || settings_recved_);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
void Http2Session::start_checking_connection() {
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ != Http2SessionState::CONNECTED ||
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_ != ConnectionCheck::REQUIRED) {
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_ = ConnectionCheck::STARTED;
|
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
|
|
|
|
|
|
|
SSLOG(INFO, this) << "Start checking connection";
|
|
|
|
// If connection is down, we may get error when writing data. Issue
|
|
|
|
// ping frame to see whether connection is alive.
|
2022-02-10 10:56:19 +01:00
|
|
|
nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, nullptr);
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
|
2015-03-09 13:22:31 +01:00
|
|
|
// set ping timeout and start timer again
|
|
|
|
reset_connection_check_timer(CONNCHK_PING_TIMEOUT);
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
signal_write();
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
2015-03-09 13:22:31 +01:00
|
|
|
void Http2Session::reset_connection_check_timer(ev_tstamp t) {
|
|
|
|
connchk_timer_.repeat = t;
|
2015-02-04 13:15:58 +01:00
|
|
|
ev_timer_again(conn_.loop, &connchk_timer_);
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
2015-03-10 16:27:51 +01:00
|
|
|
void Http2Session::reset_connection_check_timer_if_not_checking() {
|
2018-10-17 04:08:56 +02:00
|
|
|
if (connection_check_state_ != ConnectionCheck::NONE) {
|
2015-03-10 16:27:51 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
reset_connection_check_timer(CONNCHK_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
void Http2Session::connection_alive() {
|
2015-03-09 13:22:31 +01:00
|
|
|
reset_connection_check_timer(CONNCHK_TIMEOUT);
|
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
|
|
|
|
2018-10-17 04:08:56 +02:00
|
|
|
if (connection_check_state_ == ConnectionCheck::NONE) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return;
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
2015-03-09 13:22:31 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Connection alive";
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:08:56 +02:00
|
|
|
connection_check_state_ = ConnectionCheck::NONE;
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
submit_pending_requests();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Http2Session::submit_pending_requests() {
|
2015-03-11 13:35:47 +01:00
|
|
|
for (auto dconn = dconns_.head; dconn; dconn = dconn->dlnext) {
|
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
|
|
|
auto downstream = dconn->get_downstream();
|
2015-02-17 15:15:53 +01:00
|
|
|
|
2016-09-15 15:11:26 +02:00
|
|
|
if (!downstream->get_request_pending() ||
|
2016-09-14 17:25:41 +02:00
|
|
|
!downstream->request_submission_ready()) {
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-03-11 04:02:18 +01:00
|
|
|
auto &req = downstream->request();
|
2018-10-17 02:50:29 +02:00
|
|
|
if (req.connect_proto != ConnectProto::NONE && !settings_recved_) {
|
2018-03-11 04:02:18 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
if (dconn->push_request_headers() != 0) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "backend request failed";
|
|
|
|
}
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
upstream->on_downstream_abort_request(downstream, 400);
|
|
|
|
|
|
|
|
continue;
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
|
2015-02-17 15:15:53 +01:00
|
|
|
upstream->resume_read(SHRPX_NO_BUFFER, downstream, 0);
|
nghttpx: Check HTTP/2 downstream connection after certain idle time
Previously when requests are issued to HTTP/2 downstream connection,
but it turns out that connection is down, handlers of those requests
are deleted. In some situations, we only know connection is down when
we write something to network, so we'd like to handle this kind of
situation in more robust manner. In this change, certain seconds
passed after last network activity, we first issue PING frame to
downstream connection before issuing new HTTP request. If writing
PING frame is failed, it means connection was lost. In this case,
instead of deleting handler, pending requests are migrated to new
HTTP2/ downstream connection, so that it can continue without
affecting upstream connection.
2014-12-08 17:30:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:08:56 +02:00
|
|
|
void Http2Session::set_connection_check_state(ConnectionCheck state) {
|
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
|
|
|
connection_check_state_ = state;
|
|
|
|
}
|
|
|
|
|
2018-10-17 04:08:56 +02:00
|
|
|
ConnectionCheck Http2Session::get_connection_check_state() const {
|
2015-03-09 13:22:31 +01:00
|
|
|
return connection_check_state_;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int Http2Session::noop() { return 0; }
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
int Http2Session::read_noop(const uint8_t *data, size_t datalen) { return 0; }
|
|
|
|
|
|
|
|
int Http2Session::write_noop() { return 0; }
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
int Http2Session::connected() {
|
2016-08-24 17:25:03 +02:00
|
|
|
auto sock_error = util::get_socket_error(conn_.fd);
|
|
|
|
if (sock_error != 0) {
|
2016-08-19 09:25:05 +02:00
|
|
|
SSLOG(WARN, this) << "Backend connect failed; addr="
|
2016-12-04 15:43:41 +01:00
|
|
|
<< util::to_numeric_addr(raddr_)
|
2016-08-24 17:25:03 +02:00
|
|
|
<< ": errno=" << sock_error;
|
2016-02-21 08:11:50 +01:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2016-02-21 06:53:06 +01:00
|
|
|
|
2015-01-05 08:14:10 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Connection established";
|
|
|
|
}
|
|
|
|
|
2016-10-10 15:50:41 +02:00
|
|
|
// Reset timeout for write. Previously, we set timeout for connect.
|
2018-09-30 05:23:34 +02:00
|
|
|
conn_.wt.repeat = group_->shared_addr->timeout.write;
|
2016-10-10 15:50:41 +02:00
|
|
|
ev_timer_again(conn_.loop, &conn_.wt);
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
conn_.rlimit.startw();
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.again_rt();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-11-09 13:35:53 +01:00
|
|
|
read_ = &Http2Session::read_clear;
|
|
|
|
write_ = &Http2Session::write_clear;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2018-10-17 04:03:49 +02:00
|
|
|
if (state_ == Http2SessionState::PROXY_CONNECTING) {
|
2014-12-27 18:59:06 +01:00
|
|
|
return do_write();
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:35:53 +01:00
|
|
|
if (conn_.tls.ssl) {
|
|
|
|
read_ = &Http2Session::tls_handshake;
|
|
|
|
write_ = &Http2Session::tls_handshake;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
return do_write();
|
|
|
|
}
|
|
|
|
|
2015-03-28 12:19:17 +01:00
|
|
|
if (connection_made() != 0) {
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::CONNECT_FAILING;
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::read_clear() {
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.last_read = ev_now(conn_.loop);
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
std::array<uint8_t, 16_k> buf;
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
for (;;) {
|
|
|
|
auto nread = conn_.read_clear(buf.data(), buf.size());
|
2015-02-04 13:15:58 +01:00
|
|
|
|
|
|
|
if (nread == 0) {
|
2017-03-30 14:43:28 +02:00
|
|
|
return write_clear();
|
2015-02-04 13:15:58 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (nread < 0) {
|
|
|
|
return nread;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
if (on_read(buf.data(), nread) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2015-02-04 13:15:58 +01:00
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::write_clear() {
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.last_read = ev_now(conn_.loop);
|
|
|
|
|
2016-02-07 10:54:44 +01:00
|
|
|
std::array<struct iovec, MAX_WR_IOVCNT> iov;
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
for (;;) {
|
|
|
|
if (wb_.rleft() > 0) {
|
2016-02-07 10:54:44 +01:00
|
|
|
auto iovcnt = wb_.riovec(iov.data(), iov.size());
|
|
|
|
auto nwrite = conn_.writev_clear(iov.data(), iovcnt);
|
2015-02-04 13:15:58 +01:00
|
|
|
|
|
|
|
if (nwrite == 0) {
|
|
|
|
return 0;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2015-02-04 13:15:58 +01:00
|
|
|
|
|
|
|
if (nwrite < 0) {
|
2016-12-24 14:50:02 +01:00
|
|
|
// We may have pending data in receive buffer which may
|
|
|
|
// contain part of response body. So keep reading. Invoke
|
|
|
|
// read event to get read(2) error just in case.
|
|
|
|
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
|
2016-12-25 15:03:57 +01:00
|
|
|
write_ = &Http2Session::write_void;
|
2016-12-24 14:50:02 +01:00
|
|
|
break;
|
2015-02-04 13:15:58 +01:00
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
wb_.drain(nwrite);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (on_write() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (wb_.rleft() == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
conn_.wlimit.stopw();
|
|
|
|
ev_timer_stop(conn_.loop, &conn_.wt);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::tls_handshake() {
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.last_read = ev_now(conn_.loop);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-01-08 16:07:28 +01:00
|
|
|
ERR_clear_error();
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
auto rv = conn_.tls_handshake();
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (rv == SHRPX_ERR_INPROGRESS) {
|
|
|
|
return 0;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rv < 0) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2016-06-16 17:53:38 +02:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
return rv;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "SSL/TLS handshake completed";
|
|
|
|
}
|
|
|
|
|
2016-02-07 10:39:06 +01:00
|
|
|
if (!get_config()->tls.insecure &&
|
2017-04-01 08:07:32 +02:00
|
|
|
tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2016-06-16 17:53:38 +02:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
read_ = &Http2Session::read_tls;
|
|
|
|
write_ = &Http2Session::write_tls;
|
|
|
|
|
2015-03-28 12:19:17 +01:00
|
|
|
if (connection_made() != 0) {
|
2018-10-17 04:03:49 +02:00
|
|
|
state_ = Http2SessionState::CONNECT_FAILING;
|
2014-12-27 18:59:06 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::read_tls() {
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.last_read = ev_now(conn_.loop);
|
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
std::array<uint8_t, 16_k> buf;
|
|
|
|
|
2015-01-08 16:07:28 +01:00
|
|
|
ERR_clear_error();
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
for (;;) {
|
2016-02-07 11:20:17 +01:00
|
|
|
auto nread = conn_.read_tls(buf.data(), buf.size());
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (nread == 0) {
|
2017-03-30 14:43:28 +02:00
|
|
|
return write_tls();
|
2015-01-06 13:56:19 +01:00
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (nread < 0) {
|
|
|
|
return nread;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
2015-01-06 13:56:19 +01:00
|
|
|
|
2016-02-07 11:20:17 +01:00
|
|
|
if (on_read(buf.data(), nread) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::write_tls() {
|
2016-11-25 16:00:32 +01:00
|
|
|
conn_.last_read = ev_now(conn_.loop);
|
|
|
|
|
2015-01-08 16:07:28 +01:00
|
|
|
ERR_clear_error();
|
|
|
|
|
2016-02-07 10:54:44 +01:00
|
|
|
struct iovec iov;
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
for (;;) {
|
|
|
|
if (wb_.rleft() > 0) {
|
2016-02-07 10:54:44 +01:00
|
|
|
auto iovcnt = wb_.riovec(&iov, 1);
|
2017-05-18 16:10:44 +02:00
|
|
|
if (iovcnt != 1) {
|
|
|
|
assert(0);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-07 10:54:44 +01:00
|
|
|
auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (nwrite == 0) {
|
|
|
|
return 0;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
if (nwrite < 0) {
|
2016-12-24 14:50:02 +01:00
|
|
|
// We may have pending data in receive buffer which may
|
|
|
|
// contain part of response body. So keep reading. Invoke
|
|
|
|
// read event to get read(2) error just in case.
|
|
|
|
ev_feed_event(conn_.loop, &conn_.rev, EV_READ);
|
2016-12-25 15:03:57 +01:00
|
|
|
write_ = &Http2Session::write_void;
|
2016-12-24 14:50:02 +01:00
|
|
|
break;
|
2014-12-27 18:59:06 +01:00
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
wb_.drain(nwrite);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2016-02-07 10:54:44 +01:00
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
if (on_write() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (wb_.rleft() == 0) {
|
2015-07-29 13:57:11 +02:00
|
|
|
conn_.start_tls_write_idle();
|
2014-12-27 18:59:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-04 13:15:58 +01:00
|
|
|
conn_.wlimit.stopw();
|
|
|
|
ev_timer_stop(conn_.loop, &conn_.wt);
|
2014-12-27 18:59:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-25 15:03:57 +01:00
|
|
|
int Http2Session::write_void() {
|
|
|
|
conn_.wlimit.stopw();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-27 18:59:06 +01:00
|
|
|
bool Http2Session::should_hard_fail() const {
|
|
|
|
switch (state_) {
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::PROXY_CONNECTING:
|
|
|
|
case Http2SessionState::PROXY_FAILED:
|
2014-12-27 18:59:06 +01:00
|
|
|
return true;
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::DISCONNECTED: {
|
2016-04-12 16:30:52 +02:00
|
|
|
const auto &proxy = get_config()->downstream_http_proxy;
|
|
|
|
return !proxy.host.empty();
|
|
|
|
}
|
2014-12-27 18:59:06 +01:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-27 15:24:14 +01:00
|
|
|
DownstreamAddr *Http2Session::get_addr() const { return addr_; }
|
2015-07-12 15:16:20 +02:00
|
|
|
|
2015-11-15 16:12:54 +01:00
|
|
|
int Http2Session::handle_downstream_push_promise(Downstream *downstream,
|
|
|
|
int32_t promised_stream_id) {
|
|
|
|
auto upstream = downstream->get_upstream();
|
|
|
|
if (!upstream->push_enabled()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto promised_downstream =
|
|
|
|
upstream->on_downstream_push_promise(downstream, promised_stream_id);
|
|
|
|
if (!promised_downstream) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we have Downstream object for pushed stream.
|
|
|
|
// promised_downstream->get_stream() still returns 0.
|
|
|
|
|
|
|
|
auto handler = upstream->get_client_handler();
|
|
|
|
|
2018-10-15 16:02:44 +02:00
|
|
|
auto promised_dconn = std::make_unique<Http2DownstreamConnection>(this);
|
2015-11-15 16:12:54 +01:00
|
|
|
promised_dconn->set_client_handler(handler);
|
|
|
|
|
|
|
|
auto ptr = promised_dconn.get();
|
|
|
|
|
|
|
|
if (promised_downstream->attach_downstream_connection(
|
|
|
|
std::move(promised_dconn)) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-10-15 16:02:44 +02:00
|
|
|
auto promised_sd = std::make_unique<StreamData>();
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
nghttp2_session_set_stream_user_data(session_, promised_stream_id,
|
|
|
|
promised_sd.get());
|
|
|
|
|
|
|
|
ptr->attach_stream_data(promised_sd.get());
|
|
|
|
streams_.append(promised_sd.release());
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Http2Session::handle_downstream_push_promise_complete(
|
|
|
|
Downstream *downstream, Downstream *promised_downstream) {
|
2016-01-13 14:45:52 +01:00
|
|
|
auto &promised_req = promised_downstream->request();
|
|
|
|
|
2016-03-10 14:42:07 +01:00
|
|
|
auto &promised_balloc = promised_downstream->get_block_allocator();
|
|
|
|
|
2016-01-13 14:45:52 +01:00
|
|
|
auto authority = promised_req.fs.header(http2::HD__AUTHORITY);
|
|
|
|
auto path = promised_req.fs.header(http2::HD__PATH);
|
|
|
|
auto method = promised_req.fs.header(http2::HD__METHOD);
|
|
|
|
auto scheme = promised_req.fs.header(http2::HD__SCHEME);
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
if (!authority) {
|
2016-01-13 14:45:52 +01:00
|
|
|
authority = promised_req.fs.header(http2::HD_HOST);
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auto method_token = http2::lookup_method_token(method->value);
|
|
|
|
if (method_token == -1) {
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Unrecognized method: " << method->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Rewrite authority if we enabled rewrite host. But we
|
|
|
|
// really don't know how to rewrite host. Should we use the same
|
|
|
|
// host in associated stream?
|
2016-03-10 14:42:07 +01:00
|
|
|
if (authority) {
|
|
|
|
promised_req.authority = authority->value;
|
|
|
|
}
|
2016-01-13 14:45:52 +01:00
|
|
|
promised_req.method = method_token;
|
2015-11-15 16:12:54 +01:00
|
|
|
// libnghttp2 ensures that we don't have CONNECT method in
|
|
|
|
// PUSH_PROMISE, and guarantees that :scheme exists.
|
2016-03-10 14:42:07 +01:00
|
|
|
if (scheme) {
|
|
|
|
promised_req.scheme = scheme->value;
|
|
|
|
}
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
// For server-wide OPTIONS request, path is empty.
|
|
|
|
if (method_token != HTTP_OPTIONS || path->value != "*") {
|
2016-03-10 14:42:07 +01:00
|
|
|
promised_req.path = http2::rewrite_clean_path(promised_balloc, path->value);
|
2015-11-15 16:12:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
promised_downstream->inspect_http2_request();
|
|
|
|
|
|
|
|
auto upstream = promised_downstream->get_upstream();
|
|
|
|
|
2018-10-17 03:03:32 +02:00
|
|
|
promised_downstream->set_request_state(DownstreamState::MSG_COMPLETE);
|
2016-09-14 17:25:41 +02:00
|
|
|
promised_downstream->set_request_header_sent(true);
|
2015-11-15 16:12:54 +01:00
|
|
|
|
|
|
|
if (upstream->on_downstream_push_promise_complete(downstream,
|
|
|
|
promised_downstream) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-27 11:39:03 +01:00
|
|
|
size_t Http2Session::get_num_dconns() const { return dconns_.size(); }
|
|
|
|
|
|
|
|
bool Http2Session::max_concurrency_reached(size_t extra) const {
|
|
|
|
if (!session_) {
|
|
|
|
return dconns_.size() + extra >= 100;
|
|
|
|
}
|
|
|
|
|
2016-02-28 04:41:34 +01:00
|
|
|
// If session does not allow further requests, it effectively means
|
|
|
|
// that maximum concurrency is reached.
|
|
|
|
return !nghttp2_session_check_request_allowed(session_) ||
|
|
|
|
dconns_.size() + extra >=
|
|
|
|
nghttp2_session_get_remote_settings(
|
|
|
|
session_, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
|
2016-02-27 11:39:03 +01:00
|
|
|
}
|
|
|
|
|
2016-08-04 17:04:47 +02:00
|
|
|
const std::shared_ptr<DownstreamAddrGroup> &
|
|
|
|
Http2Session::get_downstream_addr_group() const {
|
|
|
|
return group_;
|
2016-02-27 15:24:14 +01:00
|
|
|
}
|
|
|
|
|
2016-04-02 16:11:03 +02:00
|
|
|
void Http2Session::add_to_extra_freelist() {
|
2018-10-17 03:15:50 +02:00
|
|
|
if (freelist_zone_ != FreelistZone::NONE) {
|
2016-04-13 12:22:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-04-02 16:11:03 +02:00
|
|
|
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Append to http2_extra_freelist, addr=" << addr_
|
|
|
|
<< ", freelist.size="
|
|
|
|
<< addr_->http2_extra_freelist.size();
|
|
|
|
}
|
|
|
|
|
2018-10-17 03:15:50 +02:00
|
|
|
freelist_zone_ = FreelistZone::EXTRA;
|
2016-04-02 16:11:03 +02:00
|
|
|
addr_->http2_extra_freelist.append(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Http2Session::remove_from_freelist() {
|
|
|
|
switch (freelist_zone_) {
|
2018-10-17 03:15:50 +02:00
|
|
|
case FreelistZone::NONE:
|
2016-04-02 16:11:03 +02:00
|
|
|
return;
|
2018-10-17 03:15:50 +02:00
|
|
|
case FreelistZone::EXTRA:
|
2016-04-02 16:11:03 +02:00
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
|
|
SSLOG(INFO, this) << "Remove from http2_extra_freelist, addr=" << addr_
|
|
|
|
<< ", freelist.size="
|
|
|
|
<< addr_->http2_extra_freelist.size();
|
|
|
|
}
|
|
|
|
addr_->http2_extra_freelist.remove(this);
|
|
|
|
break;
|
2018-10-17 03:15:50 +02:00
|
|
|
case FreelistZone::GONE:
|
2016-04-12 16:30:52 +02:00
|
|
|
return;
|
2016-04-02 16:11:03 +02:00
|
|
|
}
|
|
|
|
|
2018-10-17 03:15:50 +02:00
|
|
|
freelist_zone_ = FreelistZone::NONE;
|
2016-04-02 16:11:03 +02:00
|
|
|
}
|
|
|
|
|
2016-04-12 16:30:52 +02:00
|
|
|
void Http2Session::exclude_from_scheduling() {
|
|
|
|
remove_from_freelist();
|
2018-10-17 03:15:50 +02:00
|
|
|
freelist_zone_ = FreelistZone::GONE;
|
2016-04-12 16:30:52 +02:00
|
|
|
}
|
|
|
|
|
2016-05-14 10:17:27 +02:00
|
|
|
DefaultMemchunks *Http2Session::get_request_buf() { return &wb_; }
|
|
|
|
|
2016-05-24 14:59:24 +02:00
|
|
|
void Http2Session::on_timeout() {
|
|
|
|
switch (state_) {
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::PROXY_CONNECTING: {
|
2016-05-24 14:59:24 +02:00
|
|
|
auto worker_blocker = worker_->get_connect_blocker();
|
|
|
|
worker_blocker->on_failure();
|
|
|
|
break;
|
|
|
|
}
|
2018-10-17 04:03:49 +02:00
|
|
|
case Http2SessionState::CONNECTING:
|
2016-08-19 09:25:05 +02:00
|
|
|
SSLOG(WARN, this) << "Connect time out; addr="
|
2016-12-04 15:43:41 +01:00
|
|
|
<< util::to_numeric_addr(raddr_);
|
2016-08-19 09:25:05 +02:00
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
downstream_failure(addr_, raddr_);
|
2016-05-24 14:59:24 +02:00
|
|
|
break;
|
2018-10-17 04:03:49 +02:00
|
|
|
default:
|
|
|
|
break;
|
2016-05-24 14:59:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-04 05:36:22 +02:00
|
|
|
void Http2Session::check_retire() {
|
|
|
|
if (!group_->retired) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev_prepare_stop(conn_.loop, &prep_);
|
|
|
|
|
2020-11-15 03:18:32 +01:00
|
|
|
if (!session_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-04 05:36:22 +02:00
|
|
|
auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_);
|
|
|
|
nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id,
|
|
|
|
NGHTTP2_NO_ERROR, nullptr, 0);
|
|
|
|
|
|
|
|
signal_write();
|
|
|
|
}
|
|
|
|
|
2016-12-04 15:43:41 +01:00
|
|
|
const Address *Http2Session::get_raddr() const { return raddr_; }
|
|
|
|
|
2018-03-11 04:02:18 +01:00
|
|
|
void Http2Session::on_settings_received(const nghttp2_frame *frame) {
|
|
|
|
// TODO This effectively disallows nghttpx to change its behaviour
|
|
|
|
// based on the 2nd SETTINGS.
|
|
|
|
if (settings_recved_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
settings_recved_ = true;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < frame->settings.niv; ++i) {
|
|
|
|
auto &ent = frame->settings.iv[i];
|
|
|
|
if (ent.settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) {
|
|
|
|
allow_connect_proto_ = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
submit_pending_requests();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Http2Session::get_allow_connect_proto() const {
|
|
|
|
return allow_connect_proto_;
|
|
|
|
}
|
|
|
|
|
2012-11-20 17:29:39 +01:00
|
|
|
} // namespace shrpx
|