diff --git a/src/shrpx_spdy_session.cc b/src/shrpx_spdy_session.cc index a623e405..d61dbf8e 100644 --- a/src/shrpx_spdy_session.cc +++ b/src/shrpx_spdy_session.cc @@ -59,7 +59,8 @@ SpdySession::SpdySession(event_base *evbase, SSL_CTX *ssl_ctx) notified_(false), wrbev_(nullptr), rdbev_(nullptr), - flow_control_(false) + flow_control_(false), + settings_timerev_(nullptr) {} SpdySession::~SpdySession() @@ -75,6 +76,11 @@ int SpdySession::disconnect() nghttp2_session_del(session_); session_ = nullptr; + if(settings_timerev_) { + event_free(settings_timerev_); + settings_timerev_ = nullptr; + } + if(ssl_) { SSL_shutdown(ssl_); } @@ -746,6 +752,50 @@ int on_stream_close_callback } } // namespace +namespace { +void settings_timeout_cb(evutil_socket_t fd, short what, void *arg) +{ + auto spdy = reinterpret_cast(arg); + SSLOG(INFO, spdy) << "SETTINGS timeout"; + if(spdy->fail_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) { + spdy->disconnect(); + return; + } + if(spdy->send() != 0) { + spdy->disconnect(); + } +} +} // namespace + +int SpdySession::start_settings_timer() +{ + int rv; + // We submit SETTINGS only once + if(settings_timerev_) { + return 0; + } + settings_timerev_ = evtimer_new(evbase_, settings_timeout_cb, this); + if(settings_timerev_ == nullptr) { + return -1; + } + // SETTINGS ACK timeout is 10 seconds for now + timeval settings_timeout = { 10, 0 }; + rv = evtimer_add(settings_timerev_, &settings_timeout); + if(rv == -1) { + return -1; + } + return 0; +} + +void SpdySession::stop_settings_timer() +{ + if(settings_timerev_ == nullptr) { + return; + } + event_free(settings_timerev_); + settings_timerev_ = nullptr; +} + namespace { int on_frame_recv_callback (nghttp2_session *session, const nghttp2_frame *frame, void *user_data) @@ -898,6 +948,12 @@ int on_frame_recv_callback } break; } + case NGHTTP2_SETTINGS: + if((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + break; + } + spdy->stop_settings_timer(); + break; case NGHTTP2_PUSH_PROMISE: if(LOG_ENABLED(INFO)) { SSLOG(INFO, spdy) << "Received downstream PUSH_PROMISE stream_id=" @@ -969,6 +1025,21 @@ int before_frame_send_callback(nghttp2_session *session, } } // namespace +namespace { +int on_frame_send_callback(nghttp2_session* session, + const nghttp2_frame *frame, void *user_data) +{ + auto spdy = reinterpret_cast(user_data); + if(frame->hd.type == NGHTTP2_SETTINGS && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) { + if(spdy->start_settings_timer() != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} +} // namespace + namespace { int on_frame_not_send_callback(nghttp2_session *session, const nghttp2_frame *frame, @@ -1059,6 +1130,7 @@ int SpdySession::on_connect() callbacks.on_frame_recv_callback = on_frame_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.before_frame_send_callback = before_frame_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; callbacks.on_frame_not_send_callback = on_frame_not_send_callback; callbacks.on_frame_recv_parse_error_callback = on_frame_recv_parse_error_callback; @@ -1190,4 +1262,14 @@ void SpdySession::set_state(int state) state_ = state; } +int SpdySession::fail_session(nghttp2_error_code error_code) +{ + int rv; + rv = nghttp2_session_fail_session(session_, error_code); + if(rv != 0) { + return -1; + } + return 0; +} + } // namespace shrpx diff --git a/src/shrpx_spdy_session.h b/src/shrpx_spdy_session.h index cba9c560..b69b8833 100644 --- a/src/shrpx_spdy_session.h +++ b/src/shrpx_spdy_session.h @@ -74,6 +74,8 @@ public: // |dconn|. int submit_window_update(SpdyDownstreamConnection *dconn, int32_t amount); + int fail_session(nghttp2_error_code error_code); + int32_t get_initial_window_size() const; nghttp2_session* get_session() const; @@ -99,6 +101,9 @@ public: int get_state() const; void set_state(int state); + int start_settings_timer(); + void stop_settings_timer(); + enum { // Disconnected DISCONNECTED, @@ -134,6 +139,7 @@ private: bool flow_control_; // Used to parse the response from HTTP proxy std::unique_ptr proxy_htp_; + event *settings_timerev_; }; } // namespace shrpx