diff options
-rw-r--r-- | lib/http.h | 1 | ||||
-rw-r--r-- | lib/http2.c | 57 |
2 files changed, 39 insertions, 19 deletions
diff --git a/lib/http.h b/lib/http.h index 5000df448..8aae35206 100644 --- a/lib/http.h +++ b/lib/http.h @@ -214,6 +214,7 @@ struct http_conn { them for both cases. */ int32_t pause_stream_id; /* stream ID which paused nghttp2_session_mem_recv */ + int drain_total; /* sum of all stream's UrlState.drain */ /* this is a hash of all individual streams (SessionHandle structs) */ struct h2settings settings; diff --git a/lib/http2.c b/lib/http2.c index 206d9592d..5e9adf240 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -507,6 +507,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, stream->memlen += ncopy; data_s->state.drain++; + httpc->drain_total++; { /* get the pointer from userp again since it was re-assigned above */ struct connectdata *conn_s = (struct connectdata *)userp; @@ -583,6 +584,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, stream->memlen += nread; data_s->state.drain++; + conn->proto.httpc.drain_total++; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) @@ -665,9 +667,9 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, { struct SessionHandle *data_s; struct HTTP *stream; + struct connectdata *conn = (struct connectdata *)userp; (void)session; (void)stream_id; - (void)userp; if(stream_id) { /* get the stream from the hash based on Stream ID, stream ID zero is for @@ -686,6 +688,8 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream->error_code = error_code; stream->closed = TRUE; + data_s->state.drain++; + conn->proto.httpc.drain_total++; /* remove the entry from the hash as the stream is now gone */ nghttp2_session_set_stream_user_data(session, stream_id, 0); @@ -845,6 +849,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, /* the space character after the status code is mandatory */ Curl_add_buffer(stream->header_recvbuf, " \r\n", 3); data_s->state.drain++; + conn->proto.httpc.drain_total++; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 1); @@ -862,6 +867,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, Curl_add_buffer(stream->header_recvbuf, value, valuelen); Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); data_s->state.drain++; + conn->proto.httpc.drain_total++; /* if we receive data for another handle, wake that up */ if(conn->data != data_s) Curl_expire(data_s, 1); @@ -1053,6 +1059,14 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, return result; } +/* + * Returns nonzero if current HTTP/2 session should be closed. + */ +static int should_close_session(struct http_conn *httpc) { + return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) && + !nghttp2_session_want_write(httpc->h2); +} + static int h2_session_send(struct SessionHandle *data, nghttp2_session *h2); @@ -1102,9 +1116,7 @@ static int h2_process_pending_input(struct SessionHandle *data, return -1; } - if(httpc->pause_stream_id == 0 && - !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(data, "h2_process_pending_input: nothing to do in this session\n")); *err = CURLE_HTTP2; @@ -1125,12 +1137,17 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, httpc->pause_stream_id = 0; } + httpc->drain_total -= data->state.drain; + data->state.drain = 0; + if(httpc->pause_stream_id == 0) { if(h2_process_pending_input(data, httpc, err) != 0) { return -1; } } + DEBUGASSERT(data->state.drain == 0); + /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ stream->closed = FALSE; @@ -1141,6 +1158,14 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, return -1; } + if(!stream->bodystarted) { + failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " + " all response header fields, teated as error", + stream->stream_id); + *err = CURLE_HTTP2_STREAM; + return -1; + } + if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) { trailer_pos = stream->trailer_recvbuf->buffer; trailer_end = trailer_pos + stream->trailer_recvbuf->size_used; @@ -1228,9 +1253,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, (void)sockindex; /* we always do HTTP2 on sockindex 0 */ - if(httpc->pause_stream_id == 0 && - !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(data, "http2_recv: nothing to do in this session\n")); *err = CURLE_HTTP2; @@ -1399,9 +1422,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, return 0; } - if(httpc->pause_stream_id == 0 && - !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(data, "http2_recv: nothing to do in this session\n")); *err = CURLE_HTTP2; return -1; @@ -1419,8 +1440,10 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, DEBUGF(infof(data, "Data returned for PAUSED stream %u\n", stream->stream_id)); } - else + else if(!stream->closed) { + httpc->drain_total -= data->state.drain; data->state.drain = 0; /* this stream is hereby drained */ + } return retlen; } @@ -1484,9 +1507,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, stream->upload_mem = NULL; stream->upload_len = 0; - if(httpc->pause_stream_id == 0 && - !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n")); *err = CURLE_HTTP2; return -1; @@ -1658,9 +1679,7 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, return -1; } - if(httpc->pause_stream_id == 0 && - !nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n")); *err = CURLE_HTTP2; return -1; @@ -1719,6 +1738,7 @@ CURLcode Curl_http2_setup(struct connectdata *conn) httpc->nread_inbuf = 0; httpc->pause_stream_id = 0; + httpc->drain_total = 0; conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->httpversion = 20; @@ -1826,8 +1846,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn, return CURLE_HTTP2; } - if(!nghttp2_session_want_read(httpc->h2) && - !nghttp2_session_want_write(httpc->h2)) { + if(should_close_session(httpc)) { DEBUGF(infof(data, "nghttp2_session_send(): nothing to do in this session\n")); return CURLE_HTTP2; |