Loading lib/http.h +2 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,8 @@ struct http_conn { const uint8_t *upload_mem; /* points to a buffer to read from */ size_t upload_len; /* size of the buffer 'upload_mem' points to */ size_t upload_left; /* number of bytes left to upload */ int32_t pause_stream_id; /* stream ID which paused nghttp2_session_mem_recv */ /* this is a hash of all individual streams (SessionHandle structs) */ struct curl_hash streamsh; Loading lib/http2.c +26 −3 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, stream->data = data + nread; stream->datalen = len - nread; DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - out of buffer\n")); conn->proto.httpc.pause_stream_id = stream_id; return NGHTTP2_ERR_PAUSE; } return 0; Loading Loading @@ -762,8 +763,12 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, return result; } static ssize_t http2_handle_stream_close(struct SessionHandle *data, static ssize_t http2_handle_stream_close(struct http_conn *httpc, struct SessionHandle *data, struct HTTP *stream, CURLcode *err) { if(httpc->pause_stream_id == stream->stream_id) { httpc->pause_stream_id = 0; } /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ stream->closed = FALSE; Loading Loading @@ -798,7 +803,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, otherwise, we may be going to read from underlying connection, and gets EAGAIN, and we will get stuck there. */ if(stream->memlen == 0 && stream->closed) { return http2_handle_stream_close(data, stream, err); return http2_handle_stream_close(httpc, data, stream, err); } /* Nullify here because we call nghttp2_session_send() and they Loading Loading @@ -835,6 +840,10 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, infof(data, "%zu data bytes written\n", nread); if(stream->datalen == 0) { DEBUGF(infof(data, "Unpaused by stream %x\n", stream->stream_id)); assert(httpc->pause_stream_id == stream->stream_id); httpc->pause_stream_id = 0; stream->data = NULL; stream->datalen = 0; } Loading @@ -858,6 +867,18 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, stream->mem = mem; } } else if(httpc->pause_stream_id) { /* If a stream paused nghttp2_session_mem_recv previously, and has not processed all data, it still refers to the buffer in nghttp2_session. If we call nghttp2_session_mem_recv(), we may overwrite that buffer. To avoid that situation, just return here with CURLE_AGAIN. This could be busy loop since data in socket is not read. But it seems that usually streams are notified with its drain property, and socket is read again quickly. */ *err = CURLE_AGAIN; return -1; } else { char *inbuf; /* remember where to store incoming data for this stream and how big the Loading Loading @@ -939,7 +960,7 @@ 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(stream->closed) { return http2_handle_stream_close(data, stream, err); return http2_handle_stream_close(httpc, data, stream, err); } *err = CURLE_AGAIN; DEBUGF(infof(data, "http2_recv returns -1, AGAIN\n")); Loading Loading @@ -1169,6 +1190,8 @@ CURLcode Curl_http2_setup(struct connectdata *conn) httpc->inbuflen = 0; httpc->nread_inbuf = 0; httpc->pause_stream_id = 0; conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->httpversion = 20; conn->bundle->server_supports_pipelining = TRUE; Loading Loading
lib/http.h +2 −0 Original line number Diff line number Diff line Loading @@ -201,6 +201,8 @@ struct http_conn { const uint8_t *upload_mem; /* points to a buffer to read from */ size_t upload_len; /* size of the buffer 'upload_mem' points to */ size_t upload_left; /* number of bytes left to upload */ int32_t pause_stream_id; /* stream ID which paused nghttp2_session_mem_recv */ /* this is a hash of all individual streams (SessionHandle structs) */ struct curl_hash streamsh; Loading
lib/http2.c +26 −3 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, stream->data = data + nread; stream->datalen = len - nread; DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - out of buffer\n")); conn->proto.httpc.pause_stream_id = stream_id; return NGHTTP2_ERR_PAUSE; } return 0; Loading Loading @@ -762,8 +763,12 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, return result; } static ssize_t http2_handle_stream_close(struct SessionHandle *data, static ssize_t http2_handle_stream_close(struct http_conn *httpc, struct SessionHandle *data, struct HTTP *stream, CURLcode *err) { if(httpc->pause_stream_id == stream->stream_id) { httpc->pause_stream_id = 0; } /* Reset to FALSE to prevent infinite loop in readwrite_data function. */ stream->closed = FALSE; Loading Loading @@ -798,7 +803,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, otherwise, we may be going to read from underlying connection, and gets EAGAIN, and we will get stuck there. */ if(stream->memlen == 0 && stream->closed) { return http2_handle_stream_close(data, stream, err); return http2_handle_stream_close(httpc, data, stream, err); } /* Nullify here because we call nghttp2_session_send() and they Loading Loading @@ -835,6 +840,10 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, infof(data, "%zu data bytes written\n", nread); if(stream->datalen == 0) { DEBUGF(infof(data, "Unpaused by stream %x\n", stream->stream_id)); assert(httpc->pause_stream_id == stream->stream_id); httpc->pause_stream_id = 0; stream->data = NULL; stream->datalen = 0; } Loading @@ -858,6 +867,18 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, stream->mem = mem; } } else if(httpc->pause_stream_id) { /* If a stream paused nghttp2_session_mem_recv previously, and has not processed all data, it still refers to the buffer in nghttp2_session. If we call nghttp2_session_mem_recv(), we may overwrite that buffer. To avoid that situation, just return here with CURLE_AGAIN. This could be busy loop since data in socket is not read. But it seems that usually streams are notified with its drain property, and socket is read again quickly. */ *err = CURLE_AGAIN; return -1; } else { char *inbuf; /* remember where to store incoming data for this stream and how big the Loading Loading @@ -939,7 +960,7 @@ 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(stream->closed) { return http2_handle_stream_close(data, stream, err); return http2_handle_stream_close(httpc, data, stream, err); } *err = CURLE_AGAIN; DEBUGF(infof(data, "http2_recv returns -1, AGAIN\n")); Loading Loading @@ -1169,6 +1190,8 @@ CURLcode Curl_http2_setup(struct connectdata *conn) httpc->inbuflen = 0; httpc->nread_inbuf = 0; httpc->pause_stream_id = 0; conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->httpversion = 20; conn->bundle->server_supports_pipelining = TRUE; Loading