http2: store upload state per stream
Use a curl_off_t for upload left
This commit is contained in:
parent
155b1f5df9
commit
897a7b3a13
@ -171,6 +171,10 @@ struct HTTP {
|
|||||||
char *mem; /* points to a buffer in memory to store received data */
|
char *mem; /* points to a buffer in memory to store received data */
|
||||||
size_t len; /* size of the buffer 'mem' points to */
|
size_t len; /* size of the buffer 'mem' points to */
|
||||||
size_t memlen; /* size of data copied to mem */
|
size_t memlen; /* size of data copied to mem */
|
||||||
|
|
||||||
|
const uint8_t *upload_mem; /* points to a buffer to read from */
|
||||||
|
size_t upload_len; /* size of the buffer 'upload_mem' points to */
|
||||||
|
curl_off_t upload_left; /* number of bytes left to upload */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (*sending)(void); /* Curl_send */
|
typedef int (*sending)(void); /* Curl_send */
|
||||||
@ -199,9 +203,6 @@ struct http_conn {
|
|||||||
nghttp2_session_mem_recv() but mem buffer is still not full. In
|
nghttp2_session_mem_recv() but mem buffer is still not full. In
|
||||||
this case, we wrongly sends the content of mem buffer if we share
|
this case, we wrongly sends the content of mem buffer if we share
|
||||||
them for both cases. */
|
them for both cases. */
|
||||||
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
|
int32_t pause_stream_id; /* stream ID which paused
|
||||||
nghttp2_session_mem_recv */
|
nghttp2_session_mem_recv */
|
||||||
|
|
||||||
|
66
lib/http2.c
66
lib/http2.c
@ -588,24 +588,47 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
|
|||||||
{
|
{
|
||||||
struct connectdata *conn = (struct connectdata *)userp;
|
struct connectdata *conn = (struct connectdata *)userp;
|
||||||
struct http_conn *c = &conn->proto.httpc;
|
struct http_conn *c = &conn->proto.httpc;
|
||||||
|
struct SessionHandle *data_s;
|
||||||
|
struct HTTP *stream = NULL;
|
||||||
size_t nread;
|
size_t nread;
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)stream_id;
|
(void)stream_id;
|
||||||
(void)source;
|
(void)source;
|
||||||
|
|
||||||
nread = MIN(c->upload_len, length);
|
if(stream_id) {
|
||||||
if(nread > 0) {
|
/* get the stream from the hash based on Stream ID, stream ID zero is for
|
||||||
memcpy(buf, c->upload_mem, nread);
|
connection-oriented stuff */
|
||||||
c->upload_mem += nread;
|
data_s = Curl_hash_pick(&c->streamsh, &stream_id, sizeof(stream_id));
|
||||||
c->upload_len -= nread;
|
if(!data_s) {
|
||||||
c->upload_left -= nread;
|
/* Receiving a Stream ID not in the hash should not happen, this is an
|
||||||
|
internal error more than anything else! */
|
||||||
|
failf(conn->data, "Asked for data to stream %x not in hash!", stream_id);
|
||||||
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
stream = data_s->req.protop;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
failf(conn->data, "nghttp2 confusion");
|
||||||
|
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(c->upload_left == 0)
|
nread = MIN(stream->upload_len, length);
|
||||||
|
if(nread > 0) {
|
||||||
|
memcpy(buf, stream->upload_mem, nread);
|
||||||
|
stream->upload_mem += nread;
|
||||||
|
stream->upload_len -= nread;
|
||||||
|
stream->upload_left -= nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stream->upload_left == 0)
|
||||||
*data_flags = 1;
|
*data_flags = 1;
|
||||||
else if(nread == 0)
|
else if(nread == 0)
|
||||||
return NGHTTP2_ERR_DEFERRED;
|
return NGHTTP2_ERR_DEFERRED;
|
||||||
|
|
||||||
|
DEBUGF(infof(data_s, "data_source_read_callback: "
|
||||||
|
"returns %zu bytes stream %x\n",
|
||||||
|
nread, stream_id));
|
||||||
|
|
||||||
return nread;
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -792,8 +815,8 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
|
|
||||||
/* Nullify here because we call nghttp2_session_send() and they
|
/* Nullify here because we call nghttp2_session_send() and they
|
||||||
might refer to the old buffer. */
|
might refer to the old buffer. */
|
||||||
httpc->upload_mem = NULL;
|
stream->upload_mem = NULL;
|
||||||
httpc->upload_len = 0;
|
stream->upload_len = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point 'stream' is just in the SessionHandle the connection
|
* At this point 'stream' is just in the SessionHandle the connection
|
||||||
@ -991,15 +1014,19 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
|
|||||||
if(stream->stream_id != -1) {
|
if(stream->stream_id != -1) {
|
||||||
/* If stream_id != -1, we have dispatched request HEADERS, and now
|
/* If stream_id != -1, we have dispatched request HEADERS, and now
|
||||||
are going to send or sending request body in DATA frame */
|
are going to send or sending request body in DATA frame */
|
||||||
httpc->upload_mem = mem;
|
stream->upload_mem = mem;
|
||||||
httpc->upload_len = len;
|
stream->upload_len = len;
|
||||||
nghttp2_session_resume_data(h2, stream->stream_id);
|
nghttp2_session_resume_data(h2, stream->stream_id);
|
||||||
rv = nghttp2_session_send(h2);
|
rv = nghttp2_session_send(h2);
|
||||||
if(nghttp2_is_fatal(rv)) {
|
if(nghttp2_is_fatal(rv)) {
|
||||||
*err = CURLE_SEND_ERROR;
|
*err = CURLE_SEND_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return len - httpc->upload_len;
|
len -= stream->upload_len;
|
||||||
|
|
||||||
|
DEBUGF(infof(conn->data, "http2_send returns %zu for stream %x\n", len,
|
||||||
|
stream->stream_id));
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate number of headers contained in [mem, mem + len) */
|
/* Calculate number of headers contained in [mem, mem + len) */
|
||||||
@ -1078,12 +1105,15 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
|
|||||||
if(nva[i].namelen == 14 &&
|
if(nva[i].namelen == 14 &&
|
||||||
Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
|
Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
|
||||||
size_t j;
|
size_t j;
|
||||||
|
stream->upload_left = 0;
|
||||||
for(j = 0; j < nva[i].valuelen; ++j) {
|
for(j = 0; j < nva[i].valuelen; ++j) {
|
||||||
httpc->upload_left *= 10;
|
stream->upload_left *= 10;
|
||||||
httpc->upload_left += nva[i].value[j] - '0';
|
stream->upload_left += nva[i].value[j] - '0';
|
||||||
}
|
}
|
||||||
DEBUGF(infof(conn->data,
|
DEBUGF(infof(conn->data,
|
||||||
"request content-length=%zu\n", httpc->upload_left));
|
"request content-length=%"
|
||||||
|
CURL_FORMAT_CURL_OFF_T
|
||||||
|
"\n", stream->upload_left));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1177,9 +1207,9 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
infof(conn->data, "Using HTTP2, server supports multi-use\n");
|
infof(conn->data, "Using HTTP2, server supports multi-use\n");
|
||||||
httpc->upload_left = 0;
|
stream->upload_left = 0;
|
||||||
httpc->upload_mem = NULL;
|
stream->upload_mem = NULL;
|
||||||
httpc->upload_len = 0;
|
stream->upload_len = 0;
|
||||||
|
|
||||||
httpc->inbuflen = 0;
|
httpc->inbuflen = 0;
|
||||||
httpc->nread_inbuf = 0;
|
httpc->nread_inbuf = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user