Skip to content

Commit

Permalink
http2: Read data left in connection buffer after pause
Browse files Browse the repository at this point in the history
Previously when we do pause because of out of buffer, we just throw
away unread data in connection buffer.  This just broke protocol
framing, and I saw occasional FRAME_SIZE_ERROR.  This commit fix this
issue by remembering how much data read, and in the next iteration, we
process remaining data.
  • Loading branch information
tatsuhiro-t authored and bagder committed May 18, 2015
1 parent d261652 commit 0dc0de0
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 20 deletions.
2 changes: 2 additions & 0 deletions lib/http.h
Expand Up @@ -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
Expand Down
64 changes: 44 additions & 20 deletions lib/http2.c
Expand Up @@ -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(nread == -1) {
failf(data, "Failed receiving HTTP2 data");
*err = result;
return 0;
}
if(result == CURLE_AGAIN) {
*err = result;
return -1;
}

if(nread == 0) {
failf(data, "Unexpected EOF");
*err = CURLE_RECV_ERROR;
return -1;
}
if(nread == -1) {
failf(data, "Failed receiving HTTP2 data");
*err = result;
return 0;
}

DEBUGF(infof(data, "nread=%zd\n", nread));
if(nread == 0) {
failf(data, "Unexpected EOF");
*err = CURLE_RECV_ERROR;
return -1;
}

rv = nghttp2_session_mem_recv(httpc->h2,
(const uint8_t *)httpc->inbuf, nread);
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;

DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
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",
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 0dc0de0

Please sign in to comment.