http2: store response header in temporary buffer
This commit is contained in:

committed by
Daniel Stenberg

parent
63b26d889f
commit
dbccf497da
@@ -164,6 +164,9 @@ struct http_conn {
|
|||||||
sending send_underlying; /* underlying send Curl_send callback */
|
sending send_underlying; /* underlying send Curl_send callback */
|
||||||
recving recv_underlying; /* underlying recv Curl_recv callback */
|
recving recv_underlying; /* underlying recv Curl_recv callback */
|
||||||
bool closed; /* TRUE on HTTP2 stream close */
|
bool closed; /* TRUE on HTTP2 stream close */
|
||||||
|
Curl_send_buffer *header_recvbuf; /* store response headers */
|
||||||
|
size_t nread_header_recvbuf; /* number of bytes in header_recvbuf
|
||||||
|
fed into upper layer */
|
||||||
#else
|
#else
|
||||||
int unused; /* prevent a compiler warning */
|
int unused; /* prevent a compiler warning */
|
||||||
#endif
|
#endif
|
||||||
|
63
lib/http2.c
63
lib/http2.c
@@ -117,10 +117,23 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
|
|||||||
void *userp)
|
void *userp)
|
||||||
{
|
{
|
||||||
struct connectdata *conn = (struct connectdata *)userp;
|
struct connectdata *conn = (struct connectdata *)userp;
|
||||||
|
struct http_conn *c = &conn->proto.httpc;
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)frame;
|
(void)frame;
|
||||||
infof(conn->data, "on_frame_recv() was called with header %x\n",
|
infof(conn->data, "on_frame_recv() was called with header %x\n",
|
||||||
frame->hd.type);
|
frame->hd.type);
|
||||||
|
if(frame->hd.type == NGHTTP2_HEADERS &&
|
||||||
|
frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||||
|
c->bodystarted = TRUE;
|
||||||
|
Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
|
||||||
|
c->nread_header_recvbuf = c->len < c->header_recvbuf->size_used ?
|
||||||
|
c->len : c->header_recvbuf->size_used;
|
||||||
|
|
||||||
|
memcpy(c->mem, c->header_recvbuf->buffer, c->nread_header_recvbuf);
|
||||||
|
|
||||||
|
c->mem += c->nread_header_recvbuf;
|
||||||
|
c->len -= c->nread_header_recvbuf;
|
||||||
|
}
|
||||||
if((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) &&
|
if((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) &&
|
||||||
frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
infof(conn->data, "stream_id=%d closed\n", frame->hd.stream_id);
|
infof(conn->data, "stream_id=%d closed\n", frame->hd.stream_id);
|
||||||
@@ -152,13 +165,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
|
|||||||
infof(conn->data, "on_data_chunk_recv() "
|
infof(conn->data, "on_data_chunk_recv() "
|
||||||
"len = %u, stream = %x\n", len, stream_id);
|
"len = %u, stream = %x\n", len, stream_id);
|
||||||
|
|
||||||
if(!c->bodystarted) {
|
|
||||||
memcpy(c->mem, "\r\n", 2); /* signal end of headers */
|
|
||||||
c->mem += 2;
|
|
||||||
c->len -= 2;
|
|
||||||
c->bodystarted = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(len <= c->len) {
|
if(len <= c->len) {
|
||||||
memcpy(c->mem, data, len);
|
memcpy(c->mem, data, len);
|
||||||
c->mem += len;
|
c->mem += len;
|
||||||
@@ -242,6 +248,8 @@ static int on_begin_headers(nghttp2_session *session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char STATUS[] = ":status";
|
||||||
|
|
||||||
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
|
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
|
||||||
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
const uint8_t *name, size_t namelen,
|
const uint8_t *name, size_t namelen,
|
||||||
@@ -250,26 +258,23 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
|||||||
{
|
{
|
||||||
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;
|
||||||
size_t hlen = namelen + valuelen + 3; /* colon + CRLF == 3 bytes */
|
|
||||||
|
|
||||||
(void)session;
|
(void)session;
|
||||||
(void)frame;
|
(void)frame;
|
||||||
|
|
||||||
if(namelen && (name[0] == ':')) {
|
if(namelen == sizeof(":status") - 1 &&
|
||||||
/* special case */
|
memcmp(STATUS, name, namelen) == 0) {
|
||||||
hlen = snprintf(c->mem, c->len, "HTTP/2.0 %s\r\n", value);
|
snprintf(c->header_recvbuf->buffer, 13, "HTTP/2.0 %s", value);
|
||||||
|
c->header_recvbuf->buffer[12] = '\r';
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if(hlen + 1 < c->len) { /* hlen + a zero byte */
|
else {
|
||||||
/* convert to a HTTP1-style header */
|
/* convert to a HTTP1-style header */
|
||||||
memcpy(c->mem, name, namelen);
|
infof(conn->data, "got header\n");
|
||||||
c->mem[namelen]=':';
|
Curl_add_buffer(c->header_recvbuf, name, namelen);
|
||||||
memcpy(&c->mem[namelen+1], value, valuelen);
|
Curl_add_buffer(c->header_recvbuf, ":", 1);
|
||||||
c->mem[namelen + valuelen + 1]='\r';
|
Curl_add_buffer(c->header_recvbuf, value, valuelen);
|
||||||
c->mem[namelen + valuelen + 2]='\n';
|
Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
|
||||||
c->mem[namelen + valuelen + 3]=0; /* to display this easier */
|
|
||||||
}
|
}
|
||||||
c->mem += hlen;
|
|
||||||
c->len -= hlen;
|
|
||||||
|
|
||||||
return 0; /* 0 is successful */
|
return 0; /* 0 is successful */
|
||||||
}
|
}
|
||||||
@@ -389,6 +394,17 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
|
|
||||||
(void)sockindex; /* we always do HTTP2 on sockindex 0 */
|
(void)sockindex; /* we always do HTTP2 on sockindex 0 */
|
||||||
|
|
||||||
|
if(httpc->bodystarted &&
|
||||||
|
httpc->nread_header_recvbuf < httpc->header_recvbuf->size_used) {
|
||||||
|
size_t left =
|
||||||
|
httpc->header_recvbuf->size_used - httpc->nread_header_recvbuf;
|
||||||
|
size_t ncopy = len < left ? len : left;
|
||||||
|
memcpy(mem, httpc->header_recvbuf->buffer + httpc->nread_header_recvbuf,
|
||||||
|
ncopy);
|
||||||
|
httpc->nread_header_recvbuf += ncopy;
|
||||||
|
return ncopy;
|
||||||
|
}
|
||||||
|
|
||||||
conn->proto.httpc.mem = mem;
|
conn->proto.httpc.mem = mem;
|
||||||
conn->proto.httpc.len = len;
|
conn->proto.httpc.len = len;
|
||||||
|
|
||||||
@@ -569,6 +585,11 @@ int Curl_http2_switched(struct connectdata *conn)
|
|||||||
infof(conn->data, "We have switched to HTTP2\n");
|
infof(conn->data, "We have switched to HTTP2\n");
|
||||||
httpc->bodystarted = FALSE;
|
httpc->bodystarted = FALSE;
|
||||||
httpc->closed = FALSE;
|
httpc->closed = FALSE;
|
||||||
|
httpc->header_recvbuf = Curl_add_buffer_init();
|
||||||
|
httpc->nread_header_recvbuf = 0;
|
||||||
|
|
||||||
|
/* Put place holder for status line */
|
||||||
|
Curl_add_buffer(httpc->header_recvbuf, "HTTP/2.0 200\r\n", 14);
|
||||||
|
|
||||||
/* TODO: May get CURLE_AGAIN */
|
/* TODO: May get CURLE_AGAIN */
|
||||||
rv = (int) ((Curl_send*)httpc->send_underlying)
|
rv = (int) ((Curl_send*)httpc->send_underlying)
|
||||||
|
Reference in New Issue
Block a user