nghttpx: Refactor DownstreamQueue to avoid expensive std::map
This commit is contained in:
parent
0f87cedc2d
commit
d46e50b112
|
@ -33,6 +33,7 @@
|
||||||
#include "shrpx_config.h"
|
#include "shrpx_config.h"
|
||||||
#include "shrpx_error.h"
|
#include "shrpx_error.h"
|
||||||
#include "shrpx_downstream_connection.h"
|
#include "shrpx_downstream_connection.h"
|
||||||
|
#include "shrpx_downstream_queue.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
|
|
||||||
|
@ -106,24 +107,27 @@ void downstream_wtimeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
|
|
||||||
// upstream could be nullptr for unittests
|
// upstream could be nullptr for unittests
|
||||||
Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
Downstream::Downstream(Upstream *upstream, int32_t stream_id, int32_t priority)
|
||||||
: request_start_time_(std::chrono::high_resolution_clock::now()),
|
: dlnext(nullptr), dlprev(nullptr),
|
||||||
|
request_start_time_(std::chrono::high_resolution_clock::now()),
|
||||||
request_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
request_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
||||||
response_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
response_buf_(upstream ? upstream->get_mcpool() : nullptr),
|
||||||
request_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0),
|
request_bodylen_(0), response_bodylen_(0), response_sent_bodylen_(0),
|
||||||
request_content_length_(-1), response_content_length_(-1),
|
request_content_length_(-1), response_content_length_(-1),
|
||||||
upstream_(upstream), request_headers_sum_(0), response_headers_sum_(0),
|
upstream_(upstream), blocked_link_(nullptr), request_headers_sum_(0),
|
||||||
request_datalen_(0), response_datalen_(0), num_retry_(0),
|
response_headers_sum_(0), request_datalen_(0), response_datalen_(0),
|
||||||
stream_id_(stream_id), priority_(priority), downstream_stream_id_(-1),
|
num_retry_(0), stream_id_(stream_id), priority_(priority),
|
||||||
|
downstream_stream_id_(-1),
|
||||||
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
|
response_rst_stream_error_code_(NGHTTP2_NO_ERROR),
|
||||||
request_state_(INITIAL), request_major_(1), request_minor_(1),
|
request_state_(INITIAL), request_major_(1), request_minor_(1),
|
||||||
response_state_(INITIAL), response_http_status_(0), response_major_(1),
|
response_state_(INITIAL), response_http_status_(0), response_major_(1),
|
||||||
response_minor_(1), upgrade_request_(false), upgraded_(false),
|
response_minor_(1), dispatch_state_(DISPATCH_NONE),
|
||||||
http2_upgrade_seen_(false), chunked_request_(false),
|
upgrade_request_(false), upgraded_(false), http2_upgrade_seen_(false),
|
||||||
request_connection_close_(false), request_header_key_prev_(false),
|
chunked_request_(false), request_connection_close_(false),
|
||||||
request_trailer_key_prev_(false), request_http2_expect_body_(false),
|
request_header_key_prev_(false), request_trailer_key_prev_(false),
|
||||||
chunked_response_(false), response_connection_close_(false),
|
request_http2_expect_body_(false), chunked_response_(false),
|
||||||
response_header_key_prev_(false), response_trailer_key_prev_(false),
|
response_connection_close_(false), response_header_key_prev_(false),
|
||||||
expect_final_response_(false), request_pending_(false) {
|
response_trailer_key_prev_(false), expect_final_response_(false),
|
||||||
|
request_pending_(false) {
|
||||||
|
|
||||||
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
|
ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0.,
|
||||||
get_config()->stream_read_timeout);
|
get_config()->stream_read_timeout);
|
||||||
|
@ -148,6 +152,10 @@ Downstream::~Downstream() {
|
||||||
DLOG(INFO, this) << "Deleting";
|
DLOG(INFO, this) << "Deleting";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blocked_link_) {
|
||||||
|
detach_blocked_link(blocked_link_);
|
||||||
|
}
|
||||||
|
|
||||||
// check nullptr for unittest
|
// check nullptr for unittest
|
||||||
if (upstream_) {
|
if (upstream_) {
|
||||||
auto loop = upstream_->get_client_handler()->get_loop();
|
auto loop = upstream_->get_client_handler()->get_loop();
|
||||||
|
@ -1176,4 +1184,23 @@ bool Downstream::request_submission_ready() const {
|
||||||
request_pending_ && response_state_ == Downstream::INITIAL;
|
request_pending_ && response_state_ == Downstream::INITIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Downstream::get_dispatch_state() const { return dispatch_state_; }
|
||||||
|
|
||||||
|
void Downstream::set_dispatch_state(int s) { dispatch_state_ = s; }
|
||||||
|
|
||||||
|
void Downstream::attach_blocked_link(BlockedLink *l) {
|
||||||
|
assert(!blocked_link_);
|
||||||
|
|
||||||
|
l->downstream = this;
|
||||||
|
blocked_link_ = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Downstream::detach_blocked_link(BlockedLink *l) {
|
||||||
|
assert(blocked_link_);
|
||||||
|
assert(l->downstream == this);
|
||||||
|
|
||||||
|
l->downstream = nullptr;
|
||||||
|
blocked_link_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -48,6 +48,7 @@ namespace shrpx {
|
||||||
|
|
||||||
class Upstream;
|
class Upstream;
|
||||||
class DownstreamConnection;
|
class DownstreamConnection;
|
||||||
|
struct BlockedLink;
|
||||||
|
|
||||||
class Downstream {
|
class Downstream {
|
||||||
public:
|
public:
|
||||||
|
@ -319,11 +320,27 @@ public:
|
||||||
// true if retry attempt should not be done.
|
// true if retry attempt should not be done.
|
||||||
bool no_more_retry() const;
|
bool no_more_retry() const;
|
||||||
|
|
||||||
|
int get_dispatch_state() const;
|
||||||
|
void set_dispatch_state(int s);
|
||||||
|
|
||||||
|
void attach_blocked_link(BlockedLink *l);
|
||||||
|
void detach_blocked_link(BlockedLink *l);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EVENT_ERROR = 0x1,
|
EVENT_ERROR = 0x1,
|
||||||
EVENT_TIMEOUT = 0x2,
|
EVENT_TIMEOUT = 0x2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DISPATCH_NONE,
|
||||||
|
DISPATCH_PENDING,
|
||||||
|
DISPATCH_BLOCKED,
|
||||||
|
DISPATCH_ACTIVE,
|
||||||
|
DISPATCH_FAILURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Downstream *dlnext, *dlprev;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Headers request_headers_;
|
Headers request_headers_;
|
||||||
Headers response_headers_;
|
Headers response_headers_;
|
||||||
|
@ -370,6 +387,9 @@ private:
|
||||||
Upstream *upstream_;
|
Upstream *upstream_;
|
||||||
std::unique_ptr<DownstreamConnection> dconn_;
|
std::unique_ptr<DownstreamConnection> dconn_;
|
||||||
|
|
||||||
|
// only used by HTTP/2 or SPDY upstream
|
||||||
|
BlockedLink *blocked_link_;
|
||||||
|
|
||||||
size_t request_headers_sum_;
|
size_t request_headers_sum_;
|
||||||
size_t response_headers_sum_;
|
size_t response_headers_sum_;
|
||||||
|
|
||||||
|
@ -396,6 +416,9 @@ private:
|
||||||
int response_major_;
|
int response_major_;
|
||||||
int response_minor_;
|
int response_minor_;
|
||||||
|
|
||||||
|
// only used by HTTP/2 or SPDY upstream
|
||||||
|
int dispatch_state_;
|
||||||
|
|
||||||
http2::HeaderIndex request_hdidx_;
|
http2::HeaderIndex request_hdidx_;
|
||||||
http2::HeaderIndex response_hdidx_;
|
http2::HeaderIndex response_hdidx_;
|
||||||
|
|
||||||
|
|
|
@ -39,16 +39,21 @@ DownstreamQueue::DownstreamQueue(size_t conn_max_per_host, bool unified_host)
|
||||||
: conn_max_per_host),
|
: conn_max_per_host),
|
||||||
unified_host_(unified_host) {}
|
unified_host_(unified_host) {}
|
||||||
|
|
||||||
DownstreamQueue::~DownstreamQueue() {}
|
DownstreamQueue::~DownstreamQueue() {
|
||||||
|
dlist_delete_all(downstreams_);
|
||||||
void DownstreamQueue::add_pending(std::unique_ptr<Downstream> downstream) {
|
for (auto &p : host_entries_) {
|
||||||
auto stream_id = downstream->get_stream_id();
|
auto &ent = p.second;
|
||||||
pending_downstreams_[stream_id] = std::move(downstream);
|
dlist_delete_all(ent.blocked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::add_failure(std::unique_ptr<Downstream> downstream) {
|
void DownstreamQueue::add_pending(std::unique_ptr<Downstream> downstream) {
|
||||||
auto stream_id = downstream->get_stream_id();
|
downstream->set_dispatch_state(Downstream::DISPATCH_PENDING);
|
||||||
failure_downstreams_[stream_id] = std::move(downstream);
|
downstreams_.append(downstream.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownstreamQueue::mark_failure(Downstream *downstream) {
|
||||||
|
downstream->set_dispatch_state(Downstream::DISPATCH_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
DownstreamQueue::HostEntry &
|
DownstreamQueue::HostEntry &
|
||||||
|
@ -76,19 +81,21 @@ DownstreamQueue::make_host_key(Downstream *downstream) const {
|
||||||
return make_host_key(downstream->get_request_http2_authority());
|
return make_host_key(downstream->get_request_http2_authority());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::add_active(std::unique_ptr<Downstream> downstream) {
|
void DownstreamQueue::mark_active(Downstream *downstream) {
|
||||||
auto &ent = find_host_entry(make_host_key(downstream.get()));
|
auto &ent = find_host_entry(make_host_key(downstream));
|
||||||
++ent.num_active;
|
++ent.num_active;
|
||||||
|
|
||||||
auto stream_id = downstream->get_stream_id();
|
downstream->set_dispatch_state(Downstream::DISPATCH_ACTIVE);
|
||||||
active_downstreams_[stream_id] = std::move(downstream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownstreamQueue::add_blocked(std::unique_ptr<Downstream> downstream) {
|
void DownstreamQueue::mark_blocked(Downstream *downstream) {
|
||||||
auto &ent = find_host_entry(make_host_key(downstream.get()));
|
auto &ent = find_host_entry(make_host_key(downstream));
|
||||||
auto stream_id = downstream->get_stream_id();
|
|
||||||
ent.blocked.insert(stream_id);
|
downstream->set_dispatch_state(Downstream::DISPATCH_BLOCKED);
|
||||||
blocked_downstreams_[stream_id] = std::move(downstream);
|
|
||||||
|
auto link = new BlockedLink{};
|
||||||
|
downstream->attach_blocked_link(link);
|
||||||
|
ent.blocked.append(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownstreamQueue::can_activate(const std::string &host) const {
|
bool DownstreamQueue::can_activate(const std::string &host) const {
|
||||||
|
@ -100,16 +107,6 @@ bool DownstreamQueue::can_activate(const std::string &host) const {
|
||||||
return ent.num_active < conn_max_per_host_;
|
return ent.num_active < conn_max_per_host_;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::unique_ptr<Downstream>
|
|
||||||
pop_downstream(DownstreamQueue::DownstreamMap::iterator i,
|
|
||||||
DownstreamQueue::DownstreamMap &downstreams) {
|
|
||||||
auto downstream = std::move((*i).second);
|
|
||||||
downstreams.erase(i);
|
|
||||||
return downstream;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
|
bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
|
||||||
DownstreamQueue::HostEntryMap &host_entries,
|
DownstreamQueue::HostEntryMap &host_entries,
|
||||||
|
@ -122,106 +119,49 @@ bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<Downstream> DownstreamQueue::pop_pending(int32_t stream_id) {
|
Downstream *DownstreamQueue::remove_and_get_blocked(Downstream *downstream) {
|
||||||
auto itr = pending_downstreams_.find(stream_id);
|
// Delete downstream when this function returns.
|
||||||
if (itr == std::end(pending_downstreams_)) {
|
auto delptr = std::unique_ptr<Downstream>(downstream);
|
||||||
|
|
||||||
|
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
|
||||||
|
assert(downstream->get_dispatch_state() != Downstream::DISPATCH_NONE);
|
||||||
|
downstreams_.remove(downstream);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return pop_downstream(itr, pending_downstreams_);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<Downstream>
|
downstreams_.remove(downstream);
|
||||||
DownstreamQueue::remove_and_pop_blocked(int32_t stream_id) {
|
|
||||||
auto kv = active_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(active_downstreams_)) {
|
auto &host = make_host_key(downstream);
|
||||||
auto downstream = pop_downstream(kv, active_downstreams_);
|
auto &ent = find_host_entry(host);
|
||||||
auto &host = make_host_key(downstream.get());
|
--ent.num_active;
|
||||||
auto &ent = find_host_entry(host);
|
|
||||||
--ent.num_active;
|
|
||||||
|
|
||||||
if (remove_host_entry_if_empty(ent, host_entries_, host)) {
|
if (remove_host_entry_if_empty(ent, host_entries_, host)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent.num_active >= conn_max_per_host_) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto link = ent.blocked.head; link;) {
|
||||||
|
auto next = link->dlnext;
|
||||||
|
if (!link->downstream) {
|
||||||
|
ent.blocked.remove(link);
|
||||||
|
link = next;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
auto next_downstream = link->downstream;
|
||||||
if (ent.blocked.empty() || ent.num_active >= conn_max_per_host_) {
|
next_downstream->detach_blocked_link(link);
|
||||||
return nullptr;
|
ent.blocked.remove(link);
|
||||||
}
|
delete link;
|
||||||
|
|
||||||
auto next_stream_id = *std::begin(ent.blocked);
|
|
||||||
ent.blocked.erase(std::begin(ent.blocked));
|
|
||||||
|
|
||||||
auto itr = blocked_downstreams_.find(next_stream_id);
|
|
||||||
assert(itr != std::end(blocked_downstreams_));
|
|
||||||
|
|
||||||
auto next_downstream = pop_downstream(itr, blocked_downstreams_);
|
|
||||||
|
|
||||||
remove_host_entry_if_empty(ent, host_entries_, host);
|
remove_host_entry_if_empty(ent, host_entries_, host);
|
||||||
|
|
||||||
return next_downstream;
|
return next_downstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
kv = blocked_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(blocked_downstreams_)) {
|
|
||||||
auto downstream = pop_downstream(kv, blocked_downstreams_);
|
|
||||||
auto &host = make_host_key(downstream.get());
|
|
||||||
auto &ent = find_host_entry(host);
|
|
||||||
ent.blocked.erase(stream_id);
|
|
||||||
|
|
||||||
remove_host_entry_if_empty(ent, host_entries_, host);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
kv = pending_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(pending_downstreams_)) {
|
|
||||||
pop_downstream(kv, pending_downstreams_);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
kv = failure_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(failure_downstreams_)) {
|
|
||||||
pop_downstream(kv, failure_downstreams_);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream *DownstreamQueue::find(int32_t stream_id) {
|
Downstream *DownstreamQueue::get_downstreams() const {
|
||||||
auto kv = active_downstreams_.find(stream_id);
|
return downstreams_.head;
|
||||||
|
|
||||||
if (kv != std::end(active_downstreams_)) {
|
|
||||||
return (*kv).second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
kv = blocked_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(blocked_downstreams_)) {
|
|
||||||
return (*kv).second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
kv = pending_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(pending_downstreams_)) {
|
|
||||||
return (*kv).second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
kv = failure_downstreams_.find(stream_id);
|
|
||||||
|
|
||||||
if (kv != std::end(failure_downstreams_)) {
|
|
||||||
return (*kv).second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DownstreamQueue::DownstreamMap &
|
|
||||||
DownstreamQueue::get_active_downstreams() const {
|
|
||||||
return active_downstreams_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -33,17 +33,27 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "template.h"
|
||||||
|
|
||||||
|
using namespace nghttp2;
|
||||||
|
|
||||||
namespace shrpx {
|
namespace shrpx {
|
||||||
|
|
||||||
class Downstream;
|
class Downstream;
|
||||||
|
|
||||||
|
// Link entry in HostEntry.blocked and downstream because downstream
|
||||||
|
// could be deleted in anytime and we'd like to find Downstream in
|
||||||
|
// O(1). Downstream has field to link back to this object.
|
||||||
|
struct BlockedLink {
|
||||||
|
Downstream *downstream;
|
||||||
|
BlockedLink *dlnext, *dlprev;
|
||||||
|
};
|
||||||
|
|
||||||
class DownstreamQueue {
|
class DownstreamQueue {
|
||||||
public:
|
public:
|
||||||
typedef std::map<int32_t, std::unique_ptr<Downstream>> DownstreamMap;
|
|
||||||
|
|
||||||
struct HostEntry {
|
struct HostEntry {
|
||||||
// Set of stream ID that blocked by conn_max_per_host_.
|
// Set of stream ID that blocked by conn_max_per_host_.
|
||||||
std::set<int32_t> blocked;
|
DList<BlockedLink> blocked;
|
||||||
// The number of connections currently made to this host.
|
// The number of connections currently made to this host.
|
||||||
size_t num_active;
|
size_t num_active;
|
||||||
HostEntry();
|
HostEntry();
|
||||||
|
@ -54,52 +64,38 @@ public:
|
||||||
// conn_max_per_host == 0 means no limit for downstream connection.
|
// conn_max_per_host == 0 means no limit for downstream connection.
|
||||||
DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true);
|
DownstreamQueue(size_t conn_max_per_host = 0, bool unified_host = true);
|
||||||
~DownstreamQueue();
|
~DownstreamQueue();
|
||||||
|
// Add |downstream| to this queue. This is entry point for
|
||||||
|
// Downstream object.
|
||||||
void add_pending(std::unique_ptr<Downstream> downstream);
|
void add_pending(std::unique_ptr<Downstream> downstream);
|
||||||
void add_failure(std::unique_ptr<Downstream> downstream);
|
// Set |downstream| to failure state, which means that downstream
|
||||||
// Adds |downstream| to active_downstreams_, which means that
|
// failed to connect to backend.
|
||||||
// downstream connection has been started.
|
void mark_failure(Downstream *downstream);
|
||||||
void add_active(std::unique_ptr<Downstream> downstream);
|
// Set |downstream| to active state, which means that downstream
|
||||||
// Adds |downstream| to blocked_downstreams_, which means that
|
// connection has started.
|
||||||
// download connection was blocked because conn_max_per_host_ limit.
|
void mark_active(Downstream *downstream);
|
||||||
void add_blocked(std::unique_ptr<Downstream> downstream);
|
// Set |downstream| to blocked state, which means that download
|
||||||
|
// connection was blocked because conn_max_per_host_ limit.
|
||||||
|
void mark_blocked(Downstream *downstream);
|
||||||
// Returns true if we can make downstream connection to given
|
// Returns true if we can make downstream connection to given
|
||||||
// |host|.
|
// |host|.
|
||||||
bool can_activate(const std::string &host) const;
|
bool can_activate(const std::string &host) const;
|
||||||
// Removes pending Downstream object whose stream ID is |stream_id|
|
// Removes and frees |downstream| object. If |downstream| is in
|
||||||
// from pending_downstreams_ and returns it.
|
// Downstream::DISPATCH_ACTIVE, this function may return Downstream
|
||||||
std::unique_ptr<Downstream> pop_pending(int32_t stream_id);
|
// object with the same target host in Downstream::DISPATCH_BLOCKED
|
||||||
// Removes Downstream object whose stream ID is |stream_id| from
|
// if its connection is now not blocked by conn_max_per_host_ limit.
|
||||||
// either pending_downstreams_, active_downstreams_,
|
Downstream *remove_and_get_blocked(Downstream *downstream);
|
||||||
// blocked_downstreams_ or failure_downstreams_. If a Downstream
|
Downstream *get_downstreams() const;
|
||||||
// object is removed from active_downstreams_, this function may
|
|
||||||
// return Downstream object with the same target host in
|
|
||||||
// blocked_downstreams_ if its connection is now not blocked by
|
|
||||||
// conn_max_per_host_ limit.
|
|
||||||
std::unique_ptr<Downstream> remove_and_pop_blocked(int32_t stream_id);
|
|
||||||
// Finds Downstream object denoted by |stream_id| either in
|
|
||||||
// pending_downstreams_, active_downstreams_, blocked_downstreams_
|
|
||||||
// or failure_downstreams_.
|
|
||||||
Downstream *find(int32_t stream_id);
|
|
||||||
const DownstreamMap &get_active_downstreams() const;
|
|
||||||
HostEntry &find_host_entry(const std::string &host);
|
HostEntry &find_host_entry(const std::string &host);
|
||||||
const std::string &make_host_key(const std::string &host) const;
|
const std::string &make_host_key(const std::string &host) const;
|
||||||
const std::string &make_host_key(Downstream *downstream) const;
|
const std::string &make_host_key(Downstream *downstream) const;
|
||||||
|
|
||||||
// Maximum number of concurrent connections to the same host.
|
|
||||||
size_t conn_max_per_host_;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Per target host structure to keep track of the number of
|
// Per target host structure to keep track of the number of
|
||||||
// connections to the same host.
|
// connections to the same host.
|
||||||
std::map<std::string, HostEntry> host_entries_;
|
std::map<std::string, HostEntry> host_entries_;
|
||||||
// Downstream objects, not processed yet
|
DList<Downstream> downstreams_;
|
||||||
DownstreamMap pending_downstreams_;
|
// Maximum number of concurrent connections to the same host.
|
||||||
// Downstream objects, failed to connect to downstream server
|
size_t conn_max_per_host_;
|
||||||
DownstreamMap failure_downstreams_;
|
|
||||||
// Downstream objects, downstream connection started
|
|
||||||
DownstreamMap active_downstreams_;
|
|
||||||
// Downstream objects, blocked by conn_max_per_host_
|
|
||||||
DownstreamMap blocked_downstreams_;
|
|
||||||
// true if downstream host is treated as the same. Used for reverse
|
// true if downstream host is treated as the same. Used for reverse
|
||||||
// proxying.
|
// proxying.
|
||||||
bool unified_host_;
|
bool unified_host_;
|
||||||
|
|
|
@ -55,7 +55,8 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||||
<< " is being closed";
|
<< " is being closed";
|
||||||
}
|
}
|
||||||
|
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||||
|
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -161,7 +162,9 @@ int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
|
||||||
downstream->set_request_http2_authority(authority);
|
downstream->set_request_http2_authority(authority);
|
||||||
downstream->set_request_http2_scheme(scheme);
|
downstream->set_request_http2_scheme(scheme);
|
||||||
|
|
||||||
downstream_queue_.add_active(std::move(downstream));
|
auto ptr = downstream.get();
|
||||||
|
downstream_queue_.add_pending(std::move(downstream));
|
||||||
|
downstream_queue_.mark_active(ptr);
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
ULOG(INFO, this) << "Connection upgraded to HTTP/2";
|
ULOG(INFO, this) << "Connection upgraded to HTTP/2";
|
||||||
|
@ -191,7 +194,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto upstream = static_cast<Http2Upstream *>(user_data);
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -254,6 +258,8 @@ int on_begin_headers_callback(nghttp2_session *session,
|
||||||
|
|
||||||
// TODO Use priority 0 for now
|
// TODO Use priority 0 for now
|
||||||
auto downstream = make_unique<Downstream>(upstream, frame->hd.stream_id, 0);
|
auto downstream = make_unique<Downstream>(upstream, frame->hd.stream_id, 0);
|
||||||
|
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
|
||||||
|
downstream.get());
|
||||||
|
|
||||||
downstream->reset_upstream_rtimer();
|
downstream->reset_upstream_rtimer();
|
||||||
|
|
||||||
|
@ -268,9 +274,8 @@ int on_begin_headers_callback(nghttp2_session *session,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
int Http2Upstream::on_request_headers(Downstream *downstream,
|
||||||
int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
const nghttp2_frame *frame) {
|
||||||
nghttp2_session *session, const nghttp2_frame *frame) {
|
|
||||||
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -282,8 +287,8 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
||||||
for (auto &nv : nva) {
|
for (auto &nv : nva) {
|
||||||
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
ss << TTY_HTTP_HD << nv.name << TTY_RST << ": " << nv.value << "\n";
|
||||||
}
|
}
|
||||||
ULOG(INFO, upstream) << "HTTP request headers. stream_id="
|
ULOG(INFO, this) << "HTTP request headers. stream_id="
|
||||||
<< downstream->get_stream_id() << "\n" << ss.str();
|
<< downstream->get_stream_id() << "\n" << ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->http2_upstream_dump_request_header) {
|
if (get_config()->http2_upstream_dump_request_header) {
|
||||||
|
@ -299,7 +304,7 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
||||||
|
|
||||||
// For HTTP/2 proxy, we request :authority.
|
// For HTTP/2 proxy, we request :authority.
|
||||||
if (method->value != "CONNECT" && get_config()->http2_proxy && !authority) {
|
if (method->value != "CONNECT" && get_config()->http2_proxy && !authority) {
|
||||||
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,57 +326,51 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
|
||||||
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
downstream->set_request_state(Downstream::MSG_COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream->start_downstream(downstream);
|
start_downstream(downstream);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void Http2Upstream::start_downstream(Downstream *downstream) {
|
void Http2Upstream::start_downstream(Downstream *downstream) {
|
||||||
auto next_downstream =
|
|
||||||
downstream_queue_.pop_pending(downstream->get_stream_id());
|
|
||||||
assert(next_downstream);
|
|
||||||
|
|
||||||
if (downstream_queue_.can_activate(
|
if (downstream_queue_.can_activate(
|
||||||
downstream->get_request_http2_authority())) {
|
downstream->get_request_http2_authority())) {
|
||||||
initiate_downstream(std::move(next_downstream));
|
initiate_downstream(downstream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream_queue_.add_blocked(std::move(next_downstream));
|
downstream_queue_.mark_blocked(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
||||||
Http2Upstream::initiate_downstream(std::unique_ptr<Downstream> downstream) {
|
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
rv = downstream->attach_downstream_connection(
|
rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection());
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
// downstream connection fails, send error page
|
// downstream connection fails, send error page
|
||||||
if (error_reply(downstream.get(), 503) != 0) {
|
if (error_reply(downstream, 503) != 0) {
|
||||||
rst_stream(downstream.get(), NGHTTP2_INTERNAL_ERROR);
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
downstream_queue_.add_failure(std::move(downstream));
|
downstream_queue_.mark_failure(downstream);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rv = downstream->push_request_headers();
|
rv = downstream->push_request_headers();
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
|
||||||
if (error_reply(downstream.get(), 503) != 0) {
|
if (error_reply(downstream, 503) != 0) {
|
||||||
rst_stream(downstream.get(), NGHTTP2_INTERNAL_ERROR);
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream_queue_.add_failure(std::move(downstream));
|
downstream_queue_.mark_failure(downstream);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream_queue_.add_active(std::move(downstream));
|
downstream_queue_.mark_active(downstream);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -386,7 +385,8 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
|
|
||||||
switch (frame->hd.type) {
|
switch (frame->hd.type) {
|
||||||
case NGHTTP2_DATA: {
|
case NGHTTP2_DATA: {
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -401,7 +401,8 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case NGHTTP2_HEADERS: {
|
case NGHTTP2_HEADERS: {
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -409,7 +410,7 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
|
||||||
downstream->reset_upstream_rtimer();
|
downstream->reset_upstream_rtimer();
|
||||||
|
|
||||||
return on_request_headers(upstream, downstream, session, frame);
|
return upstream->on_request_headers(downstream, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
|
@ -449,7 +450,8 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||||
int32_t stream_id, const uint8_t *data,
|
int32_t stream_id, const uint8_t *data,
|
||||||
size_t len, void *user_data) {
|
size_t len, void *user_data) {
|
||||||
auto upstream = static_cast<Http2Upstream *>(user_data);
|
auto upstream = static_cast<Http2Upstream *>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, stream_id));
|
||||||
|
|
||||||
if (!downstream || !downstream->get_downstream_connection()) {
|
if (!downstream || !downstream->get_downstream_connection()) {
|
||||||
if (upstream->consume(stream_id, len) != 0) {
|
if (upstream->consume(stream_id, len) != 0) {
|
||||||
|
@ -566,7 +568,8 @@ int on_frame_not_send_callback(nghttp2_session *session,
|
||||||
lib_error_code != NGHTTP2_ERR_STREAM_CLOSED &&
|
lib_error_code != NGHTTP2_ERR_STREAM_CLOSED &&
|
||||||
lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) {
|
lib_error_code != NGHTTP2_ERR_STREAM_CLOSING) {
|
||||||
// To avoid stream hanging around, issue RST_STREAM.
|
// To avoid stream hanging around, issue RST_STREAM.
|
||||||
auto downstream = upstream->find_downstream(frame->hd.stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
|
||||||
if (downstream) {
|
if (downstream) {
|
||||||
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
upstream->rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -1188,18 +1191,16 @@ void Http2Upstream::remove_downstream(Downstream *downstream) {
|
||||||
handler_->write_accesslog(downstream);
|
handler_->write_accesslog(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto next_downstream =
|
nghttp2_session_set_stream_user_data(session_, downstream->get_stream_id(),
|
||||||
downstream_queue_.remove_and_pop_blocked(downstream->get_stream_id());
|
nullptr);
|
||||||
|
|
||||||
|
auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream);
|
||||||
|
|
||||||
if (next_downstream) {
|
if (next_downstream) {
|
||||||
initiate_downstream(std::move(next_downstream));
|
initiate_downstream(next_downstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream *Http2Upstream::find_downstream(int32_t stream_id) {
|
|
||||||
return downstream_queue_.find(stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING: Never call directly or indirectly nghttp2_session_send or
|
// WARNING: Never call directly or indirectly nghttp2_session_send or
|
||||||
// nghttp2_session_recv. These calls may delete downstream.
|
// nghttp2_session_recv. These calls may delete downstream.
|
||||||
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
|
@ -1447,9 +1448,10 @@ int Http2Upstream::on_timeout(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http2Upstream::on_handler_delete() {
|
void Http2Upstream::on_handler_delete() {
|
||||||
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
|
||||||
if (ent.second->accesslog_ready()) {
|
if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
|
||||||
handler_->write_accesslog(ent.second.get());
|
d->accesslog_ready()) {
|
||||||
|
handler_->write_accesslog(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1457,8 +1459,12 @@ void Http2Upstream::on_handler_delete() {
|
||||||
int Http2Upstream::on_downstream_reset(bool no_retry) {
|
int Http2Upstream::on_downstream_reset(bool no_retry) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
for (auto downstream = downstream_queue_.get_downstreams(); downstream;
|
||||||
auto downstream = ent.second.get();
|
downstream = downstream->dlnext) {
|
||||||
|
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!downstream->request_submission_ready()) {
|
if (!downstream->request_submission_ready()) {
|
||||||
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
|
|
@ -62,7 +62,6 @@ public:
|
||||||
|
|
||||||
void add_pending_downstream(std::unique_ptr<Downstream> downstream);
|
void add_pending_downstream(std::unique_ptr<Downstream> downstream);
|
||||||
void remove_downstream(Downstream *downstream);
|
void remove_downstream(Downstream *downstream);
|
||||||
Downstream *find_downstream(int32_t stream_id);
|
|
||||||
|
|
||||||
int rst_stream(Downstream *downstream, uint32_t error_code);
|
int rst_stream(Downstream *downstream, uint32_t error_code);
|
||||||
int terminate_session(uint32_t error_code);
|
int terminate_session(uint32_t error_code);
|
||||||
|
@ -93,7 +92,7 @@ public:
|
||||||
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 start_downstream(Downstream *downstream);
|
void start_downstream(Downstream *downstream);
|
||||||
void initiate_downstream(std::unique_ptr<Downstream> downstream);
|
void initiate_downstream(Downstream *downstream);
|
||||||
|
|
||||||
void submit_goaway();
|
void submit_goaway();
|
||||||
void check_shutdown();
|
void check_shutdown();
|
||||||
|
@ -101,6 +100,8 @@ public:
|
||||||
int prepare_push_promise(Downstream *downstream);
|
int prepare_push_promise(Downstream *downstream);
|
||||||
int submit_push_promise(const std::string &path, Downstream *downstream);
|
int submit_push_promise(const std::string &path, Downstream *downstream);
|
||||||
|
|
||||||
|
int on_request_headers(Downstream *downstream, const nghttp2_frame *frame);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// must be put before downstream_queue_
|
// must be put before downstream_queue_
|
||||||
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
||||||
|
|
|
@ -92,7 +92,8 @@ void on_stream_close_callback(spdylay_session *session, int32_t stream_id,
|
||||||
ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
|
ULOG(INFO, upstream) << "Stream stream_id=" << stream_id
|
||||||
<< " is being closed";
|
<< " is being closed";
|
||||||
}
|
}
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
spdylay_session_get_stream_user_data(session, stream_id));
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -223,41 +224,37 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void SpdyUpstream::start_downstream(Downstream *downstream) {
|
void SpdyUpstream::start_downstream(Downstream *downstream) {
|
||||||
auto next_downstream =
|
|
||||||
downstream_queue_.pop_pending(downstream->get_stream_id());
|
|
||||||
assert(next_downstream);
|
|
||||||
|
|
||||||
if (downstream_queue_.can_activate(
|
if (downstream_queue_.can_activate(
|
||||||
downstream->get_request_http2_authority())) {
|
downstream->get_request_http2_authority())) {
|
||||||
initiate_downstream(std::move(next_downstream));
|
initiate_downstream(downstream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream_queue_.add_blocked(std::move(next_downstream));
|
downstream_queue_.mark_blocked(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpdyUpstream::initiate_downstream(std::unique_ptr<Downstream> downstream) {
|
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
||||||
int rv = downstream->attach_downstream_connection(
|
int rv = downstream->attach_downstream_connection(
|
||||||
handler_->get_downstream_connection());
|
handler_->get_downstream_connection());
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
// If downstream connection fails, issue RST_STREAM.
|
// If downstream connection fails, issue RST_STREAM.
|
||||||
rst_stream(downstream.get(), SPDYLAY_INTERNAL_ERROR);
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
downstream_queue_.add_failure(std::move(downstream));
|
downstream_queue_.mark_failure(downstream);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rv = downstream->push_request_headers();
|
rv = downstream->push_request_headers();
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
rst_stream(downstream.get(), SPDYLAY_INTERNAL_ERROR);
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
|
|
||||||
downstream_queue_.add_failure(std::move(downstream));
|
downstream_queue_.mark_failure(downstream);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
downstream_queue_.add_active(std::move(downstream));
|
downstream_queue_.mark_active(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -265,7 +262,8 @@ void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
|
||||||
int32_t stream_id, const uint8_t *data,
|
int32_t stream_id, const uint8_t *data,
|
||||||
size_t len, void *user_data) {
|
size_t len, void *user_data) {
|
||||||
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
spdylay_session_get_stream_user_data(session, stream_id));
|
||||||
|
|
||||||
if (!downstream) {
|
if (!downstream) {
|
||||||
upstream->consume(stream_id, len);
|
upstream->consume(stream_id, len);
|
||||||
|
@ -323,7 +321,8 @@ namespace {
|
||||||
void on_data_recv_callback(spdylay_session *session, uint8_t flags,
|
void on_data_recv_callback(spdylay_session *session, uint8_t flags,
|
||||||
int32_t stream_id, int32_t length, void *user_data) {
|
int32_t stream_id, int32_t length, void *user_data) {
|
||||||
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
auto upstream = static_cast<SpdyUpstream *>(user_data);
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
spdylay_session_get_stream_user_data(session, stream_id));
|
||||||
if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
|
if (downstream && (flags & SPDYLAY_DATA_FLAG_FIN)) {
|
||||||
if (!downstream->validate_request_bodylen()) {
|
if (!downstream->validate_request_bodylen()) {
|
||||||
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
upstream->rst_stream(downstream, SPDYLAY_PROTOCOL_ERROR);
|
||||||
|
@ -351,7 +350,9 @@ void on_ctrl_not_send_callback(spdylay_session *session,
|
||||||
error_code != SPDYLAY_ERR_STREAM_CLOSING) {
|
error_code != SPDYLAY_ERR_STREAM_CLOSING) {
|
||||||
// To avoid stream hanging around, issue RST_STREAM.
|
// To avoid stream hanging around, issue RST_STREAM.
|
||||||
auto stream_id = frame->syn_reply.stream_id;
|
auto stream_id = frame->syn_reply.stream_id;
|
||||||
auto downstream = upstream->find_downstream(stream_id);
|
// TODO Could be always nullptr
|
||||||
|
auto downstream = static_cast<Downstream *>(
|
||||||
|
spdylay_session_get_stream_user_data(session, stream_id));
|
||||||
if (downstream) {
|
if (downstream) {
|
||||||
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -797,6 +798,7 @@ int SpdyUpstream::error_reply(Downstream *downstream,
|
||||||
Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id,
|
Downstream *SpdyUpstream::add_pending_downstream(int32_t stream_id,
|
||||||
int32_t priority) {
|
int32_t priority) {
|
||||||
auto downstream = make_unique<Downstream>(this, stream_id, priority);
|
auto downstream = make_unique<Downstream>(this, stream_id, priority);
|
||||||
|
spdylay_session_set_stream_user_data(session_, stream_id, downstream.get());
|
||||||
auto res = downstream.get();
|
auto res = downstream.get();
|
||||||
|
|
||||||
downstream_queue_.add_pending(std::move(downstream));
|
downstream_queue_.add_pending(std::move(downstream));
|
||||||
|
@ -809,18 +811,16 @@ void SpdyUpstream::remove_downstream(Downstream *downstream) {
|
||||||
handler_->write_accesslog(downstream);
|
handler_->write_accesslog(downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto next_downstream =
|
spdylay_session_set_stream_user_data(session_, downstream->get_stream_id(),
|
||||||
downstream_queue_.remove_and_pop_blocked(downstream->get_stream_id());
|
nullptr);
|
||||||
|
|
||||||
|
auto next_downstream = downstream_queue_.remove_and_get_blocked(downstream);
|
||||||
|
|
||||||
if (next_downstream) {
|
if (next_downstream) {
|
||||||
initiate_downstream(std::move(next_downstream));
|
initiate_downstream(next_downstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Downstream *SpdyUpstream::find_downstream(int32_t stream_id) {
|
|
||||||
return downstream_queue_.find(stream_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING: Never call directly or indirectly spdylay_session_send or
|
// WARNING: Never call directly or indirectly spdylay_session_send or
|
||||||
// spdylay_session_recv. These calls may delete downstream.
|
// spdylay_session_recv. These calls may delete downstream.
|
||||||
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
|
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
|
||||||
|
@ -1026,9 +1026,10 @@ int SpdyUpstream::on_timeout(Downstream *downstream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpdyUpstream::on_handler_delete() {
|
void SpdyUpstream::on_handler_delete() {
|
||||||
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
for (auto d = downstream_queue_.get_downstreams(); d; d = d->dlnext) {
|
||||||
if (ent.second->accesslog_ready()) {
|
if (d->get_dispatch_state() == Downstream::DISPATCH_ACTIVE &&
|
||||||
handler_->write_accesslog(ent.second.get());
|
d->accesslog_ready()) {
|
||||||
|
handler_->write_accesslog(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1036,8 +1037,12 @@ void SpdyUpstream::on_handler_delete() {
|
||||||
int SpdyUpstream::on_downstream_reset(bool no_retry) {
|
int SpdyUpstream::on_downstream_reset(bool no_retry) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
for (auto &ent : downstream_queue_.get_active_downstreams()) {
|
for (auto downstream = downstream_queue_.get_downstreams(); downstream;
|
||||||
auto downstream = ent.second.get();
|
downstream = downstream->dlnext) {
|
||||||
|
if (downstream->get_dispatch_state() != Downstream::DISPATCH_ACTIVE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!downstream->request_submission_ready()) {
|
if (!downstream->request_submission_ready()) {
|
||||||
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
rst_stream(downstream, SPDYLAY_INTERNAL_ERROR);
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
|
|
@ -58,7 +58,6 @@ public:
|
||||||
virtual int downstream_error(DownstreamConnection *dconn, int events);
|
virtual int downstream_error(DownstreamConnection *dconn, int events);
|
||||||
Downstream *add_pending_downstream(int32_t stream_id, int32_t priority);
|
Downstream *add_pending_downstream(int32_t stream_id, int32_t priority);
|
||||||
void remove_downstream(Downstream *downstream);
|
void remove_downstream(Downstream *downstream);
|
||||||
Downstream *find_downstream(int32_t stream_id);
|
|
||||||
|
|
||||||
int rst_stream(Downstream *downstream, int status_code);
|
int rst_stream(Downstream *downstream, int status_code);
|
||||||
int error_reply(Downstream *downstream, unsigned int status_code);
|
int error_reply(Downstream *downstream, unsigned int status_code);
|
||||||
|
@ -82,7 +81,7 @@ public:
|
||||||
int consume(int32_t stream_id, size_t len);
|
int consume(int32_t stream_id, size_t len);
|
||||||
|
|
||||||
void start_downstream(Downstream *downstream);
|
void start_downstream(Downstream *downstream);
|
||||||
void initiate_downstream(std::unique_ptr<Downstream> downstream);
|
void initiate_downstream(Downstream *downstream);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// must be put before downstream_queue_
|
// must be put before downstream_queue_
|
||||||
|
|
|
@ -132,9 +132,19 @@ template <typename T> struct DList {
|
||||||
t->dlprev = t->dlnext = nullptr;
|
t->dlprev = t->dlnext = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool empty() const { return head == nullptr; }
|
||||||
|
|
||||||
T *head, *tail;
|
T *head, *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T> void dlist_delete_all(DList<T> &dl) {
|
||||||
|
for (auto e = dl.head; e;) {
|
||||||
|
auto next = e->dlnext;
|
||||||
|
delete e;
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
||||||
#endif // TEMPLATE_H
|
#endif // TEMPLATE_H
|
||||||
|
|
Loading…
Reference in New Issue