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 recvbuf; /* response buffer */
202 struct bufq sendbuf; /* request buffer */
203 struct h1_req_parser h1; /* parsing the request */
204 struct dynhds resp_trailers; /* response trailer fields */
205 size_t resp_hds_len; /* amount of response header bytes in recvbuf */
206 curl_off_t nrcvd_data; /* number of DATA bytes received */
207
208 char **push_headers; /* allocated array */
209 size_t push_headers_used; /* number of entries filled in */
210 size_t push_headers_alloc; /* number of entries allocated */
211
212 int status_code; /* HTTP response status code */
213 uint32_t error; /* stream error code */
214 CURLcode xfer_result; /* Result of writing out response */
215 int32_t local_window_size; /* the local recv window size */
216 int32_t id; /* HTTP/2 protocol identifier for stream */
217 BIT(resp_hds_complete); /* we have a complete, final response */
218 BIT(closed); /* TRUE on stream close */
219 BIT(reset); /* TRUE on stream reset */
220 BIT(close_handled); /* TRUE if stream closure is handled by libcurl */
221 BIT(bodystarted);
222 BIT(body_eos); /* the complete body has been added to `sendbuf` and
223 * is being/has been processed from there. */
224 };
225
226 #define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
227 data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
228
h2_stream_ctx_create(struct cf_h2_ctx * ctx)229 static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
230 {
231 struct h2_stream_ctx *stream;
232
233 (void)ctx;
234 stream = calloc(1, sizeof(*stream));
235 if(!stream)
236 return NULL;
237
238 stream->id = -1;
239 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
240 H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
241 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
242 Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
243 stream->resp_hds_len = 0;
244 stream->bodystarted = FALSE;
245 stream->status_code = -1;
246 stream->closed = FALSE;
247 stream->close_handled = FALSE;
248 stream->error = NGHTTP2_NO_ERROR;
249 stream->local_window_size = H2_STREAM_WINDOW_SIZE_INITIAL;
250 stream->nrcvd_data = 0;
251 return stream;
252 }
253
free_push_headers(struct h2_stream_ctx * stream)254 static void free_push_headers(struct h2_stream_ctx *stream)
255 {
256 size_t i;
257 for(i = 0; i < stream->push_headers_used; i++)
258 free(stream->push_headers[i]);
259 Curl_safefree(stream->push_headers);
260 stream->push_headers_used = 0;
261 }
262
h2_stream_ctx_free(struct h2_stream_ctx * stream)263 static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
264 {
265 Curl_bufq_free(&stream->sendbuf);
266 Curl_h1_req_parse_free(&stream->h1);
267 Curl_dynhds_free(&stream->resp_trailers);
268 free_push_headers(stream);
269 free(stream);
270 }
271
h2_stream_hash_free(void * stream)272 static void h2_stream_hash_free(void *stream)
273 {
274 DEBUGASSERT(stream);
275 h2_stream_ctx_free((struct h2_stream_ctx *)stream);
276 }
277
278 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
cf_h2_get_desired_local_win(struct Curl_cfilter * cf,struct Curl_easy * data)279 static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
280 struct Curl_easy *data)
281 {
282 (void)cf;
283 if(data->set.max_recv_speed && data->set.max_recv_speed < INT32_MAX) {
284 /* The transfer should only receive `max_recv_speed` bytes per second.
285 * We restrict the stream's local window size, so that the server cannot
286 * send us "too much" at a time.
287 * This gets less precise the higher the latency. */
288 return (int32_t)data->set.max_recv_speed;
289 }
290 return H2_STREAM_WINDOW_SIZE_MAX;
291 }
292
cf_h2_update_local_win(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,bool paused)293 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
294 struct Curl_easy *data,
295 struct h2_stream_ctx *stream,
296 bool paused)
297 {
298 struct cf_h2_ctx *ctx = cf->ctx;
299 int32_t dwsize;
300 int rv;
301
302 dwsize = paused ? 0 : cf_h2_get_desired_local_win(cf, data);
303 if(dwsize != stream->local_window_size) {
304 int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
305 ctx->h2, stream->id);
306 if(dwsize > wsize) {
307 rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
308 stream->id, dwsize - wsize);
309 if(rv) {
310 failf(data, "[%d] nghttp2_submit_window_update() failed: "
311 "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
312 return CURLE_HTTP2;
313 }
314 stream->local_window_size = dwsize;
315 CURL_TRC_CF(data, cf, "[%d] local window update by %d",
316 stream->id, dwsize - wsize);
317 }
318 else {
319 rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
320 stream->id, dwsize);
321 if(rv) {
322 failf(data, "[%d] nghttp2_session_set_local_window_size() failed: "
323 "%s(%d)", stream->id, nghttp2_strerror(rv), rv);
324 return CURLE_HTTP2;
325 }
326 stream->local_window_size = dwsize;
327 CURL_TRC_CF(data, cf, "[%d] local window size now %d",
328 stream->id, dwsize);
329 }
330 }
331 return CURLE_OK;
332 }
333
334 #else /* NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
335
cf_h2_update_local_win(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,bool paused)336 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
337 struct Curl_easy *data,
338 struct h2_stream_ctx *stream,
339 bool paused)
340 {
341 (void)cf;
342 (void)data;
343 (void)stream;
344 (void)paused;
345 return CURLE_OK;
346 }
347 #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
348
349 /*
350 * Mark this transfer to get "drained".
351 */
drain_stream(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream)352 static void drain_stream(struct Curl_cfilter *cf,
353 struct Curl_easy *data,
354 struct h2_stream_ctx *stream)
355 {
356 unsigned char bits;
357
358 (void)cf;
359 bits = CURL_CSELECT_IN;
360 if(!stream->closed &&
361 (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf)))
362 bits |= CURL_CSELECT_OUT;
363 if(stream->closed || (data->state.select_bits != bits)) {
364 CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
365 stream->id, bits);
366 data->state.select_bits = bits;
367 Curl_expire(data, 0, EXPIRE_RUN_NOW);
368 }
369 }
370
http2_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx ** pstream)371 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
372 struct Curl_easy *data,
373 struct h2_stream_ctx **pstream)
374 {
375 struct cf_h2_ctx *ctx = cf->ctx;
376 struct h2_stream_ctx *stream;
377
378 (void)cf;
379 DEBUGASSERT(data);
380 stream = H2_STREAM_CTX(ctx, data);
381 if(stream) {
382 *pstream = stream;
383 return CURLE_OK;
384 }
385
386 stream = h2_stream_ctx_create(ctx);
387 if(!stream)
388 return CURLE_OUT_OF_MEMORY;
389
390 if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
391 h2_stream_ctx_free(stream);
392 return CURLE_OUT_OF_MEMORY;
393 }
394
395 *pstream = stream;
396 return CURLE_OK;
397 }
398
http2_data_done(struct Curl_cfilter * cf,struct Curl_easy * data)399 static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
400 {
401 struct cf_h2_ctx *ctx = cf->ctx;
402 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
403
404 DEBUGASSERT(ctx);
405 if(!stream || !ctx->initialized)
406 return;
407
408 if(ctx->h2) {
409 bool flush_egress = FALSE;
410 /* returns error if stream not known, which is fine here */
411 (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
412
413 if(!stream->closed && stream->id > 0) {
414 /* RST_STREAM */
415 CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
416 stream->id);
417 stream->closed = TRUE;
418 stream->reset = TRUE;
419 nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
420 stream->id, NGHTTP2_STREAM_CLOSED);
421 flush_egress = TRUE;
422 }
423
424 if(flush_egress)
425 nghttp2_session_send(ctx->h2);
426 }
427
428 Curl_hash_offt_remove(&ctx->streams, data->mid);
429 }
430
h2_client_new(struct Curl_cfilter * cf,nghttp2_session_callbacks * cbs)431 static int h2_client_new(struct Curl_cfilter *cf,
432 nghttp2_session_callbacks *cbs)
433 {
434 struct cf_h2_ctx *ctx = cf->ctx;
435 nghttp2_option *o;
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_new2(&ctx->h2, cbs, cf, o);
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 size_t data_consumed = (size_t)nread;
2059 /* Now that we transferred this to the upper layer, we report
2060 * the actual amount of DATA consumed to the H2 session, so
2061 * that it adjusts stream flow control */
2062 if(stream->resp_hds_len >= data_consumed) {
2063 stream->resp_hds_len -= data_consumed; /* no DATA */
2064 }
2065 else {
2066 if(stream->resp_hds_len) {
2067 data_consumed -= stream->resp_hds_len;
2068 stream->resp_hds_len = 0;
2069 }
2070 if(data_consumed) {
2071 nghttp2_session_consume(ctx->h2, stream->id, data_consumed);
2072 }
2073 }
2074
2075 if(stream->closed) {
2076 CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
2077 drain_stream(cf, data, stream);
2078 }
2079 }
2080
2081 out:
2082 result = h2_progress_egress(cf, data);
2083 if(result == CURLE_AGAIN) {
2084 /* pending data to send, need to be called again. Ideally, we
2085 * monitor the socket for POLLOUT, but when not SENDING
2086 * any more, we force processing of the transfer. */
2087 if(!CURL_WANT_SEND(data))
2088 drain_stream(cf, data, stream);
2089 }
2090 else if(result) {
2091 *err = result;
2092 nread = -1;
2093 }
2094 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
2095 "window=%d/%d, connection %d/%d",
2096 stream->id, len, nread, *err,
2097 nghttp2_session_get_stream_effective_recv_data_length(
2098 ctx->h2, stream->id),
2099 nghttp2_session_get_stream_effective_local_window_size(
2100 ctx->h2, stream->id),
2101 nghttp2_session_get_local_window_size(ctx->h2),
2102 HTTP2_HUGE_WINDOW_SIZE);
2103
2104 CF_DATA_RESTORE(cf, save);
2105 return nread;
2106 }
2107
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)2108 static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
2109 struct Curl_easy *data,
2110 struct h2_stream_ctx *stream,
2111 const void *buf, size_t blen, bool eos,
2112 CURLcode *err)
2113 {
2114 struct cf_h2_ctx *ctx = cf->ctx;
2115 ssize_t nwritten;
2116
2117 if(stream->closed) {
2118 if(stream->resp_hds_complete) {
2119 /* Server decided to close the stream after having sent us a final
2120 * response. This is valid if it is not interested in the request
2121 * body. This happens on 30x or 40x responses.
2122 * We silently discard the data sent, since this is not a transport
2123 * error situation. */
2124 CURL_TRC_CF(data, cf, "[%d] discarding data"
2125 "on closed stream with response", stream->id);
2126 if(eos)
2127 stream->body_eos = TRUE;
2128 *err = CURLE_OK;
2129 return (ssize_t)blen;
2130 }
2131 /* Server closed before we got a response, this is an error */
2132 infof(data, "stream %u closed", stream->id);
2133 *err = CURLE_SEND_ERROR;
2134 return -1;
2135 }
2136
2137 nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err);
2138 if(nwritten < 0)
2139 return -1;
2140
2141 if(eos && (blen == (size_t)nwritten))
2142 stream->body_eos = TRUE;
2143
2144 if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) {
2145 /* resume the potentially suspended stream */
2146 int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2147 if(nghttp2_is_fatal(rv)) {
2148 *err = CURLE_SEND_ERROR;
2149 return -1;
2150 }
2151 }
2152 return nwritten;
2153 }
2154
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)2155 static ssize_t h2_submit(struct h2_stream_ctx **pstream,
2156 struct Curl_cfilter *cf, struct Curl_easy *data,
2157 const void *buf, size_t len,
2158 bool eos, CURLcode *err)
2159 {
2160 struct cf_h2_ctx *ctx = cf->ctx;
2161 struct h2_stream_ctx *stream = NULL;
2162 struct dynhds h2_headers;
2163 nghttp2_nv *nva = NULL;
2164 const void *body = NULL;
2165 size_t nheader, bodylen, i;
2166 nghttp2_data_provider data_prd;
2167 int32_t stream_id;
2168 nghttp2_priority_spec pri_spec;
2169 ssize_t nwritten;
2170
2171 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
2172
2173 *err = http2_data_setup(cf, data, &stream);
2174 if(*err) {
2175 nwritten = -1;
2176 goto out;
2177 }
2178
2179 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
2180 if(nwritten < 0)
2181 goto out;
2182 if(!stream->h1.done) {
2183 /* need more data */
2184 goto out;
2185 }
2186 DEBUGASSERT(stream->h1.req);
2187
2188 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
2189 if(*err) {
2190 nwritten = -1;
2191 goto out;
2192 }
2193 /* no longer needed */
2194 Curl_h1_req_parse_free(&stream->h1);
2195
2196 nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
2197 if(!nva) {
2198 *err = CURLE_OUT_OF_MEMORY;
2199 nwritten = -1;
2200 goto out;
2201 }
2202
2203 h2_pri_spec(ctx, data, &pri_spec);
2204 if(!nghttp2_session_check_request_allowed(ctx->h2))
2205 CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
2206
2207 switch(data->state.httpreq) {
2208 case HTTPREQ_POST:
2209 case HTTPREQ_POST_FORM:
2210 case HTTPREQ_POST_MIME:
2211 case HTTPREQ_PUT:
2212 data_prd.read_callback = req_body_read_callback;
2213 data_prd.source.ptr = NULL;
2214 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2215 &data_prd, data);
2216 break;
2217 default:
2218 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2219 NULL, data);
2220 }
2221
2222 if(stream_id < 0) {
2223 CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
2224 nghttp2_strerror(stream_id), stream_id);
2225 *err = CURLE_SEND_ERROR;
2226 nwritten = -1;
2227 goto out;
2228 }
2229
2230 #define MAX_ACC 60000 /* <64KB to account for some overhead */
2231 if(Curl_trc_is_verbose(data)) {
2232 size_t acc = 0;
2233
2234 infof(data, "[HTTP/2] [%d] OPENED stream for %s",
2235 stream_id, data->state.url);
2236 for(i = 0; i < nheader; ++i) {
2237 acc += nva[i].namelen + nva[i].valuelen;
2238
2239 infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
2240 (int)nva[i].namelen, nva[i].name,
2241 (int)nva[i].valuelen, nva[i].value);
2242 }
2243
2244 if(acc > MAX_ACC) {
2245 infof(data, "[HTTP/2] Warning: The cumulative length of all "
2246 "headers exceeds %d bytes and that could cause the "
2247 "stream to be rejected.", MAX_ACC);
2248 }
2249 }
2250
2251 stream->id = stream_id;
2252
2253 body = (const char *)buf + nwritten;
2254 bodylen = len - nwritten;
2255
2256 if(bodylen || eos) {
2257 ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err);
2258 if(n >= 0)
2259 nwritten += n;
2260 else if(*err == CURLE_AGAIN)
2261 *err = CURLE_OK;
2262 else if(*err != CURLE_AGAIN) {
2263 *err = CURLE_SEND_ERROR;
2264 nwritten = -1;
2265 goto out;
2266 }
2267 }
2268
2269 out:
2270 CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
2271 stream ? stream->id : -1, nwritten, *err);
2272 Curl_safefree(nva);
2273 *pstream = stream;
2274 Curl_dynhds_free(&h2_headers);
2275 return nwritten;
2276 }
2277
cf_h2_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)2278 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
2279 const void *buf, size_t len, bool eos,
2280 CURLcode *err)
2281 {
2282 struct cf_h2_ctx *ctx = cf->ctx;
2283 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2284 struct cf_call_data save;
2285 ssize_t nwritten;
2286 CURLcode result;
2287
2288 CF_DATA_SAVE(save, cf, data);
2289
2290 if(!stream || stream->id == -1) {
2291 nwritten = h2_submit(&stream, cf, data, buf, len, eos, err);
2292 if(nwritten < 0) {
2293 goto out;
2294 }
2295 DEBUGASSERT(stream);
2296 }
2297 else if(stream->body_eos) {
2298 /* We already wrote this, but CURLE_AGAINed the call due to not
2299 * being able to flush stream->sendbuf. Make a 0-length write
2300 * to trigger flushing again.
2301 * If this works, we report to have written `len` bytes. */
2302 DEBUGASSERT(eos);
2303 nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err);
2304 CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d",
2305 stream->id, nwritten, *err, eos);
2306 if(nwritten < 0) {
2307 goto out;
2308 }
2309 nwritten = len;
2310 }
2311 else {
2312 nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err);
2313 CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d",
2314 stream->id, len, nwritten, *err, eos);
2315 }
2316
2317 /* Call the nghttp2 send loop and flush to write ALL buffered data,
2318 * headers and/or request body completely out to the network */
2319 result = h2_progress_egress(cf, data);
2320
2321 /* if the stream has been closed in egress handling (nghttp2 does that
2322 * when it does not like the headers, for example */
2323 if(stream && stream->closed) {
2324 infof(data, "stream %u closed", stream->id);
2325 *err = CURLE_SEND_ERROR;
2326 nwritten = -1;
2327 goto out;
2328 }
2329 else if(result && (result != CURLE_AGAIN)) {
2330 *err = result;
2331 nwritten = -1;
2332 goto out;
2333 }
2334
2335 if(should_close_session(ctx)) {
2336 /* nghttp2 thinks this session is done. If the stream has not been
2337 * closed, this is an error state for out transfer */
2338 if(stream->closed) {
2339 nwritten = http2_handle_stream_close(cf, data, stream, err);
2340 }
2341 else {
2342 CURL_TRC_CF(data, cf, "send: nothing to do in this session");
2343 *err = CURLE_HTTP2;
2344 nwritten = -1;
2345 }
2346 }
2347
2348 out:
2349 if(stream) {
2350 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
2351 "eos=%d, h2 windows %d-%d (stream-conn), "
2352 "buffers %zu-%zu (stream-conn)",
2353 stream->id, len, nwritten, *err,
2354 stream->body_eos,
2355 nghttp2_session_get_stream_remote_window_size(
2356 ctx->h2, stream->id),
2357 nghttp2_session_get_remote_window_size(ctx->h2),
2358 Curl_bufq_len(&stream->sendbuf),
2359 Curl_bufq_len(&ctx->outbufq));
2360 }
2361 else {
2362 CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
2363 "connection-window=%d, nw_send_buffer(%zu)",
2364 len, nwritten, *err,
2365 nghttp2_session_get_remote_window_size(ctx->h2),
2366 Curl_bufq_len(&ctx->outbufq));
2367 }
2368 CF_DATA_RESTORE(cf, save);
2369 return nwritten;
2370 }
2371
cf_h2_flush(struct Curl_cfilter * cf,struct Curl_easy * data)2372 static CURLcode cf_h2_flush(struct Curl_cfilter *cf,
2373 struct Curl_easy *data)
2374 {
2375 struct cf_h2_ctx *ctx = cf->ctx;
2376 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2377 struct cf_call_data save;
2378 CURLcode result = CURLE_OK;
2379
2380 CF_DATA_SAVE(save, cf, data);
2381 if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
2382 /* resume the potentially suspended stream */
2383 int rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2384 if(nghttp2_is_fatal(rv)) {
2385 result = CURLE_SEND_ERROR;
2386 goto out;
2387 }
2388 }
2389
2390 result = h2_progress_egress(cf, data);
2391
2392 out:
2393 if(stream) {
2394 CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
2395 "h2 windows %d-%d (stream-conn), "
2396 "buffers %zu-%zu (stream-conn)",
2397 stream->id, result,
2398 nghttp2_session_get_stream_remote_window_size(
2399 ctx->h2, stream->id),
2400 nghttp2_session_get_remote_window_size(ctx->h2),
2401 Curl_bufq_len(&stream->sendbuf),
2402 Curl_bufq_len(&ctx->outbufq));
2403 }
2404 else {
2405 CURL_TRC_CF(data, cf, "flush -> %d, "
2406 "connection-window=%d, nw_send_buffer(%zu)",
2407 result, nghttp2_session_get_remote_window_size(ctx->h2),
2408 Curl_bufq_len(&ctx->outbufq));
2409 }
2410 CF_DATA_RESTORE(cf, save);
2411 return result;
2412 }
2413
cf_h2_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)2414 static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
2415 struct Curl_easy *data,
2416 struct easy_pollset *ps)
2417 {
2418 struct cf_h2_ctx *ctx = cf->ctx;
2419 struct cf_call_data save;
2420 curl_socket_t sock;
2421 bool want_recv, want_send;
2422
2423 if(!ctx->h2)
2424 return;
2425
2426 sock = Curl_conn_cf_get_socket(cf, data);
2427 Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
2428 if(want_recv || want_send) {
2429 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2430 bool c_exhaust, s_exhaust;
2431
2432 CF_DATA_SAVE(save, cf, data);
2433 c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
2434 s_exhaust = want_send && stream && stream->id >= 0 &&
2435 !nghttp2_session_get_stream_remote_window_size(ctx->h2,
2436 stream->id);
2437 want_recv = (want_recv || c_exhaust || s_exhaust);
2438 want_send = (!s_exhaust && want_send) ||
2439 (!c_exhaust && nghttp2_session_want_write(ctx->h2)) ||
2440 !Curl_bufq_is_empty(&ctx->outbufq);
2441
2442 Curl_pollset_set(data, ps, sock, want_recv, want_send);
2443 CF_DATA_RESTORE(cf, save);
2444 }
2445 else if(ctx->sent_goaway && !cf->shutdown) {
2446 /* shutdown in progress */
2447 CF_DATA_SAVE(save, cf, data);
2448 want_send = nghttp2_session_want_write(ctx->h2) ||
2449 !Curl_bufq_is_empty(&ctx->outbufq);
2450 want_recv = nghttp2_session_want_read(ctx->h2);
2451 Curl_pollset_set(data, ps, sock, want_recv, want_send);
2452 CF_DATA_RESTORE(cf, save);
2453 }
2454 }
2455
cf_h2_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)2456 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
2457 struct Curl_easy *data,
2458 bool blocking, bool *done)
2459 {
2460 struct cf_h2_ctx *ctx = cf->ctx;
2461 CURLcode result = CURLE_OK;
2462 struct cf_call_data save;
2463 bool first_time = FALSE;
2464
2465 if(cf->connected) {
2466 *done = TRUE;
2467 return CURLE_OK;
2468 }
2469
2470 /* Connect the lower filters first */
2471 if(!cf->next->connected) {
2472 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2473 if(result || !*done)
2474 return result;
2475 }
2476
2477 *done = FALSE;
2478
2479 CF_DATA_SAVE(save, cf, data);
2480 DEBUGASSERT(ctx->initialized);
2481 if(!ctx->h2) {
2482 result = cf_h2_ctx_open(cf, data);
2483 if(result)
2484 goto out;
2485 first_time = TRUE;
2486 }
2487
2488 if(!first_time) {
2489 result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
2490 if(result)
2491 goto out;
2492 }
2493
2494 /* Send out our SETTINGS and ACKs and such. If that blocks, we
2495 * have it buffered and can count this filter as being connected */
2496 result = h2_progress_egress(cf, data);
2497 if(result == CURLE_AGAIN)
2498 result = CURLE_OK;
2499 else if(result)
2500 goto out;
2501
2502 *done = TRUE;
2503 cf->connected = TRUE;
2504 result = CURLE_OK;
2505
2506 out:
2507 CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
2508 CF_DATA_RESTORE(cf, save);
2509 return result;
2510 }
2511
cf_h2_close(struct Curl_cfilter * cf,struct Curl_easy * data)2512 static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2513 {
2514 struct cf_h2_ctx *ctx = cf->ctx;
2515
2516 if(ctx) {
2517 struct cf_call_data save;
2518
2519 CF_DATA_SAVE(save, cf, data);
2520 cf_h2_ctx_close(ctx);
2521 CF_DATA_RESTORE(cf, save);
2522 cf->connected = FALSE;
2523 }
2524 if(cf->next)
2525 cf->next->cft->do_close(cf->next, data);
2526 }
2527
cf_h2_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)2528 static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2529 {
2530 struct cf_h2_ctx *ctx = cf->ctx;
2531
2532 (void)data;
2533 if(ctx) {
2534 cf_h2_ctx_free(ctx);
2535 cf->ctx = NULL;
2536 }
2537 }
2538
cf_h2_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)2539 static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
2540 struct Curl_easy *data, bool *done)
2541 {
2542 struct cf_h2_ctx *ctx = cf->ctx;
2543 struct cf_call_data save;
2544 CURLcode result;
2545 int rv;
2546
2547 if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) {
2548 *done = TRUE;
2549 return CURLE_OK;
2550 }
2551
2552 CF_DATA_SAVE(save, cf, data);
2553
2554 if(!ctx->sent_goaway) {
2555 rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
2556 ctx->local_max_sid, 0,
2557 (const uint8_t *)"shutdown",
2558 sizeof("shutdown"));
2559 if(rv) {
2560 failf(data, "nghttp2_submit_goaway() failed: %s(%d)",
2561 nghttp2_strerror(rv), rv);
2562 result = CURLE_SEND_ERROR;
2563 goto out;
2564 }
2565 ctx->sent_goaway = TRUE;
2566 }
2567 /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
2568 result = CURLE_OK;
2569 if(nghttp2_session_want_write(ctx->h2) ||
2570 !Curl_bufq_is_empty(&ctx->outbufq))
2571 result = h2_progress_egress(cf, data);
2572 if(!result && nghttp2_session_want_read(ctx->h2))
2573 result = h2_progress_ingress(cf, data, 0);
2574
2575 if(result == CURLE_AGAIN)
2576 result = CURLE_OK;
2577
2578 *done = (ctx->conn_closed ||
2579 (!result && !nghttp2_session_want_write(ctx->h2) &&
2580 !nghttp2_session_want_read(ctx->h2) &&
2581 Curl_bufq_is_empty(&ctx->outbufq)));
2582
2583 out:
2584 CF_DATA_RESTORE(cf, save);
2585 cf->shutdown = (result || *done);
2586 return result;
2587 }
2588
http2_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)2589 static CURLcode http2_data_pause(struct Curl_cfilter *cf,
2590 struct Curl_easy *data,
2591 bool pause)
2592 {
2593 struct cf_h2_ctx *ctx = cf->ctx;
2594 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2595
2596 DEBUGASSERT(data);
2597 if(ctx && ctx->h2 && stream) {
2598 CURLcode result = cf_h2_update_local_win(cf, data, stream, pause);
2599 if(result)
2600 return result;
2601
2602 /* attempt to send the window update */
2603 (void)h2_progress_egress(cf, data);
2604
2605 if(!pause) {
2606 /* Unpausing a h2 transfer, requires it to be run again. The server
2607 * may send new DATA on us increasing the flow window, and it may
2608 * not. We may have already buffered and exhausted the new window
2609 * by operating on things in flight during the handling of other
2610 * transfers. */
2611 drain_stream(cf, data, stream);
2612 Curl_expire(data, 0, EXPIRE_RUN_NOW);
2613 }
2614 CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
2615 pause ? "" : "un");
2616 }
2617 return CURLE_OK;
2618 }
2619
cf_h2_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)2620 static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
2621 struct Curl_easy *data,
2622 int event, int arg1, void *arg2)
2623 {
2624 CURLcode result = CURLE_OK;
2625 struct cf_call_data save;
2626
2627 (void)arg2;
2628
2629 CF_DATA_SAVE(save, cf, data);
2630 switch(event) {
2631 case CF_CTRL_DATA_SETUP:
2632 break;
2633 case CF_CTRL_DATA_PAUSE:
2634 result = http2_data_pause(cf, data, (arg1 != 0));
2635 break;
2636 case CF_CTRL_FLUSH:
2637 result = cf_h2_flush(cf, data);
2638 break;
2639 case CF_CTRL_DATA_DETACH:
2640 http2_data_done(cf, data);
2641 break;
2642 case CF_CTRL_DATA_DONE:
2643 http2_data_done(cf, data);
2644 break;
2645 default:
2646 break;
2647 }
2648 CF_DATA_RESTORE(cf, save);
2649 return result;
2650 }
2651
cf_h2_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)2652 static bool cf_h2_data_pending(struct Curl_cfilter *cf,
2653 const struct Curl_easy *data)
2654 {
2655 struct cf_h2_ctx *ctx = cf->ctx;
2656 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2657
2658 if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
2659 || (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
2660 return TRUE;
2661 return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
2662 }
2663
cf_h2_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2664 static bool cf_h2_is_alive(struct Curl_cfilter *cf,
2665 struct Curl_easy *data,
2666 bool *input_pending)
2667 {
2668 struct cf_h2_ctx *ctx = cf->ctx;
2669 CURLcode result;
2670 struct cf_call_data save;
2671
2672 CF_DATA_SAVE(save, cf, data);
2673 result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
2674 CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
2675 result, *input_pending);
2676 CF_DATA_RESTORE(cf, save);
2677 return result;
2678 }
2679
cf_h2_keep_alive(struct Curl_cfilter * cf,struct Curl_easy * data)2680 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
2681 struct Curl_easy *data)
2682 {
2683 CURLcode result;
2684 struct cf_call_data save;
2685
2686 CF_DATA_SAVE(save, cf, data);
2687 result = http2_send_ping(cf, data);
2688 CF_DATA_RESTORE(cf, save);
2689 return result;
2690 }
2691
cf_h2_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2692 static CURLcode cf_h2_query(struct Curl_cfilter *cf,
2693 struct Curl_easy *data,
2694 int query, int *pres1, void *pres2)
2695 {
2696 struct cf_h2_ctx *ctx = cf->ctx;
2697 struct cf_call_data save;
2698 size_t effective_max;
2699
2700 switch(query) {
2701 case CF_QUERY_MAX_CONCURRENT:
2702 DEBUGASSERT(pres1);
2703
2704 CF_DATA_SAVE(save, cf, data);
2705 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
2706 /* the limit is what we have in use right now */
2707 effective_max = CONN_INUSE(cf->conn);
2708 }
2709 else {
2710 effective_max = ctx->max_concurrent_streams;
2711 }
2712 *pres1 = (effective_max > INT_MAX) ? INT_MAX : (int)effective_max;
2713 CF_DATA_RESTORE(cf, save);
2714 return CURLE_OK;
2715 case CF_QUERY_STREAM_ERROR: {
2716 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2717 *pres1 = stream ? (int)stream->error : 0;
2718 return CURLE_OK;
2719 }
2720 case CF_QUERY_NEED_FLUSH: {
2721 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2722 if(!Curl_bufq_is_empty(&ctx->outbufq) ||
2723 (stream && !Curl_bufq_is_empty(&stream->sendbuf))) {
2724 *pres1 = TRUE;
2725 return CURLE_OK;
2726 }
2727 break;
2728 }
2729 default:
2730 break;
2731 }
2732 return cf->next ?
2733 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2734 CURLE_UNKNOWN_OPTION;
2735 }
2736
2737 struct Curl_cftype Curl_cft_nghttp2 = {
2738 "HTTP/2",
2739 CF_TYPE_MULTIPLEX,
2740 CURL_LOG_LVL_NONE,
2741 cf_h2_destroy,
2742 cf_h2_connect,
2743 cf_h2_close,
2744 cf_h2_shutdown,
2745 Curl_cf_def_get_host,
2746 cf_h2_adjust_pollset,
2747 cf_h2_data_pending,
2748 cf_h2_send,
2749 cf_h2_recv,
2750 cf_h2_cntrl,
2751 cf_h2_is_alive,
2752 cf_h2_keep_alive,
2753 cf_h2_query,
2754 };
2755
http2_cfilter_add(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,int sockindex,bool via_h1_upgrade)2756 static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
2757 struct Curl_easy *data,
2758 struct connectdata *conn,
2759 int sockindex,
2760 bool via_h1_upgrade)
2761 {
2762 struct Curl_cfilter *cf = NULL;
2763 struct cf_h2_ctx *ctx;
2764 CURLcode result = CURLE_OUT_OF_MEMORY;
2765
2766 DEBUGASSERT(data->conn);
2767 ctx = calloc(1, sizeof(*ctx));
2768 if(!ctx)
2769 goto out;
2770 cf_h2_ctx_init(ctx, via_h1_upgrade);
2771
2772 result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
2773 if(result)
2774 goto out;
2775
2776 ctx = NULL;
2777 Curl_conn_cf_add(data, conn, sockindex, cf);
2778
2779 out:
2780 if(result)
2781 cf_h2_ctx_free(ctx);
2782 *pcf = result ? NULL : cf;
2783 return result;
2784 }
2785
http2_cfilter_insert_after(struct Curl_cfilter * cf,struct Curl_easy * data,bool via_h1_upgrade)2786 static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
2787 struct Curl_easy *data,
2788 bool via_h1_upgrade)
2789 {
2790 struct Curl_cfilter *cf_h2 = NULL;
2791 struct cf_h2_ctx *ctx;
2792 CURLcode result = CURLE_OUT_OF_MEMORY;
2793
2794 (void)data;
2795 ctx = calloc(1, sizeof(*ctx));
2796 if(!ctx)
2797 goto out;
2798 cf_h2_ctx_init(ctx, via_h1_upgrade);
2799
2800 result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
2801 if(result)
2802 goto out;
2803
2804 ctx = NULL;
2805 Curl_conn_cf_insert_after(cf, cf_h2);
2806
2807 out:
2808 if(result)
2809 cf_h2_ctx_free(ctx);
2810 return result;
2811 }
2812
cf_is_http2(struct Curl_cfilter * cf,const struct Curl_easy * data)2813 static bool cf_is_http2(struct Curl_cfilter *cf,
2814 const struct Curl_easy *data)
2815 {
2816 (void)data;
2817 for(; cf; cf = cf->next) {
2818 if(cf->cft == &Curl_cft_nghttp2)
2819 return TRUE;
2820 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2821 return FALSE;
2822 }
2823 return FALSE;
2824 }
2825
Curl_conn_is_http2(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)2826 bool Curl_conn_is_http2(const struct Curl_easy *data,
2827 const struct connectdata *conn,
2828 int sockindex)
2829 {
2830 return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
2831 }
2832
Curl_http2_may_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2833 bool Curl_http2_may_switch(struct Curl_easy *data,
2834 struct connectdata *conn,
2835 int sockindex)
2836 {
2837 (void)sockindex;
2838 if(!Curl_conn_is_http2(data, conn, sockindex) &&
2839 data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
2840 #ifndef CURL_DISABLE_PROXY
2841 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
2842 /* We do not support HTTP/2 proxies yet. Also it is debatable
2843 whether or not this setting should apply to HTTP/2 proxies. */
2844 infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
2845 return FALSE;
2846 }
2847 #endif
2848 return TRUE;
2849 }
2850 return FALSE;
2851 }
2852
Curl_http2_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2853 CURLcode Curl_http2_switch(struct Curl_easy *data,
2854 struct connectdata *conn, int sockindex)
2855 {
2856 struct Curl_cfilter *cf;
2857 CURLcode result;
2858
2859 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2860
2861 result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
2862 if(result)
2863 return result;
2864 CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
2865
2866 conn->httpversion = 20; /* we know we are on HTTP/2 now */
2867 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2868 Curl_multi_connchanged(data->multi);
2869
2870 if(cf->next) {
2871 bool done;
2872 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2873 }
2874 return CURLE_OK;
2875 }
2876
Curl_http2_switch_at(struct Curl_cfilter * cf,struct Curl_easy * data)2877 CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
2878 {
2879 struct Curl_cfilter *cf_h2;
2880 CURLcode result;
2881
2882 DEBUGASSERT(!cf_is_http2(cf, data));
2883
2884 result = http2_cfilter_insert_after(cf, data, FALSE);
2885 if(result)
2886 return result;
2887
2888 cf_h2 = cf->next;
2889 cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */
2890 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2891 Curl_multi_connchanged(data->multi);
2892
2893 if(cf_h2->next) {
2894 bool done;
2895 return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
2896 }
2897 return CURLE_OK;
2898 }
2899
Curl_http2_upgrade(struct Curl_easy * data,struct connectdata * conn,int sockindex,const char * mem,size_t nread)2900 CURLcode Curl_http2_upgrade(struct Curl_easy *data,
2901 struct connectdata *conn, int sockindex,
2902 const char *mem, size_t nread)
2903 {
2904 struct Curl_cfilter *cf;
2905 struct cf_h2_ctx *ctx;
2906 CURLcode result;
2907
2908 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2909 DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
2910
2911 result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
2912 if(result)
2913 return result;
2914 CURL_TRC_CF(data, cf, "upgrading connection to HTTP/2");
2915
2916 DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
2917 ctx = cf->ctx;
2918
2919 if(nread > 0) {
2920 /* Remaining data from the protocol switch reply is already using
2921 * the switched protocol, ie. HTTP/2. We add that to the network
2922 * inbufq. */
2923 ssize_t copied;
2924
2925 copied = Curl_bufq_write(&ctx->inbufq,
2926 (const unsigned char *)mem, nread, &result);
2927 if(copied < 0) {
2928 failf(data, "error on copying HTTP Upgrade response: %d", result);
2929 return CURLE_RECV_ERROR;
2930 }
2931 if((size_t)copied < nread) {
2932 failf(data, "connection buffer size could not take all data "
2933 "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
2934 copied, nread);
2935 return CURLE_HTTP2;
2936 }
2937 infof(data, "Copied HTTP/2 data in stream buffer to connection buffer"
2938 " after upgrade: len=%zu", nread);
2939 }
2940
2941 conn->httpversion = 20; /* we know we are on HTTP/2 now */
2942 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2943 Curl_multi_connchanged(data->multi);
2944
2945 if(cf->next) {
2946 bool done;
2947 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2948 }
2949 return CURLE_OK;
2950 }
2951
2952 /* Only call this function for a transfer that already got an HTTP/2
2953 CURLE_HTTP2_STREAM error! */
Curl_h2_http_1_1_error(struct Curl_easy * data)2954 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2955 {
2956 if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
2957 int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
2958 return (err == NGHTTP2_HTTP_1_1_REQUIRED);
2959 }
2960 return FALSE;
2961 }
2962
2963 #else /* !USE_NGHTTP2 */
2964
2965 /* Satisfy external references even if http2 is not compiled in. */
2966 #include <curl/curl.h>
2967
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)2968 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2969 {
2970 (void) h;
2971 (void) num;
2972 return NULL;
2973 }
2974
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)2975 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2976 {
2977 (void) h;
2978 (void) header;
2979 return NULL;
2980 }
2981
2982 #endif /* USE_NGHTTP2 */
2983