diff --git a/lib/http.h b/lib/http.h index 53d2bfeba..13fa1d99b 100644 --- a/lib/http.h +++ b/lib/http.h @@ -191,6 +191,8 @@ struct http_conn { sending send_underlying; /* underlying send Curl_send callback */ recving recv_underlying; /* underlying recv Curl_recv callback */ char *inbuf; /* buffer to receive data from underlying socket */ + size_t inbuflen; /* number of bytes filled in inbuf */ + size_t nread_inbuf; /* number of bytes read from in inbuf */ /* We need separate buffer for transmission and reception because we may call nghttp2_session_send() after the nghttp2_session_mem_recv() but mem buffer is still not full. In diff --git a/lib/http2.c b/lib/http2.c index 521a78106..78a09af8e 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -859,36 +859,47 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, } } else { + char *inbuf; /* remember where to store incoming data for this stream and how big the buffer is */ stream->mem = mem; stream->len = len; stream->memlen = 0; - nread = ((Curl_recv*)httpc->recv_underlying)(conn, FIRSTSOCKET, - httpc->inbuf, H2_BUFSIZE, - &result); - if(result == CURLE_AGAIN) { - *err = result; - return -1; + if(httpc->inbuflen == 0) { + nread = ((Curl_recv *)httpc->recv_underlying)( + conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); + + if(result == CURLE_AGAIN) { + *err = result; + return -1; + } + + if(nread == -1) { + failf(data, "Failed receiving HTTP2 data"); + *err = result; + return 0; + } + + if(nread == 0) { + failf(data, "Unexpected EOF"); + *err = CURLE_RECV_ERROR; + return -1; + } + + DEBUGF(infof(data, "nread=%zd\n", nread)); + + httpc->inbuflen = nread; + inbuf = httpc->inbuf; } + else { + nread = httpc->inbuflen - httpc->nread_inbuf; + inbuf = httpc->inbuf + httpc->nread_inbuf; - if(nread == -1) { - failf(data, "Failed receiving HTTP2 data"); - *err = result; - return 0; + DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n", + nread)); } - - if(nread == 0) { - failf(data, "Unexpected EOF"); - *err = CURLE_RECV_ERROR; - return -1; - } - - DEBUGF(infof(data, "nread=%zd\n", nread)); - - rv = nghttp2_session_mem_recv(httpc->h2, - (const uint8_t *)httpc->inbuf, nread); + rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); if(nghttp2_is_fatal((int)rv)) { failf(data, "nghttp2_session_mem_recv() returned %d:%s\n", @@ -897,6 +908,16 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex, return 0; } DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv)); + if(nread == rv) { + DEBUGF(infof(data, "All data in connection buffer processed\n")); + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + } + else { + httpc->nread_inbuf += rv; + DEBUGF(infof(data, "%zu bytes left in connection buffer\n", + httpc->inbuflen - httpc->nread_inbuf)); + } /* Always send pending frames in nghttp2 session, because nghttp2_session_mem_recv() may queue new frame */ rv = nghttp2_session_send(httpc->h2); @@ -1145,6 +1166,9 @@ CURLcode Curl_http2_setup(struct connectdata *conn) httpc->upload_mem = NULL; httpc->upload_len = 0; + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ conn->httpversion = 20; conn->bundle->server_supports_pipelining = TRUE;