nghttp, nghttpd: Add SETTINGS timeout handling

SETTINGS timeout is 10 seconds for now.
This commit is contained in:
Tatsuhiro Tsujikawa 2013-10-27 23:02:39 +09:00
parent 34b3833c71
commit dc61f705df
3 changed files with 80 additions and 1 deletions

View File

@ -177,12 +177,16 @@ Http2Handler::Http2Handler(Sessions *sessions,
int fd, SSL *ssl, int64_t session_id) int fd, SSL *ssl, int64_t session_id)
: session_(nullptr), sessions_(sessions), bev_(nullptr), fd_(fd), ssl_(ssl), : session_(nullptr), sessions_(sessions), bev_(nullptr), fd_(fd), ssl_(ssl),
session_id_(session_id), session_id_(session_id),
left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_HEADER_LEN) left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_HEADER_LEN),
settings_timerev_(nullptr)
{} {}
Http2Handler::~Http2Handler() Http2Handler::~Http2Handler()
{ {
on_session_closed(this, session_id_); on_session_closed(this, session_id_);
if(settings_timerev_) {
event_free(settings_timerev_);
}
nghttp2_session_del(session_); nghttp2_session_del(session_);
if(ssl_) { if(ssl_) {
SSL_shutdown(ssl_); SSL_shutdown(ssl_);
@ -340,6 +344,15 @@ int Http2Handler::on_write()
return rv; return rv;
} }
namespace {
void settings_timeout_cb(evutil_socket_t fd, short what, void *arg)
{
auto hd = reinterpret_cast<Http2Handler*>(arg);
hd->submit_goaway(NGHTTP2_SETTINGS_TIMEOUT);
hd->on_write();
}
} // namespace
int Http2Handler::on_connect() int Http2Handler::on_connect()
{ {
int r; int r;
@ -362,6 +375,13 @@ int Http2Handler::on_connect()
if(r != 0) { if(r != 0) {
return r; return r;
} }
assert(settings_timerev_ == nullptr);
settings_timerev_ = evtimer_new(sessions_->get_evbase(), settings_timeout_cb,
this);
// SETTINGS ACK timeout is 10 seconds for now
timeval settings_timeout = { 10, 0 };
evtimer_add(settings_timerev_, &settings_timeout);
return on_write(); return on_write();
} }
@ -524,6 +544,20 @@ void Http2Handler::set_left_connhd_len(size_t left)
left_connhd_len_ = left; left_connhd_len_ = left;
} }
void Http2Handler::remove_settings_timer()
{
if(settings_timerev_) {
evtimer_del(settings_timerev_);
event_free(settings_timerev_);
settings_timerev_ = nullptr;
}
}
void Http2Handler::submit_goaway(nghttp2_error_code error_code)
{
nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, error_code, NULL, 0);
}
namespace { namespace {
ssize_t hd_send_callback(nghttp2_session *session, ssize_t hd_send_callback(nghttp2_session *session,
const uint8_t *data, size_t len, int flags, const uint8_t *data, size_t len, int flags,
@ -736,6 +770,11 @@ int hd_on_frame_recv_callback
break; break;
} }
break; break;
case NGHTTP2_SETTINGS:
if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
hd->remove_settings_timer();
}
break;
default: default:
break; break;
} }

View File

@ -39,6 +39,7 @@
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <event2/event.h>
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
@ -113,6 +114,8 @@ public:
const Config* get_config() const; const Config* get_config() const;
size_t get_left_connhd_len() const; size_t get_left_connhd_len() const;
void set_left_connhd_len(size_t left); void set_left_connhd_len(size_t left);
void remove_settings_timer();
void submit_goaway(nghttp2_error_code error_code);
private: private:
nghttp2_session *session_; nghttp2_session *session_;
Sessions *sessions_; Sessions *sessions_;
@ -122,6 +125,7 @@ private:
int64_t session_id_; int64_t session_id_;
std::map<int32_t, std::unique_ptr<Request>> id2req_; std::map<int32_t, std::unique_ptr<Request>> id2req_;
size_t left_connhd_len_; size_t left_connhd_len_;
event *settings_timerev_;
}; };
class HttpServer { class HttpServer {

View File

@ -56,6 +56,7 @@
#include <openssl/err.h> #include <openssl/err.h>
#include <event.h> #include <event.h>
#include <event2/event.h>
#include <event2/bufferevent_ssl.h> #include <event2/bufferevent_ssl.h>
#include <event2/dns.h> #include <event2/dns.h>
@ -401,6 +402,10 @@ void check_stream_id(nghttp2_session *session, int32_t stream_id,
void *user_data); void *user_data);
} // namespace } // namespace
namespace {
void settings_timeout_cb(evutil_socket_t fd, short what, void *arg);
} // namespace
enum client_state { enum client_state {
STATE_IDLE, STATE_IDLE,
STATE_CONNECTED STATE_CONNECTED
@ -415,6 +420,7 @@ struct HttpClient {
SSL_CTX *ssl_ctx; SSL_CTX *ssl_ctx;
SSL *ssl; SSL *ssl;
bufferevent *bev; bufferevent *bev;
event *settings_timerev;
client_state state; client_state state;
std::vector<std::unique_ptr<Request>> reqvec; std::vector<std::unique_ptr<Request>> reqvec;
// Map from stream ID to Request object. // Map from stream ID to Request object.
@ -448,6 +454,7 @@ struct HttpClient {
ssl_ctx(ssl_ctx), ssl_ctx(ssl_ctx),
ssl(nullptr), ssl(nullptr),
bev(nullptr), bev(nullptr),
settings_timerev(nullptr),
state(STATE_IDLE), state(STATE_IDLE),
complete(0), complete(0),
upgrade_response_complete(false), upgrade_response_complete(false),
@ -536,6 +543,10 @@ struct HttpClient {
evdns_base_free(dnsbase, 1); evdns_base_free(dnsbase, 1);
dnsbase = nullptr; dnsbase = nullptr;
} }
if(settings_timerev) {
event_free(settings_timerev);
settings_timerev = nullptr;
}
if(ssl) { if(ssl) {
SSL_free(ssl); SSL_free(ssl);
ssl = nullptr; ssl = nullptr;
@ -690,6 +701,12 @@ struct HttpClient {
return -1; return -1;
} }
} }
assert(settings_timerev == nullptr);
settings_timerev = evtimer_new(evbase, settings_timeout_cb, this);
// SETTINGS ACK timeout is 10 seconds for now
timeval settings_timeout = { 10, 0 };
evtimer_add(settings_timerev, &settings_timeout);
if(config.connection_window_bits != -1) { if(config.connection_window_bits != -1) {
int32_t wininc = (1 << config.connection_window_bits) - 1 int32_t wininc = (1 << config.connection_window_bits) - 1
- NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
@ -1054,6 +1071,16 @@ void check_stream_id(nghttp2_session *session, int32_t stream_id,
} }
} // namespace } // namespace
namespace {
void settings_timeout_cb(evutil_socket_t fd, short what, void *arg)
{
auto client = get_session(arg);
nghttp2_submit_goaway(client->session, NGHTTP2_FLAG_NONE,
NGHTTP2_SETTINGS_TIMEOUT, nullptr, 0);
client->on_write();
}
} // namespace
namespace { namespace {
int before_frame_send_callback int before_frame_send_callback
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
@ -1129,6 +1156,15 @@ int on_frame_recv_callback2
} }
} }
check_response_header(session, frame, user_data); check_response_header(session, frame, user_data);
if(frame->hd.type == NGHTTP2_SETTINGS &&
(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
auto client = get_session(user_data);
if(client->settings_timerev) {
evtimer_del(client->settings_timerev);
event_free(client->settings_timerev);
client->settings_timerev = nullptr;
}
}
if(config.verbose) { if(config.verbose) {
on_frame_recv_callback(session, frame, user_data); on_frame_recv_callback(session, frame, user_data);
} }