diff options
Diffstat (limited to 'Utilities/cmcurl/lib/http2.c')
-rw-r--r-- | Utilities/cmcurl/lib/http2.c | 80 |
1 files changed, 60 insertions, 20 deletions
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index 6cf651f..d316da8 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c @@ -514,7 +514,7 @@ static int push_promise(struct Curl_easy *data, struct connectdata *conn, const nghttp2_push_promise *frame) { - int rv; + int rv; /* one of the CURL_PUSH_* defines */ H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", frame->promised_stream_id)); if(data->multi->push_cb) { @@ -528,7 +528,7 @@ static int push_promise(struct Curl_easy *data, struct Curl_easy *newhandle = duphandle(data); if(!newhandle) { infof(data, "failed to duplicate handle\n"); - rv = 1; /* FAIL HARD */ + rv = CURL_PUSH_DENY; /* FAIL HARD */ goto fail; } @@ -541,13 +541,15 @@ static int push_promise(struct Curl_easy *data, if(!stream) { failf(data, "Internal NULL stream!\n"); (void)Curl_close(&newhandle); - rv = 1; + rv = CURL_PUSH_DENY; goto fail; } rv = set_transfer_url(newhandle, &heads); - if(rv) + if(rv) { + rv = CURL_PUSH_DENY; goto fail; + } Curl_set_in_callback(data, true); rv = data->multi->push_cb(data, newhandle, @@ -563,6 +565,7 @@ static int push_promise(struct Curl_easy *data, stream->push_headers_used = 0; if(rv) { + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); /* denied, kill off the new handle again */ http2_stream_free(newhandle->req.protop); newhandle->req.protop = NULL; @@ -583,7 +586,7 @@ static int push_promise(struct Curl_easy *data, http2_stream_free(newhandle->req.protop); newhandle->req.protop = NULL; Curl_close(&newhandle); - rv = 1; + rv = CURL_PUSH_DENY; goto fail; } @@ -595,12 +598,13 @@ static int push_promise(struct Curl_easy *data, infof(data, "failed to set user_data for stream %d\n", frame->promised_stream_id); DEBUGASSERT(0); + rv = CURL_PUSH_DENY; goto fail; } } else { H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); - rv = 1; + rv = CURL_PUSH_DENY; } fail: return rv; @@ -737,11 +741,16 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, case NGHTTP2_PUSH_PROMISE: rv = push_promise(data_s, conn, &frame->push_promise); if(rv) { /* deny! */ - rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, + int h2; + DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); + h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); - if(nghttp2_is_fatal(rv)) { - return rv; + if(nghttp2_is_fatal(h2)) + return NGHTTP2_ERR_CALLBACK_FAILURE; + else if(rv == CURL_PUSH_ERROROUT) { + DEBUGF(infof(data_s, "Fail the parent stream (too)\n")); + return NGHTTP2_ERR_CALLBACK_FAILURE; } } break; @@ -839,7 +848,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, return 0; } H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n", - nghttp2_strerror(error_code), error_code, stream_id)); + nghttp2_http2_strerror(error_code), error_code, stream_id)); stream = data_s->req.protop; if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1006,18 +1015,11 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - struct dynbuf trail; H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen, value)); - Curl_dyn_init(&trail, DYN_H2_TRAILER); - result = Curl_dyn_addf(&trail, + result = Curl_dyn_addf(&stream->trailer_recvbuf, "%.*s: %.*s\r\n", namelen, name, valuelen, value); - if(!result) - result = Curl_client_write(conn, CLIENTWRITE_HEADER, - Curl_dyn_ptr(&trail), - Curl_dyn_len(&trail)); - Curl_dyn_free(&trail); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1165,6 +1167,7 @@ void Curl_http2_done(struct Curl_easy *data, bool premature) /* there might be allocated resources done before this got the 'h2' pointer setup */ Curl_dyn_free(&http->header_recvbuf); + Curl_dyn_free(&http->trailer_recvbuf); if(http->push_headers) { /* if they weren't used and then freed before */ for(; http->push_headers_used > 0; --http->push_headers_used) { @@ -1174,7 +1177,8 @@ void Curl_http2_done(struct Curl_easy *data, bool premature) http->push_headers = NULL; } - if(!httpc->h2) /* not HTTP/2 ? */ + if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) || + !httpc->h2) /* not HTTP/2 ? */ return; if(premature) { @@ -1203,6 +1207,13 @@ void Curl_http2_done(struct Curl_easy *data, bool premature) } http->stream_id = 0; } + + if(0 == nghttp2_session_check_request_allowed(httpc->h2)) { + /* No more requests are allowed in the current session, so the connection + may not be reused. This is set when a GOAWAY frame has been received or + when the limit of stream identifiers has been reached. */ + connclose(data->conn, "http/2: No new requests allowed"); + } } /* @@ -1456,7 +1467,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, } else if(httpc->error_code != NGHTTP2_NO_ERROR) { failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)", - stream->stream_id, nghttp2_strerror(httpc->error_code), + stream->stream_id, nghttp2_http2_strerror(httpc->error_code), httpc->error_code); *err = CURLE_HTTP2_STREAM; return -1; @@ -1470,6 +1481,31 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn, return -1; } + if(Curl_dyn_len(&stream->trailer_recvbuf)) { + char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf); + char *lf; + + do { + size_t len = 0; + CURLcode result; + /* each trailer line ends with a newline */ + lf = strchr(trailp, '\n'); + if(!lf) + break; + len = lf + 1 - trailp; + + if(data->set.verbose) + Curl_debug(data, CURLINFO_HEADER_IN, trailp, len); + /* pass the trailers one by one to the callback */ + result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailp, len); + if(result) { + *err = result; + return -1; + } + trailp = ++lf; + } while(lf); + } + stream->close_handled = TRUE; H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n")); @@ -2075,6 +2111,9 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex, h2_pri_spec(conn->data, &pri_spec); + H2BUGF(infof(conn->data, "http2_send request allowed %d (easy handle %p)\n", + nghttp2_session_check_request_allowed(h2), (void *)conn->data)); + switch(conn->data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -2151,6 +2190,7 @@ CURLcode Curl_http2_setup(struct connectdata *conn) stream->stream_id = -1; Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS); + Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS); if((conn->handler == &Curl_handler_http2_ssl) || (conn->handler == &Curl_handler_http2)) |