nghttpx: Add backend-connections-per-frontend option
This option limits the number of backend connections per frontend. This is meaningful for the combination of HTTP/2 and SPDY frontend and HTTP/1 backend.
This commit is contained in:
parent
da08ba5d50
commit
0e8419ac37
14
src/shrpx.cc
14
src/shrpx.cc
|
@ -796,6 +796,7 @@ void fill_default_config()
|
||||||
mod_config()->no_location_rewrite = false;
|
mod_config()->no_location_rewrite = false;
|
||||||
mod_config()->argc = 0;
|
mod_config()->argc = 0;
|
||||||
mod_config()->argv = nullptr;
|
mod_config()->argv = nullptr;
|
||||||
|
mod_config()->max_downstream_connections = 100;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -891,6 +892,13 @@ Performance:
|
||||||
Set maximum number of simultaneous connections
|
Set maximum number of simultaneous connections
|
||||||
frontend accepts. Setting 0 means unlimited.
|
frontend accepts. Setting 0 means unlimited.
|
||||||
Default: 0
|
Default: 0
|
||||||
|
--backend-connections-per-frontend=<NUM>
|
||||||
|
Set maximum number of backend simultaneous
|
||||||
|
connections per frontend. This option is
|
||||||
|
meaningful when the combination of HTTP/2 or SPDY
|
||||||
|
frontend and HTTP/1 backend is used.
|
||||||
|
Default: )"
|
||||||
|
<< get_config()->max_downstream_connections << R"(
|
||||||
|
|
||||||
Timeout:
|
Timeout:
|
||||||
--frontend-http2-read-timeout=<SEC>
|
--frontend-http2-read-timeout=<SEC>
|
||||||
|
@ -1246,6 +1254,7 @@ int main(int argc, char **argv)
|
||||||
{"stream-read-timeout", required_argument, &flag, 60},
|
{"stream-read-timeout", required_argument, &flag, 60},
|
||||||
{"stream-write-timeout", required_argument, &flag, 61},
|
{"stream-write-timeout", required_argument, &flag, 61},
|
||||||
{"no-location-rewrite", no_argument, &flag, 62},
|
{"no-location-rewrite", no_argument, &flag, 62},
|
||||||
|
{"backend-connections-per-frontend", required_argument, &flag, 63},
|
||||||
{nullptr, 0, nullptr, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1518,6 +1527,11 @@ int main(int argc, char **argv)
|
||||||
// --no-location-rewrite
|
// --no-location-rewrite
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes");
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes");
|
||||||
break;
|
break;
|
||||||
|
case 63:
|
||||||
|
// --backend-connections-per-frontend
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND,
|
||||||
|
optarg);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,8 @@ const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header";
|
||||||
const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] =
|
const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] =
|
||||||
"worker-frontend-connections";
|
"worker-frontend-connections";
|
||||||
const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite";
|
const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite";
|
||||||
|
const char SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND[] =
|
||||||
|
"backend-connections-per-frontend";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
Config *config = nullptr;
|
Config *config = nullptr;
|
||||||
|
@ -840,6 +842,22 @@ int parse_config(const char *opt, const char *optarg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(util::strieq(opt, SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND)) {
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
auto n = strtoul(optarg, nullptr, 10);
|
||||||
|
|
||||||
|
if(errno != 0 || n < 1) {
|
||||||
|
LOG(ERROR) << "backend-connections-per-frontend: "
|
||||||
|
<< "specify the integer more than or equal to 1";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_config()->max_downstream_connections = n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(util::strieq(opt, "conf")) {
|
if(util::strieq(opt, "conf")) {
|
||||||
LOG(WARNING) << "conf is ignored";
|
LOG(WARNING) << "conf is ignored";
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,7 @@ extern const char SHRPX_OPT_ALTSVC[];
|
||||||
extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[];
|
extern const char SHRPX_OPT_ADD_RESPONSE_HEADER[];
|
||||||
extern const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[];
|
extern const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[];
|
||||||
extern const char SHRPX_OPT_NO_LOCATION_REWRITE[];
|
extern const char SHRPX_OPT_NO_LOCATION_REWRITE[];
|
||||||
|
extern const char SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND[];
|
||||||
|
|
||||||
union sockaddr_union {
|
union sockaddr_union {
|
||||||
sockaddr sa;
|
sockaddr sa;
|
||||||
|
@ -217,6 +218,7 @@ struct Config {
|
||||||
size_t http2_downstream_window_bits;
|
size_t http2_downstream_window_bits;
|
||||||
size_t http2_upstream_connection_window_bits;
|
size_t http2_upstream_connection_window_bits;
|
||||||
size_t http2_downstream_connection_window_bits;
|
size_t http2_downstream_connection_window_bits;
|
||||||
|
size_t max_downstream_connections;
|
||||||
// actual size of downstream_http_proxy_addr
|
// actual size of downstream_http_proxy_addr
|
||||||
size_t downstream_http_proxy_addrlen;
|
size_t downstream_http_proxy_addrlen;
|
||||||
size_t read_rate;
|
size_t read_rate;
|
||||||
|
|
|
@ -35,29 +35,87 @@ DownstreamQueue::DownstreamQueue()
|
||||||
|
|
||||||
DownstreamQueue::~DownstreamQueue()
|
DownstreamQueue::~DownstreamQueue()
|
||||||
{
|
{
|
||||||
for(auto& kv : downstreams_) {
|
for(auto& kv : pending_downstreams_) {
|
||||||
|
delete kv.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& kv : active_downstreams_) {
|
||||||
|
delete kv.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto& kv : failure_downstreams_) {
|
||||||
delete kv.second;
|
delete kv.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::add(Downstream *downstream)
|
void DownstreamQueue::add_pending(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstreams_[downstream->get_stream_id()] = downstream;
|
pending_downstreams_[downstream->get_stream_id()] = downstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownstreamQueue::add_failure(Downstream *downstream)
|
||||||
|
{
|
||||||
|
failure_downstreams_[downstream->get_stream_id()] = downstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownstreamQueue::add_active(Downstream *downstream)
|
||||||
|
{
|
||||||
|
active_downstreams_[downstream->get_stream_id()] = downstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::remove(Downstream *downstream)
|
void DownstreamQueue::remove(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstreams_.erase(downstream->get_stream_id());
|
pending_downstreams_.erase(downstream->get_stream_id());
|
||||||
|
active_downstreams_.erase(downstream->get_stream_id());
|
||||||
|
failure_downstreams_.erase(downstream->get_stream_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream* DownstreamQueue::find(int32_t stream_id)
|
Downstream* DownstreamQueue::find(int32_t stream_id)
|
||||||
{
|
{
|
||||||
auto kv = downstreams_.find(stream_id);
|
auto kv = pending_downstreams_.find(stream_id);
|
||||||
if(kv == std::end(downstreams_)) {
|
|
||||||
return nullptr;
|
if(kv != std::end(pending_downstreams_)) {
|
||||||
} else {
|
|
||||||
return (*kv).second;
|
return (*kv).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kv = active_downstreams_.find(stream_id);
|
||||||
|
|
||||||
|
if(kv != std::end(active_downstreams_)) {
|
||||||
|
return (*kv).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
kv = failure_downstreams_.find(stream_id);
|
||||||
|
|
||||||
|
if(kv != std::end(failure_downstreams_)) {
|
||||||
|
return (*kv).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Downstream* DownstreamQueue::pop_pending()
|
||||||
|
{
|
||||||
|
auto i = std::begin(pending_downstreams_);
|
||||||
|
|
||||||
|
if(i == std::end(pending_downstreams_)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto downstream = (*i).second;
|
||||||
|
|
||||||
|
pending_downstreams_.erase(i);
|
||||||
|
|
||||||
|
return downstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DownstreamQueue::num_active() const
|
||||||
|
{
|
||||||
|
return active_downstreams_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DownstreamQueue::pending_empty() const
|
||||||
|
{
|
||||||
|
return pending_downstreams_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -39,11 +39,30 @@ class DownstreamQueue {
|
||||||
public:
|
public:
|
||||||
DownstreamQueue();
|
DownstreamQueue();
|
||||||
~DownstreamQueue();
|
~DownstreamQueue();
|
||||||
void add(Downstream *downstream);
|
void add_pending(Downstream *downstream);
|
||||||
|
void add_failure(Downstream *downstream);
|
||||||
|
void add_active(Downstream *downstream);
|
||||||
|
// Removes |downstream| from either pending_downstreams_,
|
||||||
|
// active_downstreams_ or failure_downstreams_.
|
||||||
void remove(Downstream *downstream);
|
void remove(Downstream *downstream);
|
||||||
|
// Finds Downstream object denoted by |stream_id| either in
|
||||||
|
// pending_downstreams_, active_downstreams_ or
|
||||||
|
// failure_downstreams_.
|
||||||
Downstream* find(int32_t stream_id);
|
Downstream* find(int32_t stream_id);
|
||||||
|
// Returns the number of active Downstream objects.
|
||||||
|
size_t num_active() const;
|
||||||
|
// Returns true if pending_downstreams_ is empty.
|
||||||
|
bool pending_empty() const;
|
||||||
|
// Pops first Downstream object in pending_downstreams_ and returns
|
||||||
|
// it.
|
||||||
|
Downstream* pop_pending();
|
||||||
private:
|
private:
|
||||||
std::map<int32_t, Downstream*> downstreams_;
|
// Downstream objects, not processed yet
|
||||||
|
std::map<int32_t, Downstream*> pending_downstreams_;
|
||||||
|
// Downstream objects in use, consuming downstream concurrency limit
|
||||||
|
std::map<int32_t, Downstream*> active_downstreams_;
|
||||||
|
// Downstream objects, failed to connect to downstream server
|
||||||
|
std::map<int32_t, Downstream*> failure_downstreams_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -295,8 +295,6 @@ int on_request_headers(Http2Upstream *upstream,
|
||||||
nghttp2_session *session,
|
nghttp2_session *session,
|
||||||
const nghttp2_frame *frame)
|
const nghttp2_frame *frame)
|
||||||
{
|
{
|
||||||
int rv;
|
|
||||||
|
|
||||||
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if(downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -370,26 +368,6 @@ int on_request_headers(Http2Upstream *upstream,
|
||||||
|
|
||||||
downstream->inspect_http2_request();
|
downstream->inspect_http2_request();
|
||||||
|
|
||||||
auto dconn = upstream->get_client_handler()->get_downstream_connection();
|
|
||||||
rv = dconn->attach_downstream(downstream);
|
|
||||||
if(rv != 0) {
|
|
||||||
// downstream connection fails, send error page
|
|
||||||
if(upstream->error_reply(downstream, 503) != 0) {
|
|
||||||
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
rv = downstream->push_request_headers();
|
|
||||||
if(rv != 0) {
|
|
||||||
if(upstream->error_reply(downstream, 503) != 0) {
|
|
||||||
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
downstream->set_request_state(Downstream::HEADER_COMPLETE);
|
||||||
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
|
@ -397,11 +375,60 @@ int on_request_headers(Http2Upstream *upstream,
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstream->maintain_downstream_concurrency();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void Http2Upstream::maintain_downstream_concurrency()
|
||||||
|
{
|
||||||
|
while(get_config()->max_downstream_connections >
|
||||||
|
downstream_queue_.num_active()) {
|
||||||
|
auto downstream = downstream_queue_.pop_pending();
|
||||||
|
|
||||||
|
if(!downstream) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
initiate_downstream(downstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http2Upstream::initiate_downstream(Downstream *downstream)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
auto dconn = handler_->get_downstream_connection();
|
||||||
|
rv = dconn->attach_downstream(downstream);
|
||||||
|
if(rv != 0) {
|
||||||
|
downstream_queue_.add_failure(downstream);
|
||||||
|
|
||||||
|
// downstream connection fails, send error page
|
||||||
|
if(error_reply(downstream, 503) != 0) {
|
||||||
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rv = downstream->push_request_headers();
|
||||||
|
if(rv != 0) {
|
||||||
|
downstream_queue_.add_failure(downstream);
|
||||||
|
|
||||||
|
if(error_reply(downstream, 503) != 0) {
|
||||||
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downstream_queue_.add_active(downstream);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int on_frame_recv_callback
|
int on_frame_recv_callback
|
||||||
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
|
||||||
|
@ -1143,12 +1170,14 @@ bufferevent_event_cb Http2Upstream::get_downstream_eventcb()
|
||||||
|
|
||||||
void Http2Upstream::add_downstream(Downstream *downstream)
|
void Http2Upstream::add_downstream(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstream_queue_.add(downstream);
|
downstream_queue_.add_pending(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http2Upstream::remove_downstream(Downstream *downstream)
|
void Http2Upstream::remove_downstream(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstream_queue_.remove(downstream);
|
downstream_queue_.remove(downstream);
|
||||||
|
|
||||||
|
maintain_downstream_concurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream* Http2Upstream::find_downstream(int32_t stream_id)
|
Downstream* Http2Upstream::find_downstream(int32_t stream_id)
|
||||||
|
|
|
@ -82,6 +82,8 @@ public:
|
||||||
int consume(int32_t stream_id, size_t len);
|
int consume(int32_t stream_id, size_t len);
|
||||||
void log_response_headers(Downstream *downstream,
|
void log_response_headers(Downstream *downstream,
|
||||||
const std::vector<nghttp2_nv>& nva) const;
|
const std::vector<nghttp2_nv>& nva) const;
|
||||||
|
void maintain_downstream_concurrency();
|
||||||
|
void initiate_downstream(Downstream *downstream);
|
||||||
private:
|
private:
|
||||||
DownstreamQueue downstream_queue_;
|
DownstreamQueue downstream_queue_;
|
||||||
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
||||||
|
|
|
@ -230,24 +230,14 @@ void on_ctrl_recv_callback
|
||||||
<< "\n" << ss.str();
|
<< "\n" << ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto 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;
|
|
||||||
}
|
|
||||||
rv = downstream->push_request_headers();
|
|
||||||
if(rv != 0) {
|
|
||||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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) {
|
||||||
downstream->disable_upstream_rtimer();
|
downstream->disable_upstream_rtimer();
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstream->maintain_downstream_concurrency();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -256,6 +246,43 @@ void on_ctrl_recv_callback
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void SpdyUpstream::maintain_downstream_concurrency()
|
||||||
|
{
|
||||||
|
while(get_config()->max_downstream_connections >
|
||||||
|
downstream_queue_.num_active()) {
|
||||||
|
auto downstream = downstream_queue_.pop_pending();
|
||||||
|
|
||||||
|
if(!downstream) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
initiate_downstream(downstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpdyUpstream::initiate_downstream(Downstream *downstream)
|
||||||
|
{
|
||||||
|
auto dconn = handler_->get_downstream_connection();
|
||||||
|
int rv = dconn->attach_downstream(downstream);
|
||||||
|
if(rv != 0) {
|
||||||
|
downstream_queue_.add_failure(downstream);
|
||||||
|
|
||||||
|
// If downstream connection fails, issue RST_STREAM.
|
||||||
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rv = downstream->push_request_headers();
|
||||||
|
if(rv != 0) {
|
||||||
|
downstream_queue_.add_failure(downstream);
|
||||||
|
|
||||||
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
downstream_queue_.add_active(downstream);
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void on_data_chunk_recv_callback(spdylay_session *session,
|
void on_data_chunk_recv_callback(spdylay_session *session,
|
||||||
uint8_t flags, int32_t stream_id,
|
uint8_t flags, int32_t stream_id,
|
||||||
|
@ -876,12 +903,14 @@ bufferevent_event_cb SpdyUpstream::get_downstream_eventcb()
|
||||||
|
|
||||||
void SpdyUpstream::add_downstream(Downstream *downstream)
|
void SpdyUpstream::add_downstream(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstream_queue_.add(downstream);
|
downstream_queue_.add_pending(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpdyUpstream::remove_downstream(Downstream *downstream)
|
void SpdyUpstream::remove_downstream(Downstream *downstream)
|
||||||
{
|
{
|
||||||
downstream_queue_.remove(downstream);
|
downstream_queue_.remove(downstream);
|
||||||
|
|
||||||
|
maintain_downstream_concurrency();
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream* SpdyUpstream::find_downstream(int32_t stream_id)
|
Downstream* SpdyUpstream::find_downstream(int32_t stream_id)
|
||||||
|
|
|
@ -74,6 +74,9 @@ public:
|
||||||
|
|
||||||
int handle_ign_data_chunk(size_t len);
|
int handle_ign_data_chunk(size_t len);
|
||||||
|
|
||||||
|
void maintain_downstream_concurrency();
|
||||||
|
void initiate_downstream(Downstream *downstream);
|
||||||
|
|
||||||
nghttp2::util::EvbufferBuffer sendbuf;
|
nghttp2::util::EvbufferBuffer sendbuf;
|
||||||
private:
|
private:
|
||||||
DownstreamQueue downstream_queue_;
|
DownstreamQueue downstream_queue_;
|
||||||
|
|
Loading…
Reference in New Issue