Rewrite connection pooling
This commit is contained in:
parent
adefcad530
commit
9d19e2bfe9
|
@ -80,6 +80,7 @@ shrpx_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \
|
||||||
shrpx_https_upstream.cc shrpx_https_upstream.h \
|
shrpx_https_upstream.cc shrpx_https_upstream.h \
|
||||||
shrpx_downstream_queue.cc shrpx_downstream_queue.h \
|
shrpx_downstream_queue.cc shrpx_downstream_queue.h \
|
||||||
shrpx_downstream.cc shrpx_downstream.h \
|
shrpx_downstream.cc shrpx_downstream.h \
|
||||||
|
shrpx_downstream_connection.cc shrpx_downstream_connection.h \
|
||||||
shrpx_log.cc shrpx_log.h \
|
shrpx_log.cc shrpx_log.h \
|
||||||
shrpx_http.cc shrpx_http.h \
|
shrpx_http.cc shrpx_http.h \
|
||||||
shrpx_io_control.cc shrpx_io_control.h \
|
shrpx_io_control.cc shrpx_io_control.h \
|
||||||
|
|
|
@ -36,4 +36,6 @@
|
||||||
#define DIE() \
|
#define DIE() \
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
|
#define SHRPX_READ_WARTER_MARK (64*1024)
|
||||||
|
|
||||||
#endif // SHRPX_H
|
#endif // SHRPX_H
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "shrpx_spdy_upstream.h"
|
#include "shrpx_spdy_upstream.h"
|
||||||
#include "shrpx_https_upstream.h"
|
#include "shrpx_https_upstream.h"
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
|
#include "shrpx_downstream_connection.h"
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ ClientHandler::ClientHandler(bufferevent *bev, SSL *ssl, const char *ipaddr)
|
||||||
should_close_after_write_(false)
|
should_close_after_write_(false)
|
||||||
{
|
{
|
||||||
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
||||||
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
|
||||||
set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
set_upstream_timeouts(&get_config()->upstream_read_timeout,
|
||||||
&get_config()->upstream_write_timeout);
|
&get_config()->upstream_write_timeout);
|
||||||
set_bev_cb(0, upstream_writecb, upstream_eventcb);
|
set_bev_cb(0, upstream_writecb, upstream_eventcb);
|
||||||
|
@ -126,6 +128,10 @@ ClientHandler::~ClientHandler()
|
||||||
shutdown(fd, SHUT_WR);
|
shutdown(fd, SHUT_WR);
|
||||||
close(fd);
|
close(fd);
|
||||||
delete upstream_;
|
delete upstream_;
|
||||||
|
for(std::set<DownstreamConnection*>::iterator i = dconn_pool_.begin();
|
||||||
|
i != dconn_pool_.end(); ++i) {
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Deleted";
|
LOG(INFO) << "Deleted";
|
||||||
}
|
}
|
||||||
|
@ -213,4 +219,39 @@ void ClientHandler::set_should_close_after_write(bool f)
|
||||||
should_close_after_write_ = f;
|
should_close_after_write_ = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientHandler::pool_downstream_connection(DownstreamConnection *dconn)
|
||||||
|
{
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Pooling downstream connection " << dconn;
|
||||||
|
}
|
||||||
|
dconn_pool_.insert(dconn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn)
|
||||||
|
{
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Removing downstream connection " << dconn
|
||||||
|
<< " from pool";
|
||||||
|
}
|
||||||
|
dconn_pool_.erase(dconn);
|
||||||
|
}
|
||||||
|
|
||||||
|
DownstreamConnection* ClientHandler::get_downstream_connection()
|
||||||
|
{
|
||||||
|
if(dconn_pool_.empty()) {
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Downstream connection pool is empty. Create new one";
|
||||||
|
}
|
||||||
|
return new DownstreamConnection(this);
|
||||||
|
} else {
|
||||||
|
DownstreamConnection *dconn = *dconn_pool_.begin();
|
||||||
|
dconn_pool_.erase(dconn);
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Reuse downstream connection " << dconn
|
||||||
|
<< " from pool";
|
||||||
|
}
|
||||||
|
return dconn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -27,12 +27,15 @@
|
||||||
|
|
||||||
#include "shrpx.h"
|
#include "shrpx.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <event.h>
|
#include <event.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
class Upstream;
|
class Upstream;
|
||||||
|
class DownstreamConnection;
|
||||||
|
|
||||||
class ClientHandler {
|
class ClientHandler {
|
||||||
public:
|
public:
|
||||||
|
@ -51,12 +54,18 @@ public:
|
||||||
bool get_should_close_after_write() const;
|
bool get_should_close_after_write() const;
|
||||||
void set_should_close_after_write(bool f);
|
void set_should_close_after_write(bool f);
|
||||||
Upstream* get_upstream();
|
Upstream* get_upstream();
|
||||||
|
|
||||||
|
void pool_downstream_connection(DownstreamConnection *dconn);
|
||||||
|
void remove_downstream_connection(DownstreamConnection *dconn);
|
||||||
|
DownstreamConnection* get_downstream_connection();
|
||||||
private:
|
private:
|
||||||
bufferevent *bev_;
|
bufferevent *bev_;
|
||||||
SSL *ssl_;
|
SSL *ssl_;
|
||||||
Upstream *upstream_;
|
Upstream *upstream_;
|
||||||
std::string ipaddr_;
|
std::string ipaddr_;
|
||||||
bool should_close_after_write_;
|
bool should_close_after_write_;
|
||||||
|
|
||||||
|
std::set<DownstreamConnection*> dconn_pool_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_error.h"
|
#include "shrpx_error.h"
|
||||||
#include "shrpx_http.h"
|
#include "shrpx_http.h"
|
||||||
|
#include "shrpx_downstream_connection.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using namespace spdylay;
|
using namespace spdylay;
|
||||||
|
@ -39,10 +40,9 @@ namespace shrpx {
|
||||||
|
|
||||||
Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
||||||
: upstream_(upstream),
|
: upstream_(upstream),
|
||||||
bev_(0),
|
dconn_(0),
|
||||||
stream_id_(stream_id),
|
stream_id_(stream_id),
|
||||||
priority_(priority),
|
priority_(priority),
|
||||||
counter_(1),
|
|
||||||
ioctrl_(0),
|
ioctrl_(0),
|
||||||
request_state_(INITIAL),
|
request_state_(INITIAL),
|
||||||
request_major_(1),
|
request_major_(1),
|
||||||
|
@ -60,11 +60,6 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
||||||
{
|
{
|
||||||
htparser_init(response_htp_, htp_type_response);
|
htparser_init(response_htp_, htp_type_response);
|
||||||
htparser_set_userdata(response_htp_, this);
|
htparser_set_userdata(response_htp_, this);
|
||||||
event_base *evbase = upstream_->get_client_handler()->get_evbase();
|
|
||||||
bev_ = bufferevent_socket_new
|
|
||||||
(evbase, -1,
|
|
||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
|
||||||
ioctrl_.set_bev(bev_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream::~Downstream()
|
Downstream::~Downstream()
|
||||||
|
@ -76,51 +71,28 @@ Downstream::~Downstream()
|
||||||
// Passing NULL to evbuffer_free() causes segmentation fault.
|
// Passing NULL to evbuffer_free() causes segmentation fault.
|
||||||
evbuffer_free(response_body_buf_);
|
evbuffer_free(response_body_buf_);
|
||||||
}
|
}
|
||||||
bufferevent_disable(bev_, EV_READ | EV_WRITE);
|
if(dconn_) {
|
||||||
bufferevent_free(bev_);
|
delete dconn_;
|
||||||
|
}
|
||||||
free(response_htp_);
|
free(response_htp_);
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Deleted";
|
LOG(INFO) << "Deleted";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Downstream::get_counter() const
|
void Downstream::set_downstream_connection(DownstreamConnection *dconn)
|
||||||
{
|
{
|
||||||
return counter_;
|
dconn_ = dconn;
|
||||||
|
if(dconn_) {
|
||||||
|
ioctrl_.set_bev(dconn_->get_bev());
|
||||||
|
} else {
|
||||||
|
ioctrl_.set_bev(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::reuse(int stream_id)
|
DownstreamConnection* Downstream::get_downstream_connection()
|
||||||
{
|
{
|
||||||
stream_id_ = stream_id;
|
return dconn_;
|
||||||
++counter_;
|
|
||||||
request_state_ = INITIAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Downstream::idle()
|
|
||||||
{
|
|
||||||
stream_id_ = -1;
|
|
||||||
priority_ = 0;
|
|
||||||
ioctrl_.force_resume_read();
|
|
||||||
request_state_ = IDLE;
|
|
||||||
request_method_.clear();
|
|
||||||
request_path_.clear();
|
|
||||||
request_major_ = 1;
|
|
||||||
request_minor_ = 1;
|
|
||||||
chunked_request_ = false;
|
|
||||||
request_connection_close_ = false;
|
|
||||||
request_headers_.clear();
|
|
||||||
|
|
||||||
response_state_ = INITIAL;
|
|
||||||
response_http_status_ = 0;
|
|
||||||
response_major_ = 1;
|
|
||||||
response_minor_ = 1;
|
|
||||||
chunked_response_ = false;
|
|
||||||
response_connection_close_ = false;
|
|
||||||
response_headers_.clear();
|
|
||||||
if(response_body_buf_) {
|
|
||||||
size_t len = evbuffer_get_length(response_body_buf_);
|
|
||||||
evbuffer_drain(response_body_buf_, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Downstream::pause_read(IOCtrlReason reason)
|
void Downstream::pause_read(IOCtrlReason reason)
|
||||||
|
@ -218,28 +190,6 @@ int32_t Downstream::get_stream_id() const
|
||||||
return stream_id_;
|
return stream_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Downstream::start_connection()
|
|
||||||
{
|
|
||||||
bufferevent_setcb(bev_,
|
|
||||||
upstream_->get_downstream_readcb(),
|
|
||||||
upstream_->get_downstream_writecb(),
|
|
||||||
upstream_->get_downstream_eventcb(), this);
|
|
||||||
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
|
||||||
bufferevent_set_timeouts(bev_,
|
|
||||||
&get_config()->downstream_read_timeout,
|
|
||||||
&get_config()->downstream_write_timeout);
|
|
||||||
int rv = bufferevent_socket_connect
|
|
||||||
(bev_,
|
|
||||||
// TODO maybe not thread-safe?
|
|
||||||
const_cast<sockaddr*>(&get_config()->downstream_addr.sa),
|
|
||||||
get_config()->downstream_addrlen);
|
|
||||||
if(rv == 0) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return SHRPX_ERR_NETWORK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Downstream::set_request_state(int state)
|
void Downstream::set_request_state(int state)
|
||||||
{
|
{
|
||||||
request_state_ = state;
|
request_state_ = state;
|
||||||
|
@ -286,6 +236,9 @@ int Downstream::push_request_headers()
|
||||||
via_value = (*i).second;
|
via_value = (*i).second;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if(util::strieq((*i).first.c_str(), "host")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
hdrs += (*i).first;
|
hdrs += (*i).first;
|
||||||
hdrs += ": ";
|
hdrs += ": ";
|
||||||
hdrs += (*i).second;
|
hdrs += (*i).second;
|
||||||
|
@ -318,7 +271,8 @@ int Downstream::push_request_headers()
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Downstream request headers\n" << hdrs;
|
LOG(INFO) << "Downstream request headers\n" << hdrs;
|
||||||
}
|
}
|
||||||
evbuffer *output = bufferevent_get_output(bev_);
|
bufferevent *bev = dconn_->get_bev();
|
||||||
|
evbuffer *output = bufferevent_get_output(bev);
|
||||||
evbuffer_add(output, hdrs.c_str(), hdrs.size());
|
evbuffer_add(output, hdrs.c_str(), hdrs.size());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -329,13 +283,17 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen)
|
||||||
// buffer using push_request_headers().
|
// buffer using push_request_headers().
|
||||||
ssize_t res = 0;
|
ssize_t res = 0;
|
||||||
int rv;
|
int rv;
|
||||||
evbuffer *output = bufferevent_get_output(bev_);
|
bufferevent *bev = dconn_->get_bev();
|
||||||
|
evbuffer *output = bufferevent_get_output(bev);
|
||||||
if(chunked_request_) {
|
if(chunked_request_) {
|
||||||
char chunk_size_hex[16];
|
char chunk_size_hex[16];
|
||||||
rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), "%X\r\n",
|
rv = snprintf(chunk_size_hex, sizeof(chunk_size_hex), "%X\r\n",
|
||||||
static_cast<unsigned int>(datalen));
|
static_cast<unsigned int>(datalen));
|
||||||
evbuffer_add(output, chunk_size_hex, rv);
|
|
||||||
res += rv;
|
res += rv;
|
||||||
|
rv = evbuffer_add(output, chunk_size_hex, rv);
|
||||||
|
if(rv == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rv = evbuffer_add(output, data, datalen);
|
rv = evbuffer_add(output, data, datalen);
|
||||||
if(rv == -1) {
|
if(rv == -1) {
|
||||||
|
@ -348,7 +306,8 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen)
|
||||||
int Downstream::end_upload_data()
|
int Downstream::end_upload_data()
|
||||||
{
|
{
|
||||||
if(chunked_request_) {
|
if(chunked_request_) {
|
||||||
evbuffer *output = bufferevent_get_output(bev_);
|
bufferevent *bev = dconn_->get_bev();
|
||||||
|
evbuffer *output = bufferevent_get_output(bev);
|
||||||
evbuffer_add(output, "0\r\n\r\n", 5);
|
evbuffer_add(output, "0\r\n\r\n", 5);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -493,7 +452,8 @@ htparse_hooks htp_hooks = {
|
||||||
|
|
||||||
int Downstream::parse_http_response()
|
int Downstream::parse_http_response()
|
||||||
{
|
{
|
||||||
evbuffer *input = bufferevent_get_input(bev_);
|
bufferevent *bev = dconn_->get_bev();
|
||||||
|
evbuffer *input = bufferevent_get_input(bev);
|
||||||
unsigned char *mem = evbuffer_pullup(input, -1);
|
unsigned char *mem = evbuffer_pullup(input, -1);
|
||||||
size_t nread = htparser_run(response_htp_, &htp_hooks,
|
size_t nread = htparser_run(response_htp_, &htp_hooks,
|
||||||
reinterpret_cast<const char*>(mem),
|
reinterpret_cast<const char*>(mem),
|
||||||
|
|
|
@ -44,6 +44,7 @@ extern "C" {
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
class Upstream;
|
class Upstream;
|
||||||
|
class DownstreamConnection;
|
||||||
|
|
||||||
typedef std::vector<std::pair<std::string, std::string> > Headers;
|
typedef std::vector<std::pair<std::string, std::string> > Headers;
|
||||||
|
|
||||||
|
@ -51,16 +52,14 @@ class Downstream {
|
||||||
public:
|
public:
|
||||||
Downstream(Upstream *upstream, int stream_id, int priority);
|
Downstream(Upstream *upstream, int stream_id, int priority);
|
||||||
~Downstream();
|
~Downstream();
|
||||||
int start_connection();
|
|
||||||
Upstream* get_upstream() const;
|
Upstream* get_upstream() const;
|
||||||
int32_t get_stream_id() const;
|
int32_t get_stream_id() const;
|
||||||
void set_priority(int pri);
|
void set_priority(int pri);
|
||||||
void pause_read(IOCtrlReason reason);
|
void pause_read(IOCtrlReason reason);
|
||||||
bool resume_read(IOCtrlReason reason);
|
bool resume_read(IOCtrlReason reason);
|
||||||
void force_resume_read();
|
void force_resume_read();
|
||||||
void idle();
|
void set_downstream_connection(DownstreamConnection *dconn);
|
||||||
void reuse(int stream_id);
|
DownstreamConnection* get_downstream_connection();
|
||||||
int get_counter() const;
|
|
||||||
// downstream request API
|
// downstream request API
|
||||||
const Headers& get_request_headers() const;
|
const Headers& get_request_headers() const;
|
||||||
void add_request_header(const std::string& name, const std::string& value);
|
void add_request_header(const std::string& name, const std::string& value);
|
||||||
|
@ -106,10 +105,9 @@ public:
|
||||||
evbuffer* get_response_body_buf();
|
evbuffer* get_response_body_buf();
|
||||||
private:
|
private:
|
||||||
Upstream *upstream_;
|
Upstream *upstream_;
|
||||||
bufferevent *bev_;
|
DownstreamConnection *dconn_;
|
||||||
int32_t stream_id_;
|
int32_t stream_id_;
|
||||||
int priority_;
|
int priority_;
|
||||||
int counter_;
|
|
||||||
IOControl ioctrl_;
|
IOControl ioctrl_;
|
||||||
int request_state_;
|
int request_state_;
|
||||||
std::string request_method_;
|
std::string request_method_;
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Spdylay - SPDY Library
|
||||||
|
*
|
||||||
|
* 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_downstream_connection.h"
|
||||||
|
|
||||||
|
#include "shrpx_client_handler.h"
|
||||||
|
#include "shrpx_upstream.h"
|
||||||
|
#include "shrpx_downstream.h"
|
||||||
|
#include "shrpx_config.h"
|
||||||
|
#include "shrpx_error.h"
|
||||||
|
|
||||||
|
namespace shrpx {
|
||||||
|
|
||||||
|
DownstreamConnection::DownstreamConnection(ClientHandler *client_handler)
|
||||||
|
: client_handler_(client_handler),
|
||||||
|
bev_(0),
|
||||||
|
downstream_(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DownstreamConnection::~DownstreamConnection()
|
||||||
|
{
|
||||||
|
if(bev_) {
|
||||||
|
bufferevent_disable(bev_, EV_READ | EV_WRITE);
|
||||||
|
bufferevent_free(bev_);
|
||||||
|
}
|
||||||
|
// Downstream and DownstreamConnection may be deleted
|
||||||
|
// asynchronously.
|
||||||
|
if(downstream_) {
|
||||||
|
downstream_->set_downstream_connection(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DownstreamConnection::attach_downstream(Downstream *downstream)
|
||||||
|
{
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Attaching downstream connection " << this << " to "
|
||||||
|
<< "downstream " << downstream;
|
||||||
|
}
|
||||||
|
Upstream *upstream = downstream->get_upstream();
|
||||||
|
if(!bev_) {
|
||||||
|
event_base *evbase = client_handler_->get_evbase();
|
||||||
|
bev_ = bufferevent_socket_new
|
||||||
|
(evbase, -1,
|
||||||
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
|
int rv = bufferevent_socket_connect
|
||||||
|
(bev_,
|
||||||
|
// TODO maybe not thread-safe?
|
||||||
|
const_cast<sockaddr*>(&get_config()->downstream_addr.sa),
|
||||||
|
get_config()->downstream_addrlen);
|
||||||
|
if(rv != 0) {
|
||||||
|
bufferevent_free(bev_);
|
||||||
|
bev_ = 0;
|
||||||
|
return SHRPX_ERR_NETWORK;
|
||||||
|
}
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Connecting to downstream server " << this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downstream->set_downstream_connection(this);
|
||||||
|
downstream_ = downstream;
|
||||||
|
bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
|
||||||
|
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
||||||
|
bufferevent_setcb(bev_,
|
||||||
|
upstream->get_downstream_readcb(),
|
||||||
|
upstream->get_downstream_writecb(),
|
||||||
|
upstream->get_downstream_eventcb(), this);
|
||||||
|
|
||||||
|
bufferevent_set_timeouts(bev_,
|
||||||
|
&get_config()->downstream_read_timeout,
|
||||||
|
&get_config()->downstream_write_timeout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Gets called when DownstreamConnection is pooled in ClientHandler.
|
||||||
|
void idle_eventcb(bufferevent *bev, short events, void *arg)
|
||||||
|
{
|
||||||
|
DownstreamConnection *dconn = reinterpret_cast<DownstreamConnection*>(arg);
|
||||||
|
if(events & BEV_EVENT_CONNECTED) {
|
||||||
|
// Downstream was detached before connection established?
|
||||||
|
// This may be safe to be left.
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Idle downstream connected?" << dconn;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(events & BEV_EVENT_EOF) {
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Idle downstream connection EOF " << dconn;
|
||||||
|
}
|
||||||
|
} else if(events & BEV_EVENT_TIMEOUT) {
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Idle downstream connection timeout " << dconn;
|
||||||
|
}
|
||||||
|
} else if(events & BEV_EVENT_ERROR) {
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Idle downstream connection error " << dconn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientHandler *client_handler = dconn->get_client_handler();
|
||||||
|
client_handler->remove_downstream_connection(dconn);
|
||||||
|
delete dconn;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void DownstreamConnection::detach_downstream(Downstream *downstream)
|
||||||
|
{
|
||||||
|
if(ENABLE_LOG) {
|
||||||
|
LOG(INFO) << "Detaching downstream connection " << this << " from "
|
||||||
|
<< "downstream " << downstream;
|
||||||
|
}
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
downstream_ = 0;
|
||||||
|
bufferevent_enable(bev_, EV_READ | EV_WRITE);
|
||||||
|
bufferevent_setcb(bev_, 0, 0, idle_eventcb, this);
|
||||||
|
client_handler_->pool_downstream_connection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientHandler* DownstreamConnection::get_client_handler()
|
||||||
|
{
|
||||||
|
return client_handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Downstream* DownstreamConnection::get_downstream()
|
||||||
|
{
|
||||||
|
return downstream_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferevent* DownstreamConnection::get_bev()
|
||||||
|
{
|
||||||
|
return bev_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace shrpx
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Spdylay - SPDY Library
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#ifndef SHRPX_DOWNSTREAM_CONNECTION_H
|
||||||
|
#define SHRPX_DOWNSTREAM_CONNECTION_H
|
||||||
|
|
||||||
|
#include "shrpx.h"
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
|
namespace shrpx {
|
||||||
|
|
||||||
|
class ClientHandler;
|
||||||
|
class Downstream;
|
||||||
|
|
||||||
|
class DownstreamConnection {
|
||||||
|
public:
|
||||||
|
DownstreamConnection(ClientHandler *client_handler);
|
||||||
|
~DownstreamConnection();
|
||||||
|
int attach_downstream(Downstream *downstream);
|
||||||
|
void detach_downstream(Downstream *downstream);
|
||||||
|
bufferevent* get_bev();
|
||||||
|
int push_data(const void *data, size_t len);
|
||||||
|
|
||||||
|
ClientHandler* get_client_handler();
|
||||||
|
Downstream* get_downstream();
|
||||||
|
private:
|
||||||
|
ClientHandler *client_handler_;
|
||||||
|
bufferevent *bev_;
|
||||||
|
Downstream *downstream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace shrpx
|
||||||
|
|
||||||
|
#endif // SHRPX_DOWNSTREAM_CONNECTION_H
|
|
@ -39,10 +39,6 @@ DownstreamQueue::~DownstreamQueue()
|
||||||
i != downstreams_.end(); ++i) {
|
i != downstreams_.end(); ++i) {
|
||||||
delete (*i).second;
|
delete (*i).second;
|
||||||
}
|
}
|
||||||
for(std::set<Downstream*>::iterator i = idle_downstreams_.begin();
|
|
||||||
i != idle_downstreams_.end(); ++i) {
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::add(Downstream *downstream)
|
void DownstreamQueue::add(Downstream *downstream)
|
||||||
|
@ -50,19 +46,10 @@ void DownstreamQueue::add(Downstream *downstream)
|
||||||
downstreams_[downstream->get_stream_id()] = downstream;
|
downstreams_[downstream->get_stream_id()] = downstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DownstreamQueue::start(Downstream *downstream)
|
|
||||||
{
|
|
||||||
return downstream->start_connection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownstreamQueue::remove(Downstream *downstream)
|
void DownstreamQueue::remove(Downstream *downstream)
|
||||||
{
|
{
|
||||||
if(downstream->get_request_state() == Downstream::IDLE) {
|
|
||||||
idle_downstreams_.erase(downstream);
|
|
||||||
} else {
|
|
||||||
downstreams_.erase(downstream->get_stream_id());
|
downstreams_.erase(downstream->get_stream_id());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Downstream* DownstreamQueue::find(int32_t stream_id)
|
Downstream* DownstreamQueue::find(int32_t stream_id)
|
||||||
{
|
{
|
||||||
|
@ -74,24 +61,4 @@ Downstream* DownstreamQueue::find(int32_t stream_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream* DownstreamQueue::reuse(int32_t stream_id)
|
|
||||||
{
|
|
||||||
if(idle_downstreams_.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Downstream* downstream = *idle_downstreams_.begin();
|
|
||||||
idle_downstreams_.erase(downstream);
|
|
||||||
downstream->reuse(stream_id);
|
|
||||||
add(downstream);
|
|
||||||
return downstream;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownstreamQueue::idle(Downstream *downstream)
|
|
||||||
{
|
|
||||||
assert(downstream->get_request_state() != Downstream::IDLE);
|
|
||||||
remove(downstream);
|
|
||||||
downstream->idle();
|
|
||||||
idle_downstreams_.insert(downstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
@ -41,14 +40,10 @@ public:
|
||||||
DownstreamQueue();
|
DownstreamQueue();
|
||||||
~DownstreamQueue();
|
~DownstreamQueue();
|
||||||
void add(Downstream *downstream);
|
void add(Downstream *downstream);
|
||||||
int start(Downstream *downstream);
|
|
||||||
void remove(Downstream *downstream);
|
void remove(Downstream *downstream);
|
||||||
Downstream* find(int32_t stream_id);
|
Downstream* find(int32_t stream_id);
|
||||||
Downstream* reuse(int32_t stream_id);
|
|
||||||
void idle(Downstream *downstream);
|
|
||||||
private:
|
private:
|
||||||
std::map<int32_t, Downstream*> downstreams_;
|
std::map<int32_t, Downstream*> downstreams_;
|
||||||
std::set<Downstream*> idle_downstreams_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "shrpx_client_handler.h"
|
#include "shrpx_client_handler.h"
|
||||||
#include "shrpx_downstream.h"
|
#include "shrpx_downstream.h"
|
||||||
|
#include "shrpx_downstream_connection.h"
|
||||||
#include "shrpx_http.h"
|
#include "shrpx_http.h"
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_error.h"
|
#include "shrpx_error.h"
|
||||||
|
@ -39,7 +40,7 @@ using namespace spdylay;
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const size_t SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
|
const size_t SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES = 64*1024;
|
||||||
const size_t SHRPX_HTTPS_MAX_HEADER_LENGTH = 64*1024;
|
const size_t SHRPX_HTTPS_MAX_HEADER_LENGTH = 64*1024;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -76,20 +77,8 @@ int htp_msg_begin(htparser *htp)
|
||||||
HttpsUpstream *upstream;
|
HttpsUpstream *upstream;
|
||||||
upstream = reinterpret_cast<HttpsUpstream*>(htparser_get_userdata(htp));
|
upstream = reinterpret_cast<HttpsUpstream*>(htparser_get_userdata(htp));
|
||||||
upstream->reset_current_header_length();
|
upstream->reset_current_header_length();
|
||||||
Downstream *downstream = upstream->get_top_downstream();
|
Downstream *downstream = new Downstream(upstream, 0, 0);
|
||||||
if(downstream) {
|
|
||||||
// Keep-Alived connection
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Reusing downstream";
|
|
||||||
}
|
|
||||||
downstream->reuse(0);
|
|
||||||
} else {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Creating new downstream";
|
|
||||||
}
|
|
||||||
downstream = new Downstream(upstream, 0, 0);
|
|
||||||
upstream->add_downstream(downstream);
|
upstream->add_downstream(downstream);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -169,19 +158,21 @@ int htp_hdrs_completecb(htparser *htp)
|
||||||
downstream->set_request_major(htparser_get_major(htp));
|
downstream->set_request_major(htparser_get_major(htp));
|
||||||
downstream->set_request_minor(htparser_get_minor(htp));
|
downstream->set_request_minor(htparser_get_minor(htp));
|
||||||
|
|
||||||
|
DownstreamConnection *dconn;
|
||||||
|
dconn = upstream->get_client_handler()->get_downstream_connection();
|
||||||
|
|
||||||
|
int rv = dconn->attach_downstream(downstream);
|
||||||
|
if(rv != 0) {
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
delete dconn;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
downstream->push_request_headers();
|
downstream->push_request_headers();
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
|
|
||||||
if(downstream->get_counter() == 1) {
|
|
||||||
int rv = downstream->start_connection();
|
|
||||||
if(rv != 0) {
|
|
||||||
LOG(ERROR) << "Upstream connection failed";
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -250,19 +241,29 @@ int HttpsUpstream::on_read()
|
||||||
htpparse_error htperr = htparser_get_error(htp_);
|
htpparse_error htperr = htparser_get_error(htp_);
|
||||||
if(htperr == htparse_error_user) {
|
if(htperr == htparse_error_user) {
|
||||||
Downstream *downstream = get_top_downstream();
|
Downstream *downstream = get_top_downstream();
|
||||||
if(downstream &&
|
if(downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
||||||
downstream->get_request_state() == Downstream::CONNECT_FAIL) {
|
|
||||||
get_client_handler()->set_should_close_after_write(true);
|
get_client_handler()->set_should_close_after_write(true);
|
||||||
error_reply(503);
|
error_reply(503);
|
||||||
} else if(current_header_length_ > SHRPX_HTTPS_MAX_HEADER_LENGTH) {
|
// Downstream gets deleted after response body is read.
|
||||||
|
} else {
|
||||||
|
assert(downstream->get_request_state() == Downstream::MSG_COMPLETE);
|
||||||
|
if(downstream->get_downstream_connection() == 0) {
|
||||||
|
// Error response already be sent
|
||||||
|
assert(downstream->get_response_state() == Downstream::MSG_COMPLETE);
|
||||||
|
pop_downstream();
|
||||||
|
delete downstream;
|
||||||
|
} else {
|
||||||
|
pause_read(SHRPX_MSG_BLOCK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(htperr == htparse_error_none) {
|
||||||
|
if(current_header_length_ > SHRPX_HTTPS_MAX_HEADER_LENGTH) {
|
||||||
LOG(WARNING) << "Request Header too long:" << current_header_length_
|
LOG(WARNING) << "Request Header too long:" << current_header_length_
|
||||||
<< " bytes";
|
<< " bytes";
|
||||||
get_client_handler()->set_should_close_after_write(true);
|
get_client_handler()->set_should_close_after_write(true);
|
||||||
error_reply(400);
|
error_reply(400);
|
||||||
} else {
|
|
||||||
pause_read(SHRPX_MSG_BLOCK);
|
|
||||||
}
|
}
|
||||||
} else if(htperr != htparse_error_none) {
|
} else {
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Upstream http parse failure: "
|
LOG(INFO) << "Upstream http parse failure: "
|
||||||
<< htparser_get_strerror(htp_);
|
<< htparser_get_strerror(htp_);
|
||||||
|
@ -273,6 +274,10 @@ int HttpsUpstream::on_read()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void https_downstream_readcb(bufferevent *bev, void *ptr);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
int HttpsUpstream::on_write()
|
int HttpsUpstream::on_write()
|
||||||
{
|
{
|
||||||
Downstream *downstream = get_top_downstream();
|
Downstream *downstream = get_top_downstream();
|
||||||
|
@ -309,28 +314,28 @@ void HttpsUpstream::resume_read(IOCtrlReason reason)
|
||||||
namespace {
|
namespace {
|
||||||
void https_downstream_readcb(bufferevent *bev, void *ptr)
|
void https_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
{
|
{
|
||||||
Downstream *downstream = reinterpret_cast<Downstream*>(ptr);
|
DownstreamConnection *dconn = reinterpret_cast<DownstreamConnection*>(ptr);
|
||||||
|
Downstream *downstream = dconn->get_downstream();
|
||||||
HttpsUpstream *upstream;
|
HttpsUpstream *upstream;
|
||||||
upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
||||||
if(downstream->get_request_state() == Downstream::IDLE) {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Delete idle downstream in https_downstream_readcb";
|
|
||||||
}
|
|
||||||
upstream->pop_downstream();
|
|
||||||
delete downstream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int rv = downstream->parse_http_response();
|
int rv = downstream->parse_http_response();
|
||||||
if(rv == 0) {
|
if(rv == 0) {
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
assert(downstream == upstream->get_top_downstream());
|
|
||||||
if(downstream->get_response_connection_close()) {
|
if(downstream->get_response_connection_close()) {
|
||||||
|
// Connection close
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
delete dconn;
|
||||||
|
dconn = 0;
|
||||||
|
} else {
|
||||||
|
// Keep-alive
|
||||||
|
dconn->detach_downstream(downstream);
|
||||||
|
}
|
||||||
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
// Process next HTTP request
|
||||||
downstream->idle();
|
|
||||||
}
|
|
||||||
upstream->resume_read(SHRPX_MSG_BLOCK);
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ClientHandler *handler = upstream->get_client_handler();
|
ClientHandler *handler = upstream->get_client_handler();
|
||||||
bufferevent *bev = handler->get_bev();
|
bufferevent *bev = handler->get_bev();
|
||||||
|
@ -341,16 +346,22 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
|
// We already sent HTTP response headers to upstream
|
||||||
|
// client. Just close the upstream connection.
|
||||||
delete upstream->get_client_handler();
|
delete upstream->get_client_handler();
|
||||||
} else {
|
} else {
|
||||||
|
// We did not sent any HTTP response, so sent error
|
||||||
|
// response. Cannot reuse downstream connection in this case.
|
||||||
upstream->error_reply(502);
|
upstream->error_reply(502);
|
||||||
assert(downstream == upstream->get_top_downstream());
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
|
// Process next HTTP request
|
||||||
upstream->resume_read(SHRPX_MSG_BLOCK);
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -362,18 +373,10 @@ void https_downstream_writecb(bufferevent *bev, void *ptr)
|
||||||
namespace {
|
namespace {
|
||||||
void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
{
|
{
|
||||||
Downstream *downstream = reinterpret_cast<Downstream*>(ptr);
|
DownstreamConnection *dconn = reinterpret_cast<DownstreamConnection*>(ptr);
|
||||||
|
Downstream *downstream = dconn->get_downstream();
|
||||||
HttpsUpstream *upstream;
|
HttpsUpstream *upstream;
|
||||||
upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
upstream = static_cast<HttpsUpstream*>(downstream->get_upstream());
|
||||||
if(downstream->get_request_state() == Downstream::IDLE) {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Delete idle downstream in https_downstream_eventcb";
|
|
||||||
}
|
|
||||||
upstream->pop_downstream();
|
|
||||||
delete downstream;
|
|
||||||
upstream->resume_read(SHRPX_MSG_BLOCK);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
if(events & BEV_EVENT_CONNECTED) {
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Downstream connection established. downstream "
|
LOG(INFO) << "Downstream connection established. downstream "
|
||||||
|
@ -400,9 +403,11 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
}
|
}
|
||||||
upstream->error_reply(502);
|
upstream->error_reply(502);
|
||||||
}
|
}
|
||||||
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read(SHRPX_MSG_BLOCK);
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
|
}
|
||||||
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
} else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Downstream error/timeout. " << downstream;
|
LOG(INFO) << "Downstream error/timeout. " << downstream;
|
||||||
|
@ -416,11 +421,13 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
}
|
}
|
||||||
upstream->error_reply(status);
|
upstream->error_reply(status);
|
||||||
}
|
}
|
||||||
|
if(downstream->get_request_state() == Downstream::MSG_COMPLETE) {
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read(SHRPX_MSG_BLOCK);
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void HttpsUpstream::error_reply(int status_code)
|
void HttpsUpstream::error_reply(int status_code)
|
||||||
|
@ -439,6 +446,10 @@ void HttpsUpstream::error_reply(int status_code)
|
||||||
evbuffer *output = bufferevent_get_output(handler_->get_bev());
|
evbuffer *output = bufferevent_get_output(handler_->get_bev());
|
||||||
evbuffer_add(output, header.c_str(), header.size());
|
evbuffer_add(output, header.c_str(), header.size());
|
||||||
evbuffer_add(output, html.c_str(), html.size());
|
evbuffer_add(output, html.c_str(), html.size());
|
||||||
|
Downstream *downstream = get_top_downstream();
|
||||||
|
if(downstream) {
|
||||||
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferevent_data_cb HttpsUpstream::get_downstream_readcb()
|
bufferevent_data_cb HttpsUpstream::get_downstream_readcb()
|
||||||
|
|
|
@ -44,14 +44,18 @@ void IOControl::set_bev(bufferevent *bev)
|
||||||
void IOControl::pause_read(IOCtrlReason reason)
|
void IOControl::pause_read(IOCtrlReason reason)
|
||||||
{
|
{
|
||||||
rdbits_ |= reason;
|
rdbits_ |= reason;
|
||||||
|
if(bev_) {
|
||||||
bufferevent_disable(bev_, EV_READ);
|
bufferevent_disable(bev_, EV_READ);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IOControl::resume_read(IOCtrlReason reason)
|
bool IOControl::resume_read(IOCtrlReason reason)
|
||||||
{
|
{
|
||||||
rdbits_ &= ~reason;
|
rdbits_ &= ~reason;
|
||||||
if(rdbits_ == 0) {
|
if(rdbits_ == 0) {
|
||||||
|
if(bev_) {
|
||||||
bufferevent_enable(bev_, EV_READ);
|
bufferevent_enable(bev_, EV_READ);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -61,7 +65,9 @@ bool IOControl::resume_read(IOCtrlReason reason)
|
||||||
void IOControl::force_resume_read()
|
void IOControl::force_resume_read()
|
||||||
{
|
{
|
||||||
rdbits_ = 0;
|
rdbits_ = 0;
|
||||||
|
if(bev_) {
|
||||||
bufferevent_enable(bev_, EV_READ);
|
bufferevent_enable(bev_, EV_READ);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "shrpx_client_handler.h"
|
#include "shrpx_client_handler.h"
|
||||||
#include "shrpx_downstream.h"
|
#include "shrpx_downstream.h"
|
||||||
|
#include "shrpx_downstream_connection.h"
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_http.h"
|
#include "shrpx_http.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -38,7 +39,7 @@ using namespace spdylay;
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const size_t SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
|
const size_t SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES = 64*1024;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -101,7 +102,16 @@ void on_stream_close_callback
|
||||||
} else {
|
} else {
|
||||||
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
downstream->set_request_state(Downstream::STREAM_CLOSED);
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
upstream->remove_or_idle_downstream(downstream);
|
// At this point, downstream response was read
|
||||||
|
if(!downstream->get_response_connection_close()) {
|
||||||
|
// Keep-alive
|
||||||
|
DownstreamConnection *dconn;
|
||||||
|
dconn = downstream->get_downstream_connection();
|
||||||
|
if(dconn) {
|
||||||
|
dconn->detach_downstream(downstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upstream->remove_downstream(downstream);
|
||||||
} else {
|
} else {
|
||||||
// At this point, downstream read may be paused. To reclaim
|
// At this point, downstream read may be paused. To reclaim
|
||||||
// file descriptor, enable read here and catch read
|
// file descriptor, enable read here and catch read
|
||||||
|
@ -126,22 +136,10 @@ void on_ctrl_recv_callback
|
||||||
<< frame->syn_stream.stream_id;
|
<< frame->syn_stream.stream_id;
|
||||||
}
|
}
|
||||||
Downstream *downstream;
|
Downstream *downstream;
|
||||||
downstream = upstream->reuse_downstream(frame->syn_stream.stream_id);
|
|
||||||
if(downstream) {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Reusing downstream for stream_id="
|
|
||||||
<< frame->syn_stream.stream_id;
|
|
||||||
}
|
|
||||||
downstream->set_priority(frame->syn_stream.pri);
|
|
||||||
} else {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Creating new downstream for stream_id="
|
|
||||||
<< frame->syn_stream.stream_id;
|
|
||||||
}
|
|
||||||
downstream = new Downstream(upstream,
|
downstream = new Downstream(upstream,
|
||||||
frame->syn_stream.stream_id,
|
frame->syn_stream.stream_id,
|
||||||
frame->syn_stream.pri);
|
frame->syn_stream.pri);
|
||||||
}
|
upstream->add_downstream(downstream);
|
||||||
downstream->init_response_body_buf();
|
downstream->init_response_body_buf();
|
||||||
|
|
||||||
char **nv = frame->syn_stream.nv;
|
char **nv = frame->syn_stream.nv;
|
||||||
|
@ -164,6 +162,15 @@ void on_ctrl_recv_callback
|
||||||
LOG(INFO) << "Upstream spdy request headers:\n" << ss.str();
|
LOG(INFO) << "Upstream spdy request headers:\n" << ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DownstreamConnection *dconn;
|
||||||
|
dconn = upstream->get_client_handler()->get_downstream_connection();
|
||||||
|
int rv = dconn->attach_downstream(downstream);
|
||||||
|
if(rv != 0) {
|
||||||
|
// If downstream connection fails, issue RST_STREAM.
|
||||||
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
downstream->push_request_headers();
|
downstream->push_request_headers();
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
if(frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
||||||
|
@ -174,14 +181,6 @@ void on_ctrl_recv_callback
|
||||||
}
|
}
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
if(downstream->get_counter() == 1) {
|
|
||||||
upstream->add_downstream(downstream);
|
|
||||||
if(upstream->start_downstream(downstream) != 0) {
|
|
||||||
// If downstream connection fails, issue RST_STREAM.
|
|
||||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -297,20 +296,14 @@ void spdy_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "spdy_downstream_readcb";
|
LOG(INFO) << "spdy_downstream_readcb";
|
||||||
}
|
}
|
||||||
Downstream *downstream = reinterpret_cast<Downstream*>(ptr);
|
DownstreamConnection *dconn = reinterpret_cast<DownstreamConnection*>(ptr);
|
||||||
|
Downstream *downstream = dconn->get_downstream();
|
||||||
SpdyUpstream *upstream;
|
SpdyUpstream *upstream;
|
||||||
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
||||||
if(downstream->get_request_state() == Downstream::IDLE) {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Delete idle downstream in spdy_downstream_readcb";
|
|
||||||
}
|
|
||||||
upstream->remove_downstream(downstream);
|
|
||||||
delete downstream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If upstream SPDY stream was closed, we just close downstream,
|
|
||||||
// because there is no consumer now.
|
|
||||||
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
|
// If upstream SPDY stream was closed, we just close downstream,
|
||||||
|
// because there is no consumer now. Downstream connection is also
|
||||||
|
// closed in this case.
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
return;
|
return;
|
||||||
|
@ -326,6 +319,11 @@ void spdy_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
upstream->error_reply(downstream, 502);
|
upstream->error_reply(downstream, 502);
|
||||||
}
|
}
|
||||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
|
// Clearly, we have to close downstream connection on http parser
|
||||||
|
// failure.
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
delete dconn;
|
||||||
|
dconn = 0;
|
||||||
}
|
}
|
||||||
upstream->send();
|
upstream->send();
|
||||||
}
|
}
|
||||||
|
@ -340,17 +338,10 @@ void spdy_downstream_writecb(bufferevent *bev, void *ptr)
|
||||||
namespace {
|
namespace {
|
||||||
void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
{
|
{
|
||||||
Downstream *downstream = reinterpret_cast<Downstream*>(ptr);
|
DownstreamConnection *dconn = reinterpret_cast<DownstreamConnection*>(ptr);
|
||||||
|
Downstream *downstream = dconn->get_downstream();
|
||||||
SpdyUpstream *upstream;
|
SpdyUpstream *upstream;
|
||||||
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
upstream = static_cast<SpdyUpstream*>(downstream->get_upstream());
|
||||||
if(downstream->get_request_state() == Downstream::IDLE) {
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "Delete idle downstream in spdy_downstream_eventcb";
|
|
||||||
}
|
|
||||||
upstream->remove_downstream(downstream);
|
|
||||||
delete downstream;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(events & BEV_EVENT_CONNECTED) {
|
if(events & BEV_EVENT_CONNECTED) {
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "Downstream connection established. Downstream "
|
LOG(INFO) << "Downstream connection established. Downstream "
|
||||||
|
@ -367,6 +358,11 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
} else {
|
||||||
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
|
// will be pooled in on_stream_close_callback.
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
delete dconn;
|
||||||
|
dconn = 0;
|
||||||
// downstream wil be deleted in on_stream_close_callback.
|
// downstream wil be deleted in on_stream_close_callback.
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
// Server may indicate the end of the request by EOF
|
// Server may indicate the end of the request by EOF
|
||||||
|
@ -391,6 +387,11 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
upstream->remove_downstream(downstream);
|
upstream->remove_downstream(downstream);
|
||||||
delete downstream;
|
delete downstream;
|
||||||
} else {
|
} else {
|
||||||
|
// Delete downstream connection. If we don't delete it here, it
|
||||||
|
// will be pooled in on_stream_close_callback.
|
||||||
|
downstream->set_downstream_connection(0);
|
||||||
|
delete dconn;
|
||||||
|
dconn = 0;
|
||||||
if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
@ -499,11 +500,6 @@ void SpdyUpstream::add_downstream(Downstream *downstream)
|
||||||
downstream_queue_.add(downstream);
|
downstream_queue_.add(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpdyUpstream::start_downstream(Downstream *downstream)
|
|
||||||
{
|
|
||||||
return downstream_queue_.start(downstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpdyUpstream::remove_downstream(Downstream *downstream)
|
void SpdyUpstream::remove_downstream(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstream_queue_.remove(downstream);
|
downstream_queue_.remove(downstream);
|
||||||
|
@ -514,21 +510,6 @@ Downstream* SpdyUpstream::find_downstream(int32_t stream_id)
|
||||||
return downstream_queue_.find(stream_id);
|
return downstream_queue_.find(stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream* SpdyUpstream::reuse_downstream(int32_t stream_id)
|
|
||||||
{
|
|
||||||
return downstream_queue_.reuse(stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpdyUpstream::remove_or_idle_downstream(Downstream *downstream)
|
|
||||||
{
|
|
||||||
if(downstream->get_response_connection_close()) {
|
|
||||||
downstream_queue_.remove(downstream);
|
|
||||||
delete downstream;
|
|
||||||
} else {
|
|
||||||
downstream_queue_.idle(downstream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spdylay_session* SpdyUpstream::get_spdy_session()
|
spdylay_session* SpdyUpstream::get_spdy_session()
|
||||||
{
|
{
|
||||||
return session_;
|
return session_;
|
||||||
|
|
|
@ -50,10 +50,7 @@ public:
|
||||||
virtual bufferevent_event_cb get_downstream_eventcb();
|
virtual bufferevent_event_cb get_downstream_eventcb();
|
||||||
void add_downstream(Downstream *downstream);
|
void add_downstream(Downstream *downstream);
|
||||||
void remove_downstream(Downstream *downstream);
|
void remove_downstream(Downstream *downstream);
|
||||||
int start_downstream(Downstream *downstream);
|
|
||||||
Downstream* find_downstream(int32_t stream_id);
|
Downstream* find_downstream(int32_t stream_id);
|
||||||
Downstream* reuse_downstream(int32_t stream_id);
|
|
||||||
void remove_or_idle_downstream(Downstream *downstream);
|
|
||||||
|
|
||||||
spdylay_session* get_spdy_session();
|
spdylay_session* get_spdy_session();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue