1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifdef USE_NGHTTP2
28 #include <stdint.h>
29 #include <nghttp2/nghttp2.h>
30 #include "urldata.h"
31 #include "bufq.h"
32 #include "hash.h"
33 #include "http1.h"
34 #include "http2.h"
35 #include "http.h"
36 #include "sendf.h"
37 #include "select.h"
38 #include "curl_base64.h"
39 #include "strcase.h"
40 #include "multiif.h"
41 #include "url.h"
42 #include "urlapi-int.h"
43 #include "cfilters.h"
44 #include "connect.h"
45 #include "rand.h"
46 #include "strtoofft.h"
47 #include "strdup.h"
48 #include "transfer.h"
49 #include "dynbuf.h"
50 #include "headers.h"
51 /* The last 3 #include files should be in this order */
52 #include "curl_printf.h"
53 #include "curl_memory.h"
54 #include "memdebug.h"
55
56 #if (NGHTTP2_VERSION_NUM < 0x010c00)
57 #error too old nghttp2 version, upgrade!
58 #endif
59
60 #ifdef CURL_DISABLE_VERBOSE_STRINGS
61 #define nghttp2_session_callbacks_set_error_callback(x,y)
62 #endif
63
64 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
65 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
66 #endif
67
68
69 /* buffer dimensioning:
70 * use 16K as chunk size, as that fits H2 DATA frames well */
71 #define H2_CHUNK_SIZE (16 * 1024)
72 /* connection window size */
73 #define H2_CONN_WINDOW_SIZE (10 * 1024 * 1024)
74 /* on receiving from TLS, we prep for holding a full stream window */
75 #define H2_NW_RECV_CHUNKS (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE)
76 /* on send into TLS, we just want to accumulate small frames */
77 #define H2_NW_SEND_CHUNKS 1
78 /* this is how much we want "in flight" for a stream, unthrottled */
79 #define H2_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024)
80 /* this is how much we want "in flight" for a stream, initially, IFF
81 * nghttp2 allows us to tweak the local window size. */
82 #if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
83 #define H2_STREAM_WINDOW_SIZE_INITIAL (64 * 1024)
84 #else
85 #define H2_STREAM_WINDOW_SIZE_INITIAL H2_STREAM_WINDOW_SIZE_MAX
86 #endif
87 /* keep smaller stream upload buffer (default h2 window size) to have
88 * our progress bars and "upload done" reporting closer to reality */
89 #define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE)
90 /* spare chunks we keep for a full window */
91 #define H2_STREAM_POOL_SPARES (H2_CONN_WINDOW_SIZE / H2_CHUNK_SIZE)
92
93 /* We need to accommodate the max number of streams with their window sizes on
94 * the overall connection. Streams might become PAUSED which will block their
95 * received QUOTA in the connection window. If we run out of space, the server
96 * is blocked from sending us any data. See #10988 for an issue with this. */
97 #define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE_MAX)
98
99 #define H2_SETTINGS_IV_LEN 3
100 #define H2_BINSETTINGS_LEN 80
101
populate_settings(nghttp2_settings_entry * iv,struct Curl_easy * data)102 static size_t populate_settings(nghttp2_settings_entry *iv,
103 struct Curl_easy *data)
104 {
105 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
106 iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
107
108 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
109 iv[1].value = H2_STREAM_WINDOW_SIZE_INITIAL;
110
111 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
112 iv[2].value = data->multi->push_cb != NULL;
113
114 return 3;
115 }
116
populate_binsettings(uint8_t * binsettings,struct Curl_easy * data)117 static ssize_t populate_binsettings(uint8_t *binsettings,
118 struct Curl_easy *data)
119 {
120 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
121 size_t ivlen;
122
123 ivlen = populate_settings(iv, data);
124 /* this returns number of bytes it wrote or a negative number on error. */
125 return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
126 iv, ivlen);
127 }
128
129 struct cf_h2_ctx {
130 nghttp2_session *h2;
131 /* The easy handle used in the current filter call, cleared at return */
132 struct cf_call_data call_data;
133
134 struct bufq inbufq; /* network input */
135 struct bufq outbufq; /* network output */
136 struct bufc_pool stream_bufcp; /* spares for stream buffers */
137 struct dynbuf scratch; /* scratch buffer for temp use */
138
139 struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
140 size_t drain_total; /* sum of all stream's UrlState drain */
141 uint32_t max_concurrent_streams;
142 uint32_t goaway_error; /* goaway error code from server */
143 int32_t remote_max_sid; /* max id processed by server */
144 int32_t local_max_sid; /* max id processed by us */
145 BIT(initialized);
146 BIT(via_h1_upgrade);
147 BIT(conn_closed);
148 BIT(rcvd_goaway);
149 BIT(sent_goaway);
150 BIT(enable_push);
151 BIT(nw_out_blocked);
152 };
153
154 /* How to access `call_data` from a cf_h2 filter */
155 #undef CF_CTX_CALL_DATA
156 #define CF_CTX_CALL_DATA(cf) \
157 ((struct cf_h2_ctx *)(cf)->ctx)->call_data
158
159 static void h2_stream_hash_free(void *stream);
160
cf_h2_ctx_init(struct cf_h2_ctx * ctx,bool via_h1_upgrade)161 static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
162 {
163 Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
164 Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
165 Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
166 Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
167 Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
168 ctx->remote_max_sid = 2147483647;
169 ctx->via_h1_upgrade = via_h1_upgrade;
170 ctx->initialized = TRUE;
171 }
172
cf_h2_ctx_free(struct cf_h2_ctx * ctx)173 static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
174 {
175 if(ctx && ctx->initialized) {
176 Curl_bufq_free(&ctx->inbufq);
177 Curl_bufq_free(&ctx->outbufq);
178 Curl_bufcp_free(&ctx->stream_bufcp);
179 Curl_dyn_free(&ctx->scratch);
180 Curl_hash_clean(&ctx->streams);
181 Curl_hash_destroy(&ctx->streams);
182 memset(ctx, 0, sizeof(*ctx));
183 }
184 free(ctx);
185 }
186
cf_h2_ctx_close(struct cf_h2_ctx * ctx)187 static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
188 {
189 if(ctx->h2) {
190 nghttp2_session_del(ctx->h2);
191 }
192 }
193
194 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
195 struct Curl_easy *data);
196
197 /**
198 * All about the H2 internals of a stream
199 */
200 struct h2_stream_ctx {
201 struct bufq sendbuf; /* request buffer */
202 struct h1_req_parser h1; /* parsing the request */
203 struct dynhds resp_trailers; /* response trailer fields */
204 size_t resp_hds_len; /* amount of response header bytes in recvbuf */
205 curl_off_t nrcvd_data; /* number of DATA bytes received */
206
207 char **push_headers; /* allocated array */
208 size_t push_headers_used; /* number of entries filled in */
209 size_t push_headers_alloc; /* number of entries allocated */
210
211 int status_code; /* HTTP response status code */
212 uint32_t error; /* stream error code */
213 CURLcode xfer_result; /* Result of writing out response */
214 int32_t local_window_size; /* the local recv window size */
215 int32_t id; /* HTTP/2 protocol identifier for stream */
216 BIT(resp_hds_complete); /* we have a complete, final response */
217 BIT(closed); /* TRUE on stream close */
218 BIT(reset); /* TRUE on stream reset */
219 BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
220 BIT(bodystarted);
221 BIT(body_eos); /* the complete body has been added to `sendbuf` and
222 * is being/has been processed from there. */
223 };
224
225 #define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
226 data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
227
h2_stream_ctx_create(struct cf_h2_ctx * ctx)228 static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
229 {
230 struct h2_stream_ctx *stream;
231
232 (void)ctx;
233 stream = calloc(1, sizeof(*stream));
234 if(!stream)
235 return NULL;
236
237 stream->id = -1;
238 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
239 H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
240 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
241 Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
242 stream->bodystarted = FALSE;
243 stream->status_code = -1;
244 stream->closed = FALSE;
245 stream->close_handled = FALSE;
246 stream->error = NGHTTP2_NO_ERROR;
247 stream->local_window_size = H2_STREAM_WINDOW_SIZE_INITIAL;
248 stream->nrcvd_data = 0;
249 return stream;
250 }
251
free_push_headers(struct h2_stream_ctx * stream)252 static void free_push_headers(struct h2_stream_ctx *stream)
253 {
254 size_t i;
255 for(i = 0; i < stream->push_headers_used; i++)
256 free(stream->push_headers[i]);
257 Curl_safefree(stream->push_headers);
258 stream->push_headers_used = 0;
259 }
260
h2_stream_ctx_free(struct h2_stream_ctx * stream)261 static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
262 {
263 Curl_bufq_free(&stream->sendbuf);
264 Curl_h1_req_parse_free(&stream->h1);
265 Curl_dynhds_free(&stream->resp_trailers);
266 free_push_headers(stream);
267 free(stream);
268 }
269
h2_stream_hash_free(void * stream)270 static void h2_stream_hash_free(void *stream)
271 {
272 DEBUGASSERT(stream);
273 h2_stream_ctx_free((struct h2_stream_ctx *)stream);
274 }
275
276 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
cf_h2_get_desired_local_win(struct Curl_cfilter * cf,struct Curl_easy * data)277 static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
278 struct Curl_easy *data)
279 {
280 (void)cf;
281 if(data->set.max_recv_speed && data->set.max_recv_speed < INT32_MAX) {
282 /* The transfer should only receive `max_recv_speed` bytes per second.
283 * We restrict the stream's local window size, so that the server cannot
284 * send us "too much" at a time.
285 * This gets less precise the higher the latency. */
286 return (int32_t)data->set.max_recv_speed;
287 }
288 return H2_STREAM_WINDOW_SIZE_MAX;
289 }
290
cf_h2_update_local_win(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,bool paused)291 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
292 struct Curl_easy *data,
293 struct h2_stream_ctx *stream,
294 bool paused)
295 {
296 struct cf_h2_ctx *ctx = cf->ctx;
297 int32_t dwsize;
298 int rv;
299
300 dwsize = paused ? 0 : cf_h2_get_desired_local_win(cf, data);
301 if(dwsize != stream->local_window_size) {
302 int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
303 ctx->h2, stream->id);
304 if(dwsize > wsize) {
305 rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
306 stream->id, dwsize - wsize);
307 if(rv) {
308 failf(data, "[%d] nghttp2_submit_window_update() failed: "
309 "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
310 return CURLE_HTTP2;
311 }
312 stream->local_window_size = dwsize;
313 CURL_TRC_CF(data, cf, "[%d] local window update by %d",
314 stream->id, dwsize - wsize);
315 }
316 else {
317 rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
318 stream->id, dwsize);
319 if(rv) {
320 failf(data, "[%d] nghttp2_session_set_local_window_size() failed: "
321 "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
322 return CURLE_HTTP2;
323 }
324 stream->local_window_size = dwsize;
325 CURL_TRC_CF(data, cf, "[%d] local window size now %d",
326 stream->id, dwsize);
327 }
328 }
329 return CURLE_OK;
330 }
331
332 #else /* NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
333
cf_h2_update_local_win(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,bool paused)334 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
335 struct Curl_easy *data,
336 struct h2_stream_ctx *stream,
337 bool paused)
338 {
339 (void)cf;
340 (void)data;
341 (void)stream;
342 (void)paused;
343 return CURLE_OK;
344 }
345 #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
346
347 /*
348 * Mark this transfer to get "drained".
349 */
drain_stream(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream)350 static void drain_stream(struct Curl_cfilter *cf,
351 struct Curl_easy *data,
352 struct h2_stream_ctx *stream)
353 {
354 unsigned char bits;
355
356 (void)cf;
357 bits = CURL_CSELECT_IN;
358 if(!stream->closed &&
359 (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf)))
360 bits |= CURL_CSELECT_OUT;
361 if(stream->closed || (data->state.select_bits != bits)) {
362 CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
363 stream->id, bits);
364 data->state.select_bits = bits;
365 Curl_expire(data, 0, EXPIRE_RUN_NOW);
366 }
367 }
368
http2_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx ** pstream)369 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
370 struct Curl_easy *data,
371 struct h2_stream_ctx **pstream)
372 {
373 struct cf_h2_ctx *ctx = cf->ctx;
374 struct h2_stream_ctx *stream;
375
376 (void)cf;
377 DEBUGASSERT(data);
378 stream = H2_STREAM_CTX(ctx, data);
379 if(stream) {
380 *pstream = stream;
381 return CURLE_OK;
382 }
383
384 stream = h2_stream_ctx_create(ctx);
385 if(!stream)
386 return CURLE_OUT_OF_MEMORY;
387
388 if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
389 h2_stream_ctx_free(stream);
390 return CURLE_OUT_OF_MEMORY;
391 }
392
393 *pstream = stream;
394 return CURLE_OK;
395 }
396
http2_data_done(struct Curl_cfilter * cf,struct Curl_easy * data)397 static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
398 {
399 struct cf_h2_ctx *ctx = cf->ctx;
400 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
401
402 DEBUGASSERT(ctx);
403 if(!stream || !ctx->initialized)
404 return;
405
406 if(ctx->h2) {
407 bool flush_egress = FALSE;
408 /* returns error if stream not known, which is fine here */
409 (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
410
411 if(!stream->closed && stream->id > 0) {
412 /* RST_STREAM */
413 CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
414 stream->id);
415 stream->closed = TRUE;
416 stream->reset = TRUE;
417 nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
418 stream->id, NGHTTP2_STREAM_CLOSED);
419 flush_egress = TRUE;
420 }
421
422 if(flush_egress)
423 nghttp2_session_send(ctx->h2);
424 }
425
426 Curl_hash_offt_remove(&ctx->streams, data->mid);
427 }
428
h2_client_new(struct Curl_cfilter * cf,nghttp2_session_callbacks * cbs)429 static int h2_client_new(struct Curl_cfilter *cf,
430 nghttp2_session_callbacks *cbs)
431 {
432 struct cf_h2_ctx *ctx = cf->ctx;
433 nghttp2_option *o;
434 nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free,
435 Curl_nghttp2_calloc, Curl_nghttp2_realloc};
436
437 int rc = nghttp2_option_new(&o);
438 if(rc)
439 return rc;
440 /* We handle window updates ourself to enforce buffer limits */
441 nghttp2_option_set_no_auto_window_update(o, 1);
442 #if NGHTTP2_VERSION_NUM >= 0x013200
443 /* with 1.50.0 */
444 /* turn off RFC 9113 leading and trailing white spaces validation against
445 HTTP field value. */
446 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
447 #endif
448 rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem);
449 nghttp2_option_del(o);
450 return rc;
451 }
452
nw_in_reader(void * reader_ctx,unsigned char * buf,size_t buflen,CURLcode * err)453 static ssize_t nw_in_reader(void *reader_ctx,
454 unsigned char *buf, size_t buflen,
455 CURLcode *err)
456 {
457 struct Curl_cfilter *cf = reader_ctx;
458 struct Curl_easy *data = CF_DATA_CURRENT(cf);
459
460 return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
461 }
462
nw_out_writer(void * writer_ctx,const unsigned char * buf,size_t buflen,CURLcode * err)463 static ssize_t nw_out_writer(void *writer_ctx,
464 const unsigned char *buf, size_t buflen,
465 CURLcode *err)
466 {
467 struct Curl_cfilter *cf = writer_ctx;
468 struct Curl_easy *data = CF_DATA_CURRENT(cf);
469
470 if(data) {
471 ssize_t nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf,
472 buflen, FALSE, err);
473 if(nwritten > 0)
474 CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
475 return nwritten;
476 }
477 return 0;
478 }
479
480 static ssize_t send_callback(nghttp2_session *h2,
481 const uint8_t *mem, size_t length, int flags,
482 void *userp);
483 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
484 void *userp);
485 #ifndef CURL_DISABLE_VERBOSE_STRINGS
486 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
487 void *userp);
488 #endif
489 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
490 int32_t stream_id,
491 const uint8_t *mem, size_t len, void *userp);
492 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
493 uint32_t error_code, void *userp);
494 static int on_begin_headers(nghttp2_session *session,
495 const nghttp2_frame *frame, void *userp);
496 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
497 const uint8_t *name, size_t namelen,
498 const uint8_t *value, size_t valuelen,
499 uint8_t flags,
500 void *userp);
501 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
502 static int error_callback(nghttp2_session *session, const char *msg,
503 size_t len, void *userp);
504 #endif
cf_h2_ctx_open(struct Curl_cfilter * cf,struct Curl_easy * data)505 static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
506 struct Curl_easy *data)
507 {
508 struct cf_h2_ctx *ctx = cf->ctx;
509 struct h2_stream_ctx *stream;
510 CURLcode result = CURLE_OUT_OF_MEMORY;
511 int rc;
512 nghttp2_session_callbacks *cbs = NULL;
513
514 DEBUGASSERT(!ctx->h2);
515 DEBUGASSERT(ctx->initialized);
516
517 rc = nghttp2_session_callbacks_new(&cbs);
518 if(rc) {
519 failf(data, "Couldn't initialize nghttp2 callbacks");
520 goto out;
521 }
522
523 nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
524 nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
525 #ifndef CURL_DISABLE_VERBOSE_STRINGS
526 nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
527 #endif
528 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
529 cbs, on_data_chunk_recv);
530 nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
531 nghttp2_session_callbacks_set_on_begin_headers_callback(
532 cbs, on_begin_headers);
533 nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
534 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
535 nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
536 #endif
537
538 /* The nghttp2 session is not yet setup, do it */
539 rc = h2_client_new(cf, cbs);
540 if(rc) {
541 failf(data, "Couldn't initialize nghttp2");
542 goto out;
543 }
544 ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
545
546 if(ctx->via_h1_upgrade) {
547 /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
548 * in the H1 request and we upgrade from there. This stream
549 * is opened implicitly as #1. */
550 uint8_t binsettings[H2_BINSETTINGS_LEN];
551 ssize_t binlen; /* length of the binsettings data */
552
553 binlen = populate_binsettings(binsettings, data);
554 if(binlen <= 0) {
555 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
556 result = CURLE_FAILED_INIT;
557 goto out;
558 }
559
560 result = http2_data_setup(cf, data, &stream);
561 if(result)
562 goto out;
563 DEBUGASSERT(stream);
564 stream->id = 1;
565 /* queue SETTINGS frame (again) */
566 rc = nghttp2_session_upgrade2(ctx->h2, binsettings, (size_t)binlen,
567 data->state.httpreq == HTTPREQ_HEAD,
568 NULL);
569 if(rc) {
570 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
571 nghttp2_strerror(rc), rc);
572 result = CURLE_HTTP2;
573 goto out;
574 }
575
576 rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
577 data);
578 if(rc) {
579 infof(data, "http/2: failed to set user_data for stream %u",
580 stream->id);
581 DEBUGASSERT(0);
582 }
583 CURL_TRC_CF(data, cf, "created session via Upgrade");
584 }
585 else {
586 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
587 size_t ivlen;
588
589 ivlen = populate_settings(iv, data);
590 rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
591 iv, ivlen);
592 if(rc) {
593 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
594 nghttp2_strerror(rc), rc);
595 result = CURLE_HTTP2;
596 goto out;
597 }
598 }
599
600 rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
601 HTTP2_HUGE_WINDOW_SIZE);
602 if(rc) {
603 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
604 nghttp2_strerror(rc), rc);
605 result = CURLE_HTTP2;
606 goto out;
607 }
608
609 /* all set, traffic will be send on connect */
610 result = CURLE_OK;
611 CURL_TRC_CF(data, cf, "[0] created h2 session%s",
612 ctx->via_h1_upgrade ? " (via h1 upgrade)" : "");
613
614 out:
615 if(cbs)
616 nghttp2_session_callbacks_del(cbs);
617 return result;
618 }
619
620 /*
621 * Returns nonzero if current HTTP/2 session should be closed.
622 */
should_close_session(struct cf_h2_ctx * ctx)623 static int should_close_session(struct cf_h2_ctx *ctx)
624 {
625 return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) &&
626 !nghttp2_session_want_write(ctx->h2);
627 }
628
629 /*
630 * Processes pending input left in network input buffer.
631 * This function returns 0 if it succeeds, or -1 and error code will
632 * be assigned to *err.
633 */
h2_process_pending_input(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)634 static int h2_process_pending_input(struct Curl_cfilter *cf,
635 struct Curl_easy *data,
636 CURLcode *err)
637 {
638 struct cf_h2_ctx *ctx = cf->ctx;
639 const unsigned char *buf;
640 size_t blen;
641 ssize_t rv;
642
643 while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) {
644
645 rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
646 if(rv < 0) {
647 failf(data,
648 "process_pending_input: nghttp2_session_mem_recv() returned "
649 "%zd:%s", rv, nghttp2_strerror((int)rv));
650 *err = CURLE_RECV_ERROR;
651 return -1;
652 }
653 Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
654 if(Curl_bufq_is_empty(&ctx->inbufq)) {
655 break;
656 }
657 else {
658 CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left "
659 "in connection buffer", Curl_bufq_len(&ctx->inbufq));
660 }
661 }
662
663 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
664 /* No more requests are allowed in the current session, so
665 the connection may not be reused. This is set when a
666 GOAWAY frame has been received or when the limit of stream
667 identifiers has been reached. */
668 connclose(cf->conn, "http/2: No new requests allowed");
669 }
670
671 return 0;
672 }
673
674 /*
675 * The server may send us data at any point (e.g. PING frames). Therefore,
676 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
677 *
678 * Check the lower filters first and, if successful, peek at the socket
679 * and distinguish between closed and data.
680 */
http2_connisalive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)681 static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
682 bool *input_pending)
683 {
684 struct cf_h2_ctx *ctx = cf->ctx;
685 bool alive = TRUE;
686
687 *input_pending = FALSE;
688 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
689 return FALSE;
690
691 if(*input_pending) {
692 /* This happens before we have sent off a request and the connection is
693 not in use by any other transfer, there should not be any data here,
694 only "protocol frames" */
695 CURLcode result;
696 ssize_t nread = -1;
697
698 *input_pending = FALSE;
699 nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
700 if(nread != -1) {
701 CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying "
702 "h2 connection", nread);
703 if(h2_process_pending_input(cf, data, &result) < 0)
704 /* immediate error, considered dead */
705 alive = FALSE;
706 else {
707 alive = !should_close_session(ctx);
708 }
709 }
710 else if(result != CURLE_AGAIN) {
711 /* the read failed so let's say this is dead anyway */
712 alive = FALSE;
713 }
714 }
715
716 return alive;
717 }
718
http2_send_ping(struct Curl_cfilter * cf,struct Curl_easy * data)719 static CURLcode http2_send_ping(struct Curl_cfilter *cf,
720 struct Curl_easy *data)
721 {
722 struct cf_h2_ctx *ctx = cf->ctx;
723 int rc;
724
725 rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL);
726 if(rc) {
727 failf(data, "nghttp2_submit_ping() failed: %s(%d)",
728 nghttp2_strerror(rc), rc);
729 return CURLE_HTTP2;
730 }
731
732 rc = nghttp2_session_send(ctx->h2);
733 if(rc) {
734 failf(data, "nghttp2_session_send() failed: %s(%d)",
735 nghttp2_strerror(rc), rc);
736 return CURLE_SEND_ERROR;
737 }
738 return CURLE_OK;
739 }
740
741 /*
742 * Store nghttp2 version info in this buffer.
743 */
Curl_http2_ver(char * p,size_t len)744 void Curl_http2_ver(char *p, size_t len)
745 {
746 nghttp2_info *h2 = nghttp2_version(0);
747 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
748 }
749
nw_out_flush(struct Curl_cfilter * cf,struct Curl_easy * data)750 static CURLcode nw_out_flush(struct Curl_cfilter *cf,
751 struct Curl_easy *data)
752 {
753 struct cf_h2_ctx *ctx = cf->ctx;
754 ssize_t nwritten;
755 CURLcode result;
756
757 (void)data;
758 if(Curl_bufq_is_empty(&ctx->outbufq))
759 return CURLE_OK;
760
761 nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
762 if(nwritten < 0) {
763 if(result == CURLE_AGAIN) {
764 CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
765 Curl_bufq_len(&ctx->outbufq));
766 ctx->nw_out_blocked = 1;
767 }
768 return result;
769 }
770 return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN;
771 }
772
773 /*
774 * The implementation of nghttp2_send_callback type. Here we write |data| with
775 * size |length| to the network and return the number of bytes actually
776 * written. See the documentation of nghttp2_send_callback for the details.
777 */
send_callback(nghttp2_session * h2,const uint8_t * buf,size_t blen,int flags,void * userp)778 static ssize_t send_callback(nghttp2_session *h2,
779 const uint8_t *buf, size_t blen, int flags,
780 void *userp)
781 {
782 struct Curl_cfilter *cf = userp;
783 struct cf_h2_ctx *ctx = cf->ctx;
784 struct Curl_easy *data = CF_DATA_CURRENT(cf);
785 ssize_t nwritten;
786 CURLcode result = CURLE_OK;
787
788 (void)h2;
789 (void)flags;
790 DEBUGASSERT(data);
791
792 if(!cf->connected)
793 nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result);
794 else
795 nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
796 nw_out_writer, cf, &result);
797 if(nwritten < 0) {
798 if(result == CURLE_AGAIN) {
799 ctx->nw_out_blocked = 1;
800 return NGHTTP2_ERR_WOULDBLOCK;
801 }
802 failf(data, "Failed sending HTTP2 data");
803 return NGHTTP2_ERR_CALLBACK_FAILURE;
804 }
805
806 if(!nwritten) {
807 ctx->nw_out_blocked = 1;
808 return NGHTTP2_ERR_WOULDBLOCK;
809 }
810 return nwritten;
811 }
812
813
814 /* We pass a pointer to this struct in the push callback, but the contents of
815 the struct are hidden from the user. */
816 struct curl_pushheaders {
817 struct Curl_easy *data;
818 struct h2_stream_ctx *stream;
819 const nghttp2_push_promise *frame;
820 };
821
822 /*
823 * push header access function. Only to be used from within the push callback
824 */
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)825 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
826 {
827 /* Verify that we got a good easy handle in the push header struct, mostly to
828 detect rubbish input fast(er). */
829 if(!h || !GOOD_EASY_HANDLE(h->data))
830 return NULL;
831 else {
832 if(h->stream && num < h->stream->push_headers_used)
833 return h->stream->push_headers[num];
834 }
835 return NULL;
836 }
837
838 /*
839 * push header access function. Only to be used from within the push callback
840 */
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)841 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
842 {
843 struct h2_stream_ctx *stream;
844 size_t len;
845 size_t i;
846 /* Verify that we got a good easy handle in the push header struct,
847 mostly to detect rubbish input fast(er). Also empty header name
848 is just a rubbish too. We have to allow ":" at the beginning of
849 the header, but header == ":" must be rejected. If we have ':' in
850 the middle of header, it could be matched in middle of the value,
851 this is because we do prefix match.*/
852 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
853 !strcmp(header, ":") || strchr(header + 1, ':'))
854 return NULL;
855
856 stream = h->stream;
857 if(!stream)
858 return NULL;
859
860 len = strlen(header);
861 for(i = 0; i < stream->push_headers_used; i++) {
862 if(!strncmp(header, stream->push_headers[i], len)) {
863 /* sub-match, make sure that it is followed by a colon */
864 if(stream->push_headers[i][len] != ':')
865 continue;
866 return &stream->push_headers[i][len + 1];
867 }
868 }
869 return NULL;
870 }
871
h2_duphandle(struct Curl_cfilter * cf,struct Curl_easy * data)872 static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
873 struct Curl_easy *data)
874 {
875 struct Curl_easy *second = curl_easy_duphandle(data);
876 if(second) {
877 struct h2_stream_ctx *second_stream;
878 http2_data_setup(cf, second, &second_stream);
879 second->state.priority.weight = data->state.priority.weight;
880 }
881 return second;
882 }
883
set_transfer_url(struct Curl_easy * data,struct curl_pushheaders * hp)884 static int set_transfer_url(struct Curl_easy *data,
885 struct curl_pushheaders *hp)
886 {
887 const char *v;
888 CURLUcode uc;
889 char *url = NULL;
890 int rc = 0;
891 CURLU *u = curl_url();
892
893 if(!u)
894 return 5;
895
896 v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
897 if(v) {
898 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
899 if(uc) {
900 rc = 1;
901 goto fail;
902 }
903 }
904
905 v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
906 if(v) {
907 uc = Curl_url_set_authority(u, v);
908 if(uc) {
909 rc = 2;
910 goto fail;
911 }
912 }
913
914 v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH);
915 if(v) {
916 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
917 if(uc) {
918 rc = 3;
919 goto fail;
920 }
921 }
922
923 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
924 if(uc)
925 rc = 4;
926 fail:
927 curl_url_cleanup(u);
928 if(rc)
929 return rc;
930
931 if(data->state.url_alloc)
932 free(data->state.url);
933 data->state.url_alloc = TRUE;
934 data->state.url = url;
935 return 0;
936 }
937
discard_newhandle(struct Curl_cfilter * cf,struct Curl_easy * newhandle)938 static void discard_newhandle(struct Curl_cfilter *cf,
939 struct Curl_easy *newhandle)
940 {
941 http2_data_done(cf, newhandle);
942 (void)Curl_close(&newhandle);
943 }
944
push_promise(struct Curl_cfilter * cf,struct Curl_easy * data,const nghttp2_push_promise * frame)945 static int push_promise(struct Curl_cfilter *cf,
946 struct Curl_easy *data,
947 const nghttp2_push_promise *frame)
948 {
949 struct cf_h2_ctx *ctx = cf->ctx;
950 int rv; /* one of the CURL_PUSH_* defines */
951
952 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
953 frame->promised_stream_id);
954 if(data->multi->push_cb) {
955 struct h2_stream_ctx *stream;
956 struct h2_stream_ctx *newstream;
957 struct curl_pushheaders heads;
958 CURLMcode rc;
959 CURLcode result;
960 /* clone the parent */
961 struct Curl_easy *newhandle = h2_duphandle(cf, data);
962 if(!newhandle) {
963 infof(data, "failed to duplicate handle");
964 rv = CURL_PUSH_DENY; /* FAIL HARD */
965 goto fail;
966 }
967
968 /* ask the application */
969 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
970
971 stream = H2_STREAM_CTX(ctx, data);
972 if(!stream) {
973 failf(data, "Internal NULL stream");
974 discard_newhandle(cf, newhandle);
975 rv = CURL_PUSH_DENY;
976 goto fail;
977 }
978
979 heads.data = data;
980 heads.stream = stream;
981 heads.frame = frame;
982
983 rv = set_transfer_url(newhandle, &heads);
984 if(rv) {
985 discard_newhandle(cf, newhandle);
986 rv = CURL_PUSH_DENY;
987 goto fail;
988 }
989
990 result = http2_data_setup(cf, newhandle, &newstream);
991 if(result) {
992 failf(data, "error setting up stream: %d", result);
993 discard_newhandle(cf, newhandle);
994 rv = CURL_PUSH_DENY;
995 goto fail;
996 }
997 DEBUGASSERT(stream);
998
999 Curl_set_in_callback(data, TRUE);
1000 rv = data->multi->push_cb(data, newhandle,
1001 stream->push_headers_used, &heads,
1002 data->multi->push_userp);
1003 Curl_set_in_callback(data, FALSE);
1004
1005 /* free the headers again */
1006 free_push_headers(stream);
1007
1008 if(rv) {
1009 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
1010 /* denied, kill off the new handle again */
1011 discard_newhandle(cf, newhandle);
1012 goto fail;
1013 }
1014
1015 newstream->id = frame->promised_stream_id;
1016 newhandle->req.maxdownload = -1;
1017 newhandle->req.size = -1;
1018
1019 /* approved, add to the multi handle and immediately switch to PERFORM
1020 state with the given connection !*/
1021 rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
1022 if(rc) {
1023 infof(data, "failed to add handle to multi");
1024 discard_newhandle(cf, newhandle);
1025 rv = CURL_PUSH_DENY;
1026 goto fail;
1027 }
1028
1029 rv = nghttp2_session_set_stream_user_data(ctx->h2,
1030 newstream->id,
1031 newhandle);
1032 if(rv) {
1033 infof(data, "failed to set user_data for stream %u",
1034 newstream->id);
1035 DEBUGASSERT(0);
1036 rv = CURL_PUSH_DENY;
1037 goto fail;
1038 }
1039
1040 /* success, remember max stream id processed */
1041 if(newstream->id > ctx->local_max_sid)
1042 ctx->local_max_sid = newstream->id;
1043 }
1044 else {
1045 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
1046 rv = CURL_PUSH_DENY;
1047 }
1048 fail:
1049 return rv;
1050 }
1051
h2_xfer_write_resp_hd(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,const char * buf,size_t blen,bool eos)1052 static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
1053 struct Curl_easy *data,
1054 struct h2_stream_ctx *stream,
1055 const char *buf, size_t blen, bool eos)
1056 {
1057
1058 /* If we already encountered an error, skip further writes */
1059 if(!stream->xfer_result) {
1060 stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
1061 if(!stream->xfer_result && !eos)
1062 stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
1063 if(stream->xfer_result)
1064 CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
1065 stream->id, stream->xfer_result, blen);
1066 }
1067 }
1068
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)1069 static void h2_xfer_write_resp(struct Curl_cfilter *cf,
1070 struct Curl_easy *data,
1071 struct h2_stream_ctx *stream,
1072 const char *buf, size_t blen, bool eos)
1073 {
1074
1075 /* If we already encountered an error, skip further writes */
1076 if(!stream->xfer_result)
1077 stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
1078 if(!stream->xfer_result && !eos)
1079 stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
1080 /* If the transfer write is errored, we do not want any more data */
1081 if(stream->xfer_result) {
1082 struct cf_h2_ctx *ctx = cf->ctx;
1083 CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, "
1084 "RST-ing stream",
1085 stream->id, stream->xfer_result, blen);
1086 nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
1087 (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE);
1088 }
1089 }
1090
on_stream_frame(struct Curl_cfilter * cf,struct Curl_easy * data,const nghttp2_frame * frame)1091 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
1092 struct Curl_easy *data,
1093 const nghttp2_frame *frame)
1094 {
1095 struct cf_h2_ctx *ctx = cf->ctx;
1096 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
1097 int32_t stream_id = frame->hd.stream_id;
1098 int rv;
1099
1100 if(!stream) {
1101 CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id);
1102 return CURLE_FAILED_INIT;
1103 }
1104
1105 switch(frame->hd.type) {
1106 case NGHTTP2_DATA:
1107 CURL_TRC_CF(data, cf, "[%d] DATA, window=%d/%d",
1108 stream_id,
1109 nghttp2_session_get_stream_effective_recv_data_length(
1110 ctx->h2, stream->id),
1111 nghttp2_session_get_stream_effective_local_window_size(
1112 ctx->h2, stream->id));
1113 /* If !body started on this stream, then receiving DATA is illegal. */
1114 if(!stream->bodystarted) {
1115 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
1116 stream_id, NGHTTP2_PROTOCOL_ERROR);
1117
1118 if(nghttp2_is_fatal(rv)) {
1119 return CURLE_RECV_ERROR;
1120 }
1121 }
1122 break;
1123 case NGHTTP2_HEADERS:
1124 if(stream->bodystarted) {
1125 /* Only valid HEADERS after body started is trailer HEADERS. We
1126 buffer them in on_header callback. */
1127 break;
1128 }
1129
1130 /* nghttp2 guarantees that :status is received, and we store it to
1131 stream->status_code. Fuzzing has proven this can still be reached
1132 without status code having been set. */
1133 if(stream->status_code == -1)
1134 return CURLE_RECV_ERROR;
1135
1136 /* Only final status code signals the end of header */
1137 if(stream->status_code / 100 != 1)
1138 stream->bodystarted = TRUE;
1139 else
1140 stream->status_code = -1;
1141
1142 h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
1143
1144 if(stream->status_code / 100 != 1) {
1145 stream->resp_hds_complete = TRUE;
1146 }
1147 drain_stream(cf, data, stream);
1148 break;
1149 case NGHTTP2_PUSH_PROMISE:
1150 rv = push_promise(cf, data, &frame->push_promise);
1151 if(rv) { /* deny! */
1152 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
1153 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
1154 frame->push_promise.promised_stream_id,
1155 NGHTTP2_CANCEL);
1156 if(nghttp2_is_fatal(rv))
1157 return CURLE_SEND_ERROR;
1158 else if(rv == CURL_PUSH_ERROROUT) {
1159 CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received",
1160 stream_id);
1161 return CURLE_RECV_ERROR;
1162 }
1163 }
1164 break;
1165 case NGHTTP2_RST_STREAM:
1166 stream->closed = TRUE;
1167 if(frame->rst_stream.error_code) {
1168 stream->reset = TRUE;
1169 }
1170 drain_stream(cf, data, stream);
1171 break;
1172 case NGHTTP2_WINDOW_UPDATE:
1173 if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
1174 /* need more data, force processing of transfer */
1175 drain_stream(cf, data, stream);
1176 }
1177 else if(!Curl_bufq_is_empty(&stream->sendbuf)) {
1178 /* resume the potentially suspended stream */
1179 rv = nghttp2_session_resume_data(ctx->h2, stream->id);
1180 if(nghttp2_is_fatal(rv))
1181 return CURLE_SEND_ERROR;
1182 }
1183 break;
1184 default:
1185 break;
1186 }
1187
1188 if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1189 if(!stream->closed && !stream->body_eos &&
1190 ((stream->status_code >= 400) || (stream->status_code < 200))) {
1191 /* The server did not give us a positive response and we are not
1192 * done uploading the request body. We need to stop doing that and
1193 * also inform the server that we aborted our side. */
1194 CURL_TRC_CF(data, cf, "[%d] EOS frame with unfinished upload and "
1195 "HTTP status %d, abort upload by RST",
1196 stream_id, stream->status_code);
1197 nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
1198 stream->id, NGHTTP2_STREAM_CLOSED);
1199 stream->closed = TRUE;
1200 }
1201 drain_stream(cf, data, stream);
1202 }
1203 return CURLE_OK;
1204 }
1205
1206 #ifndef CURL_DISABLE_VERBOSE_STRINGS
fr_print(const nghttp2_frame * frame,char * buffer,size_t blen)1207 static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
1208 {
1209 switch(frame->hd.type) {
1210 case NGHTTP2_DATA: {
1211 return msnprintf(buffer, blen,
1212 "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
1213 (int)frame->hd.length,
1214 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
1215 (int)frame->data.padlen);
1216 }
1217 case NGHTTP2_HEADERS: {
1218 return msnprintf(buffer, blen,
1219 "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
1220 (int)frame->hd.length,
1221 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1222 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1223 }
1224 case NGHTTP2_PRIORITY: {
1225 return msnprintf(buffer, blen,
1226 "FRAME[PRIORITY, len=%d, flags=%d]",
1227 (int)frame->hd.length, frame->hd.flags);
1228 }
1229 case NGHTTP2_RST_STREAM: {
1230 return msnprintf(buffer, blen,
1231 "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
1232 (int)frame->hd.length, frame->hd.flags,
1233 frame->rst_stream.error_code);
1234 }
1235 case NGHTTP2_SETTINGS: {
1236 if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
1237 return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
1238 }
1239 return msnprintf(buffer, blen,
1240 "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
1241 }
1242 case NGHTTP2_PUSH_PROMISE: {
1243 return msnprintf(buffer, blen,
1244 "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
1245 (int)frame->hd.length,
1246 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
1247 }
1248 case NGHTTP2_PING: {
1249 return msnprintf(buffer, blen,
1250 "FRAME[PING, len=%d, ack=%d]",
1251 (int)frame->hd.length,
1252 frame->hd.flags&NGHTTP2_FLAG_ACK);
1253 }
1254 case NGHTTP2_GOAWAY: {
1255 char scratch[128];
1256 size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1257 size_t len = (frame->goaway.opaque_data_len < s_len) ?
1258 frame->goaway.opaque_data_len : s_len-1;
1259 if(len)
1260 memcpy(scratch, frame->goaway.opaque_data, len);
1261 scratch[len] = '\0';
1262 return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
1263 "last_stream=%d]", frame->goaway.error_code,
1264 scratch, frame->goaway.last_stream_id);
1265 }
1266 case NGHTTP2_WINDOW_UPDATE: {
1267 return msnprintf(buffer, blen,
1268 "FRAME[WINDOW_UPDATE, incr=%d]",
1269 frame->window_update.window_size_increment);
1270 }
1271 default:
1272 return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
1273 frame->hd.type, (int)frame->hd.length,
1274 frame->hd.flags);
1275 }
1276 }
1277
on_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1278 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
1279 void *userp)
1280 {
1281 struct Curl_cfilter *cf = userp;
1282 struct Curl_easy *data = CF_DATA_CURRENT(cf);
1283
1284 (void)session;
1285 DEBUGASSERT(data);
1286 if(data && Curl_trc_cf_is_verbose(cf, data)) {
1287 char buffer[256];
1288 int len;
1289 len = fr_print(frame, buffer, sizeof(buffer)-1);
1290 buffer[len] = 0;
1291 CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
1292 }
1293 return 0;
1294 }
1295 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
1296
on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1297 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
1298 void *userp)
1299 {
1300 struct Curl_cfilter *cf = userp;
1301 struct cf_h2_ctx *ctx = cf->ctx;
1302 struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s;
1303 int32_t stream_id = frame->hd.stream_id;
1304
1305 DEBUGASSERT(data);
1306 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1307 if(Curl_trc_cf_is_verbose(cf, data)) {
1308 char buffer[256];
1309 int len;
1310 len = fr_print(frame, buffer, sizeof(buffer)-1);
1311 buffer[len] = 0;
1312 CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
1313 }
1314 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
1315
1316 if(!stream_id) {
1317 /* stream ID zero is for connection-oriented stuff */
1318 DEBUGASSERT(data);
1319 switch(frame->hd.type) {
1320 case NGHTTP2_SETTINGS: {
1321 if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
1322 uint32_t max_conn = ctx->max_concurrent_streams;
1323 ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
1324 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
1325 ctx->enable_push = nghttp2_session_get_remote_settings(
1326 session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
1327 CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d",
1328 ctx->max_concurrent_streams);
1329 CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s",
1330 ctx->enable_push ? "TRUE" : "false");
1331 if(data && max_conn != ctx->max_concurrent_streams) {
1332 /* only signal change if the value actually changed */
1333 CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u",
1334 ctx->max_concurrent_streams);
1335 Curl_multi_connchanged(data->multi);
1336 }
1337 /* Since the initial stream window is 64K, a request might be on HOLD,
1338 * due to exhaustion. The (initial) SETTINGS may announce a much larger
1339 * window and *assume* that we treat this like a WINDOW_UPDATE. Some
1340 * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
1341 * To be safe, we UNHOLD a stream in order not to stall. */
1342 if(CURL_WANT_SEND(data)) {
1343 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
1344 if(stream)
1345 drain_stream(cf, data, stream);
1346 }
1347 }
1348 break;
1349 }
1350 case NGHTTP2_GOAWAY:
1351 ctx->rcvd_goaway = TRUE;
1352 ctx->goaway_error = frame->goaway.error_code;
1353 ctx->remote_max_sid = frame->goaway.last_stream_id;
1354 if(data) {
1355 infof(data, "received GOAWAY, error=%u, last_stream=%u",
1356 ctx->goaway_error, ctx->remote_max_sid);
1357 Curl_multi_connchanged(data->multi);
1358 }
1359 break;
1360 default:
1361 break;
1362 }
1363 return 0;
1364 }
1365
1366 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1367 if(!data_s) {
1368 CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id);
1369 return 0;
1370 }
1371
1372 return on_stream_frame(cf, data_s, frame) ? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
1373 }
1374
on_data_chunk_recv(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * mem,size_t len,void * userp)1375 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
1376 int32_t stream_id,
1377 const uint8_t *mem, size_t len, void *userp)
1378 {
1379 struct Curl_cfilter *cf = userp;
1380 struct cf_h2_ctx *ctx = cf->ctx;
1381 struct h2_stream_ctx *stream;
1382 struct Curl_easy *data_s;
1383 (void)flags;
1384
1385 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1386 DEBUGASSERT(CF_DATA_CURRENT(cf));
1387
1388 /* get the stream from the hash based on Stream ID */
1389 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1390 if(!data_s) {
1391 /* Receiving a Stream ID not in the hash should not happen - unless
1392 we have aborted a transfer artificially and there were more data
1393 in the pipeline. Silently ignore. */
1394 CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown",
1395 stream_id);
1396 /* consumed explicitly as no one will read it */
1397 nghttp2_session_consume(session, stream_id, len);
1398 return 0;
1399 }
1400
1401 stream = H2_STREAM_CTX(ctx, data_s);
1402 if(!stream)
1403 return NGHTTP2_ERR_CALLBACK_FAILURE;
1404
1405 h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE);
1406
1407 nghttp2_session_consume(ctx->h2, stream_id, len);
1408 stream->nrcvd_data += (curl_off_t)len;
1409 return 0;
1410 }
1411
on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)1412 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
1413 uint32_t error_code, void *userp)
1414 {
1415 struct Curl_cfilter *cf = userp;
1416 struct cf_h2_ctx *ctx = cf->ctx;
1417 struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
1418 struct h2_stream_ctx *stream;
1419 int rv;
1420 (void)session;
1421
1422 DEBUGASSERT(call_data);
1423 /* stream id 0 is the connection, do not look there for streams. */
1424 data_s = stream_id ?
1425 nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
1426 if(!data_s) {
1427 CURL_TRC_CF(call_data, cf,
1428 "[%d] on_stream_close, no easy set on stream", stream_id);
1429 return 0;
1430 }
1431 if(!GOOD_EASY_HANDLE(data_s)) {
1432 /* nghttp2 still has an easy registered for the stream which has
1433 * been freed be libcurl. This points to a code path that does not
1434 * trigger DONE or DETACH events as it must. */
1435 CURL_TRC_CF(call_data, cf,
1436 "[%d] on_stream_close, not a GOOD easy on stream", stream_id);
1437 (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
1438 return NGHTTP2_ERR_CALLBACK_FAILURE;
1439 }
1440 stream = H2_STREAM_CTX(ctx, data_s);
1441 if(!stream) {
1442 CURL_TRC_CF(data_s, cf,
1443 "[%d] on_stream_close, GOOD easy but no stream", stream_id);
1444 return NGHTTP2_ERR_CALLBACK_FAILURE;
1445 }
1446
1447 stream->closed = TRUE;
1448 stream->error = error_code;
1449 if(stream->error) {
1450 stream->reset = TRUE;
1451 }
1452
1453 if(stream->error)
1454 CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
1455 stream_id, nghttp2_http2_strerror(error_code), error_code);
1456 else
1457 CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
1458 drain_stream(cf, data_s, stream);
1459
1460 /* remove `data_s` from the nghttp2 stream */
1461 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
1462 if(rv) {
1463 infof(data_s, "http/2: failed to clear user_data for stream %u",
1464 stream_id);
1465 DEBUGASSERT(0);
1466 }
1467 return 0;
1468 }
1469
on_begin_headers(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1470 static int on_begin_headers(nghttp2_session *session,
1471 const nghttp2_frame *frame, void *userp)
1472 {
1473 struct Curl_cfilter *cf = userp;
1474 struct cf_h2_ctx *ctx = cf->ctx;
1475 struct h2_stream_ctx *stream;
1476 struct Curl_easy *data_s = NULL;
1477
1478 (void)cf;
1479 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
1480 if(!data_s) {
1481 return 0;
1482 }
1483
1484 if(frame->hd.type != NGHTTP2_HEADERS) {
1485 return 0;
1486 }
1487
1488 stream = H2_STREAM_CTX(ctx, data_s);
1489 if(!stream || !stream->bodystarted) {
1490 return 0;
1491 }
1492
1493 return 0;
1494 }
1495
1496 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
on_header(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * userp)1497 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
1498 const uint8_t *name, size_t namelen,
1499 const uint8_t *value, size_t valuelen,
1500 uint8_t flags,
1501 void *userp)
1502 {
1503 struct Curl_cfilter *cf = userp;
1504 struct cf_h2_ctx *ctx = cf->ctx;
1505 struct h2_stream_ctx *stream;
1506 struct Curl_easy *data_s;
1507 int32_t stream_id = frame->hd.stream_id;
1508 CURLcode result;
1509 (void)flags;
1510
1511 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1512
1513 /* get the stream from the hash based on Stream ID */
1514 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1515 if(!data_s)
1516 /* Receiving a Stream ID not in the hash should not happen, this is an
1517 internal error more than anything else! */
1518 return NGHTTP2_ERR_CALLBACK_FAILURE;
1519
1520 stream = H2_STREAM_CTX(ctx, data_s);
1521 if(!stream) {
1522 failf(data_s, "Internal NULL stream");
1523 return NGHTTP2_ERR_CALLBACK_FAILURE;
1524 }
1525
1526 /* Store received PUSH_PROMISE headers to be used when the subsequent
1527 PUSH_PROMISE callback comes */
1528 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
1529 char *h;
1530
1531 if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) {
1532 /* pseudo headers are lower case */
1533 int rc = 0;
1534 char *check = aprintf("%s:%d", cf->conn->host.name,
1535 cf->conn->remote_port);
1536 if(!check)
1537 /* no memory */
1538 return NGHTTP2_ERR_CALLBACK_FAILURE;
1539 if(!strcasecompare(check, (const char *)value) &&
1540 ((cf->conn->remote_port != cf->conn->given->defport) ||
1541 !strcasecompare(cf->conn->host.name, (const char *)value))) {
1542 /* This is push is not for the same authority that was asked for in
1543 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
1544 * PUSH_PROMISE for which the server is not authoritative as a stream
1545 * error of type PROTOCOL_ERROR."
1546 */
1547 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1548 stream_id, NGHTTP2_PROTOCOL_ERROR);
1549 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
1550 }
1551 free(check);
1552 if(rc)
1553 return rc;
1554 }
1555
1556 if(!stream->push_headers) {
1557 stream->push_headers_alloc = 10;
1558 stream->push_headers = malloc(stream->push_headers_alloc *
1559 sizeof(char *));
1560 if(!stream->push_headers)
1561 return NGHTTP2_ERR_CALLBACK_FAILURE;
1562 stream->push_headers_used = 0;
1563 }
1564 else if(stream->push_headers_used ==
1565 stream->push_headers_alloc) {
1566 char **headp;
1567 if(stream->push_headers_alloc > 1000) {
1568 /* this is beyond crazy many headers, bail out */
1569 failf(data_s, "Too many PUSH_PROMISE headers");
1570 free_push_headers(stream);
1571 return NGHTTP2_ERR_CALLBACK_FAILURE;
1572 }
1573 stream->push_headers_alloc *= 2;
1574 headp = realloc(stream->push_headers,
1575 stream->push_headers_alloc * sizeof(char *));
1576 if(!headp) {
1577 free_push_headers(stream);
1578 return NGHTTP2_ERR_CALLBACK_FAILURE;
1579 }
1580 stream->push_headers = headp;
1581 }
1582 h = aprintf("%s:%s", name, value);
1583 if(h)
1584 stream->push_headers[stream->push_headers_used++] = h;
1585 return 0;
1586 }
1587
1588 if(stream->bodystarted) {
1589 /* This is a trailer */
1590 CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
1591 stream->id, (int)namelen, name, (int)valuelen, value);
1592 result = Curl_dynhds_add(&stream->resp_trailers,
1593 (const char *)name, namelen,
1594 (const char *)value, valuelen);
1595 if(result)
1596 return NGHTTP2_ERR_CALLBACK_FAILURE;
1597
1598 return 0;
1599 }
1600
1601 if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
1602 memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
1603 /* nghttp2 guarantees :status is received first and only once. */
1604 char buffer[32];
1605 result = Curl_http_decode_status(&stream->status_code,
1606 (const char *)value, valuelen);
1607 if(result)
1608 return NGHTTP2_ERR_CALLBACK_FAILURE;
1609 msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
1610 stream->status_code);
1611 result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
1612 if(result)
1613 return NGHTTP2_ERR_CALLBACK_FAILURE;
1614 Curl_dyn_reset(&ctx->scratch);
1615 result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
1616 if(!result)
1617 result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
1618 if(!result)
1619 result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
1620 if(!result)
1621 h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
1622 Curl_dyn_len(&ctx->scratch), FALSE);
1623 if(result)
1624 return NGHTTP2_ERR_CALLBACK_FAILURE;
1625 /* if we receive data for another handle, wake that up */
1626 if(CF_DATA_CURRENT(cf) != data_s)
1627 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1628
1629 CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
1630 stream->id, stream->status_code);
1631 return 0;
1632 }
1633
1634 /* nghttp2 guarantees that namelen > 0, and :status was already
1635 received, and this is not pseudo-header field . */
1636 /* convert to an HTTP1-style header */
1637 Curl_dyn_reset(&ctx->scratch);
1638 result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
1639 if(!result)
1640 result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
1641 if(!result)
1642 result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
1643 if(!result)
1644 result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
1645 if(!result)
1646 h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
1647 Curl_dyn_len(&ctx->scratch), FALSE);
1648 if(result)
1649 return NGHTTP2_ERR_CALLBACK_FAILURE;
1650 /* if we receive data for another handle, wake that up */
1651 if(CF_DATA_CURRENT(cf) != data_s)
1652 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1653
1654 CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
1655 stream->id, (int)namelen, name, (int)valuelen, value);
1656
1657 return 0; /* 0 is successful */
1658 }
1659
req_body_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * userp)1660 static ssize_t req_body_read_callback(nghttp2_session *session,
1661 int32_t stream_id,
1662 uint8_t *buf, size_t length,
1663 uint32_t *data_flags,
1664 nghttp2_data_source *source,
1665 void *userp)
1666 {
1667 struct Curl_cfilter *cf = userp;
1668 struct cf_h2_ctx *ctx = cf->ctx;
1669 struct Curl_easy *data_s;
1670 struct h2_stream_ctx *stream = NULL;
1671 CURLcode result;
1672 ssize_t nread;
1673 (void)source;
1674
1675 (void)cf;
1676 if(!stream_id)
1677 return NGHTTP2_ERR_INVALID_ARGUMENT;
1678
1679 /* get the stream from the hash based on Stream ID, stream ID zero is for
1680 connection-oriented stuff */
1681 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1682 if(!data_s)
1683 /* Receiving a Stream ID not in the hash should not happen, this is an
1684 internal error more than anything else! */
1685 return NGHTTP2_ERR_CALLBACK_FAILURE;
1686
1687 stream = H2_STREAM_CTX(ctx, data_s);
1688 if(!stream)
1689 return NGHTTP2_ERR_CALLBACK_FAILURE;
1690
1691 nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result);
1692 if(nread < 0) {
1693 if(result != CURLE_AGAIN)
1694 return NGHTTP2_ERR_CALLBACK_FAILURE;
1695 nread = 0;
1696 }
1697
1698 CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
1699 stream_id, length, stream->body_eos, nread, result);
1700
1701 if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) {
1702 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1703 return nread;
1704 }
1705 return (nread == 0) ? NGHTTP2_ERR_DEFERRED : nread;
1706 }
1707
1708 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
error_callback(nghttp2_session * session,const char * msg,size_t len,void * userp)1709 static int error_callback(nghttp2_session *session,
1710 const char *msg,
1711 size_t len,
1712 void *userp)
1713 {
1714 struct Curl_cfilter *cf = userp;
1715 struct Curl_easy *data = CF_DATA_CURRENT(cf);
1716 (void)session;
1717 failf(data, "%.*s", (int)len, msg);
1718 return 0;
1719 }
1720 #endif
1721
1722 /*
1723 * Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
1724 */
Curl_http2_request_upgrade(struct dynbuf * req,struct Curl_easy * data)1725 CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1726 struct Curl_easy *data)
1727 {
1728 CURLcode result;
1729 char *base64;
1730 size_t blen;
1731 struct SingleRequest *k = &data->req;
1732 uint8_t binsettings[H2_BINSETTINGS_LEN];
1733 ssize_t binlen; /* length of the binsettings data */
1734
1735 binlen = populate_binsettings(binsettings, data);
1736 if(binlen <= 0) {
1737 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1738 Curl_dyn_free(req);
1739 return CURLE_FAILED_INIT;
1740 }
1741
1742 result = Curl_base64url_encode((const char *)binsettings, (size_t)binlen,
1743 &base64, &blen);
1744 if(result) {
1745 Curl_dyn_free(req);
1746 return result;
1747 }
1748
1749 result = Curl_dyn_addf(req,
1750 "Connection: Upgrade, HTTP2-Settings\r\n"
1751 "Upgrade: %s\r\n"
1752 "HTTP2-Settings: %s\r\n",
1753 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1754 free(base64);
1755
1756 k->upgr101 = UPGR101_H2;
1757 data->conn->bits.asks_multiplex = TRUE;
1758
1759 return result;
1760 }
1761
http2_handle_stream_close(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,CURLcode * err)1762 static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
1763 struct Curl_easy *data,
1764 struct h2_stream_ctx *stream,
1765 CURLcode *err)
1766 {
1767 ssize_t rv = 0;
1768
1769 if(stream->error == NGHTTP2_REFUSED_STREAM) {
1770 CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
1771 "connection", stream->id);
1772 connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
1773 data->state.refused_stream = TRUE;
1774 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1775 return -1;
1776 }
1777 else if(stream->error != NGHTTP2_NO_ERROR) {
1778 if(stream->resp_hds_complete && data->req.no_body) {
1779 CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
1780 "not want a body anyway, ignore: %s (err %u)",
1781 stream->id, nghttp2_http2_strerror(stream->error),
1782 stream->error);
1783 stream->close_handled = TRUE;
1784 *err = CURLE_OK;
1785 goto out;
1786 }
1787 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1788 stream->id, nghttp2_http2_strerror(stream->error),
1789 stream->error);
1790 *err = CURLE_HTTP2_STREAM;
1791 return -1;
1792 }
1793 else if(stream->reset) {
1794 failf(data, "HTTP/2 stream %u was reset", stream->id);
1795 *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
1796 return -1;
1797 }
1798
1799 if(!stream->bodystarted) {
1800 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1801 " all response header fields, treated as error",
1802 stream->id);
1803 *err = CURLE_HTTP2_STREAM;
1804 return -1;
1805 }
1806
1807 if(Curl_dynhds_count(&stream->resp_trailers)) {
1808 struct dynhds_entry *e;
1809 struct dynbuf dbuf;
1810 size_t i;
1811
1812 *err = CURLE_OK;
1813 Curl_dyn_init(&dbuf, DYN_TRAILERS);
1814 for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
1815 e = Curl_dynhds_getn(&stream->resp_trailers, i);
1816 if(!e)
1817 break;
1818 Curl_dyn_reset(&dbuf);
1819 *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
1820 (int)e->namelen, e->name,
1821 (int)e->valuelen, e->value);
1822 if(*err)
1823 break;
1824 Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
1825 Curl_dyn_len(&dbuf));
1826 *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
1827 Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
1828 if(*err)
1829 break;
1830 }
1831 Curl_dyn_free(&dbuf);
1832 if(*err)
1833 goto out;
1834 }
1835
1836 stream->close_handled = TRUE;
1837 *err = CURLE_OK;
1838 rv = 0;
1839
1840 out:
1841 CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
1842 return rv;
1843 }
1844
sweight_wanted(const struct Curl_easy * data)1845 static int sweight_wanted(const struct Curl_easy *data)
1846 {
1847 /* 0 weight is not set by user and we take the nghttp2 default one */
1848 return data->set.priority.weight ?
1849 data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1850 }
1851
sweight_in_effect(const struct Curl_easy * data)1852 static int sweight_in_effect(const struct Curl_easy *data)
1853 {
1854 /* 0 weight is not set by user and we take the nghttp2 default one */
1855 return data->state.priority.weight ?
1856 data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1857 }
1858
1859 /*
1860 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1861 * and dependency to the peer. It also stores the updated values in the state
1862 * struct.
1863 */
1864
h2_pri_spec(struct cf_h2_ctx * ctx,struct Curl_easy * data,nghttp2_priority_spec * pri_spec)1865 static void h2_pri_spec(struct cf_h2_ctx *ctx,
1866 struct Curl_easy *data,
1867 nghttp2_priority_spec *pri_spec)
1868 {
1869 struct Curl_data_priority *prio = &data->set.priority;
1870 struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
1871 int32_t depstream_id = depstream ? depstream->id : 0;
1872 nghttp2_priority_spec_init(pri_spec, depstream_id,
1873 sweight_wanted(data),
1874 data->set.priority.exclusive);
1875 data->state.priority = *prio;
1876 }
1877
1878 /*
1879 * Check if there is been an update in the priority /
1880 * dependency settings and if so it submits a PRIORITY frame with the updated
1881 * info.
1882 * Flush any out data pending in the network buffer.
1883 */
h2_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data)1884 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
1885 struct Curl_easy *data)
1886 {
1887 struct cf_h2_ctx *ctx = cf->ctx;
1888 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
1889 int rv = 0;
1890
1891 if(stream && stream->id > 0 &&
1892 ((sweight_wanted(data) != sweight_in_effect(data)) ||
1893 (data->set.priority.exclusive != data->state.priority.exclusive) ||
1894 (data->set.priority.parent != data->state.priority.parent)) ) {
1895 /* send new weight and/or dependency */
1896 nghttp2_priority_spec pri_spec;
1897
1898 h2_pri_spec(ctx, data, &pri_spec);
1899 CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
1900 DEBUGASSERT(stream->id != -1);
1901 rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
1902 stream->id, &pri_spec);
1903 if(rv)
1904 goto out;
1905 }
1906
1907 ctx->nw_out_blocked = 0;
1908 while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
1909 rv = nghttp2_session_send(ctx->h2);
1910
1911 out:
1912 if(nghttp2_is_fatal(rv)) {
1913 CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d",
1914 nghttp2_strerror(rv), rv);
1915 return CURLE_SEND_ERROR;
1916 }
1917 /* Defer flushing during the connect phase so that the SETTINGS and
1918 * other initial frames are sent together with the first request.
1919 * Unless we are 'connect_only' where the request will never come. */
1920 if(!cf->connected && !cf->conn->connect_only)
1921 return CURLE_OK;
1922 return nw_out_flush(cf, data);
1923 }
1924
stream_recv(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,char * buf,size_t len,CURLcode * err)1925 static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1926 struct h2_stream_ctx *stream,
1927 char *buf, size_t len, CURLcode *err)
1928 {
1929 struct cf_h2_ctx *ctx = cf->ctx;
1930 ssize_t nread = -1;
1931
1932 (void)buf;
1933 *err = CURLE_AGAIN;
1934 if(stream->xfer_result) {
1935 CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
1936 *err = stream->xfer_result;
1937 nread = -1;
1938 }
1939 else if(stream->closed) {
1940 CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
1941 nread = http2_handle_stream_close(cf, data, stream, err);
1942 }
1943 else if(stream->reset ||
1944 (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
1945 (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
1946 CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
1947 *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
1948 nread = -1;
1949 }
1950
1951 if(nread < 0 && *err != CURLE_AGAIN)
1952 CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
1953 stream->id, len, nread, *err);
1954 return nread;
1955 }
1956
h2_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data,size_t data_max_bytes)1957 static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
1958 struct Curl_easy *data,
1959 size_t data_max_bytes)
1960 {
1961 struct cf_h2_ctx *ctx = cf->ctx;
1962 struct h2_stream_ctx *stream;
1963 CURLcode result = CURLE_OK;
1964 ssize_t nread;
1965
1966 /* Process network input buffer fist */
1967 if(!Curl_bufq_is_empty(&ctx->inbufq)) {
1968 CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
1969 Curl_bufq_len(&ctx->inbufq));
1970 if(h2_process_pending_input(cf, data, &result) < 0)
1971 return result;
1972 }
1973
1974 /* Receive data from the "lower" filters, e.g. network until
1975 * it is time to stop due to connection close or us not processing
1976 * all network input */
1977 while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
1978 stream = H2_STREAM_CTX(ctx, data);
1979 if(stream && (stream->closed || !data_max_bytes)) {
1980 /* We would like to abort here and stop processing, so that
1981 * the transfer loop can handle the data/close here. However,
1982 * this may leave data in underlying buffers that will not
1983 * be consumed. */
1984 if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
1985 drain_stream(cf, data, stream);
1986 break;
1987 }
1988
1989 nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result);
1990 if(nread < 0) {
1991 if(result != CURLE_AGAIN) {
1992 failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
1993 curl_easy_strerror(result));
1994 return result;
1995 }
1996 break;
1997 }
1998 else if(nread == 0) {
1999 CURL_TRC_CF(data, cf, "[0] ingress: connection closed");
2000 ctx->conn_closed = TRUE;
2001 break;
2002 }
2003 else {
2004 CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread);
2005 data_max_bytes = (data_max_bytes > (size_t)nread) ?
2006 (data_max_bytes - (size_t)nread) : 0;
2007 }
2008
2009 if(h2_process_pending_input(cf, data, &result))
2010 return result;
2011 CURL_TRC_CF(data, cf, "[0] progress ingress: inbufg=%zu",
2012 Curl_bufq_len(&ctx->inbufq));
2013 }
2014
2015 if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
2016 connclose(cf->conn, "GOAWAY received");
2017 }
2018
2019 CURL_TRC_CF(data, cf, "[0] progress ingress: done");
2020 return CURLE_OK;
2021 }
2022
cf_h2_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)2023 static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
2024 char *buf, size_t len, CURLcode *err)
2025 {
2026 struct cf_h2_ctx *ctx = cf->ctx;
2027 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2028 ssize_t nread = -1;
2029 CURLcode result;
2030 struct cf_call_data save;
2031
2032 if(!stream) {
2033 /* Abnormal call sequence: either this transfer has never opened a stream
2034 * (unlikely) or the transfer has been done, cleaned up its resources, but
2035 * a read() is called anyway. It is not clear what the calling sequence
2036 * is for such a case. */
2037 failf(data, "http/2 recv on a transfer never opened "
2038 "or already cleared, mid=%" FMT_OFF_T, data->mid);
2039 *err = CURLE_HTTP2;
2040 return -1;
2041 }
2042
2043 CF_DATA_SAVE(save, cf, data);
2044
2045 nread = stream_recv(cf, data, stream, buf, len, err);
2046 if(nread < 0 && *err != CURLE_AGAIN)
2047 goto out;
2048
2049 if(nread < 0) {
2050 *err = h2_progress_ingress(cf, data, len);
2051 if(*err)
2052 goto out;
2053
2054 nread = stream_recv(cf, data, stream, buf, len, err);
2055 }
2056
2057 if(nread > 0) {
2058 /* Now that we transferred this to the upper layer, we report
2059 * the actual amount of DATA consumed to the H2 session, so
2060 * that it adjusts stream flow control */
2061 nghttp2_session_consume(ctx->h2, stream->id, (size_t)nread);
2062 if(stream->closed) {
2063 CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
2064 drain_stream(cf, data, stream);
2065 }
2066 }
2067
2068 out:
2069 result = h2_progress_egress(cf, data);
2070 if(result == CURLE_AGAIN) {
2071 /* pending data to send, need to be called again. Ideally, we
2072 * monitor the socket for POLLOUT, but when not SENDING
2073 * any more, we force processing of the transfer. */
2074 if(!CURL_WANT_SEND(data))
2075 drain_stream(cf, data, stream);
2076 }
2077 else if(result) {
2078 *err = result;
2079 nread = -1;
2080 }
2081 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
2082 "window=%d/%d, connection %d/%d",
2083 stream->id, len, nread, *err,
2084 nghttp2_session_get_stream_effective_recv_data_length(
2085 ctx->h2, stream->id),
2086 nghttp2_session_get_stream_effective_local_window_size(
2087 ctx->h2, stream->id),
2088 nghttp2_session_get_local_window_size(ctx->h2),
2089 HTTP2_HUGE_WINDOW_SIZE);
2090
2091 CF_DATA_RESTORE(cf, save);
2092 return nread;
2093 }
2094
cf_h2_body_send(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,const void * buf,size_t blen,bool eos,CURLcode * err)2095 static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
2096 struct Curl_easy *data,
2097 struct h2_stream_ctx *stream,
2098 const void *buf, size_t blen, bool eos,
2099 CURLcode *err)
2100 {
2101 struct cf_h2_ctx *ctx = cf->ctx;
2102 ssize_t nwritten;
2103
2104 if(stream->closed) {
2105 if(stream->resp_hds_complete) {
2106 /* Server decided to close the stream after having sent us a final
2107 * response. This is valid if it is not interested in the request
2108 * body. This happens on 30x or 40x responses.
2109 * We silently discard the data sent, since this is not a transport
2110 * error situation. */
2111 CURL_TRC_CF(data, cf, "[%d] discarding data"
2112 "on closed stream with response", stream->id);
2113 if(eos)
2114 stream->body_eos = TRUE;
2115 *err = CURLE_OK;
2116 return (ssize_t)blen;
2117 }
2118 /* Server closed before we got a response, this is an error */
2119 infof(data, "stream %u closed", stream->id);
2120 *err = CURLE_SEND_ERROR;
2121 return -1;
2122 }
2123
2124 nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err);
2125 if(nwritten < 0)
2126 return -1;
2127
2128 if(eos && (blen == (size_t)nwritten))
2129 stream->body_eos = TRUE;
2130
2131 if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) {
2132 /* resume the potentially suspended stream */
2133 int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2134 if(nghttp2_is_fatal(rv)) {
2135 *err = CURLE_SEND_ERROR;
2136 return -1;
2137 }
2138 }
2139 return nwritten;
2140 }
2141
h2_submit(struct h2_stream_ctx ** pstream,struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)2142 static ssize_t h2_submit(struct h2_stream_ctx **pstream,
2143 struct Curl_cfilter *cf, struct Curl_easy *data,
2144 const void *buf, size_t len,
2145 bool eos, CURLcode *err)
2146 {
2147 struct cf_h2_ctx *ctx = cf->ctx;
2148 struct h2_stream_ctx *stream = NULL;
2149 struct dynhds h2_headers;
2150 nghttp2_nv *nva = NULL;
2151 const void *body = NULL;
2152 size_t nheader, bodylen, i;
2153 nghttp2_data_provider data_prd;
2154 int32_t stream_id;
2155 nghttp2_priority_spec pri_spec;
2156 ssize_t nwritten;
2157
2158 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
2159
2160 *err = http2_data_setup(cf, data, &stream);
2161 if(*err) {
2162 nwritten = -1;
2163 goto out;
2164 }
2165
2166 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
2167 if(nwritten < 0)
2168 goto out;
2169 if(!stream->h1.done) {
2170 /* need more data */
2171 goto out;
2172 }
2173 DEBUGASSERT(stream->h1.req);
2174
2175 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
2176 if(*err) {
2177 nwritten = -1;
2178 goto out;
2179 }
2180 /* no longer needed */
2181 Curl_h1_req_parse_free(&stream->h1);
2182
2183 nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
2184 if(!nva) {
2185 *err = CURLE_OUT_OF_MEMORY;
2186 nwritten = -1;
2187 goto out;
2188 }
2189
2190 h2_pri_spec(ctx, data, &pri_spec);
2191 if(!nghttp2_session_check_request_allowed(ctx->h2))
2192 CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
2193
2194 switch(data->state.httpreq) {
2195 case HTTPREQ_POST:
2196 case HTTPREQ_POST_FORM:
2197 case HTTPREQ_POST_MIME:
2198 case HTTPREQ_PUT:
2199 data_prd.read_callback = req_body_read_callback;
2200 data_prd.source.ptr = NULL;
2201 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2202 &data_prd, data);
2203 break;
2204 default:
2205 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2206 NULL, data);
2207 }
2208
2209 if(stream_id < 0) {
2210 CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
2211 nghttp2_strerror(stream_id), stream_id);
2212 *err = CURLE_SEND_ERROR;
2213 nwritten = -1;
2214 goto out;
2215 }
2216
2217 #define MAX_ACC 60000 /* <64KB to account for some overhead */
2218 if(Curl_trc_is_verbose(data)) {
2219 size_t acc = 0;
2220
2221 infof(data, "[HTTP/2] [%d] OPENED stream for %s",
2222 stream_id, data->state.url);
2223 for(i = 0; i < nheader; ++i) {
2224 acc += nva[i].namelen + nva[i].valuelen;
2225
2226 infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
2227 (int)nva[i].namelen, nva[i].name,
2228 (int)nva[i].valuelen, nva[i].value);
2229 }
2230
2231 if(acc > MAX_ACC) {
2232 infof(data, "[HTTP/2] Warning: The cumulative length of all "
2233 "headers exceeds %d bytes and that could cause the "
2234 "stream to be rejected.", MAX_ACC);
2235 }
2236 }
2237
2238 stream->id = stream_id;
2239
2240 body = (const char *)buf + nwritten;
2241 bodylen = len - nwritten;
2242
2243 if(bodylen || eos) {
2244 ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err);
2245 if(n >= 0)
2246 nwritten += n;
2247 else if(*err == CURLE_AGAIN)
2248 *err = CURLE_OK;
2249 else if(*err != CURLE_AGAIN) {
2250 *err = CURLE_SEND_ERROR;
2251 nwritten = -1;
2252 goto out;
2253 }
2254 }
2255
2256 out:
2257 CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
2258 stream ? stream->id : -1, nwritten, *err);
2259 Curl_safefree(nva);
2260 *pstream = stream;
2261 Curl_dynhds_free(&h2_headers);
2262 return nwritten;
2263 }
2264
cf_h2_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)2265 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
2266 const void *buf, size_t len, bool eos,
2267 CURLcode *err)
2268 {
2269 struct cf_h2_ctx *ctx = cf->ctx;
2270 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2271 struct cf_call_data save;
2272 ssize_t nwritten;
2273 CURLcode result;
2274
2275 CF_DATA_SAVE(save, cf, data);
2276
2277 if(!stream || stream->id == -1) {
2278 nwritten = h2_submit(&stream, cf, data, buf, len, eos, err);
2279 if(nwritten < 0) {
2280 goto out;
2281 }
2282 DEBUGASSERT(stream);
2283 }
2284 else if(stream->body_eos) {
2285 /* We already wrote this, but CURLE_AGAINed the call due to not
2286 * being able to flush stream->sendbuf. Make a 0-length write
2287 * to trigger flushing again.
2288 * If this works, we report to have written `len` bytes. */
2289 DEBUGASSERT(eos);
2290 nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err);
2291 CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d",
2292 stream->id, nwritten, *err, eos);
2293 if(nwritten < 0) {
2294 goto out;
2295 }
2296 nwritten = len;
2297 }
2298 else {
2299 nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err);
2300 CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d",
2301 stream->id, len, nwritten, *err, eos);
2302 }
2303
2304 /* Call the nghttp2 send loop and flush to write ALL buffered data,
2305 * headers and/or request body completely out to the network */
2306 result = h2_progress_egress(cf, data);
2307
2308 /* if the stream has been closed in egress handling (nghttp2 does that
2309 * when it does not like the headers, for example */
2310 if(stream && stream->closed) {
2311 infof(data, "stream %u closed", stream->id);
2312 *err = CURLE_SEND_ERROR;
2313 nwritten = -1;
2314 goto out;
2315 }
2316 else if(result && (result != CURLE_AGAIN)) {
2317 *err = result;
2318 nwritten = -1;
2319 goto out;
2320 }
2321
2322 if(should_close_session(ctx)) {
2323 /* nghttp2 thinks this session is done. If the stream has not been
2324 * closed, this is an error state for out transfer */
2325 if(stream && stream->closed) {
2326 nwritten = http2_handle_stream_close(cf, data, stream, err);
2327 }
2328 else {
2329 CURL_TRC_CF(data, cf, "send: nothing to do in this session");
2330 *err = CURLE_HTTP2;
2331 nwritten = -1;
2332 }
2333 }
2334
2335 out:
2336 if(stream) {
2337 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
2338 "eos=%d, h2 windows %d-%d (stream-conn), "
2339 "buffers %zu-%zu (stream-conn)",
2340 stream->id, len, nwritten, *err,
2341 stream->body_eos,
2342 nghttp2_session_get_stream_remote_window_size(
2343 ctx->h2, stream->id),
2344 nghttp2_session_get_remote_window_size(ctx->h2),
2345 Curl_bufq_len(&stream->sendbuf),
2346 Curl_bufq_len(&ctx->outbufq));
2347 }
2348 else {
2349 CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
2350 "connection-window=%d, nw_send_buffer(%zu)",
2351 len, nwritten, *err,
2352 nghttp2_session_get_remote_window_size(ctx->h2),
2353 Curl_bufq_len(&ctx->outbufq));
2354 }
2355 CF_DATA_RESTORE(cf, save);
2356 return nwritten;
2357 }
2358
cf_h2_flush(struct Curl_cfilter * cf,struct Curl_easy * data)2359 static CURLcode cf_h2_flush(struct Curl_cfilter *cf,
2360 struct Curl_easy *data)
2361 {
2362 struct cf_h2_ctx *ctx = cf->ctx;
2363 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2364 struct cf_call_data save;
2365 CURLcode result = CURLE_OK;
2366
2367 CF_DATA_SAVE(save, cf, data);
2368 if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
2369 /* resume the potentially suspended stream */
2370 int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2371 if(nghttp2_is_fatal(rv)) {
2372 result = CURLE_SEND_ERROR;
2373 goto out;
2374 }
2375 }
2376
2377 result = h2_progress_egress(cf, data);
2378
2379 out:
2380 if(stream) {
2381 CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
2382 "h2 windows %d-%d (stream-conn), "
2383 "buffers %zu-%zu (stream-conn)",
2384 stream->id, result,
2385 nghttp2_session_get_stream_remote_window_size(
2386 ctx->h2, stream->id),
2387 nghttp2_session_get_remote_window_size(ctx->h2),
2388 Curl_bufq_len(&stream->sendbuf),
2389 Curl_bufq_len(&ctx->outbufq));
2390 }
2391 else {
2392 CURL_TRC_CF(data, cf, "flush -> %d, "
2393 "connection-window=%d, nw_send_buffer(%zu)",
2394 result, nghttp2_session_get_remote_window_size(ctx->h2),
2395 Curl_bufq_len(&ctx->outbufq));
2396 }
2397 CF_DATA_RESTORE(cf, save);
2398 return result;
2399 }
2400
cf_h2_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)2401 static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
2402 struct Curl_easy *data,
2403 struct easy_pollset *ps)
2404 {
2405 struct cf_h2_ctx *ctx = cf->ctx;
2406 struct cf_call_data save;
2407 curl_socket_t sock;
2408 bool want_recv, want_send;
2409
2410 if(!ctx->h2)
2411 return;
2412
2413 sock = Curl_conn_cf_get_socket(cf, data);
2414 Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
2415 if(want_recv || want_send) {
2416 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2417 bool c_exhaust, s_exhaust;
2418
2419 CF_DATA_SAVE(save, cf, data);
2420 c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
2421 s_exhaust = want_send && stream && stream->id >= 0 &&
2422 !nghttp2_session_get_stream_remote_window_size(ctx->h2,
2423 stream->id);
2424 want_recv = (want_recv || c_exhaust || s_exhaust);
2425 want_send = (!s_exhaust && want_send) ||
2426 (!c_exhaust && nghttp2_session_want_write(ctx->h2)) ||
2427 !Curl_bufq_is_empty(&ctx->outbufq);
2428
2429 Curl_pollset_set(data, ps, sock, want_recv, want_send);
2430 CF_DATA_RESTORE(cf, save);
2431 }
2432 else if(ctx->sent_goaway && !cf->shutdown) {
2433 /* shutdown in progress */
2434 CF_DATA_SAVE(save, cf, data);
2435 want_send = nghttp2_session_want_write(ctx->h2) ||
2436 !Curl_bufq_is_empty(&ctx->outbufq);
2437 want_recv = nghttp2_session_want_read(ctx->h2);
2438 Curl_pollset_set(data, ps, sock, want_recv, want_send);
2439 CF_DATA_RESTORE(cf, save);
2440 }
2441 }
2442
cf_h2_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)2443 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
2444 struct Curl_easy *data,
2445 bool blocking, bool *done)
2446 {
2447 struct cf_h2_ctx *ctx = cf->ctx;
2448 CURLcode result = CURLE_OK;
2449 struct cf_call_data save;
2450 bool first_time = FALSE;
2451
2452 if(cf->connected) {
2453 *done = TRUE;
2454 return CURLE_OK;
2455 }
2456
2457 /* Connect the lower filters first */
2458 if(!cf->next->connected) {
2459 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2460 if(result || !*done)
2461 return result;
2462 }
2463
2464 *done = FALSE;
2465
2466 CF_DATA_SAVE(save, cf, data);
2467 DEBUGASSERT(ctx->initialized);
2468 if(!ctx->h2) {
2469 result = cf_h2_ctx_open(cf, data);
2470 if(result)
2471 goto out;
2472 first_time = TRUE;
2473 }
2474
2475 if(!first_time) {
2476 result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
2477 if(result)
2478 goto out;
2479 }
2480
2481 /* Send out our SETTINGS and ACKs and such. If that blocks, we
2482 * have it buffered and can count this filter as being connected */
2483 result = h2_progress_egress(cf, data);
2484 if(result && (result != CURLE_AGAIN))
2485 goto out;
2486
2487 *done = TRUE;
2488 cf->connected = TRUE;
2489 result = CURLE_OK;
2490
2491 out:
2492 CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
2493 CF_DATA_RESTORE(cf, save);
2494 return result;
2495 }
2496
cf_h2_close(struct Curl_cfilter * cf,struct Curl_easy * data)2497 static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2498 {
2499 struct cf_h2_ctx *ctx = cf->ctx;
2500
2501 if(ctx) {
2502 struct cf_call_data save;
2503
2504 CF_DATA_SAVE(save, cf, data);
2505 cf_h2_ctx_close(ctx);
2506 CF_DATA_RESTORE(cf, save);
2507 cf->connected = FALSE;
2508 }
2509 if(cf->next)
2510 cf->next->cft->do_close(cf->next, data);
2511 }
2512
cf_h2_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)2513 static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2514 {
2515 struct cf_h2_ctx *ctx = cf->ctx;
2516
2517 (void)data;
2518 if(ctx) {
2519 cf_h2_ctx_free(ctx);
2520 cf->ctx = NULL;
2521 }
2522 }
2523
cf_h2_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)2524 static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
2525 struct Curl_easy *data, bool *done)
2526 {
2527 struct cf_h2_ctx *ctx = cf->ctx;
2528 struct cf_call_data save;
2529 CURLcode result;
2530 int rv;
2531
2532 if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) {
2533 *done = TRUE;
2534 return CURLE_OK;
2535 }
2536
2537 CF_DATA_SAVE(save, cf, data);
2538
2539 if(!ctx->sent_goaway) {
2540 rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
2541 ctx->local_max_sid, 0,
2542 (const uint8_t *)"shutdown",
2543 sizeof("shutdown"));
2544 if(rv) {
2545 failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
2546 nghttp2_strerror(rv), rv);
2547 result = CURLE_SEND_ERROR;
2548 goto out;
2549 }
2550 ctx->sent_goaway = TRUE;
2551 }
2552 /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
2553 result = CURLE_OK;
2554 if(nghttp2_session_want_write(ctx->h2) ||
2555 !Curl_bufq_is_empty(&ctx->outbufq))
2556 result = h2_progress_egress(cf, data);
2557 if(!result && nghttp2_session_want_read(ctx->h2))
2558 result = h2_progress_ingress(cf, data, 0);
2559
2560 if(result == CURLE_AGAIN)
2561 result = CURLE_OK;
2562
2563 *done = (ctx->conn_closed ||
2564 (!result && !nghttp2_session_want_write(ctx->h2) &&
2565 !nghttp2_session_want_read(ctx->h2) &&
2566 Curl_bufq_is_empty(&ctx->outbufq)));
2567
2568 out:
2569 CF_DATA_RESTORE(cf, save);
2570 cf->shutdown = (result || *done);
2571 return result;
2572 }
2573
http2_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)2574 static CURLcode http2_data_pause(struct Curl_cfilter *cf,
2575 struct Curl_easy *data,
2576 bool pause)
2577 {
2578 struct cf_h2_ctx *ctx = cf->ctx;
2579 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2580
2581 DEBUGASSERT(data);
2582 if(ctx && ctx->h2 && stream) {
2583 CURLcode result = cf_h2_update_local_win(cf, data, stream, pause);
2584 if(result)
2585 return result;
2586
2587 /* attempt to send the window update */
2588 (void)h2_progress_egress(cf, data);
2589
2590 if(!pause) {
2591 /* Unpausing a h2 transfer, requires it to be run again. The server
2592 * may send new DATA on us increasing the flow window, and it may
2593 * not. We may have already buffered and exhausted the new window
2594 * by operating on things in flight during the handling of other
2595 * transfers. */
2596 drain_stream(cf, data, stream);
2597 Curl_expire(data, 0, EXPIRE_RUN_NOW);
2598 }
2599 CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
2600 pause ? "" : "un");
2601 }
2602 return CURLE_OK;
2603 }
2604
cf_h2_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)2605 static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
2606 struct Curl_easy *data,
2607 int event, int arg1, void *arg2)
2608 {
2609 CURLcode result = CURLE_OK;
2610 struct cf_call_data save;
2611
2612 (void)arg2;
2613
2614 CF_DATA_SAVE(save, cf, data);
2615 switch(event) {
2616 case CF_CTRL_DATA_SETUP:
2617 break;
2618 case CF_CTRL_DATA_PAUSE:
2619 result = http2_data_pause(cf, data, (arg1 != 0));
2620 break;
2621 case CF_CTRL_FLUSH:
2622 result = cf_h2_flush(cf, data);
2623 break;
2624 case CF_CTRL_DATA_DONE:
2625 http2_data_done(cf, data);
2626 break;
2627 default:
2628 break;
2629 }
2630 CF_DATA_RESTORE(cf, save);
2631 return result;
2632 }
2633
cf_h2_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)2634 static bool cf_h2_data_pending(struct Curl_cfilter *cf,
2635 const struct Curl_easy *data)
2636 {
2637 struct cf_h2_ctx *ctx = cf->ctx;
2638
2639 if(ctx && !Curl_bufq_is_empty(&ctx->inbufq))
2640 return TRUE;
2641 return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
2642 }
2643
cf_h2_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2644 static bool cf_h2_is_alive(struct Curl_cfilter *cf,
2645 struct Curl_easy *data,
2646 bool *input_pending)
2647 {
2648 struct cf_h2_ctx *ctx = cf->ctx;
2649 CURLcode result;
2650 struct cf_call_data save;
2651
2652 CF_DATA_SAVE(save, cf, data);
2653 result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
2654 CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
2655 result, *input_pending);
2656 CF_DATA_RESTORE(cf, save);
2657 return result;
2658 }
2659
cf_h2_keep_alive(struct Curl_cfilter * cf,struct Curl_easy * data)2660 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
2661 struct Curl_easy *data)
2662 {
2663 CURLcode result;
2664 struct cf_call_data save;
2665
2666 CF_DATA_SAVE(save, cf, data);
2667 result = http2_send_ping(cf, data);
2668 CF_DATA_RESTORE(cf, save);
2669 return result;
2670 }
2671
cf_h2_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2672 static CURLcode cf_h2_query(struct Curl_cfilter *cf,
2673 struct Curl_easy *data,
2674 int query, int *pres1, void *pres2)
2675 {
2676 struct cf_h2_ctx *ctx = cf->ctx;
2677 struct cf_call_data save;
2678 size_t effective_max;
2679
2680 switch(query) {
2681 case CF_QUERY_MAX_CONCURRENT:
2682 DEBUGASSERT(pres1);
2683
2684 CF_DATA_SAVE(save, cf, data);
2685 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
2686 /* the limit is what we have in use right now */
2687 effective_max = CONN_INUSE(cf->conn);
2688 }
2689 else {
2690 effective_max = ctx->max_concurrent_streams;
2691 }
2692 *pres1 = (effective_max > INT_MAX) ? INT_MAX : (int)effective_max;
2693 CF_DATA_RESTORE(cf, save);
2694 return CURLE_OK;
2695 case CF_QUERY_STREAM_ERROR: {
2696 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2697 *pres1 = stream ? (int)stream->error : 0;
2698 return CURLE_OK;
2699 }
2700 case CF_QUERY_NEED_FLUSH: {
2701 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2702 if(!Curl_bufq_is_empty(&ctx->outbufq) ||
2703 (stream && !Curl_bufq_is_empty(&stream->sendbuf))) {
2704 *pres1 = TRUE;
2705 return CURLE_OK;
2706 }
2707 break;
2708 }
2709 case CF_QUERY_HTTP_VERSION:
2710 *pres1 = 20;
2711 return CURLE_OK;
2712 default:
2713 break;
2714 }
2715 return cf->next ?
2716 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2717 CURLE_UNKNOWN_OPTION;
2718 }
2719
2720 struct Curl_cftype Curl_cft_nghttp2 = {
2721 "HTTP/2",
2722 CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
2723 CURL_LOG_LVL_NONE,
2724 cf_h2_destroy,
2725 cf_h2_connect,
2726 cf_h2_close,
2727 cf_h2_shutdown,
2728 Curl_cf_def_get_host,
2729 cf_h2_adjust_pollset,
2730 cf_h2_data_pending,
2731 cf_h2_send,
2732 cf_h2_recv,
2733 cf_h2_cntrl,
2734 cf_h2_is_alive,
2735 cf_h2_keep_alive,
2736 cf_h2_query,
2737 };
2738
http2_cfilter_add(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,int sockindex,bool via_h1_upgrade)2739 static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
2740 struct Curl_easy *data,
2741 struct connectdata *conn,
2742 int sockindex,
2743 bool via_h1_upgrade)
2744 {
2745 struct Curl_cfilter *cf = NULL;
2746 struct cf_h2_ctx *ctx;
2747 CURLcode result = CURLE_OUT_OF_MEMORY;
2748
2749 DEBUGASSERT(data->conn);
2750 ctx = calloc(1, sizeof(*ctx));
2751 if(!ctx)
2752 goto out;
2753 cf_h2_ctx_init(ctx, via_h1_upgrade);
2754
2755 result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
2756 if(result)
2757 goto out;
2758
2759 ctx = NULL;
2760 Curl_conn_cf_add(data, conn, sockindex, cf);
2761
2762 out:
2763 if(result)
2764 cf_h2_ctx_free(ctx);
2765 *pcf = result ? NULL : cf;
2766 return result;
2767 }
2768
http2_cfilter_insert_after(struct Curl_cfilter * cf,struct Curl_easy * data,bool via_h1_upgrade)2769 static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
2770 struct Curl_easy *data,
2771 bool via_h1_upgrade)
2772 {
2773 struct Curl_cfilter *cf_h2 = NULL;
2774 struct cf_h2_ctx *ctx;
2775 CURLcode result = CURLE_OUT_OF_MEMORY;
2776
2777 (void)data;
2778 ctx = calloc(1, sizeof(*ctx));
2779 if(!ctx)
2780 goto out;
2781 cf_h2_ctx_init(ctx, via_h1_upgrade);
2782
2783 result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
2784 if(result)
2785 goto out;
2786
2787 ctx = NULL;
2788 Curl_conn_cf_insert_after(cf, cf_h2);
2789
2790 out:
2791 if(result)
2792 cf_h2_ctx_free(ctx);
2793 return result;
2794 }
2795
Curl_http2_may_switch(struct Curl_easy * data)2796 bool Curl_http2_may_switch(struct Curl_easy *data)
2797 {
2798 if(Curl_conn_http_version(data) < 20 &&
2799 data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
2800 #ifndef CURL_DISABLE_PROXY
2801 if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
2802 /* We do not support HTTP/2 proxies yet. Also it is debatable
2803 whether or not this setting should apply to HTTP/2 proxies. */
2804 infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
2805 return FALSE;
2806 }
2807 #endif
2808 return TRUE;
2809 }
2810 return FALSE;
2811 }
2812
Curl_http2_switch(struct Curl_easy * data)2813 CURLcode Curl_http2_switch(struct Curl_easy *data)
2814 {
2815 struct Curl_cfilter *cf;
2816 CURLcode result;
2817
2818 DEBUGASSERT(Curl_conn_http_version(data) < 20);
2819
2820 result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, FALSE);
2821 if(result)
2822 return result;
2823 CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
2824
2825 data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2826 Curl_multi_connchanged(data->multi);
2827
2828 if(cf->next) {
2829 bool done;
2830 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2831 }
2832 return CURLE_OK;
2833 }
2834
Curl_http2_switch_at(struct Curl_cfilter * cf,struct Curl_easy * data)2835 CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
2836 {
2837 struct Curl_cfilter *cf_h2;
2838 CURLcode result;
2839
2840 DEBUGASSERT(Curl_conn_http_version(data) < 20);
2841
2842 result = http2_cfilter_insert_after(cf, data, FALSE);
2843 if(result)
2844 return result;
2845
2846 cf_h2 = cf->next;
2847 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2848 Curl_multi_connchanged(data->multi);
2849
2850 if(cf_h2->next) {
2851 bool done;
2852 return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
2853 }
2854 return CURLE_OK;
2855 }
2856
Curl_http2_upgrade(struct Curl_easy * data,struct connectdata * conn,int sockindex,const char * mem,size_t nread)2857 CURLcode Curl_http2_upgrade(struct Curl_easy *data,
2858 struct connectdata *conn, int sockindex,
2859 const char *mem, size_t nread)
2860 {
2861 struct Curl_cfilter *cf;
2862 struct cf_h2_ctx *ctx;
2863 CURLcode result;
2864
2865 DEBUGASSERT(Curl_conn_http_version(data) < 20);
2866 DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
2867
2868 result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
2869 if(result)
2870 return result;
2871 CURL_TRC_CF(data, cf, "upgrading connection to HTTP/2");
2872
2873 DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
2874 ctx = cf->ctx;
2875
2876 if(nread > 0) {
2877 /* Remaining data from the protocol switch reply is already using
2878 * the switched protocol, ie. HTTP/2. We add that to the network
2879 * inbufq. */
2880 ssize_t copied;
2881
2882 copied = Curl_bufq_write(&ctx->inbufq,
2883 (const unsigned char *)mem, nread, &result);
2884 if(copied < 0) {
2885 failf(data, "error on copying HTTP Upgrade response: %d", result);
2886 return CURLE_RECV_ERROR;
2887 }
2888 if((size_t)copied < nread) {
2889 failf(data, "connection buffer size could not take all data "
2890 "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
2891 copied, nread);
2892 return CURLE_HTTP2;
2893 }
2894 infof(data, "Copied HTTP/2 data in stream buffer to connection buffer"
2895 " after upgrade: len=%zu", nread);
2896 }
2897
2898 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2899 Curl_multi_connchanged(data->multi);
2900
2901 if(cf->next) {
2902 bool done;
2903 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2904 }
2905 return CURLE_OK;
2906 }
2907
2908 /* Only call this function for a transfer that already got an HTTP/2
2909 CURLE_HTTP2_STREAM error! */
Curl_h2_http_1_1_error(struct Curl_easy * data)2910 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2911 {
2912 if(Curl_conn_http_version(data) == 20) {
2913 int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
2914 return err == NGHTTP2_HTTP_1_1_REQUIRED;
2915 }
2916 return FALSE;
2917 }
2918
Curl_nghttp2_malloc(size_t size,void * user_data)2919 void *Curl_nghttp2_malloc(size_t size, void *user_data)
2920 {
2921 (void)user_data;
2922 return Curl_cmalloc(size);
2923 }
2924
Curl_nghttp2_free(void * ptr,void * user_data)2925 void Curl_nghttp2_free(void *ptr, void *user_data)
2926 {
2927 (void)user_data;
2928 Curl_cfree(ptr);
2929 }
2930
Curl_nghttp2_calloc(size_t nmemb,size_t size,void * user_data)2931 void *Curl_nghttp2_calloc(size_t nmemb, size_t size, void *user_data)
2932 {
2933 (void)user_data;
2934 return Curl_ccalloc(nmemb, size);
2935 }
2936
Curl_nghttp2_realloc(void * ptr,size_t size,void * user_data)2937 void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data)
2938 {
2939 (void)user_data;
2940 return Curl_crealloc(ptr, size);
2941 }
2942
2943 #else /* !USE_NGHTTP2 */
2944
2945 /* Satisfy external references even if http2 is not compiled in. */
2946 #include <curl/curl.h>
2947
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)2948 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2949 {
2950 (void) h;
2951 (void) num;
2952 return NULL;
2953 }
2954
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)2955 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2956 {
2957 (void) h;
2958 (void) header;
2959 return NULL;
2960 }
2961
2962 #endif /* USE_NGHTTP2 */
2963