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