summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/http2.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/http2.c')
-rw-r--r--libs/libcurl/src/http2.c281
1 files changed, 181 insertions, 100 deletions
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index c7f369e1cb..261c9fbb8a 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -29,6 +29,7 @@
#include <nghttp2/nghttp2.h>
#include "urldata.h"
#include "bufq.h"
+#include "hash.h"
#include "http1.h"
#include "http2.h"
#include "http.h"
@@ -127,7 +128,9 @@ struct cf_h2_ctx {
struct bufq inbufq; /* network input */
struct bufq outbufq; /* network output */
struct bufc_pool stream_bufcp; /* spares for stream buffers */
+ struct dynbuf scratch; /* scratch buffer for temp use */
+ struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t max_concurrent_streams;
int32_t goaway_error;
@@ -153,6 +156,9 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
Curl_bufq_free(&ctx->inbufq);
Curl_bufq_free(&ctx->outbufq);
Curl_bufcp_free(&ctx->stream_bufcp);
+ Curl_dyn_free(&ctx->scratch);
+ Curl_hash_clean(&ctx->streams);
+ Curl_hash_destroy(&ctx->streams);
memset(ctx, 0, sizeof(*ctx));
ctx->call_data = save;
}
@@ -187,6 +193,7 @@ struct h2_stream_ctx {
int status_code; /* HTTP response status code */
uint32_t error; /* stream error code */
+ CURLcode xfer_result; /* Result of writing out response */
uint32_t local_window_size; /* the local recv window size */
int32_t id; /* HTTP/2 protocol identifier for stream */
BIT(resp_hds_complete); /* we have a complete, final response */
@@ -198,13 +205,58 @@ struct h2_stream_ctx {
buffered data in stream->sendbuf to upload. */
};
-#define H2_STREAM_CTX(d) ((struct h2_stream_ctx *)(((d) && \
- (d)->req.p.http)? \
- ((struct HTTP *)(d)->req.p.http)->h2_ctx \
- : NULL))
-#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx
-#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \
- H2_STREAM_CTX(d)->id : -2)
+#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+
+static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
+{
+ struct h2_stream_ctx *stream;
+
+ (void)ctx;
+ stream = calloc(1, sizeof(*stream));
+ if(!stream)
+ return NULL;
+
+ stream->id = -1;
+ Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
+ H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
+ Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
+ Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
+ stream->resp_hds_len = 0;
+ stream->bodystarted = FALSE;
+ stream->status_code = -1;
+ stream->closed = FALSE;
+ stream->close_handled = FALSE;
+ stream->error = NGHTTP2_NO_ERROR;
+ stream->local_window_size = H2_STREAM_WINDOW_SIZE;
+ stream->upload_left = 0;
+ stream->nrcvd_data = 0;
+ return stream;
+}
+
+static void free_push_headers(struct h2_stream_ctx *stream)
+{
+ size_t i;
+ for(i = 0; i<stream->push_headers_used; i++)
+ free(stream->push_headers[i]);
+ Curl_safefree(stream->push_headers);
+ stream->push_headers_used = 0;
+}
+
+static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
+{
+ Curl_bufq_free(&stream->sendbuf);
+ Curl_h1_req_parse_free(&stream->h1);
+ Curl_dynhds_free(&stream->resp_trailers);
+ free_push_headers(stream);
+ free(stream);
+}
+
+static void h2_stream_hash_free(void *stream)
+{
+ DEBUGASSERT(stream);
+ h2_stream_ctx_free((struct h2_stream_ctx *)stream);
+}
/*
* Mark this transfer to get "drained".
@@ -241,49 +293,29 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
failf(data, "initialization failure, transfer not http initialized");
return CURLE_FAILED_INIT;
}
- stream = H2_STREAM_CTX(data);
+ stream = H2_STREAM_CTX(ctx, data);
if(stream) {
*pstream = stream;
return CURLE_OK;
}
- stream = calloc(1, sizeof(*stream));
+ stream = h2_stream_ctx_create(ctx);
if(!stream)
return CURLE_OUT_OF_MEMORY;
- stream->id = -1;
- Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
- H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
- Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
- stream->resp_hds_len = 0;
- stream->bodystarted = FALSE;
- stream->status_code = -1;
- stream->closed = FALSE;
- stream->close_handled = FALSE;
- stream->error = NGHTTP2_NO_ERROR;
- stream->local_window_size = H2_STREAM_WINDOW_SIZE;
- stream->upload_left = 0;
- stream->nrcvd_data = 0;
+ if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ h2_stream_ctx_free(stream);
+ return CURLE_OUT_OF_MEMORY;
+ }
- H2_STREAM_LCTX(data) = stream;
*pstream = stream;
return CURLE_OK;
}
-static void free_push_headers(struct h2_stream_ctx *stream)
-{
- size_t i;
- for(i = 0; i<stream->push_headers_used; i++)
- free(stream->push_headers[i]);
- Curl_safefree(stream->push_headers);
- stream->push_headers_used = 0;
-}
-
static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(ctx);
if(!stream)
@@ -310,12 +342,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
nghttp2_session_send(ctx->h2);
}
- Curl_bufq_free(&stream->sendbuf);
- Curl_h1_req_parse_free(&stream->h1);
- Curl_dynhds_free(&stream->resp_trailers);
- free_push_headers(stream);
- free(stream);
- H2_STREAM_LCTX(data) = NULL;
+ Curl_hash_offt_remove(&ctx->streams, data->id);
}
static int h2_client_new(struct Curl_cfilter *cf,
@@ -408,6 +435,8 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
+ Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+ Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
ctx->last_stream_id = 2147483647;
rc = nghttp2_session_callbacks_new(&cbs);
@@ -706,6 +735,7 @@ static ssize_t send_callback(nghttp2_session *h2,
the struct are hidden from the user. */
struct curl_pushheaders {
struct Curl_easy *data;
+ struct h2_stream_ctx *stream;
const nghttp2_push_promise *frame;
};
@@ -719,9 +749,8 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
if(!h || !GOOD_EASY_HANDLE(h->data))
return NULL;
else {
- struct h2_stream_ctx *stream = H2_STREAM_CTX(h->data);
- if(stream && num < stream->push_headers_used)
- return stream->push_headers[num];
+ if(h->stream && num < h->stream->push_headers_used)
+ return h->stream->push_headers[num];
}
return NULL;
}
@@ -744,7 +773,7 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
!strcmp(header, ":") || strchr(header + 1, ':'))
return NULL;
- stream = H2_STREAM_CTX(h->data);
+ stream = h->stream;
if(!stream)
return NULL;
@@ -804,7 +833,7 @@ static int set_transfer_url(struct Curl_easy *data,
v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
if(v) {
- uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER);
+ uc = Curl_url_set_authority(u, v);
if(uc) {
rc = 2;
goto fail;
@@ -867,12 +896,10 @@ static int push_promise(struct Curl_cfilter *cf,
goto fail;
}
- heads.data = data;
- heads.frame = frame;
/* ask the application */
CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
- stream = H2_STREAM_CTX(data);
+ stream = H2_STREAM_CTX(ctx, data);
if(!stream) {
failf(data, "Internal NULL stream");
discard_newhandle(cf, newhandle);
@@ -880,6 +907,10 @@ static int push_promise(struct Curl_cfilter *cf,
goto fail;
}
+ heads.data = data;
+ heads.stream = stream;
+ heads.frame = frame;
+
rv = set_transfer_url(newhandle, &heads);
if(rv) {
discard_newhandle(cf, newhandle);
@@ -945,12 +976,39 @@ fail:
return rv;
}
-static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
+static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const char *buf, size_t blen)
+ struct h2_stream_ctx *stream,
+ const char *buf, size_t blen, bool eos)
{
- (void)cf;
- return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
+
+ /* If we already encountered an error, skip further writes */
+ if(!stream->xfer_result) {
+ stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
+ if(stream->xfer_result)
+ CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
+ stream->id, stream->xfer_result, blen);
+ }
+}
+
+static void h2_xfer_write_resp(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h2_stream_ctx *stream,
+ const char *buf, size_t blen, bool eos)
+{
+
+ /* If we already encountered an error, skip further writes */
+ if(!stream->xfer_result)
+ stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
+ /* If the transfer write is errored, we do not want any more data */
+ if(stream->xfer_result) {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, "
+ "RST-ing stream",
+ stream->id, stream->xfer_result, blen);
+ nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
+ NGHTTP2_ERR_CALLBACK_FAILURE);
+ }
}
static CURLcode on_stream_frame(struct Curl_cfilter *cf,
@@ -958,9 +1016,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
const nghttp2_frame *frame)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
int32_t stream_id = frame->hd.stream_id;
- CURLcode result;
int rv;
if(!stream) {
@@ -1008,9 +1065,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
stream->status_code = -1;
}
- result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
- if(result)
- return result;
+ h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
if(stream->status_code / 100 != 1) {
stream->resp_hds_complete = TRUE;
@@ -1189,7 +1244,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
* servers send an explicit WINDOW_UPDATE, but not all seem to do that.
* To be safe, we UNHOLD a stream in order not to stall. */
if(CURL_WANT_SEND(data)) {
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
if(stream)
drain_stream(cf, data, stream);
}
@@ -1229,7 +1284,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
- CURLcode result;
(void)flags;
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
@@ -1248,13 +1302,11 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
return 0;
}
- stream = H2_STREAM_CTX(data_s);
+ stream = H2_STREAM_CTX(ctx, data_s);
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = Curl_xfer_write_resp(data_s, (char *)mem, len, FALSE);
- if(result && result != CURLE_AGAIN)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
+ h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE);
nghttp2_session_consume(ctx->h2, stream_id, len);
stream->nrcvd_data += (curl_off_t)len;
@@ -1268,6 +1320,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
struct h2_stream_ctx *stream;
int rv;
@@ -1292,7 +1345,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
(void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- stream = H2_STREAM_CTX(data_s);
+ stream = H2_STREAM_CTX(ctx, data_s);
if(!stream) {
CURL_TRC_CF(data_s, cf,
"[%d] on_stream_close, GOOD easy but no stream", stream_id);
@@ -1327,6 +1380,7 @@ static int on_begin_headers(nghttp2_session *session,
const nghttp2_frame *frame, void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s = NULL;
@@ -1340,7 +1394,7 @@ static int on_begin_headers(nghttp2_session *session,
return 0;
}
- stream = H2_STREAM_CTX(data_s);
+ stream = H2_STREAM_CTX(ctx, data_s);
if(!stream || !stream->bodystarted) {
return 0;
}
@@ -1356,6 +1410,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
int32_t stream_id = frame->hd.stream_id;
@@ -1371,7 +1426,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
- stream = H2_STREAM_CTX(data_s);
+ stream = H2_STREAM_CTX(ctx, data_s);
if(!stream) {
failf(data_s, "Internal NULL stream");
return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1465,14 +1520,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- /* the space character after the status code is mandatory */
- result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+ if(!result)
+ h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
@@ -1487,16 +1543,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
/* nghttp2 guarantees that namelen > 0, and :status was already
received, and this is not pseudo-header field . */
/* convert to an HTTP1-style header */
- result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
- if(result)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
- result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
+ Curl_dyn_reset(&ctx->scratch);
+ result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+ if(!result)
+ result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+ if(!result)
+ h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
+ Curl_dyn_len(&ctx->scratch), FALSE);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
@@ -1517,6 +1574,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s;
struct h2_stream_ctx *stream = NULL;
CURLcode result;
@@ -1533,7 +1591,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
- stream = H2_STREAM_CTX(data_s);
+ stream = H2_STREAM_CTX(ctx, data_s);
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@@ -1620,7 +1678,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
{
struct cf_h2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
if(!ctx || !ctx->h2 || !stream)
goto out;
@@ -1658,6 +1716,15 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
return -1;
}
else if(stream->error != NGHTTP2_NO_ERROR) {
+ if(stream->resp_hds_complete && data->req.no_body) {
+ CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
+ "not want a body anyway, ignore: %s (err %u)",
+ stream->id, nghttp2_http2_strerror(stream->error),
+ stream->error);
+ stream->close_handled = TRUE;
+ *err = CURLE_OK;
+ goto out;
+ }
failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
stream->id, nghttp2_http2_strerror(stream->error),
stream->error);
@@ -1736,11 +1803,12 @@ static int sweight_in_effect(const struct Curl_easy *data)
* struct.
*/
-static void h2_pri_spec(struct Curl_easy *data,
+static void h2_pri_spec(struct cf_h2_ctx *ctx,
+ struct Curl_easy *data,
nghttp2_priority_spec *pri_spec)
{
struct Curl_data_priority *prio = &data->set.priority;
- struct h2_stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
+ struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
int32_t depstream_id = depstream? depstream->id:0;
nghttp2_priority_spec_init(pri_spec, depstream_id,
sweight_wanted(data),
@@ -1758,7 +1826,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
int rv = 0;
if(stream && stream->id > 0 &&
@@ -1768,7 +1836,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
/* send new weight and/or dependency */
nghttp2_priority_spec pri_spec;
- h2_pri_spec(data, &pri_spec);
+ h2_pri_spec(ctx, data, &pri_spec);
CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
DEBUGASSERT(stream->id != -1);
rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
@@ -1799,7 +1867,12 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
(void)buf;
*err = CURLE_AGAIN;
- if(stream->closed) {
+ if(stream->xfer_result) {
+ CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
+ *err = stream->xfer_result;
+ nread = -1;
+ }
+ else if(stream->closed) {
CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
nread = http2_handle_stream_close(cf, data, stream, err);
}
@@ -1838,7 +1911,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
* it is time to stop due to connection close or us not processing
* all network input */
while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
- stream = H2_STREAM_CTX(data);
+ stream = H2_STREAM_CTX(ctx, data);
if(stream && (stream->closed || !data_max_bytes)) {
/* We would like to abort here and stop processing, so that
* the transfer loop can handle the data/close here. However,
@@ -1884,7 +1957,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
ssize_t nread = -1;
CURLcode result;
struct cf_call_data save;
@@ -2016,7 +2089,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
goto out;
}
- h2_pri_spec(data, &pri_spec);
+ h2_pri_spec(ctx, data, &pri_spec);
if(!nghttp2_session_check_request_allowed(ctx->h2))
CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
@@ -2113,7 +2186,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
struct cf_call_data save;
int rv;
ssize_t nwritten;
@@ -2294,7 +2367,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
sock = Curl_conn_cf_get_socket(cf, data);
Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
if(want_recv || want_send) {
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
struct cf_call_data save;
bool c_exhaust, s_exhaust;
@@ -2395,7 +2468,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
{
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
DEBUGASSERT(data);
if(ctx && ctx->h2 && stream) {
@@ -2480,7 +2553,7 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
|| (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
@@ -2539,6 +2612,11 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
*pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
CF_DATA_RESTORE(cf, save);
return CURLE_OK;
+ case CF_QUERY_STREAM_ERROR: {
+ struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
+ *pres1 = stream? (int)stream->error : 0;
+ return CURLE_OK;
+ }
default:
break;
}
@@ -2768,8 +2846,11 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
CURLE_HTTP2_STREAM error! */
bool Curl_h2_http_1_1_error(struct Curl_easy *data)
{
- struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
- return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
+ if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
+ int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
+ return (err == NGHTTP2_HTTP_1_1_REQUIRED);
+ }
+ return FALSE;
}
#else /* !USE_NGHTTP2 */