Loading lib/http.c +1 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) conn->data->req.protop = http; Curl_http2_setup_conn(conn); Curl_http2_setup_req(conn->data); return CURLE_OK; } Loading lib/http.h +1 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,7 @@ struct HTTP { 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 */ Curl_send_buffer *push_recvbuf; /* store incoming push headers */ #endif }; Loading lib/http2.c +102 −38 Original line number Diff line number Diff line Loading @@ -95,12 +95,9 @@ static CURLcode http2_disconnect(struct connectdata *conn, } /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn) void Curl_http2_setup_req(struct SessionHandle *data) { struct HTTP *http = conn->data->req.protop; conn->proto.httpc.settings.max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; struct HTTP *http = data->req.protop; http->nread_header_recvbuf = 0; http->bodystarted = FALSE; Loading @@ -109,13 +106,18 @@ void Curl_http2_setup_conn(struct connectdata *conn) http->pauselen = 0; http->error_code = NGHTTP2_NO_ERROR; http->closed = FALSE; /* where to store incoming data for this stream and how big the buffer is */ http->mem = conn->data->state.buffer; http->mem = data->state.buffer; http->len = BUFSIZE; http->memlen = 0; } /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn) { conn->proto.httpc.settings.max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; } /* * HTTP2 handler interface. This isn't added to the general list of protocols * but will be used at run-time when the protocol is dynamically switched from Loading Loading @@ -228,46 +230,98 @@ struct curl_headerpair *curl_pushheader_bynum(struct curl_pushheaders *h, return NULL; } static CURL *duphandle(struct SessionHandle *data) { struct SessionHandle *second = curl_easy_duphandle(data); if(second) { /* setup the request struct */ struct HTTP *http = calloc(1, sizeof(struct HTTP)); if(!http) { (void)Curl_close(second); second = NULL; } else { second->req.protop = http; http->header_recvbuf = Curl_add_buffer_init(); if(!http->header_recvbuf) { free(http); (void)Curl_close(second); second = NULL; } else Curl_http2_setup_req(second); } } return second; } static int push_promise(struct SessionHandle *data, struct connectdata *conn, const nghttp2_push_promise *frame) { int rv; DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", frame->promised_stream_id)); if(data->multi->push_cb) { struct HTTP *stream; struct curl_pushheaders heads; CURLMcode rc; struct http_conn *httpc; /* clone the parent */ CURL *newhandle = curl_easy_duphandle(data); CURL *newhandle = duphandle(data); if(!newhandle) { infof(data, "failed to duplicate handle\n"); rv = 1; /* FAIL HARD */ goto fail; } else { struct curl_pushheaders heads; heads.data = data; heads.frame = frame; /* ask the application */ DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n")); stream = data->req.protop; #ifdef CURLDEBUG fprintf(stderr, "PUSHHDR %s\n", stream->push_recvbuf->buffer); #endif rv = data->multi->push_cb(data, newhandle, frame->nvlen, &heads, data->multi->push_userp); if(rv) if(rv) { /* denied, kill off the new handle again */ (void)Curl_close(newhandle); else { /* approved, add to the multi handle */ CURLMcode rc = curl_multi_add_handle(data->multi, newhandle); goto fail; } /* approved, add to the multi handle and immediately switch to PERFORM state with the given connection !*/ rc = Curl_multi_add_perform(data->multi, newhandle, conn); if(rc) { infof(data, "failed to add handle to multi\n"); Curl_close(newhandle); rv = 1; goto fail; } httpc = &conn->proto.httpc; /* put the newhandle in the hash with the stream id as key */ if(!Curl_hash_add(&httpc->streamsh, (size_t *)&frame->promised_stream_id, sizeof(frame->promised_stream_id), newhandle)) { failf(conn->data, "Couldn't add stream to hash!"); rv = 1; } else rv = 0; } } } else { DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); rv = 1; } fail: return rv; } Loading Loading @@ -358,7 +412,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, Curl_expire(data_s, 1); break; case NGHTTP2_PUSH_PROMISE: rv = push_promise(data_s, &frame->push_promise); rv = push_promise(data_s, conn, &frame->push_promise); if(rv) { /* deny! */ rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, Loading Loading @@ -591,11 +645,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, (void)frame; (void)flags; /* Ignore PUSH_PROMISE for now */ if(frame->hd.type != NGHTTP2_HEADERS) { return 0; } DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ /* get the stream from the hash based on Stream ID */ Loading @@ -615,6 +664,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, consequence is handled in on_frame_recv(). */ return 0; /* Store received PUSH_PROMISE headers to be used when the subsequent PUSH_PROMISE callback comes */ if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { fprintf(stderr, "*** PUSH_PROMISE headers on stream %u for %u\n", stream_id, frame->push_promise.promised_stream_id); if(!stream->push_recvbuf) stream->push_recvbuf = Curl_add_buffer_init(); Curl_add_buffer(stream->push_recvbuf, name, namelen); Curl_add_buffer(stream->push_recvbuf, ":", 1); Curl_add_buffer(stream->push_recvbuf, value, valuelen); Curl_add_buffer(stream->push_recvbuf, "\r\n", 2); return 0; } if(namelen == sizeof(":status") - 1 && memcmp(":status", name, namelen) == 0) { /* nghttp2 guarantees :status is received first and only once, and Loading lib/http2.h +2 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn, const char *data, size_t nread); /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn); void Curl_http2_setup_req(struct SessionHandle *data); #else /* USE_NGHTTP2 */ #define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL Loading @@ -53,6 +54,7 @@ void Curl_http2_setup_conn(struct connectdata *conn); #define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_setup_conn(x) #define Curl_http2_setup_req(x) #endif #endif /* HEADER_CURL_HTTP2_H */ Loading lib/multi.c +15 −0 Original line number Diff line number Diff line Loading @@ -950,6 +950,21 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear) return retval; } CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct SessionHandle *data, struct connectdata *conn) { CURLMcode rc; rc = curl_multi_add_handle(multi, data); if(!rc) { /* take this handle to the perform state right away */ multistate(data, CURLM_STATE_PERFORM); data->easy_conn = conn; } return rc; } static CURLMcode multi_runsingle(struct Curl_multi *multi, struct timeval now, struct SessionHandle *data) Loading Loading
lib/http.c +1 −0 Original line number Diff line number Diff line Loading @@ -164,6 +164,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) conn->data->req.protop = http; Curl_http2_setup_conn(conn); Curl_http2_setup_req(conn->data); return CURLE_OK; } Loading
lib/http.h +1 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,7 @@ struct HTTP { 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 */ Curl_send_buffer *push_recvbuf; /* store incoming push headers */ #endif }; Loading
lib/http2.c +102 −38 Original line number Diff line number Diff line Loading @@ -95,12 +95,9 @@ static CURLcode http2_disconnect(struct connectdata *conn, } /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn) void Curl_http2_setup_req(struct SessionHandle *data) { struct HTTP *http = conn->data->req.protop; conn->proto.httpc.settings.max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; struct HTTP *http = data->req.protop; http->nread_header_recvbuf = 0; http->bodystarted = FALSE; Loading @@ -109,13 +106,18 @@ void Curl_http2_setup_conn(struct connectdata *conn) http->pauselen = 0; http->error_code = NGHTTP2_NO_ERROR; http->closed = FALSE; /* where to store incoming data for this stream and how big the buffer is */ http->mem = conn->data->state.buffer; http->mem = data->state.buffer; http->len = BUFSIZE; http->memlen = 0; } /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn) { conn->proto.httpc.settings.max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; } /* * HTTP2 handler interface. This isn't added to the general list of protocols * but will be used at run-time when the protocol is dynamically switched from Loading Loading @@ -228,46 +230,98 @@ struct curl_headerpair *curl_pushheader_bynum(struct curl_pushheaders *h, return NULL; } static CURL *duphandle(struct SessionHandle *data) { struct SessionHandle *second = curl_easy_duphandle(data); if(second) { /* setup the request struct */ struct HTTP *http = calloc(1, sizeof(struct HTTP)); if(!http) { (void)Curl_close(second); second = NULL; } else { second->req.protop = http; http->header_recvbuf = Curl_add_buffer_init(); if(!http->header_recvbuf) { free(http); (void)Curl_close(second); second = NULL; } else Curl_http2_setup_req(second); } } return second; } static int push_promise(struct SessionHandle *data, struct connectdata *conn, const nghttp2_push_promise *frame) { int rv; DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", frame->promised_stream_id)); if(data->multi->push_cb) { struct HTTP *stream; struct curl_pushheaders heads; CURLMcode rc; struct http_conn *httpc; /* clone the parent */ CURL *newhandle = curl_easy_duphandle(data); CURL *newhandle = duphandle(data); if(!newhandle) { infof(data, "failed to duplicate handle\n"); rv = 1; /* FAIL HARD */ goto fail; } else { struct curl_pushheaders heads; heads.data = data; heads.frame = frame; /* ask the application */ DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n")); stream = data->req.protop; #ifdef CURLDEBUG fprintf(stderr, "PUSHHDR %s\n", stream->push_recvbuf->buffer); #endif rv = data->multi->push_cb(data, newhandle, frame->nvlen, &heads, data->multi->push_userp); if(rv) if(rv) { /* denied, kill off the new handle again */ (void)Curl_close(newhandle); else { /* approved, add to the multi handle */ CURLMcode rc = curl_multi_add_handle(data->multi, newhandle); goto fail; } /* approved, add to the multi handle and immediately switch to PERFORM state with the given connection !*/ rc = Curl_multi_add_perform(data->multi, newhandle, conn); if(rc) { infof(data, "failed to add handle to multi\n"); Curl_close(newhandle); rv = 1; goto fail; } httpc = &conn->proto.httpc; /* put the newhandle in the hash with the stream id as key */ if(!Curl_hash_add(&httpc->streamsh, (size_t *)&frame->promised_stream_id, sizeof(frame->promised_stream_id), newhandle)) { failf(conn->data, "Couldn't add stream to hash!"); rv = 1; } else rv = 0; } } } else { DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); rv = 1; } fail: return rv; } Loading Loading @@ -358,7 +412,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, Curl_expire(data_s, 1); break; case NGHTTP2_PUSH_PROMISE: rv = push_promise(data_s, &frame->push_promise); rv = push_promise(data_s, conn, &frame->push_promise); if(rv) { /* deny! */ rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, Loading Loading @@ -591,11 +645,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, (void)frame; (void)flags; /* Ignore PUSH_PROMISE for now */ if(frame->hd.type != NGHTTP2_HEADERS) { return 0; } DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ /* get the stream from the hash based on Stream ID */ Loading @@ -615,6 +664,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, consequence is handled in on_frame_recv(). */ return 0; /* Store received PUSH_PROMISE headers to be used when the subsequent PUSH_PROMISE callback comes */ if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { fprintf(stderr, "*** PUSH_PROMISE headers on stream %u for %u\n", stream_id, frame->push_promise.promised_stream_id); if(!stream->push_recvbuf) stream->push_recvbuf = Curl_add_buffer_init(); Curl_add_buffer(stream->push_recvbuf, name, namelen); Curl_add_buffer(stream->push_recvbuf, ":", 1); Curl_add_buffer(stream->push_recvbuf, value, valuelen); Curl_add_buffer(stream->push_recvbuf, "\r\n", 2); return 0; } if(namelen == sizeof(":status") - 1 && memcmp(":status", name, namelen) == 0) { /* nghttp2 guarantees :status is received first and only once, and Loading
lib/http2.h +2 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn, const char *data, size_t nread); /* called from Curl_http_setup_conn */ void Curl_http2_setup_conn(struct connectdata *conn); void Curl_http2_setup_req(struct SessionHandle *data); #else /* USE_NGHTTP2 */ #define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL Loading @@ -53,6 +54,7 @@ void Curl_http2_setup_conn(struct connectdata *conn); #define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_setup_conn(x) #define Curl_http2_setup_req(x) #endif #endif /* HEADER_CURL_HTTP2_H */ Loading
lib/multi.c +15 −0 Original line number Diff line number Diff line Loading @@ -950,6 +950,21 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear) return retval; } CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct SessionHandle *data, struct connectdata *conn) { CURLMcode rc; rc = curl_multi_add_handle(multi, data); if(!rc) { /* take this handle to the perform state right away */ multistate(data, CURLM_STATE_PERFORM); data->easy_conn = conn; } return rc; } static CURLMcode multi_runsingle(struct Curl_multi *multi, struct timeval now, struct SessionHandle *data) Loading