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