diff --git a/lib/http.c b/lib/http.c index aa1dcbf0e..4b67db4b9 100644 --- a/lib/http.c +++ b/lib/http.c @@ -168,6 +168,8 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) http->status_code = -1; http->data = NULL; http->datalen = 0; + http->error_code = NGHTTP2_NO_ERROR; + http->closed = FALSE; return CURLE_OK; } diff --git a/lib/http.h b/lib/http.h index a64d83f18..48f5c7350 100644 --- a/lib/http.h +++ b/lib/http.h @@ -164,6 +164,8 @@ struct HTTP { int status_code; /* HTTP status code */ const uint8_t *data; /* pointer to data chunk, received in on_data_chunk */ size_t datalen; /* the number of bytes left in data */ + bool closed; /* TRUE on HTTP2 stream close */ + uint32_t error_code; /* HTTP/2 error code */ }; typedef int (*sending)(void); /* Curl_send */ @@ -179,8 +181,6 @@ struct http_conn { size_t len; /* size of the buffer 'mem' points to */ sending send_underlying; /* underlying send Curl_send callback */ recving recv_underlying; /* underlying recv Curl_recv callback */ - bool closed; /* TRUE on HTTP2 stream close */ - uint32_t error_code; /* HTTP/2 error code */ char *inbuf; /* buffer to receive data from underlying socket */ /* We need separate buffer for transmission and reception because we may call nghttp2_session_send() after the diff --git a/lib/http2.c b/lib/http2.c index 3959a4f8a..8a60c3747 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -379,23 +379,30 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *userp) { struct connectdata *conn = (struct connectdata *)userp; - struct http_conn *c = &conn->proto.httpc; - struct HTTP *stream = conn->data->req.protop; + struct SessionHandle *data_s; + struct HTTP *stream; (void)session; (void)stream_id; - DEBUGF(infof(conn->data, "on_stream_close() was called, error_code = %d\n", - error_code)); + DEBUGF(infof(conn->data, "on_stream_close(), error_code = %d, stream %x\n", + error_code, stream_id)); - if(stream_id != stream->stream_id) { - DEBUGF(infof(conn->data, "on_stream_close() " - "got stream %x, expected stream %x\n", - stream_id, stream->stream_id)); - return 0; + if(stream_id) { + /* get the stream from the hash based on Stream ID, stream ID zero is for + connection-oriented stuff */ + data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id, + sizeof(stream_id)); + if(!data_s) { + /* Receiving a Stream ID not in the hash should not happen, this is an + internal error more than anything else! */ + failf(conn->data, "Received frame on Stream ID: %x not in stream hash!", + stream_id); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream = data_s->req.protop; + + stream->error_code = error_code; + stream->closed = TRUE; } - - c->error_code = error_code; - c->closed = TRUE; - return 0; } @@ -732,10 +739,10 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, (void)sockindex; /* we always do HTTP2 on sockindex 0 */ - if(httpc->closed) { + if(stream->closed) { /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ - httpc->closed = FALSE; + stream->closed = FALSE; return 0; } @@ -826,14 +833,14 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, } /* If stream is closed, return 0 to signal the http routine to close the connection */ - if(httpc->closed) { + if(stream->closed) { /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ - httpc->closed = FALSE; - if(httpc->error_code != NGHTTP2_NO_ERROR) { + stream->closed = FALSE; + if(stream->error_code != NGHTTP2_NO_ERROR) { failf(conn->data, "HTTP/2 stream = %x was not closed cleanly: error_code = %d", - stream->stream_id, httpc->error_code); + stream->stream_id, stream->error_code); *err = CURLE_HTTP2; return -1; } @@ -1058,8 +1065,6 @@ CURLcode Curl_http2_setup(struct connectdata *conn) return result; infof(conn->data, "Using HTTP2, server supports multi-use\n"); - httpc->error_code = NGHTTP2_NO_ERROR; - httpc->closed = FALSE; httpc->upload_left = 0; httpc->upload_mem = NULL; httpc->upload_len = 0; diff --git a/lib/transfer.c b/lib/transfer.c index 267e0a1cc..2d7b13785 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -317,8 +317,7 @@ static int data_pending(const struct connectdata *conn) TRUE. The thing is if we read everything, then http2_recv won't be called and we cannot signal the HTTP/2 stream has closed. As a workaround, we return nonzero here to call http2_recv. */ - ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20 && - conn->proto.httpc.closed); + ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20); #else Curl_ssl_data_pending(conn, FIRSTSOCKET); #endif