http2: Don't call nghttp2_session_mem_recv while it is paused by a stream

This commit is contained in:
Tatsuhiro Tsujikawa 2015-05-07 17:52:48 +02:00 committed by Daniel Stenberg
parent 0dc0de0351
commit d722138f29
2 changed files with 28 additions and 3 deletions

View File

@ -201,6 +201,8 @@ struct http_conn {
const uint8_t *upload_mem; /* points to a buffer to read from */ 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_len; /* size of the buffer 'upload_mem' points to */
size_t upload_left; /* number of bytes left to upload */ 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) */ /* this is a hash of all individual streams (SessionHandle structs) */
struct curl_hash streamsh; struct curl_hash streamsh;

View File

@ -372,6 +372,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
stream->data = data + nread; stream->data = data + nread;
stream->datalen = len - nread; stream->datalen = len - nread;
DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - out of buffer\n")); DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - out of buffer\n"));
conn->proto.httpc.pause_stream_id = stream_id;
return NGHTTP2_ERR_PAUSE; return NGHTTP2_ERR_PAUSE;
} }
return 0; return 0;
@ -762,8 +763,12 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
return result; 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) { 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 /* Reset to FALSE to prevent infinite loop in readwrite_data
function. */ function. */
stream->closed = FALSE; stream->closed = FALSE;
@ -798,7 +803,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
otherwise, we may be going to read from underlying connection, otherwise, we may be going to read from underlying connection,
and gets EAGAIN, and we will get stuck there. */ and gets EAGAIN, and we will get stuck there. */
if(stream->memlen == 0 && stream->closed) { 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 /* Nullify here because we call nghttp2_session_send() and they
@ -835,6 +840,10 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
infof(data, "%zu data bytes written\n", nread); infof(data, "%zu data bytes written\n", nread);
if(stream->datalen == 0) { 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->data = NULL;
stream->datalen = 0; stream->datalen = 0;
} }
@ -858,6 +867,18 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
stream->mem = mem; 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 { else {
char *inbuf; char *inbuf;
/* remember where to store incoming data for this stream and how big the /* remember where to store incoming data for this stream and how big the
@ -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 /* If stream is closed, return 0 to signal the http routine to close
the connection */ the connection */
if(stream->closed) { if(stream->closed) {
return http2_handle_stream_close(data, stream, err); return http2_handle_stream_close(httpc, data, stream, err);
} }
*err = CURLE_AGAIN; *err = CURLE_AGAIN;
DEBUGF(infof(data, "http2_recv returns -1, AGAIN\n")); DEBUGF(infof(data, "http2_recv returns -1, AGAIN\n"));
@ -1169,6 +1190,8 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
httpc->inbuflen = 0; httpc->inbuflen = 0;
httpc->nread_inbuf = 0; httpc->nread_inbuf = 0;
httpc->pause_stream_id = 0;
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
conn->httpversion = 20; conn->httpversion = 20;
conn->bundle->server_supports_pipelining = TRUE; conn->bundle->server_supports_pipelining = TRUE;