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.c124
1 files changed, 76 insertions, 48 deletions
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index cd83e564b1..b9a21d2987 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -254,7 +254,7 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
static void free_push_headers(struct h2_stream_ctx *stream)
{
size_t i;
- for(i = 0; i<stream->push_headers_used; 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;
@@ -299,7 +299,7 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
int32_t dwsize;
int rv;
- dwsize = paused? 0 : cf_h2_get_desired_local_win(cf, data);
+ dwsize = paused ? 0 : cf_h2_get_desired_local_win(cf, data);
if(dwsize != stream->local_window_size) {
int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
ctx->h2, stream->id);
@@ -498,9 +498,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *value, size_t valuelen,
uint8_t flags,
void *userp);
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
static int error_callback(nghttp2_session *session, const char *msg,
size_t len, void *userp);
-
+#endif
static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -530,7 +531,9 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
nghttp2_session_callbacks_set_on_begin_headers_callback(
cbs, on_begin_headers);
nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
+#endif
/* The nghttp2 session is not yet setup, do it */
rc = h2_client_new(cf, cbs);
@@ -606,7 +609,7 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
/* all set, traffic will be send on connect */
result = CURLE_OK;
CURL_TRC_CF(data, cf, "[0] created h2 session%s",
- ctx->via_h1_upgrade? " (via h1 upgrade)" : "");
+ ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
out:
if(cbs)
@@ -764,7 +767,7 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf,
}
return result;
}
- return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
+ return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
}
/*
@@ -786,8 +789,11 @@ static ssize_t send_callback(nghttp2_session *h2,
(void)flags;
DEBUGASSERT(data);
- nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
- nw_out_writer, cf, &result);
+ if(!cf->connected)
+ nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result);
+ else
+ nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
+ nw_out_writer, cf, &result);
if(nwritten < 0) {
if(result == CURLE_AGAIN) {
ctx->nw_out_blocked = 1;
@@ -852,7 +858,7 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
return NULL;
len = strlen(header);
- for(i = 0; i<stream->push_headers_used; i++) {
+ for(i = 0; i < stream->push_headers_used; i++) {
if(!strncmp(header, stream->push_headers[i], len)) {
/* sub-match, make sure that it is followed by a colon */
if(stream->push_headers[i][len] != ':')
@@ -990,11 +996,11 @@ static int push_promise(struct Curl_cfilter *cf,
}
DEBUGASSERT(stream);
- Curl_set_in_callback(data, true);
+ Curl_set_in_callback(data, TRUE);
rv = data->multi->push_cb(data, newhandle,
stream->push_headers_used, &heads,
data->multi->push_userp);
- Curl_set_in_callback(data, false);
+ Curl_set_in_callback(data, FALSE);
/* free the headers again */
free_push_headers(stream);
@@ -1113,9 +1119,6 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
return CURLE_RECV_ERROR;
}
}
- if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
- drain_stream(cf, data, stream);
- }
break;
case NGHTTP2_HEADERS:
if(stream->bodystarted) {
@@ -1131,10 +1134,10 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
return CURLE_RECV_ERROR;
/* Only final status code signals the end of header */
- if(stream->status_code / 100 != 1) {
+ if(stream->status_code / 100 != 1)
stream->bodystarted = TRUE;
+ else
stream->status_code = -1;
- }
h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
@@ -1181,6 +1184,22 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
default:
break;
}
+
+ if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ if(!stream->closed && !stream->body_eos &&
+ ((stream->status_code >= 400) || (stream->status_code < 200))) {
+ /* The server did not give us a positive response and we are not
+ * done uploading the request body. We need to stop doing that and
+ * also inform the server that we aborted our side. */
+ CURL_TRC_CF(data, cf, "[%d] EOS frame with unfinished upload and "
+ "HTTP status %d, abort upload by RST",
+ stream_id, stream->status_code);
+ nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, NGHTTP2_STREAM_CLOSED);
+ stream->closed = TRUE;
+ }
+ drain_stream(cf, data, stream);
+ }
return CURLE_OK;
}
@@ -1235,14 +1254,14 @@ static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
case NGHTTP2_GOAWAY: {
char scratch[128];
size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
- size_t len = (frame->goaway.opaque_data_len < s_len)?
- frame->goaway.opaque_data_len : s_len-1;
- if(len)
- memcpy(scratch, frame->goaway.opaque_data, len);
- scratch[len] = '\0';
- return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
- "last_stream=%d]", frame->goaway.error_code,
- scratch, frame->goaway.last_stream_id);
+ size_t len = (frame->goaway.opaque_data_len < s_len) ?
+ frame->goaway.opaque_data_len : s_len-1;
+ if(len)
+ memcpy(scratch, frame->goaway.opaque_data, len);
+ scratch[len] = '\0';
+ return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
+ "last_stream=%d]", frame->goaway.error_code,
+ scratch, frame->goaway.last_stream_id);
}
case NGHTTP2_WINDOW_UPDATE: {
return msnprintf(buffer, blen,
@@ -1350,7 +1369,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
return 0;
}
- return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
+ return on_stream_frame(cf, data_s, frame) ? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
}
static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
@@ -1402,8 +1421,8 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
DEBUGASSERT(call_data);
/* stream id 0 is the connection, do not look there for streams. */
- data_s = stream_id?
- nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
+ data_s = stream_id ?
+ nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
if(!data_s) {
CURL_TRC_CF(call_data, cf,
"[%d] on_stream_close, no easy set on stream", stream_id);
@@ -1683,7 +1702,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
*data_flags = NGHTTP2_DATA_FLAG_EOF;
return nread;
}
- return (nread == 0)? NGHTTP2_ERR_DEFERRED : nread;
+ return (nread == 0) ? NGHTTP2_ERR_DEFERRED : nread;
}
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -1773,7 +1792,7 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
}
else if(stream->reset) {
failf(data, "HTTP/2 stream %u was reset", stream->id);
- *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
+ *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
return -1;
}
@@ -1826,14 +1845,14 @@ out:
static int sweight_wanted(const struct Curl_easy *data)
{
/* 0 weight is not set by user and we take the nghttp2 default one */
- return data->set.priority.weight?
+ return data->set.priority.weight ?
data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
}
static int sweight_in_effect(const struct Curl_easy *data)
{
/* 0 weight is not set by user and we take the nghttp2 default one */
- return data->state.priority.weight?
+ return data->state.priority.weight ?
data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
}
@@ -1849,7 +1868,7 @@ static void h2_pri_spec(struct cf_h2_ctx *ctx,
{
struct Curl_data_priority *prio = &data->set.priority;
struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
- int32_t depstream_id = depstream? depstream->id:0;
+ int32_t depstream_id = depstream ? depstream->id : 0;
nghttp2_priority_spec_init(pri_spec, depstream_id,
sweight_wanted(data),
data->set.priority.exclusive);
@@ -1895,6 +1914,11 @@ out:
nghttp2_strerror(rv), rv);
return CURLE_SEND_ERROR;
}
+ /* Defer flushing during the connect phase so that the SETTINGS and
+ * other initial frames are sent together with the first request.
+ * Unless we are 'connect_only' where the request will never come. */
+ if(!cf->connected && !cf->conn->connect_only)
+ return CURLE_OK;
return nw_out_flush(cf, data);
}
@@ -1920,7 +1944,7 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
(ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
- *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
+ *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
nread = -1;
}
@@ -1978,8 +2002,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
}
else {
CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread);
- data_max_bytes = (data_max_bytes > (size_t)nread)?
- (data_max_bytes - (size_t)nread) : 0;
+ data_max_bytes = (data_max_bytes > (size_t)nread) ?
+ (data_max_bytes - (size_t)nread) : 0;
}
if(h2_process_pending_input(cf, data, &result))
@@ -2244,7 +2268,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
out:
CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
- stream? stream->id : -1, nwritten, *err);
+ stream ? stream->id : -1, nwritten, *err);
Curl_safefree(nva);
*pstream = stream;
Curl_dynhds_free(&h2_headers);
@@ -2436,6 +2460,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
struct cf_h2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct cf_call_data save;
+ bool first_time = FALSE;
if(cf->connected) {
*done = TRUE;
@@ -2457,11 +2482,14 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
result = cf_h2_ctx_open(cf, data);
if(result)
goto out;
+ first_time = TRUE;
}
- result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
- if(result)
- goto out;
+ if(!first_time) {
+ result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
+ if(result)
+ goto out;
+ }
/* Send out our SETTINGS and ACKs and such. If that blocks, we
* have it buffered and can count this filter as being connected */
@@ -2584,7 +2612,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
- pause? "" : "un");
+ pause ? "" : "un");
}
return CURLE_OK;
}
@@ -2630,7 +2658,7 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
|| (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
return TRUE;
- return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
+ return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
static bool cf_h2_is_alive(struct Curl_cfilter *cf,
@@ -2681,12 +2709,12 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
else {
effective_max = ctx->max_concurrent_streams;
}
- *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
+ *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;
+ *pres1 = stream ? (int)stream->error : 0;
return CURLE_OK;
}
case CF_QUERY_NEED_FLUSH: {
@@ -2701,7 +2729,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
default:
break;
}
- return cf->next?
+ return cf->next ?
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
CURLE_UNKNOWN_OPTION;
}
@@ -2751,7 +2779,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
out:
if(result)
cf_h2_ctx_free(ctx);
- *pcf = result? NULL : cf;
+ *pcf = result ? NULL : cf;
return result;
}
@@ -2782,8 +2810,8 @@ out:
return result;
}
-static bool Curl_cf_is_http2(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static bool cf_is_http2(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
(void)data;
for(; cf; cf = cf->next) {
@@ -2799,7 +2827,7 @@ bool Curl_conn_is_http2(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex)
{
- return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
+ return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
}
bool Curl_http2_may_switch(struct Curl_easy *data,
@@ -2851,7 +2879,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
struct Curl_cfilter *cf_h2;
CURLcode result;
- DEBUGASSERT(!Curl_cf_is_http2(cf, data));
+ DEBUGASSERT(!cf_is_http2(cf, data));
result = http2_cfilter_insert_after(cf, data, FALSE);
if(result)