2012-06-04 16:48:31 +02:00
|
|
|
/*
|
2014-03-30 12:09:21 +02:00
|
|
|
* nghttp2 - HTTP/2 C Library
|
2012-06-04 16:48:31 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
* a copy of this software and associated documentation files (the
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
* the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include "shrpx_client_handler.h"
|
|
|
|
|
2012-08-21 17:14:02 +02:00
|
|
|
#include <unistd.h>
|
2012-07-15 14:15:28 +02:00
|
|
|
#include <cerrno>
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
#include "shrpx_upstream.h"
|
2013-07-26 12:38:54 +02:00
|
|
|
#include "shrpx_http2_upstream.h"
|
2012-06-04 16:48:31 +02:00
|
|
|
#include "shrpx_https_upstream.h"
|
|
|
|
#include "shrpx_config.h"
|
2012-11-18 13:23:13 +01:00
|
|
|
#include "shrpx_http_downstream_connection.h"
|
2013-11-04 09:53:57 +01:00
|
|
|
#include "shrpx_http2_downstream_connection.h"
|
2014-01-01 16:53:07 +01:00
|
|
|
#include "shrpx_ssl.h"
|
2014-06-26 15:55:22 +02:00
|
|
|
#include "shrpx_worker.h"
|
2014-08-12 15:22:02 +02:00
|
|
|
#include "shrpx_worker_config.h"
|
2014-10-13 14:09:00 +02:00
|
|
|
#include "shrpx_downstream_connection_pool.h"
|
2013-07-26 13:12:55 +02:00
|
|
|
#ifdef HAVE_SPDYLAY
|
|
|
|
#include "shrpx_spdy_upstream.h"
|
|
|
|
#endif // HAVE_SPDYLAY
|
2013-09-23 17:02:02 +02:00
|
|
|
#include "util.h"
|
2014-09-21 14:50:12 +02:00
|
|
|
#include "libevent_util.h"
|
2013-09-23 17:02:02 +02:00
|
|
|
|
|
|
|
using namespace nghttp2;
|
2013-07-26 13:12:55 +02:00
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
namespace shrpx {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void upstream_readcb(bufferevent *bev, void *arg)
|
|
|
|
{
|
2014-01-19 11:46:58 +01:00
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
2014-09-18 16:03:36 +02:00
|
|
|
auto upstream = handler->get_upstream();
|
|
|
|
if(upstream) {
|
|
|
|
upstream->reset_timeouts();
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
int rv = handler->on_read();
|
|
|
|
if(rv != 0) {
|
|
|
|
delete handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void upstream_writecb(bufferevent *bev, void *arg)
|
|
|
|
{
|
2014-01-19 11:46:58 +01:00
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
2014-09-18 16:03:36 +02:00
|
|
|
auto upstream = handler->get_upstream();
|
|
|
|
if(upstream) {
|
|
|
|
upstream->reset_timeouts();
|
|
|
|
}
|
2014-06-10 17:07:51 +02:00
|
|
|
|
2014-11-05 16:56:07 +01:00
|
|
|
handler->update_last_write_time();
|
|
|
|
|
2014-05-31 19:29:01 +02:00
|
|
|
// We actually depend on write low-water mark == 0.
|
2014-01-19 09:49:04 +01:00
|
|
|
if(handler->get_outbuf_length() > 0) {
|
2012-11-21 19:13:30 +01:00
|
|
|
// Possibly because of deferred callback, we may get this callback
|
|
|
|
// when the output buffer is not empty.
|
|
|
|
return;
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
if(handler->get_should_close_after_write()) {
|
|
|
|
delete handler;
|
2014-01-19 15:32:07 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-09-18 16:03:36 +02:00
|
|
|
|
2014-01-19 15:32:07 +01:00
|
|
|
if(!upstream) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int rv = upstream->on_write();
|
|
|
|
if(rv != 0) {
|
|
|
|
delete handler;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void upstream_eventcb(bufferevent *bev, short events, void *arg)
|
|
|
|
{
|
2014-01-19 11:46:58 +01:00
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
2012-06-04 16:48:31 +02:00
|
|
|
bool finish = false;
|
|
|
|
if(events & BEV_EVENT_EOF) {
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, handler) << "EOF";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
finish = true;
|
|
|
|
}
|
|
|
|
if(events & BEV_EVENT_ERROR) {
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, handler) << "Network error: "
|
|
|
|
<< evutil_socket_error_to_string
|
|
|
|
(EVUTIL_SOCKET_ERROR());
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
finish = true;
|
|
|
|
}
|
|
|
|
if(events & BEV_EVENT_TIMEOUT) {
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, handler) << "Time out";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
finish = true;
|
|
|
|
}
|
|
|
|
if(finish) {
|
|
|
|
delete handler;
|
|
|
|
} else {
|
|
|
|
if(events & BEV_EVENT_CONNECTED) {
|
2014-01-18 11:53:52 +01:00
|
|
|
handler->set_tls_handshake(true);
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2014-01-18 11:53:52 +01:00
|
|
|
CLOG(INFO, handler) << "SSL/TLS handshake completed";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-01-01 16:53:07 +01:00
|
|
|
if(handler->validate_next_proto() != 0) {
|
|
|
|
delete handler;
|
|
|
|
return;
|
|
|
|
}
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-07-14 16:24:03 +02:00
|
|
|
if(SSL_session_reused(handler->get_ssl())) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, handler) << "SSL/TLS session reused";
|
2012-07-14 16:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-07-26 14:35:14 +02:00
|
|
|
namespace {
|
2013-08-03 11:51:01 +02:00
|
|
|
void upstream_http2_connhd_readcb(bufferevent *bev, void *arg)
|
2013-07-26 14:35:14 +02:00
|
|
|
{
|
2013-08-03 11:51:01 +02:00
|
|
|
// This callback assumes upstream is Http2Upstream.
|
2014-01-19 11:46:58 +01:00
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
2014-10-30 13:47:38 +01:00
|
|
|
if(handler->on_http2_connhd_read() != 0) {
|
2013-07-26 14:35:14 +02:00
|
|
|
delete handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2013-08-03 11:51:01 +02:00
|
|
|
namespace {
|
|
|
|
void upstream_http1_connhd_readcb(bufferevent *bev, void *arg)
|
|
|
|
{
|
|
|
|
// This callback assumes upstream is HttpsUpstream.
|
2014-01-19 11:46:58 +01:00
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
2014-10-30 13:47:38 +01:00
|
|
|
if(handler->on_http1_connhd_read() != 0) {
|
2013-08-03 11:51:01 +02:00
|
|
|
delete handler;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-03-09 06:53:28 +01:00
|
|
|
ClientHandler::ClientHandler(bufferevent *bev,
|
|
|
|
bufferevent_rate_limit_group *rate_limit_group,
|
|
|
|
int fd, SSL *ssl,
|
2014-06-26 15:55:22 +02:00
|
|
|
const char *ipaddr,
|
2014-10-13 14:09:00 +02:00
|
|
|
WorkerStat *worker_stat,
|
|
|
|
DownstreamConnectionPool *dconn_pool)
|
2013-12-06 15:17:38 +01:00
|
|
|
: ipaddr_(ipaddr),
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn_pool_(dconn_pool),
|
2013-12-06 15:17:38 +01:00
|
|
|
bev_(bev),
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_(nullptr),
|
2013-12-06 15:17:38 +01:00
|
|
|
ssl_(ssl),
|
2014-06-10 18:16:49 +02:00
|
|
|
reneg_shutdown_timerev_(nullptr),
|
2014-06-26 15:55:22 +02:00
|
|
|
worker_stat_(worker_stat),
|
2014-11-05 16:56:07 +01:00
|
|
|
last_write_time_(0),
|
|
|
|
warmup_writelen_(0),
|
2014-03-30 14:02:25 +02:00
|
|
|
left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN),
|
2013-12-06 15:17:38 +01:00
|
|
|
fd_(fd),
|
2014-01-18 11:53:52 +01:00
|
|
|
should_close_after_write_(false),
|
|
|
|
tls_handshake_(false),
|
2014-06-10 18:16:49 +02:00
|
|
|
tls_renegotiation_(false)
|
2012-06-04 16:48:31 +02:00
|
|
|
{
|
2013-12-20 15:28:54 +01:00
|
|
|
int rv;
|
2014-03-09 06:53:28 +01:00
|
|
|
|
2014-06-26 15:55:22 +02:00
|
|
|
++worker_stat->num_connections;
|
|
|
|
|
2014-08-17 09:17:10 +02:00
|
|
|
rv = bufferevent_set_rate_limit(bev_, get_config()->rate_limit_cfg);
|
|
|
|
if(rv == -1) {
|
|
|
|
CLOG(FATAL, this) << "bufferevent_set_rate_limit() failed";
|
|
|
|
}
|
|
|
|
|
2014-06-10 18:16:49 +02:00
|
|
|
rv = bufferevent_add_to_rate_limit_group(bev_, rate_limit_group);
|
2014-03-09 06:53:28 +01:00
|
|
|
if(rv == -1) {
|
|
|
|
CLOG(FATAL, this) << "bufferevent_add_to_rate_limit_group() failed";
|
|
|
|
}
|
|
|
|
|
2014-09-18 16:56:01 +02:00
|
|
|
util::bev_enable_unless(bev_, EV_READ | EV_WRITE);
|
2014-05-31 19:29:01 +02:00
|
|
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WATERMARK);
|
2012-06-04 16:48:31 +02:00
|
|
|
set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
|
|
|
&get_config()->upstream_write_timeout);
|
2012-11-18 13:23:13 +01:00
|
|
|
if(ssl_) {
|
2014-01-18 11:53:52 +01:00
|
|
|
SSL_set_app_data(ssl_, reinterpret_cast<char*>(this));
|
2013-07-26 12:33:25 +02:00
|
|
|
set_bev_cb(nullptr, upstream_writecb, upstream_eventcb);
|
2012-11-18 13:23:13 +01:00
|
|
|
} else {
|
2013-08-03 11:51:01 +02:00
|
|
|
// For non-TLS version, first create HttpsUpstream. It may be
|
2014-03-30 12:09:21 +02:00
|
|
|
// upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2
|
2013-08-03 11:51:01 +02:00
|
|
|
// connection.
|
2013-09-23 17:02:02 +02:00
|
|
|
upstream_ = util::make_unique<HttpsUpstream>(this);
|
2013-08-03 11:51:01 +02:00
|
|
|
set_bev_cb(upstream_http1_connhd_readcb, nullptr, upstream_eventcb);
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ClientHandler::~ClientHandler()
|
|
|
|
{
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, this) << "Deleting";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-06-10 17:07:51 +02:00
|
|
|
|
2014-06-26 15:55:22 +02:00
|
|
|
--worker_stat_->num_connections;
|
|
|
|
|
2014-08-12 15:22:02 +02:00
|
|
|
// TODO If backend is http/2, and it is in CONNECTED state, signal
|
|
|
|
// it and make it loopbreak when output is zero.
|
2014-08-19 14:33:54 +02:00
|
|
|
if(worker_config->graceful_shutdown && worker_stat_->num_connections == 0) {
|
2014-08-12 15:22:02 +02:00
|
|
|
event_base_loopbreak(get_evbase());
|
|
|
|
}
|
|
|
|
|
2014-06-10 18:16:49 +02:00
|
|
|
if(reneg_shutdown_timerev_) {
|
|
|
|
event_free(reneg_shutdown_timerev_);
|
|
|
|
}
|
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
if(ssl_) {
|
2014-01-18 11:53:52 +01:00
|
|
|
SSL_set_app_data(ssl_, nullptr);
|
2014-01-08 15:32:47 +01:00
|
|
|
SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
|
2012-11-18 13:23:13 +01:00
|
|
|
SSL_shutdown(ssl_);
|
|
|
|
}
|
2014-03-09 06:53:28 +01:00
|
|
|
|
2014-06-10 18:16:49 +02:00
|
|
|
bufferevent_remove_from_rate_limit_group(bev_);
|
2014-03-09 06:53:28 +01:00
|
|
|
|
2014-09-18 16:56:01 +02:00
|
|
|
util::bev_disable_unless(bev_, EV_READ | EV_WRITE);
|
2012-06-04 16:48:31 +02:00
|
|
|
bufferevent_free(bev_);
|
2014-06-10 18:16:49 +02:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
if(ssl_) {
|
|
|
|
SSL_free(ssl_);
|
|
|
|
}
|
2014-06-10 18:16:49 +02:00
|
|
|
|
2012-11-18 13:23:13 +01:00
|
|
|
shutdown(fd_, SHUT_WR);
|
|
|
|
close(fd_);
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, this) << "Deleted";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Upstream* ClientHandler::get_upstream()
|
|
|
|
{
|
2013-09-23 17:02:02 +02:00
|
|
|
return upstream_.get();
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bufferevent* ClientHandler::get_bev() const
|
|
|
|
{
|
|
|
|
return bev_;
|
|
|
|
}
|
|
|
|
|
|
|
|
event_base* ClientHandler::get_evbase() const
|
|
|
|
{
|
|
|
|
return bufferevent_get_base(bev_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::set_bev_cb
|
|
|
|
(bufferevent_data_cb readcb, bufferevent_data_cb writecb,
|
|
|
|
bufferevent_event_cb eventcb)
|
|
|
|
{
|
|
|
|
bufferevent_setcb(bev_, readcb, writecb, eventcb, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::set_upstream_timeouts(const timeval *read_timeout,
|
|
|
|
const timeval *write_timeout)
|
|
|
|
{
|
2014-06-10 18:16:49 +02:00
|
|
|
bufferevent_set_timeouts(bev_, read_timeout, write_timeout);
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ClientHandler::validate_next_proto()
|
|
|
|
{
|
2013-07-26 12:33:25 +02:00
|
|
|
const unsigned char *next_proto = nullptr;
|
2012-06-04 16:48:31 +02:00
|
|
|
unsigned int next_proto_len;
|
2014-06-10 17:07:51 +02:00
|
|
|
int rv;
|
|
|
|
|
2013-07-26 14:35:14 +02:00
|
|
|
// First set callback for catch all cases
|
|
|
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
2012-06-04 16:48:31 +02:00
|
|
|
SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
|
2014-01-01 15:54:28 +01:00
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
|
|
if(next_proto) {
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
std::string proto(next_proto, next_proto+next_proto_len);
|
|
|
|
CLOG(INFO, this) << "The negotiated next protocol: " << proto;
|
2013-07-26 13:12:55 +02:00
|
|
|
}
|
2014-01-01 16:53:07 +01:00
|
|
|
if(!ssl::in_proto_list(get_config()->npn_list,
|
|
|
|
next_proto, next_proto_len)) {
|
|
|
|
break;
|
|
|
|
}
|
2014-11-14 15:14:39 +01:00
|
|
|
if(util::check_h2_is_selected(next_proto, next_proto_len)) {
|
2014-04-26 15:51:39 +02:00
|
|
|
|
2014-06-10 17:07:51 +02:00
|
|
|
set_bev_cb(upstream_http2_connhd_readcb, upstream_writecb,
|
|
|
|
upstream_eventcb);
|
|
|
|
|
|
|
|
auto http2_upstream = util::make_unique<Http2Upstream>(this);
|
|
|
|
|
2014-04-26 15:51:39 +02:00
|
|
|
if(!ssl::check_http2_requirement(ssl_)) {
|
2014-06-10 17:07:51 +02:00
|
|
|
rv = http2_upstream->terminate_session(NGHTTP2_INADEQUATE_SECURITY);
|
|
|
|
|
|
|
|
if(rv != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-04-26 15:51:39 +02:00
|
|
|
}
|
|
|
|
|
2014-06-10 17:07:51 +02:00
|
|
|
upstream_ = std::move(http2_upstream);
|
|
|
|
|
2014-06-19 15:40:24 +02:00
|
|
|
// At this point, input buffer is already filled with some
|
|
|
|
// bytes. The read callback is not called until new data
|
|
|
|
// come. So consume input buffer here.
|
2014-10-30 13:47:38 +01:00
|
|
|
if(on_http2_connhd_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-06-19 15:40:24 +02:00
|
|
|
|
2014-01-01 15:54:28 +01:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
#ifdef HAVE_SPDYLAY
|
|
|
|
uint16_t version = spdylay_npn_get_version(next_proto, next_proto_len);
|
|
|
|
if(version) {
|
|
|
|
upstream_ = util::make_unique<SpdyUpstream>(version, this);
|
2014-06-19 15:40:24 +02:00
|
|
|
|
|
|
|
// At this point, input buffer is already filled with some
|
|
|
|
// bytes. The read callback is not called until new data
|
|
|
|
// come. So consume input buffer here.
|
|
|
|
if(upstream_->on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-01 15:54:28 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2013-07-26 13:12:55 +02:00
|
|
|
#endif // HAVE_SPDYLAY
|
2014-01-01 16:53:07 +01:00
|
|
|
if(next_proto_len == 8 && memcmp("http/1.1", next_proto, 8) == 0) {
|
|
|
|
upstream_ = util::make_unique<HttpsUpstream>(this);
|
2014-06-19 15:40:24 +02:00
|
|
|
|
|
|
|
// At this point, input buffer is already filled with some
|
|
|
|
// bytes. The read callback is not called until new data
|
|
|
|
// come. So consume input buffer here.
|
|
|
|
if(upstream_->on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-01 16:53:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-01-01 15:54:28 +01:00
|
|
|
}
|
|
|
|
break;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-01-01 15:54:28 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
|
|
|
|
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
|
|
|
|
break;
|
|
|
|
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
|
|
|
|
}
|
|
|
|
if(!next_proto) {
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2014-01-01 16:53:07 +01:00
|
|
|
CLOG(INFO, this) << "No protocol negotiated. Fallback to HTTP/1.1";
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2014-01-01 16:53:07 +01:00
|
|
|
upstream_ = util::make_unique<HttpsUpstream>(this);
|
2014-06-19 15:40:24 +02:00
|
|
|
|
|
|
|
// At this point, input buffer is already filled with some bytes.
|
|
|
|
// The read callback is not called until new data come. So consume
|
|
|
|
// input buffer here.
|
|
|
|
if(upstream_->on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-01 16:53:07 +01:00
|
|
|
return 0;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2014-01-01 16:53:07 +01:00
|
|
|
CLOG(INFO, this) << "The negotiated protocol is not supported";
|
2012-06-05 18:26:04 +02:00
|
|
|
}
|
2014-01-01 16:53:07 +01:00
|
|
|
return -1;
|
2012-06-04 16:48:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ClientHandler::on_read()
|
|
|
|
{
|
|
|
|
return upstream_->on_read();
|
|
|
|
}
|
|
|
|
|
|
|
|
int ClientHandler::on_event()
|
|
|
|
{
|
|
|
|
return upstream_->on_event();
|
|
|
|
}
|
|
|
|
|
2014-10-30 13:47:38 +01:00
|
|
|
int ClientHandler::on_http2_connhd_read()
|
|
|
|
{
|
|
|
|
// This callback assumes upstream is Http2Upstream.
|
|
|
|
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
|
|
|
|
auto input = bufferevent_get_input(bev_);
|
|
|
|
auto readlen = evbuffer_remove(input, data, left_connhd_len_);
|
|
|
|
|
|
|
|
if(readlen == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
|
|
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
|
|
|
|
data, readlen) != 0) {
|
|
|
|
// There is no downgrade path here. Just drop the connection.
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, this) << "invalid client connection header";
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
left_connhd_len_ -= readlen;
|
|
|
|
|
|
|
|
if(left_connhd_len_ > 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
|
|
|
|
|
|
|
// Run on_read to process data left in buffer since they are not
|
|
|
|
// notified further
|
|
|
|
if(on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ClientHandler::on_http1_connhd_read()
|
|
|
|
{
|
|
|
|
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
|
|
|
|
auto input = bufferevent_get_input(bev_);
|
|
|
|
auto readlen = evbuffer_copyout(input, data, left_connhd_len_);
|
|
|
|
|
|
|
|
if(readlen == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
|
|
|
|
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
|
|
|
|
data, readlen) != 0) {
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
|
|
|
|
<< "but may be upgraded to HTTP/2 later.";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset header length for later HTTP/2 upgrade
|
|
|
|
left_connhd_len_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
|
|
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
|
|
|
|
|
|
|
if(on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(evbuffer_drain(input, readlen) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
left_connhd_len_ -= readlen;
|
|
|
|
|
|
|
|
if(left_connhd_len_ > 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, this) << "direct HTTP/2 connection";
|
|
|
|
}
|
|
|
|
|
|
|
|
direct_http2_upgrade();
|
|
|
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
|
|
|
|
|
|
|
// Run on_read to process data left in buffer since they are not
|
|
|
|
// notified further
|
|
|
|
if(on_read() != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
const std::string& ClientHandler::get_ipaddr() const
|
|
|
|
{
|
|
|
|
return ipaddr_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientHandler::get_should_close_after_write() const
|
|
|
|
{
|
|
|
|
return should_close_after_write_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::set_should_close_after_write(bool f)
|
|
|
|
{
|
|
|
|
should_close_after_write_ = f;
|
|
|
|
}
|
|
|
|
|
2014-08-18 17:16:51 +02:00
|
|
|
void ClientHandler::pool_downstream_connection
|
|
|
|
(std::unique_ptr<DownstreamConnection> dconn)
|
2012-06-09 16:14:00 +02:00
|
|
|
{
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2014-08-18 17:16:51 +02:00
|
|
|
CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get();
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn->set_client_handler(nullptr);
|
|
|
|
dconn_pool_->add_downstream_connection(std::move(dconn));
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn)
|
|
|
|
{
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, this) << "Removing downstream connection DCONN:" << dconn
|
|
|
|
<< " from pool";
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn_pool_->remove_downstream_connection(dconn);
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
|
|
|
|
2014-08-18 17:16:51 +02:00
|
|
|
std::unique_ptr<DownstreamConnection>
|
|
|
|
ClientHandler::get_downstream_connection()
|
2012-06-09 16:14:00 +02:00
|
|
|
{
|
2014-10-13 14:09:00 +02:00
|
|
|
auto dconn = dconn_pool_->pop_downstream_connection();
|
|
|
|
|
|
|
|
if(!dconn) {
|
2013-01-21 14:42:49 +01:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
2012-12-09 11:15:14 +01:00
|
|
|
CLOG(INFO, this) << "Downstream connection pool is empty."
|
|
|
|
<< " Create new one";
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-10-13 14:09:00 +02:00
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
if(http2session_) {
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn = util::make_unique<Http2DownstreamConnection>
|
|
|
|
(dconn_pool_, http2session_);
|
2012-11-18 13:23:13 +01:00
|
|
|
} else {
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn = util::make_unique<HttpDownstreamConnection>(dconn_pool_);
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn->set_client_handler(this);
|
|
|
|
return dconn;
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
2014-08-18 17:16:51 +02:00
|
|
|
|
2014-10-13 14:09:00 +02:00
|
|
|
dconn->set_client_handler(this);
|
|
|
|
|
2014-08-18 17:16:51 +02:00
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, this) << "Reuse downstream connection DCONN:" << dconn.get()
|
|
|
|
<< " from pool";
|
|
|
|
}
|
|
|
|
|
|
|
|
return dconn;
|
2012-06-09 16:14:00 +02:00
|
|
|
}
|
|
|
|
|
2014-01-19 09:49:04 +01:00
|
|
|
size_t ClientHandler::get_outbuf_length()
|
2012-07-11 09:20:16 +02:00
|
|
|
{
|
2014-06-10 18:16:49 +02:00
|
|
|
return evbuffer_get_length(bufferevent_get_output(bev_));
|
2012-07-11 09:20:16 +02:00
|
|
|
}
|
|
|
|
|
2012-07-14 16:24:03 +02:00
|
|
|
SSL* ClientHandler::get_ssl() const
|
|
|
|
{
|
|
|
|
return ssl_;
|
|
|
|
}
|
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
void ClientHandler::set_http2_session(Http2Session *http2session)
|
2012-11-18 13:23:13 +01:00
|
|
|
{
|
2013-11-04 09:53:57 +01:00
|
|
|
http2session_ = http2session;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2013-11-04 09:53:57 +01:00
|
|
|
Http2Session* ClientHandler::get_http2_session() const
|
2012-11-18 13:23:13 +01:00
|
|
|
{
|
2013-11-04 09:53:57 +01:00
|
|
|
return http2session_;
|
2012-11-18 13:23:13 +01:00
|
|
|
}
|
|
|
|
|
2014-08-19 16:36:04 +02:00
|
|
|
void ClientHandler::set_http1_connect_blocker
|
|
|
|
(ConnectBlocker *http1_connect_blocker)
|
|
|
|
{
|
|
|
|
http1_connect_blocker_ = http1_connect_blocker;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConnectBlocker* ClientHandler::get_http1_connect_blocker() const
|
|
|
|
{
|
|
|
|
return http1_connect_blocker_;
|
|
|
|
}
|
|
|
|
|
2013-08-03 11:51:01 +02:00
|
|
|
void ClientHandler::direct_http2_upgrade()
|
|
|
|
{
|
2013-09-23 17:02:02 +02:00
|
|
|
upstream_= util::make_unique<Http2Upstream>(this);
|
2013-08-03 11:51:01 +02:00
|
|
|
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ClientHandler::perform_http2_upgrade(HttpsUpstream *http)
|
|
|
|
{
|
|
|
|
int rv;
|
2013-09-23 17:02:02 +02:00
|
|
|
auto upstream = util::make_unique<Http2Upstream>(this);
|
2013-08-03 11:51:01 +02:00
|
|
|
if(upstream->upgrade_upstream(http) != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2013-09-26 14:39:19 +02:00
|
|
|
// http pointer is now owned by upstream.
|
|
|
|
upstream_.release();
|
2013-09-23 17:02:02 +02:00
|
|
|
upstream_ = std::move(upstream);
|
2013-08-03 11:51:01 +02:00
|
|
|
set_bev_cb(upstream_http2_connhd_readcb, upstream_writecb, upstream_eventcb);
|
|
|
|
static char res[] = "HTTP/1.1 101 Switching Protocols\r\n"
|
|
|
|
"Connection: Upgrade\r\n"
|
2014-03-30 14:30:47 +02:00
|
|
|
"Upgrade: " NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "\r\n"
|
2013-08-03 11:51:01 +02:00
|
|
|
"\r\n";
|
|
|
|
rv = bufferevent_write(bev_, res, sizeof(res) - 1);
|
|
|
|
if(rv != 0) {
|
|
|
|
CLOG(FATAL, this) << "bufferevent_write() faild";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientHandler::get_http2_upgrade_allowed() const
|
|
|
|
{
|
|
|
|
return !ssl_;
|
|
|
|
}
|
|
|
|
|
2013-12-21 09:49:31 +01:00
|
|
|
std::string ClientHandler::get_upstream_scheme() const
|
|
|
|
{
|
|
|
|
if(ssl_) {
|
|
|
|
return "https";
|
|
|
|
} else {
|
|
|
|
return "http";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-12 16:37:33 +02:00
|
|
|
void ClientHandler::set_tls_handshake(bool f)
|
|
|
|
{
|
|
|
|
tls_handshake_ = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientHandler::get_tls_handshake() const
|
|
|
|
{
|
|
|
|
return tls_handshake_;
|
|
|
|
}
|
|
|
|
|
2014-06-10 18:16:49 +02:00
|
|
|
namespace {
|
|
|
|
void shutdown_cb(evutil_socket_t fd, short what, void *arg)
|
|
|
|
{
|
|
|
|
auto handler = static_cast<ClientHandler*>(arg);
|
|
|
|
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, handler) << "Close connection due to TLS renegotiation";
|
|
|
|
}
|
|
|
|
|
|
|
|
delete handler;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2014-01-18 11:53:52 +01:00
|
|
|
void ClientHandler::set_tls_renegotiation(bool f)
|
|
|
|
{
|
2014-06-10 18:16:49 +02:00
|
|
|
if(tls_renegotiation_ == false) {
|
|
|
|
if(LOG_ENABLED(INFO)) {
|
|
|
|
CLOG(INFO, this) << "TLS renegotiation detected. "
|
|
|
|
<< "Start shutdown timer now.";
|
|
|
|
}
|
|
|
|
|
|
|
|
reneg_shutdown_timerev_ = evtimer_new(get_evbase(), shutdown_cb, this);
|
|
|
|
event_priority_set(reneg_shutdown_timerev_, 0);
|
|
|
|
|
|
|
|
timeval timeout = {0, 0};
|
|
|
|
|
|
|
|
// TODO What to do if this failed?
|
|
|
|
evtimer_add(reneg_shutdown_timerev_, &timeout);
|
|
|
|
}
|
2014-01-18 11:53:52 +01:00
|
|
|
tls_renegotiation_ = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClientHandler::get_tls_renegotiation() const
|
|
|
|
{
|
|
|
|
return tls_renegotiation_;
|
|
|
|
}
|
|
|
|
|
2014-11-05 16:56:07 +01:00
|
|
|
namespace {
|
|
|
|
const size_t SHRPX_SMALL_WRITE_LIMIT = 1300;
|
|
|
|
const size_t SHRPX_WARMUP_THRESHOLD = 1 << 20;
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
ssize_t ClientHandler::get_write_limit()
|
|
|
|
{
|
|
|
|
if(!ssl_) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
timeval tv;
|
|
|
|
if(event_base_gettimeofday_cached(get_evbase(), &tv) == 0) {
|
|
|
|
auto now = util::to_time64(tv);
|
|
|
|
if(now - last_write_time_ > 1000000) {
|
|
|
|
// Time out, use small record size
|
|
|
|
warmup_writelen_ = 0;
|
|
|
|
return SHRPX_SMALL_WRITE_LIMIT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If event_base_gettimeofday_cached() failed, we just skip timer
|
|
|
|
// checking. Don't know how to treat this.
|
|
|
|
|
|
|
|
if(warmup_writelen_ >= SHRPX_WARMUP_THRESHOLD) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SHRPX_SMALL_WRITE_LIMIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::update_warmup_writelen(size_t n)
|
|
|
|
{
|
|
|
|
if(warmup_writelen_ < SHRPX_WARMUP_THRESHOLD) {
|
|
|
|
warmup_writelen_ += n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientHandler::update_last_write_time()
|
|
|
|
{
|
|
|
|
timeval tv;
|
|
|
|
if(event_base_gettimeofday_cached(get_evbase(), &tv) == 0) {
|
|
|
|
last_write_time_ = util::to_time64(tv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-04 16:48:31 +02:00
|
|
|
} // namespace shrpx
|