Avoid too large buffering in upstream output.
This commit is contained in:
parent
fad7f51f8d
commit
c04c09ff3e
|
@ -77,6 +77,7 @@ shrpx_SOURCES = ${HELPER_OBJECTS} ${HELPER_HFILES} \
|
||||||
shrpx_downstream.cc shrpx_downstream.h \
|
shrpx_downstream.cc shrpx_downstream.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 \
|
||||||
htparse/htparse.c htparse/htparse.h
|
htparse/htparse.c htparse/htparse.h
|
||||||
|
|
||||||
noinst_PROGRAMS = spdycli
|
noinst_PROGRAMS = spdycli
|
||||||
|
|
|
@ -285,7 +285,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
mod_config()->upstream_read_timeout.tv_sec = 30;
|
mod_config()->upstream_read_timeout.tv_sec = 30;
|
||||||
mod_config()->upstream_read_timeout.tv_usec = 0;
|
mod_config()->upstream_read_timeout.tv_usec = 0;
|
||||||
mod_config()->upstream_write_timeout.tv_sec = 30;
|
mod_config()->upstream_write_timeout.tv_sec = 60;
|
||||||
mod_config()->upstream_write_timeout.tv_usec = 0;
|
mod_config()->upstream_write_timeout.tv_usec = 0;
|
||||||
|
|
||||||
mod_config()->spdy_upstream_read_timeout.tv_sec = 600;
|
mod_config()->spdy_upstream_read_timeout.tv_sec = 600;
|
||||||
|
|
|
@ -48,12 +48,12 @@ void upstream_readcb(bufferevent *bev, void *arg)
|
||||||
namespace {
|
namespace {
|
||||||
void upstream_writecb(bufferevent *bev, void *arg)
|
void upstream_writecb(bufferevent *bev, void *arg)
|
||||||
{
|
{
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "<upstream> upstream_writecb";
|
|
||||||
}
|
|
||||||
ClientHandler *handler = reinterpret_cast<ClientHandler*>(arg);
|
ClientHandler *handler = reinterpret_cast<ClientHandler*>(arg);
|
||||||
if(handler->get_should_close_after_write()) {
|
if(handler->get_should_close_after_write()) {
|
||||||
delete handler;
|
delete handler;
|
||||||
|
} else {
|
||||||
|
Upstream *upstream = handler->get_upstream();
|
||||||
|
upstream->on_write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#ifndef SHRPX_CONFIG_H
|
#ifndef SHRPX_CONFIG_H
|
||||||
#define SHRPX_CONFIG_H
|
#define SHRPX_CONFIG_H
|
||||||
|
|
||||||
|
#include "shrpx.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
|
@ -41,6 +41,7 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
||||||
bev_(0),
|
bev_(0),
|
||||||
stream_id_(stream_id),
|
stream_id_(stream_id),
|
||||||
priority_(priority),
|
priority_(priority),
|
||||||
|
ioctrl_(0),
|
||||||
request_state_(INITIAL),
|
request_state_(INITIAL),
|
||||||
chunked_request_(false),
|
chunked_request_(false),
|
||||||
request_connection_close_(false),
|
request_connection_close_(false),
|
||||||
|
@ -56,6 +57,7 @@ Downstream::Downstream(Upstream *upstream, int stream_id, int priority)
|
||||||
bev_ = bufferevent_socket_new
|
bev_ = bufferevent_socket_new
|
||||||
(evbase, -1,
|
(evbase, -1,
|
||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
|
||||||
|
ioctrl_.set_bev(bev_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream::~Downstream()
|
Downstream::~Downstream()
|
||||||
|
@ -75,6 +77,16 @@ Downstream::~Downstream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Downstream::pause_read(IOCtrlReason reason)
|
||||||
|
{
|
||||||
|
ioctrl_.pause_read(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Downstream::resume_read(IOCtrlReason reason)
|
||||||
|
{
|
||||||
|
return ioctrl_.resume_read(reason);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void check_transfer_encoding_chunked(bool *chunked,
|
void check_transfer_encoding_chunked(bool *chunked,
|
||||||
const Headers::value_type &item)
|
const Headers::value_type &item)
|
||||||
|
@ -394,6 +406,16 @@ int Downstream::get_response_state() const
|
||||||
return response_state_;
|
return response_state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void body_buf_cb(evbuffer *body, size_t oldlen, size_t newlen, void *arg)
|
||||||
|
{
|
||||||
|
Downstream *downstream = reinterpret_cast<Downstream*>(arg);
|
||||||
|
if(newlen == 0) {
|
||||||
|
downstream->resume_read(SHRPX_NO_BUFFER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
int Downstream::init_response_body_buf()
|
int Downstream::init_response_body_buf()
|
||||||
{
|
{
|
||||||
assert(response_body_buf_ == 0);
|
assert(response_body_buf_ == 0);
|
||||||
|
@ -401,6 +423,7 @@ int Downstream::init_response_body_buf()
|
||||||
if(response_body_buf_ == 0) {
|
if(response_body_buf_ == 0) {
|
||||||
DIE();
|
DIE();
|
||||||
}
|
}
|
||||||
|
evbuffer_setcb(response_body_buf_, body_buf_cb, this);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ extern "C" {
|
||||||
#include "htparse/htparse.h"
|
#include "htparse/htparse.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "shrpx_io_control.h"
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
class Upstream;
|
class Upstream;
|
||||||
|
@ -52,6 +54,8 @@ public:
|
||||||
int start_connection();
|
int start_connection();
|
||||||
Upstream* get_upstream() const;
|
Upstream* get_upstream() const;
|
||||||
int32_t get_stream_id() const;
|
int32_t get_stream_id() const;
|
||||||
|
void pause_read(IOCtrlReason reason);
|
||||||
|
bool resume_read(IOCtrlReason reason);
|
||||||
// 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);
|
||||||
|
@ -89,7 +93,7 @@ private:
|
||||||
bufferevent *bev_;
|
bufferevent *bev_;
|
||||||
int32_t stream_id_;
|
int32_t stream_id_;
|
||||||
int priority_;
|
int priority_;
|
||||||
|
IOControl ioctrl_;
|
||||||
int request_state_;
|
int request_state_;
|
||||||
std::string request_method_;
|
std::string request_method_;
|
||||||
std::string request_path_;
|
std::string request_path_;
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#ifndef SHRPX_ERROR_H
|
#ifndef SHRPX_ERROR_H
|
||||||
#define SHRPX_ERROR_H
|
#define SHRPX_ERROR_H
|
||||||
|
|
||||||
|
#include "shrpx.h"
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
|
|
|
@ -38,9 +38,14 @@ using namespace spdylay;
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const size_t SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
HttpsUpstream::HttpsUpstream(ClientHandler *handler)
|
||||||
: handler_(handler),
|
: handler_(handler),
|
||||||
htp_(htparser_new())
|
htp_(htparser_new()),
|
||||||
|
ioctrl_(handler->get_bev())
|
||||||
{
|
{
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "HttpsUpstream ctor";
|
LOG(INFO) << "HttpsUpstream ctor";
|
||||||
|
@ -221,7 +226,7 @@ int HttpsUpstream::on_read()
|
||||||
evbuffer_drain(input, nread);
|
evbuffer_drain(input, nread);
|
||||||
htpparse_error htperr = htparser_get_error(htp_);
|
htpparse_error htperr = htparser_get_error(htp_);
|
||||||
if(htperr == htparse_error_user) {
|
if(htperr == htparse_error_user) {
|
||||||
bufferevent_disable(bev, EV_READ);
|
pause_read(SHRPX_MSG_BLOCK);
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "<upstream> remaining bytes " << evbuffer_get_length(input);
|
LOG(INFO) << "<upstream> remaining bytes " << evbuffer_get_length(input);
|
||||||
}
|
}
|
||||||
|
@ -235,6 +240,15 @@ int HttpsUpstream::on_read()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HttpsUpstream::on_write()
|
||||||
|
{
|
||||||
|
Downstream *downstream = get_top_downstream();
|
||||||
|
if(downstream) {
|
||||||
|
downstream->resume_read(SHRPX_NO_BUFFER);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int HttpsUpstream::on_event()
|
int HttpsUpstream::on_event()
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -245,12 +259,19 @@ ClientHandler* HttpsUpstream::get_client_handler() const
|
||||||
return handler_;
|
return handler_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpsUpstream::resume_read()
|
void HttpsUpstream::pause_read(IOCtrlReason reason)
|
||||||
{
|
{
|
||||||
bufferevent_enable(handler_->get_bev(), EV_READ);
|
ioctrl_.pause_read(reason);
|
||||||
// Process remaining data in input buffer here.
|
}
|
||||||
|
|
||||||
|
void HttpsUpstream::resume_read(IOCtrlReason reason)
|
||||||
|
{
|
||||||
|
if(ioctrl_.resume_read(reason)) {
|
||||||
|
// Process remaining data in input buffer here because these bytes
|
||||||
|
// are not notified by readcb until new data arrive.
|
||||||
on_read();
|
on_read();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void https_downstream_readcb(bufferevent *bev, void *ptr)
|
void https_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
|
@ -264,7 +285,14 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
assert(downstream == upstream->get_top_downstream());
|
assert(downstream == upstream->get_top_downstream());
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read();
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
|
} else {
|
||||||
|
ClientHandler *handler = upstream->get_client_handler();
|
||||||
|
bufferevent *bev = handler->get_bev();
|
||||||
|
size_t outputlen = evbuffer_get_length(bufferevent_get_output(bev));
|
||||||
|
if(outputlen > SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES) {
|
||||||
|
downstream->pause_read(SHRPX_NO_BUFFER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
|
||||||
|
@ -274,7 +302,7 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
|
||||||
assert(downstream == upstream->get_top_downstream());
|
assert(downstream == upstream->get_top_downstream());
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read();
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +348,7 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
}
|
}
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read();
|
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;
|
||||||
|
@ -336,7 +364,7 @@ void https_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
}
|
}
|
||||||
upstream->pop_downstream();
|
upstream->pop_downstream();
|
||||||
delete downstream;
|
delete downstream;
|
||||||
upstream->resume_read();
|
upstream->resume_read(SHRPX_MSG_BLOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -384,13 +412,21 @@ void HttpsUpstream::pop_downstream()
|
||||||
|
|
||||||
Downstream* HttpsUpstream::get_top_downstream()
|
Downstream* HttpsUpstream::get_top_downstream()
|
||||||
{
|
{
|
||||||
|
if(downstream_queue_.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
return downstream_queue_.front();
|
return downstream_queue_.front();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Downstream* HttpsUpstream::get_last_downstream()
|
Downstream* HttpsUpstream::get_last_downstream()
|
||||||
{
|
{
|
||||||
|
if(downstream_queue_.empty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
return downstream_queue_.back();
|
return downstream_queue_.back();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
int HttpsUpstream::on_downstream_header_complete(Downstream *downstream)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "shrpx_upstream.h"
|
#include "shrpx_upstream.h"
|
||||||
|
#include "shrpx_io_control.h"
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
@ -46,6 +47,7 @@ public:
|
||||||
HttpsUpstream(ClientHandler *handler);
|
HttpsUpstream(ClientHandler *handler);
|
||||||
virtual ~HttpsUpstream();
|
virtual ~HttpsUpstream();
|
||||||
virtual int on_read();
|
virtual int on_read();
|
||||||
|
virtual int on_write();
|
||||||
virtual int on_event();
|
virtual int on_event();
|
||||||
//int send();
|
//int send();
|
||||||
virtual ClientHandler* get_client_handler() const;
|
virtual ClientHandler* get_client_handler() const;
|
||||||
|
@ -58,7 +60,8 @@ public:
|
||||||
Downstream* get_last_downstream();
|
Downstream* get_last_downstream();
|
||||||
void error_reply(Downstream *downstream, int status_code);
|
void error_reply(Downstream *downstream, int status_code);
|
||||||
|
|
||||||
void resume_read();
|
void pause_read(IOCtrlReason reason);
|
||||||
|
void resume_read(IOCtrlReason reason);
|
||||||
|
|
||||||
virtual int on_downstream_header_complete(Downstream *downstream);
|
virtual int on_downstream_header_complete(Downstream *downstream);
|
||||||
virtual int on_downstream_body(Downstream *downstream,
|
virtual int on_downstream_body(Downstream *downstream,
|
||||||
|
@ -69,6 +72,7 @@ private:
|
||||||
ClientHandler *handler_;
|
ClientHandler *handler_;
|
||||||
htparser *htp_;
|
htparser *htp_;
|
||||||
std::deque<Downstream*> downstream_queue_;
|
std::deque<Downstream*> downstream_queue_;
|
||||||
|
IOControl ioctrl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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_io_control.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace shrpx {
|
||||||
|
|
||||||
|
IOControl::IOControl(bufferevent *bev)
|
||||||
|
: bev_(bev),
|
||||||
|
ctrlv_(SHRPX_REASON_MAX)
|
||||||
|
{}
|
||||||
|
|
||||||
|
IOControl::~IOControl()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void IOControl::set_bev(bufferevent *bev)
|
||||||
|
{
|
||||||
|
bev_ = bev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOControl::pause_read(IOCtrlReason reason)
|
||||||
|
{
|
||||||
|
ctrlv_[reason] = 1;
|
||||||
|
bufferevent_disable(bev_, EV_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IOControl::resume_read(IOCtrlReason reason)
|
||||||
|
{
|
||||||
|
ctrlv_[reason] = 0;
|
||||||
|
if(std::find(ctrlv_.begin(), ctrlv_.end(), 1) == ctrlv_.end()) {
|
||||||
|
bufferevent_enable(bev_, EV_READ);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace shrpx
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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_IO_CONTROL_H
|
||||||
|
#define SHRPX_IO_CONTROL_H
|
||||||
|
|
||||||
|
#include "shrpx.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <event.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
|
namespace shrpx {
|
||||||
|
|
||||||
|
enum IOCtrlReason {
|
||||||
|
SHRPX_NO_BUFFER = 0,
|
||||||
|
SHRPX_MSG_BLOCK,
|
||||||
|
SHRPX_REASON_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
class IOControl {
|
||||||
|
public:
|
||||||
|
IOControl(bufferevent *bev);
|
||||||
|
~IOControl();
|
||||||
|
void set_bev(bufferevent *bev);
|
||||||
|
void pause_read(IOCtrlReason reason);
|
||||||
|
// Returns true if read operation is enabled after this call
|
||||||
|
bool resume_read(IOCtrlReason reason);
|
||||||
|
private:
|
||||||
|
bufferevent *bev_;
|
||||||
|
std::vector<int> ctrlv_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace shrpx
|
||||||
|
|
||||||
|
#endif // SHRPX_IO_CONTROL_H
|
|
@ -37,6 +37,10 @@ using namespace spdylay;
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const size_t SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES = 512*1024;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
ssize_t send_callback(spdylay_session *session,
|
ssize_t send_callback(spdylay_session *session,
|
||||||
const uint8_t *data, size_t len, int flags,
|
const uint8_t *data, size_t len, int flags,
|
||||||
|
@ -47,6 +51,11 @@ ssize_t send_callback(spdylay_session *session,
|
||||||
ClientHandler *handler = upstream->get_client_handler();
|
ClientHandler *handler = upstream->get_client_handler();
|
||||||
bufferevent *bev = handler->get_bev();
|
bufferevent *bev = handler->get_bev();
|
||||||
evbuffer *output = bufferevent_get_output(bev);
|
evbuffer *output = bufferevent_get_output(bev);
|
||||||
|
// Check buffer length and return WOULDBLOCK if it is large enough.
|
||||||
|
if(evbuffer_get_length(output) > SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) {
|
||||||
|
return SPDYLAY_ERR_WOULDBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
rv = evbuffer_add(output, data, len);
|
rv = evbuffer_add(output, data, len);
|
||||||
if(rv == -1) {
|
if(rv == -1) {
|
||||||
return SPDYLAY_ERR_CALLBACK_FAILURE;
|
return SPDYLAY_ERR_CALLBACK_FAILURE;
|
||||||
|
@ -228,6 +237,12 @@ int SpdyUpstream::on_read()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SpdyUpstream::on_write()
|
||||||
|
{
|
||||||
|
send();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int SpdyUpstream::send()
|
int SpdyUpstream::send()
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -306,32 +321,36 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
LOG(INFO) << "<downstream> EOF stream_id="
|
LOG(INFO) << "<downstream> EOF stream_id="
|
||||||
<< downstream->get_stream_id();
|
<< downstream->get_stream_id();
|
||||||
}
|
}
|
||||||
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
|
//If stream was closed already, we don't need to send reply at
|
||||||
|
// the first place. We can delete downstream.
|
||||||
|
upstream->remove_downstream(downstream);
|
||||||
|
delete downstream;
|
||||||
|
} else {
|
||||||
|
// 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
|
||||||
if(ENABLE_LOG) {
|
if(ENABLE_LOG) {
|
||||||
LOG(INFO) << "<downstream> Assuming content-length is 0 byte";
|
LOG(INFO) << "<downstream> Assuming content-length is 0 byte";
|
||||||
}
|
}
|
||||||
upstream->on_downstream_body_complete(downstream);
|
|
||||||
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
// downstream wil be deleted in on_stream_close_callback.
|
upstream->on_downstream_body_complete(downstream);
|
||||||
} else if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
} else if(downstream->get_response_state() != Downstream::MSG_COMPLETE) {
|
||||||
// nothing todo
|
// If stream was not closed, then we set MSG_COMPLETE and let
|
||||||
} else {
|
// on_stream_close_callback delete downstream.
|
||||||
// error
|
|
||||||
if(ENABLE_LOG) {
|
|
||||||
LOG(INFO) << "<downstream> Treated as error";
|
|
||||||
}
|
|
||||||
upstream->error_reply(downstream, 502);
|
upstream->error_reply(downstream, 502);
|
||||||
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
upstream->send();
|
upstream->send();
|
||||||
upstream->remove_downstream(downstream);
|
}
|
||||||
delete downstream;
|
|
||||||
}
|
}
|
||||||
} 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 " << downstream;
|
LOG(INFO) << "<downstream> error/timeout. Downstream " << downstream;
|
||||||
}
|
}
|
||||||
// For Downstream::MSG_COMPLETE case, downstream will be deleted
|
if(downstream->get_request_state() == Downstream::STREAM_CLOSED) {
|
||||||
// in on_stream_close_callback.
|
upstream->remove_downstream(downstream);
|
||||||
|
delete downstream;
|
||||||
|
} else {
|
||||||
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);
|
||||||
|
@ -344,9 +363,9 @@ void spdy_downstream_eventcb(bufferevent *bev, short events, void *ptr)
|
||||||
}
|
}
|
||||||
upstream->error_reply(downstream, status);
|
upstream->error_reply(downstream, status);
|
||||||
}
|
}
|
||||||
|
downstream->set_response_state(Downstream::MSG_COMPLETE);
|
||||||
upstream->send();
|
upstream->send();
|
||||||
upstream->remove_downstream(downstream);
|
}
|
||||||
delete downstream;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,6 +530,12 @@ int SpdyUpstream::on_downstream_body(Downstream *downstream,
|
||||||
evbuffer_add(body, data, len);
|
evbuffer_add(body, data, len);
|
||||||
spdylay_session_resume_data(session_, downstream->get_stream_id());
|
spdylay_session_resume_data(session_, downstream->get_stream_id());
|
||||||
//send();
|
//send();
|
||||||
|
|
||||||
|
size_t bodylen = evbuffer_get_length(body);
|
||||||
|
if(bodylen > SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) {
|
||||||
|
downstream->pause_read(SHRPX_NO_BUFFER);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ public:
|
||||||
SpdyUpstream(uint16_t version, ClientHandler *handler);
|
SpdyUpstream(uint16_t version, ClientHandler *handler);
|
||||||
virtual ~SpdyUpstream();
|
virtual ~SpdyUpstream();
|
||||||
virtual int on_read();
|
virtual int on_read();
|
||||||
|
virtual int on_write();
|
||||||
virtual int on_event();
|
virtual int on_event();
|
||||||
int send();
|
int send();
|
||||||
virtual ClientHandler* get_client_handler() const;
|
virtual ClientHandler* get_client_handler() const;
|
||||||
|
|
|
@ -38,6 +38,7 @@ class Upstream {
|
||||||
public:
|
public:
|
||||||
virtual ~Upstream() {}
|
virtual ~Upstream() {}
|
||||||
virtual int on_read() = 0;
|
virtual int on_read() = 0;
|
||||||
|
virtual int on_write() = 0;
|
||||||
virtual int on_event() = 0;
|
virtual int on_event() = 0;
|
||||||
virtual bufferevent_data_cb get_downstream_readcb() = 0;
|
virtual bufferevent_data_cb get_downstream_readcb() = 0;
|
||||||
virtual bufferevent_data_cb get_downstream_writecb() = 0;
|
virtual bufferevent_data_cb get_downstream_writecb() = 0;
|
||||||
|
|
Loading…
Reference in New Issue