diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 3015c9d0..1b82d8e5 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -177,12 +177,16 @@ Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl, int64_t session_id) : session_(nullptr), sessions_(sessions), bev_(nullptr), fd_(fd), ssl_(ssl), 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() { on_session_closed(this, session_id_); + if(settings_timerev_) { + event_free(settings_timerev_); + } nghttp2_session_del(session_); if(ssl_) { SSL_shutdown(ssl_); @@ -340,6 +344,15 @@ int Http2Handler::on_write() return rv; } +namespace { +void settings_timeout_cb(evutil_socket_t fd, short what, void *arg) +{ + auto hd = reinterpret_cast(arg); + hd->submit_goaway(NGHTTP2_SETTINGS_TIMEOUT); + hd->on_write(); +} +} // namespace + int Http2Handler::on_connect() { int r; @@ -362,6 +375,13 @@ int Http2Handler::on_connect() if(r != 0) { 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(); } @@ -524,6 +544,20 @@ void Http2Handler::set_left_connhd_len(size_t 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 { ssize_t hd_send_callback(nghttp2_session *session, const uint8_t *data, size_t len, int flags, @@ -736,6 +770,11 @@ int hd_on_frame_recv_callback break; } break; + case NGHTTP2_SETTINGS: + if(frame->hd.flags & NGHTTP2_FLAG_ACK) { + hd->remove_settings_timer(); + } + break; default: break; } diff --git a/src/HttpServer.h b/src/HttpServer.h index e00a446c..bd4645db 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -39,6 +39,7 @@ #include +#include #include #include @@ -113,6 +114,8 @@ public: const Config* get_config() const; size_t get_left_connhd_len() const; void set_left_connhd_len(size_t left); + void remove_settings_timer(); + void submit_goaway(nghttp2_error_code error_code); private: nghttp2_session *session_; Sessions *sessions_; @@ -122,6 +125,7 @@ private: int64_t session_id_; std::map> id2req_; size_t left_connhd_len_; + event *settings_timerev_; }; class HttpServer { diff --git a/src/nghttp.cc b/src/nghttp.cc index fed0db8d..65c1143b 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -56,6 +56,7 @@ #include #include +#include #include #include @@ -401,6 +402,10 @@ void check_stream_id(nghttp2_session *session, int32_t stream_id, void *user_data); } // namespace +namespace { +void settings_timeout_cb(evutil_socket_t fd, short what, void *arg); +} // namespace + enum client_state { STATE_IDLE, STATE_CONNECTED @@ -415,6 +420,7 @@ struct HttpClient { SSL_CTX *ssl_ctx; SSL *ssl; bufferevent *bev; + event *settings_timerev; client_state state; std::vector> reqvec; // Map from stream ID to Request object. @@ -448,6 +454,7 @@ struct HttpClient { ssl_ctx(ssl_ctx), ssl(nullptr), bev(nullptr), + settings_timerev(nullptr), state(STATE_IDLE), complete(0), upgrade_response_complete(false), @@ -536,6 +543,10 @@ struct HttpClient { evdns_base_free(dnsbase, 1); dnsbase = nullptr; } + if(settings_timerev) { + event_free(settings_timerev); + settings_timerev = nullptr; + } if(ssl) { SSL_free(ssl); ssl = nullptr; @@ -690,6 +701,12 @@ struct HttpClient { 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) { int32_t wininc = (1 << config.connection_window_bits) - 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; @@ -1054,6 +1071,16 @@ void check_stream_id(nghttp2_session *session, int32_t stream_id, } } // 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 { int before_frame_send_callback (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); + 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) { on_frame_recv_callback(session, frame, user_data); }