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 if(stream->resp_hds_complete && data->req.no_body) {
1720 CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
1721 "not want a body anyway, ignore: %s (err %u)",
1722 stream->id, nghttp2_http2_strerror(stream->error),
1723 stream->error);
1724 stream->close_handled = TRUE;
1725 *err = CURLE_OK;
1726 goto out;
1727 }
1728 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1729 stream->id, nghttp2_http2_strerror(stream->error),
1730 stream->error);
1731 *err = CURLE_HTTP2_STREAM;
1732 return -1;
1733 }
1734 else if(stream->reset) {
1735 failf(data, "HTTP/2 stream %u was reset", stream->id);
1736 *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
1737 return -1;
1738 }
1739
1740 if(!stream->bodystarted) {
1741 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1742 " all response header fields, treated as error",
1743 stream->id);
1744 *err = CURLE_HTTP2_STREAM;
1745 return -1;
1746 }
1747
1748 if(Curl_dynhds_count(&stream->resp_trailers)) {
1749 struct dynhds_entry *e;
1750 struct dynbuf dbuf;
1751 size_t i;
1752
1753 *err = CURLE_OK;
1754 Curl_dyn_init(&dbuf, DYN_TRAILERS);
1755 for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
1756 e = Curl_dynhds_getn(&stream->resp_trailers, i);
1757 if(!e)
1758 break;
1759 Curl_dyn_reset(&dbuf);
1760 *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
1761 (int)e->namelen, e->name,
1762 (int)e->valuelen, e->value);
1763 if(*err)
1764 break;
1765 Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
1766 Curl_dyn_len(&dbuf));
1767 *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
1768 Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
1769 if(*err)
1770 break;
1771 }
1772 Curl_dyn_free(&dbuf);
1773 if(*err)
1774 goto out;
1775 }
1776
1777 stream->close_handled = TRUE;
1778 *err = CURLE_OK;
1779 rv = 0;
1780
1781 out:
1782 CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
1783 return rv;
1784 }
1785
sweight_wanted(const struct Curl_easy * data)1786 static int sweight_wanted(const struct Curl_easy *data)
1787 {
1788 /* 0 weight is not set by user and we take the nghttp2 default one */
1789 return data->set.priority.weight?
1790 data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1791 }
1792
sweight_in_effect(const struct Curl_easy * data)1793 static int sweight_in_effect(const struct Curl_easy *data)
1794 {
1795 /* 0 weight is not set by user and we take the nghttp2 default one */
1796 return data->state.priority.weight?
1797 data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1798 }
1799
1800 /*
1801 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1802 * and dependency to the peer. It also stores the updated values in the state
1803 * struct.
1804 */
1805
h2_pri_spec(struct cf_h2_ctx * ctx,struct Curl_easy * data,nghttp2_priority_spec * pri_spec)1806 static void h2_pri_spec(struct cf_h2_ctx *ctx,
1807 struct Curl_easy *data,
1808 nghttp2_priority_spec *pri_spec)
1809 {
1810 struct Curl_data_priority *prio = &data->set.priority;
1811 struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
1812 int32_t depstream_id = depstream? depstream->id:0;
1813 nghttp2_priority_spec_init(pri_spec, depstream_id,
1814 sweight_wanted(data),
1815 data->set.priority.exclusive);
1816 data->state.priority = *prio;
1817 }
1818
1819 /*
1820 * Check if there's been an update in the priority /
1821 * dependency settings and if so it submits a PRIORITY frame with the updated
1822 * info.
1823 * Flush any out data pending in the network buffer.
1824 */
h2_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data)1825 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
1826 struct Curl_easy *data)
1827 {
1828 struct cf_h2_ctx *ctx = cf->ctx;
1829 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
1830 int rv = 0;
1831
1832 if(stream && stream->id > 0 &&
1833 ((sweight_wanted(data) != sweight_in_effect(data)) ||
1834 (data->set.priority.exclusive != data->state.priority.exclusive) ||
1835 (data->set.priority.parent != data->state.priority.parent)) ) {
1836 /* send new weight and/or dependency */
1837 nghttp2_priority_spec pri_spec;
1838
1839 h2_pri_spec(ctx, data, &pri_spec);
1840 CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
1841 DEBUGASSERT(stream->id != -1);
1842 rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
1843 stream->id, &pri_spec);
1844 if(rv)
1845 goto out;
1846 }
1847
1848 ctx->nw_out_blocked = 0;
1849 while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
1850 rv = nghttp2_session_send(ctx->h2);
1851
1852 out:
1853 if(nghttp2_is_fatal(rv)) {
1854 CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d",
1855 nghttp2_strerror(rv), rv);
1856 return CURLE_SEND_ERROR;
1857 }
1858 return nw_out_flush(cf, data);
1859 }
1860
stream_recv(struct Curl_cfilter * cf,struct Curl_easy * data,struct h2_stream_ctx * stream,char * buf,size_t len,CURLcode * err)1861 static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1862 struct h2_stream_ctx *stream,
1863 char *buf, size_t len, CURLcode *err)
1864 {
1865 struct cf_h2_ctx *ctx = cf->ctx;
1866 ssize_t nread = -1;
1867
1868 (void)buf;
1869 *err = CURLE_AGAIN;
1870 if(stream->xfer_result) {
1871 CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
1872 *err = stream->xfer_result;
1873 nread = -1;
1874 }
1875 else if(stream->closed) {
1876 CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
1877 nread = http2_handle_stream_close(cf, data, stream, err);
1878 }
1879 else if(stream->reset ||
1880 (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
1881 (ctx->goaway && ctx->last_stream_id < stream->id)) {
1882 CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
1883 *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP2;
1884 nread = -1;
1885 }
1886
1887 if(nread < 0 && *err != CURLE_AGAIN)
1888 CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
1889 stream->id, len, nread, *err);
1890 return nread;
1891 }
1892
h2_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data,size_t data_max_bytes)1893 static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
1894 struct Curl_easy *data,
1895 size_t data_max_bytes)
1896 {
1897 struct cf_h2_ctx *ctx = cf->ctx;
1898 struct h2_stream_ctx *stream;
1899 CURLcode result = CURLE_OK;
1900 ssize_t nread;
1901
1902 /* Process network input buffer fist */
1903 if(!Curl_bufq_is_empty(&ctx->inbufq)) {
1904 CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
1905 Curl_bufq_len(&ctx->inbufq));
1906 if(h2_process_pending_input(cf, data, &result) < 0)
1907 return result;
1908 }
1909
1910 /* Receive data from the "lower" filters, e.g. network until
1911 * it is time to stop due to connection close or us not processing
1912 * all network input */
1913 while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
1914 stream = H2_STREAM_CTX(ctx, data);
1915 if(stream && (stream->closed || !data_max_bytes)) {
1916 /* We would like to abort here and stop processing, so that
1917 * the transfer loop can handle the data/close here. However,
1918 * this may leave data in underlying buffers that will not
1919 * be consumed. */
1920 if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
1921 drain_stream(cf, data, stream);
1922 break;
1923 }
1924
1925 nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result);
1926 if(nread < 0) {
1927 if(result != CURLE_AGAIN) {
1928 failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
1929 curl_easy_strerror(result));
1930 return result;
1931 }
1932 break;
1933 }
1934 else if(nread == 0) {
1935 CURL_TRC_CF(data, cf, "[0] ingress: connection closed");
1936 ctx->conn_closed = TRUE;
1937 break;
1938 }
1939 else {
1940 CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread);
1941 data_max_bytes = (data_max_bytes > (size_t)nread)?
1942 (data_max_bytes - (size_t)nread) : 0;
1943 }
1944
1945 if(h2_process_pending_input(cf, data, &result))
1946 return result;
1947 }
1948
1949 if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
1950 connclose(cf->conn, "GOAWAY received");
1951 }
1952
1953 return CURLE_OK;
1954 }
1955
cf_h2_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1956 static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1957 char *buf, size_t len, CURLcode *err)
1958 {
1959 struct cf_h2_ctx *ctx = cf->ctx;
1960 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
1961 ssize_t nread = -1;
1962 CURLcode result;
1963 struct cf_call_data save;
1964
1965 if(!stream) {
1966 /* Abnormal call sequence: either this transfer has never opened a stream
1967 * (unlikely) or the transfer has been done, cleaned up its resources, but
1968 * a read() is called anyway. It is not clear what the calling sequence
1969 * is for such a case. */
1970 failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
1971 "or already cleared", (ssize_t)data->id,
1972 (ssize_t)cf->conn->connection_id);
1973 *err = CURLE_HTTP2;
1974 return -1;
1975 }
1976
1977 CF_DATA_SAVE(save, cf, data);
1978
1979 nread = stream_recv(cf, data, stream, buf, len, err);
1980 if(nread < 0 && *err != CURLE_AGAIN)
1981 goto out;
1982
1983 if(nread < 0) {
1984 *err = h2_progress_ingress(cf, data, len);
1985 if(*err)
1986 goto out;
1987
1988 nread = stream_recv(cf, data, stream, buf, len, err);
1989 }
1990
1991 if(nread > 0) {
1992 size_t data_consumed = (size_t)nread;
1993 /* Now that we transferred this to the upper layer, we report
1994 * the actual amount of DATA consumed to the H2 session, so
1995 * that it adjusts stream flow control */
1996 if(stream->resp_hds_len >= data_consumed) {
1997 stream->resp_hds_len -= data_consumed; /* no DATA */
1998 }
1999 else {
2000 if(stream->resp_hds_len) {
2001 data_consumed -= stream->resp_hds_len;
2002 stream->resp_hds_len = 0;
2003 }
2004 if(data_consumed) {
2005 nghttp2_session_consume(ctx->h2, stream->id, data_consumed);
2006 }
2007 }
2008
2009 if(stream->closed) {
2010 CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
2011 drain_stream(cf, data, stream);
2012 }
2013 }
2014
2015 out:
2016 result = h2_progress_egress(cf, data);
2017 if(result == CURLE_AGAIN) {
2018 /* pending data to send, need to be called again. Ideally, we'd
2019 * monitor the socket for POLLOUT, but we might not be in SENDING
2020 * transfer state any longer and are unable to make this happen.
2021 */
2022 drain_stream(cf, data, stream);
2023 }
2024 else if(result) {
2025 *err = result;
2026 nread = -1;
2027 }
2028 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
2029 "window=%d/%d, connection %d/%d",
2030 stream->id, len, nread, *err,
2031 nghttp2_session_get_stream_effective_recv_data_length(
2032 ctx->h2, stream->id),
2033 nghttp2_session_get_stream_effective_local_window_size(
2034 ctx->h2, stream->id),
2035 nghttp2_session_get_local_window_size(ctx->h2),
2036 HTTP2_HUGE_WINDOW_SIZE);
2037
2038 CF_DATA_RESTORE(cf, save);
2039 return nread;
2040 }
2041
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)2042 static ssize_t h2_submit(struct h2_stream_ctx **pstream,
2043 struct Curl_cfilter *cf, struct Curl_easy *data,
2044 const void *buf, size_t len,
2045 size_t *phdslen, CURLcode *err)
2046 {
2047 struct cf_h2_ctx *ctx = cf->ctx;
2048 struct h2_stream_ctx *stream = NULL;
2049 struct dynhds h2_headers;
2050 nghttp2_nv *nva = NULL;
2051 const void *body = NULL;
2052 size_t nheader, bodylen, i;
2053 nghttp2_data_provider data_prd;
2054 int32_t stream_id;
2055 nghttp2_priority_spec pri_spec;
2056 ssize_t nwritten;
2057
2058 *phdslen = 0;
2059 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
2060
2061 *err = http2_data_setup(cf, data, &stream);
2062 if(*err) {
2063 nwritten = -1;
2064 goto out;
2065 }
2066
2067 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
2068 if(nwritten < 0)
2069 goto out;
2070 *phdslen = (size_t)nwritten;
2071 if(!stream->h1.done) {
2072 /* need more data */
2073 goto out;
2074 }
2075 DEBUGASSERT(stream->h1.req);
2076
2077 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
2078 if(*err) {
2079 nwritten = -1;
2080 goto out;
2081 }
2082 /* no longer needed */
2083 Curl_h1_req_parse_free(&stream->h1);
2084
2085 nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
2086 if(!nva) {
2087 *err = CURLE_OUT_OF_MEMORY;
2088 nwritten = -1;
2089 goto out;
2090 }
2091
2092 h2_pri_spec(ctx, data, &pri_spec);
2093 if(!nghttp2_session_check_request_allowed(ctx->h2))
2094 CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
2095
2096 switch(data->state.httpreq) {
2097 case HTTPREQ_POST:
2098 case HTTPREQ_POST_FORM:
2099 case HTTPREQ_POST_MIME:
2100 case HTTPREQ_PUT:
2101 if(data->state.infilesize != -1)
2102 stream->upload_left = data->state.infilesize;
2103 else
2104 /* data sending without specifying the data amount up front */
2105 stream->upload_left = -1; /* unknown */
2106
2107 data_prd.read_callback = req_body_read_callback;
2108 data_prd.source.ptr = NULL;
2109 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2110 &data_prd, data);
2111 break;
2112 default:
2113 stream->upload_left = 0; /* no request body */
2114 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2115 NULL, data);
2116 }
2117
2118 if(stream_id < 0) {
2119 CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
2120 nghttp2_strerror(stream_id), stream_id);
2121 *err = CURLE_SEND_ERROR;
2122 nwritten = -1;
2123 goto out;
2124 }
2125
2126 #define MAX_ACC 60000 /* <64KB to account for some overhead */
2127 if(Curl_trc_is_verbose(data)) {
2128 size_t acc = 0;
2129
2130 infof(data, "[HTTP/2] [%d] OPENED stream for %s",
2131 stream_id, data->state.url);
2132 for(i = 0; i < nheader; ++i) {
2133 acc += nva[i].namelen + nva[i].valuelen;
2134
2135 infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
2136 (int)nva[i].namelen, nva[i].name,
2137 (int)nva[i].valuelen, nva[i].value);
2138 }
2139
2140 if(acc > MAX_ACC) {
2141 infof(data, "[HTTP/2] Warning: The cumulative length of all "
2142 "headers exceeds %d bytes and that could cause the "
2143 "stream to be rejected.", MAX_ACC);
2144 }
2145 }
2146
2147 stream->id = stream_id;
2148 stream->local_window_size = H2_STREAM_WINDOW_SIZE;
2149 if(data->set.max_recv_speed) {
2150 /* We are asked to only receive `max_recv_speed` bytes per second.
2151 * Let's limit our stream window size around that, otherwise the server
2152 * will send in large bursts only. We make the window 50% larger to
2153 * allow for data in flight and avoid stalling. */
2154 curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
2155 n += CURLMAX((n/2), 1);
2156 if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
2157 n < (UINT_MAX / H2_CHUNK_SIZE)) {
2158 stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
2159 }
2160 }
2161
2162 body = (const char *)buf + nwritten;
2163 bodylen = len - nwritten;
2164
2165 if(bodylen) {
2166 /* We have request body to send in DATA frame */
2167 ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
2168 if(n < 0) {
2169 *err = CURLE_SEND_ERROR;
2170 nwritten = -1;
2171 goto out;
2172 }
2173 nwritten += n;
2174 }
2175
2176 out:
2177 CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
2178 stream? stream->id : -1, nwritten, *err);
2179 Curl_safefree(nva);
2180 *pstream = stream;
2181 Curl_dynhds_free(&h2_headers);
2182 return nwritten;
2183 }
2184
cf_h2_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)2185 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
2186 const void *buf, size_t len, CURLcode *err)
2187 {
2188 struct cf_h2_ctx *ctx = cf->ctx;
2189 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2190 struct cf_call_data save;
2191 int rv;
2192 ssize_t nwritten;
2193 size_t hdslen = 0;
2194 CURLcode result;
2195 int blocked = 0, was_blocked = 0;
2196
2197 CF_DATA_SAVE(save, cf, data);
2198
2199 if(stream && stream->id != -1) {
2200 if(stream->upload_blocked_len) {
2201 /* the data in `buf` has already been submitted or added to the
2202 * buffers, but have been EAGAINed on the last invocation. */
2203 /* TODO: this assertion triggers in OSSFuzz runs and it is not
2204 * clear why. Disable for now to let OSSFuzz continue its tests. */
2205 DEBUGASSERT(len >= stream->upload_blocked_len);
2206 if(len < stream->upload_blocked_len) {
2207 /* Did we get called again with a smaller `len`? This should not
2208 * happen. We are not prepared to handle that. */
2209 failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)",
2210 len, stream->upload_blocked_len);
2211 *err = CURLE_HTTP2;
2212 nwritten = -1;
2213 goto out;
2214 }
2215 nwritten = (ssize_t)stream->upload_blocked_len;
2216 stream->upload_blocked_len = 0;
2217 was_blocked = 1;
2218 }
2219 else if(stream->closed) {
2220 if(stream->resp_hds_complete) {
2221 /* Server decided to close the stream after having sent us a findl
2222 * response. This is valid if it is not interested in the request
2223 * body. This happens on 30x or 40x responses.
2224 * We silently discard the data sent, since this is not a transport
2225 * error situation. */
2226 CURL_TRC_CF(data, cf, "[%d] discarding data"
2227 "on closed stream with response", stream->id);
2228 *err = CURLE_OK;
2229 nwritten = (ssize_t)len;
2230 goto out;
2231 }
2232 infof(data, "stream %u closed", stream->id);
2233 *err = CURLE_SEND_ERROR;
2234 nwritten = -1;
2235 goto out;
2236 }
2237 else {
2238 /* If stream_id != -1, we have dispatched request HEADERS and
2239 * optionally request body, and now are going to send or sending
2240 * more request body in DATA frame */
2241 nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
2242 if(nwritten < 0 && *err != CURLE_AGAIN)
2243 goto out;
2244 }
2245
2246 if(!Curl_bufq_is_empty(&stream->sendbuf)) {
2247 /* req body data is buffered, resume the potentially suspended stream */
2248 rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2249 if(nghttp2_is_fatal(rv)) {
2250 *err = CURLE_SEND_ERROR;
2251 nwritten = -1;
2252 goto out;
2253 }
2254 }
2255 }
2256 else {
2257 nwritten = h2_submit(&stream, cf, data, buf, len, &hdslen, err);
2258 if(nwritten < 0) {
2259 goto out;
2260 }
2261 DEBUGASSERT(stream);
2262 DEBUGASSERT(hdslen <= (size_t)nwritten);
2263 }
2264
2265 /* Call the nghttp2 send loop and flush to write ALL buffered data,
2266 * headers and/or request body completely out to the network */
2267 result = h2_progress_egress(cf, data);
2268 /* if the stream has been closed in egress handling (nghttp2 does that
2269 * when it does not like the headers, for example */
2270 if(stream && stream->closed && !was_blocked) {
2271 infof(data, "stream %u closed", stream->id);
2272 *err = CURLE_SEND_ERROR;
2273 nwritten = -1;
2274 goto out;
2275 }
2276 else if(result == CURLE_AGAIN) {
2277 blocked = 1;
2278 }
2279 else if(result) {
2280 *err = result;
2281 nwritten = -1;
2282 goto out;
2283 }
2284 else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
2285 /* although we wrote everything that nghttp2 wants to send now,
2286 * there is data left in our stream send buffer unwritten. This may
2287 * be due to the stream's HTTP/2 flow window being exhausted. */
2288 blocked = 1;
2289 }
2290
2291 if(stream && blocked && nwritten > 0) {
2292 /* Unable to send all data, due to connection blocked or H2 window
2293 * exhaustion. Data is left in our stream buffer, or nghttp2's internal
2294 * frame buffer or our network out buffer. */
2295 size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
2296 stream->id);
2297 /* At the start of a stream, we are called with request headers
2298 * and, possibly, parts of the body. Later, only body data.
2299 * If we cannot send pure body data, we EAGAIN. If there had been
2300 * header, we return that *they* have been written and remember the
2301 * block on the data length only. */
2302 stream->upload_blocked_len = ((size_t)nwritten) - hdslen;
2303 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
2304 "hds_len=%zu blocked_len=%zu",
2305 stream->id, len,
2306 nghttp2_session_get_remote_window_size(ctx->h2), rwin,
2307 hdslen, stream->upload_blocked_len);
2308 if(hdslen) {
2309 *err = CURLE_OK;
2310 nwritten = hdslen;
2311 }
2312 else {
2313 *err = CURLE_AGAIN;
2314 nwritten = -1;
2315 goto out;
2316 }
2317 }
2318 else if(should_close_session(ctx)) {
2319 /* nghttp2 thinks this session is done. If the stream has not been
2320 * closed, this is an error state for out transfer */
2321 if(stream->closed) {
2322 nwritten = http2_handle_stream_close(cf, data, stream, err);
2323 }
2324 else {
2325 CURL_TRC_CF(data, cf, "send: nothing to do in this session");
2326 *err = CURLE_HTTP2;
2327 nwritten = -1;
2328 }
2329 }
2330
2331 out:
2332 if(stream) {
2333 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
2334 "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
2335 "h2 windows %d-%d (stream-conn), "
2336 "buffers %zu-%zu (stream-conn)",
2337 stream->id, len, nwritten, *err,
2338 stream->upload_left,
2339 nghttp2_session_get_stream_remote_window_size(
2340 ctx->h2, stream->id),
2341 nghttp2_session_get_remote_window_size(ctx->h2),
2342 Curl_bufq_len(&stream->sendbuf),
2343 Curl_bufq_len(&ctx->outbufq));
2344 }
2345 else {
2346 CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
2347 "connection-window=%d, nw_send_buffer(%zu)",
2348 len, nwritten, *err,
2349 nghttp2_session_get_remote_window_size(ctx->h2),
2350 Curl_bufq_len(&ctx->outbufq));
2351 }
2352 CF_DATA_RESTORE(cf, save);
2353 return nwritten;
2354 }
2355
cf_h2_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)2356 static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
2357 struct Curl_easy *data,
2358 struct easy_pollset *ps)
2359 {
2360 struct cf_h2_ctx *ctx = cf->ctx;
2361 curl_socket_t sock;
2362 bool want_recv, want_send;
2363
2364 if(!ctx->h2)
2365 return;
2366
2367 sock = Curl_conn_cf_get_socket(cf, data);
2368 Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
2369 if(want_recv || want_send) {
2370 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2371 struct cf_call_data save;
2372 bool c_exhaust, s_exhaust;
2373
2374 CF_DATA_SAVE(save, cf, data);
2375 c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
2376 s_exhaust = want_send && stream && stream->id >= 0 &&
2377 !nghttp2_session_get_stream_remote_window_size(ctx->h2,
2378 stream->id);
2379 want_recv = (want_recv || c_exhaust || s_exhaust);
2380 want_send = (!s_exhaust && want_send) ||
2381 (!c_exhaust && nghttp2_session_want_write(ctx->h2));
2382
2383 Curl_pollset_set(data, ps, sock, want_recv, want_send);
2384 CF_DATA_RESTORE(cf, save);
2385 }
2386 }
2387
cf_h2_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)2388 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
2389 struct Curl_easy *data,
2390 bool blocking, bool *done)
2391 {
2392 struct cf_h2_ctx *ctx = cf->ctx;
2393 CURLcode result = CURLE_OK;
2394 struct cf_call_data save;
2395
2396 if(cf->connected) {
2397 *done = TRUE;
2398 return CURLE_OK;
2399 }
2400
2401 /* Connect the lower filters first */
2402 if(!cf->next->connected) {
2403 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2404 if(result || !*done)
2405 return result;
2406 }
2407
2408 *done = FALSE;
2409
2410 CF_DATA_SAVE(save, cf, data);
2411 if(!ctx->h2) {
2412 result = cf_h2_ctx_init(cf, data, FALSE);
2413 if(result)
2414 goto out;
2415 }
2416
2417 result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE);
2418 if(result)
2419 goto out;
2420
2421 /* Send out our SETTINGS and ACKs and such. If that blocks, we
2422 * have it buffered and can count this filter as being connected */
2423 result = h2_progress_egress(cf, data);
2424 if(result == CURLE_AGAIN)
2425 result = CURLE_OK;
2426 else if(result)
2427 goto out;
2428
2429 *done = TRUE;
2430 cf->connected = TRUE;
2431 result = CURLE_OK;
2432
2433 out:
2434 CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
2435 CF_DATA_RESTORE(cf, save);
2436 return result;
2437 }
2438
cf_h2_close(struct Curl_cfilter * cf,struct Curl_easy * data)2439 static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2440 {
2441 struct cf_h2_ctx *ctx = cf->ctx;
2442
2443 if(ctx) {
2444 struct cf_call_data save;
2445
2446 CF_DATA_SAVE(save, cf, data);
2447 cf_h2_ctx_clear(ctx);
2448 CF_DATA_RESTORE(cf, save);
2449 }
2450 if(cf->next)
2451 cf->next->cft->do_close(cf->next, data);
2452 }
2453
cf_h2_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)2454 static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2455 {
2456 struct cf_h2_ctx *ctx = cf->ctx;
2457
2458 (void)data;
2459 if(ctx) {
2460 cf_h2_ctx_free(ctx);
2461 cf->ctx = NULL;
2462 }
2463 }
2464
http2_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)2465 static CURLcode http2_data_pause(struct Curl_cfilter *cf,
2466 struct Curl_easy *data,
2467 bool pause)
2468 {
2469 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2470 struct cf_h2_ctx *ctx = cf->ctx;
2471 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2472
2473 DEBUGASSERT(data);
2474 if(ctx && ctx->h2 && stream) {
2475 uint32_t window = pause? 0 : stream->local_window_size;
2476
2477 int rv = nghttp2_session_set_local_window_size(ctx->h2,
2478 NGHTTP2_FLAG_NONE,
2479 stream->id,
2480 window);
2481 if(rv) {
2482 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2483 nghttp2_strerror(rv), rv);
2484 return CURLE_HTTP2;
2485 }
2486
2487 if(!pause)
2488 drain_stream(cf, data, stream);
2489
2490 /* attempt to send the window update */
2491 (void)h2_progress_egress(cf, data);
2492
2493 if(!pause) {
2494 /* Unpausing a h2 transfer, requires it to be run again. The server
2495 * may send new DATA on us increasing the flow window, and it may
2496 * not. We may have already buffered and exhausted the new window
2497 * by operating on things in flight during the handling of other
2498 * transfers. */
2499 drain_stream(cf, data, stream);
2500 Curl_expire(data, 0, EXPIRE_RUN_NOW);
2501 }
2502 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2503 window, stream->id));
2504
2505 #ifdef DEBUGBUILD
2506 {
2507 /* read out the stream local window again */
2508 uint32_t window2 =
2509 nghttp2_session_get_stream_local_window_size(ctx->h2,
2510 stream->id);
2511 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2512 window2, stream->id));
2513 }
2514 #endif
2515 }
2516 #endif
2517 return CURLE_OK;
2518 }
2519
cf_h2_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)2520 static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
2521 struct Curl_easy *data,
2522 int event, int arg1, void *arg2)
2523 {
2524 CURLcode result = CURLE_OK;
2525 struct cf_call_data save;
2526
2527 (void)arg2;
2528
2529 CF_DATA_SAVE(save, cf, data);
2530 switch(event) {
2531 case CF_CTRL_DATA_SETUP:
2532 break;
2533 case CF_CTRL_DATA_PAUSE:
2534 result = http2_data_pause(cf, data, (arg1 != 0));
2535 break;
2536 case CF_CTRL_DATA_DONE_SEND:
2537 result = http2_data_done_send(cf, data);
2538 break;
2539 case CF_CTRL_DATA_DETACH:
2540 http2_data_done(cf, data);
2541 break;
2542 case CF_CTRL_DATA_DONE:
2543 http2_data_done(cf, data);
2544 break;
2545 default:
2546 break;
2547 }
2548 CF_DATA_RESTORE(cf, save);
2549 return result;
2550 }
2551
cf_h2_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)2552 static bool cf_h2_data_pending(struct Curl_cfilter *cf,
2553 const struct Curl_easy *data)
2554 {
2555 struct cf_h2_ctx *ctx = cf->ctx;
2556 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2557
2558 if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
2559 || (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
2560 return TRUE;
2561 return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
2562 }
2563
cf_h2_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2564 static bool cf_h2_is_alive(struct Curl_cfilter *cf,
2565 struct Curl_easy *data,
2566 bool *input_pending)
2567 {
2568 struct cf_h2_ctx *ctx = cf->ctx;
2569 CURLcode result;
2570 struct cf_call_data save;
2571
2572 CF_DATA_SAVE(save, cf, data);
2573 result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
2574 CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
2575 result, *input_pending);
2576 CF_DATA_RESTORE(cf, save);
2577 return result;
2578 }
2579
cf_h2_keep_alive(struct Curl_cfilter * cf,struct Curl_easy * data)2580 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
2581 struct Curl_easy *data)
2582 {
2583 CURLcode result;
2584 struct cf_call_data save;
2585
2586 CF_DATA_SAVE(save, cf, data);
2587 result = http2_send_ping(cf, data);
2588 CF_DATA_RESTORE(cf, save);
2589 return result;
2590 }
2591
cf_h2_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2592 static CURLcode cf_h2_query(struct Curl_cfilter *cf,
2593 struct Curl_easy *data,
2594 int query, int *pres1, void *pres2)
2595 {
2596 struct cf_h2_ctx *ctx = cf->ctx;
2597 struct cf_call_data save;
2598 size_t effective_max;
2599
2600 switch(query) {
2601 case CF_QUERY_MAX_CONCURRENT:
2602 DEBUGASSERT(pres1);
2603
2604 CF_DATA_SAVE(save, cf, data);
2605 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
2606 /* the limit is what we have in use right now */
2607 effective_max = CONN_INUSE(cf->conn);
2608 }
2609 else {
2610 effective_max = ctx->max_concurrent_streams;
2611 }
2612 *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
2613 CF_DATA_RESTORE(cf, save);
2614 return CURLE_OK;
2615 case CF_QUERY_STREAM_ERROR: {
2616 struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
2617 *pres1 = stream? (int)stream->error : 0;
2618 return CURLE_OK;
2619 }
2620 default:
2621 break;
2622 }
2623 return cf->next?
2624 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2625 CURLE_UNKNOWN_OPTION;
2626 }
2627
2628 struct Curl_cftype Curl_cft_nghttp2 = {
2629 "HTTP/2",
2630 CF_TYPE_MULTIPLEX,
2631 CURL_LOG_LVL_NONE,
2632 cf_h2_destroy,
2633 cf_h2_connect,
2634 cf_h2_close,
2635 Curl_cf_def_get_host,
2636 cf_h2_adjust_pollset,
2637 cf_h2_data_pending,
2638 cf_h2_send,
2639 cf_h2_recv,
2640 cf_h2_cntrl,
2641 cf_h2_is_alive,
2642 cf_h2_keep_alive,
2643 cf_h2_query,
2644 };
2645
http2_cfilter_add(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,int sockindex,bool via_h1_upgrade)2646 static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
2647 struct Curl_easy *data,
2648 struct connectdata *conn,
2649 int sockindex,
2650 bool via_h1_upgrade)
2651 {
2652 struct Curl_cfilter *cf = NULL;
2653 struct cf_h2_ctx *ctx;
2654 CURLcode result = CURLE_OUT_OF_MEMORY;
2655
2656 DEBUGASSERT(data->conn);
2657 ctx = calloc(1, sizeof(*ctx));
2658 if(!ctx)
2659 goto out;
2660
2661 result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
2662 if(result)
2663 goto out;
2664
2665 ctx = NULL;
2666 Curl_conn_cf_add(data, conn, sockindex, cf);
2667 result = cf_h2_ctx_init(cf, data, via_h1_upgrade);
2668
2669 out:
2670 if(result)
2671 cf_h2_ctx_free(ctx);
2672 *pcf = result? NULL : cf;
2673 return result;
2674 }
2675
http2_cfilter_insert_after(struct Curl_cfilter * cf,struct Curl_easy * data,bool via_h1_upgrade)2676 static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
2677 struct Curl_easy *data,
2678 bool via_h1_upgrade)
2679 {
2680 struct Curl_cfilter *cf_h2 = NULL;
2681 struct cf_h2_ctx *ctx;
2682 CURLcode result = CURLE_OUT_OF_MEMORY;
2683
2684 (void)data;
2685 ctx = calloc(1, sizeof(*ctx));
2686 if(!ctx)
2687 goto out;
2688
2689 result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
2690 if(result)
2691 goto out;
2692
2693 ctx = NULL;
2694 Curl_conn_cf_insert_after(cf, cf_h2);
2695 result = cf_h2_ctx_init(cf_h2, data, via_h1_upgrade);
2696
2697 out:
2698 if(result)
2699 cf_h2_ctx_free(ctx);
2700 return result;
2701 }
2702
Curl_cf_is_http2(struct Curl_cfilter * cf,const struct Curl_easy * data)2703 static bool Curl_cf_is_http2(struct Curl_cfilter *cf,
2704 const struct Curl_easy *data)
2705 {
2706 (void)data;
2707 for(; cf; cf = cf->next) {
2708 if(cf->cft == &Curl_cft_nghttp2)
2709 return TRUE;
2710 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2711 return FALSE;
2712 }
2713 return FALSE;
2714 }
2715
Curl_conn_is_http2(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)2716 bool Curl_conn_is_http2(const struct Curl_easy *data,
2717 const struct connectdata *conn,
2718 int sockindex)
2719 {
2720 return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
2721 }
2722
Curl_http2_may_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2723 bool Curl_http2_may_switch(struct Curl_easy *data,
2724 struct connectdata *conn,
2725 int sockindex)
2726 {
2727 (void)sockindex;
2728 if(!Curl_conn_is_http2(data, conn, sockindex) &&
2729 data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
2730 #ifndef CURL_DISABLE_PROXY
2731 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
2732 /* We don't support HTTP/2 proxies yet. Also it's debatable
2733 whether or not this setting should apply to HTTP/2 proxies. */
2734 infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
2735 return FALSE;
2736 }
2737 #endif
2738 return TRUE;
2739 }
2740 return FALSE;
2741 }
2742
Curl_http2_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2743 CURLcode Curl_http2_switch(struct Curl_easy *data,
2744 struct connectdata *conn, int sockindex)
2745 {
2746 struct Curl_cfilter *cf;
2747 CURLcode result;
2748
2749 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2750 DEBUGF(infof(data, "switching to HTTP/2"));
2751
2752 result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
2753 if(result)
2754 return result;
2755
2756 conn->httpversion = 20; /* we know we're on HTTP/2 now */
2757 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2758 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2759 Curl_multi_connchanged(data->multi);
2760
2761 if(cf->next) {
2762 bool done;
2763 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2764 }
2765 return CURLE_OK;
2766 }
2767
Curl_http2_switch_at(struct Curl_cfilter * cf,struct Curl_easy * data)2768 CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
2769 {
2770 struct Curl_cfilter *cf_h2;
2771 CURLcode result;
2772
2773 DEBUGASSERT(!Curl_cf_is_http2(cf, data));
2774
2775 result = http2_cfilter_insert_after(cf, data, FALSE);
2776 if(result)
2777 return result;
2778
2779 cf_h2 = cf->next;
2780 cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
2781 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2782 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2783 Curl_multi_connchanged(data->multi);
2784
2785 if(cf_h2->next) {
2786 bool done;
2787 return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
2788 }
2789 return CURLE_OK;
2790 }
2791
Curl_http2_upgrade(struct Curl_easy * data,struct connectdata * conn,int sockindex,const char * mem,size_t nread)2792 CURLcode Curl_http2_upgrade(struct Curl_easy *data,
2793 struct connectdata *conn, int sockindex,
2794 const char *mem, size_t nread)
2795 {
2796 struct Curl_cfilter *cf;
2797 struct cf_h2_ctx *ctx;
2798 CURLcode result;
2799
2800 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2801 DEBUGF(infof(data, "upgrading to HTTP/2"));
2802 DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
2803
2804 result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
2805 if(result)
2806 return result;
2807
2808 DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
2809 ctx = cf->ctx;
2810
2811 if(nread > 0) {
2812 /* Remaining data from the protocol switch reply is already using
2813 * the switched protocol, ie. HTTP/2. We add that to the network
2814 * inbufq. */
2815 ssize_t copied;
2816
2817 copied = Curl_bufq_write(&ctx->inbufq,
2818 (const unsigned char *)mem, nread, &result);
2819 if(copied < 0) {
2820 failf(data, "error on copying HTTP Upgrade response: %d", result);
2821 return CURLE_RECV_ERROR;
2822 }
2823 if((size_t)copied < nread) {
2824 failf(data, "connection buffer size could not take all data "
2825 "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
2826 copied, nread);
2827 return CURLE_HTTP2;
2828 }
2829 infof(data, "Copied HTTP/2 data in stream buffer to connection buffer"
2830 " after upgrade: len=%zu", nread);
2831 }
2832
2833 conn->httpversion = 20; /* we know we're on HTTP/2 now */
2834 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2835 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2836 Curl_multi_connchanged(data->multi);
2837
2838 if(cf->next) {
2839 bool done;
2840 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2841 }
2842 return CURLE_OK;
2843 }
2844
2845 /* Only call this function for a transfer that already got an HTTP/2
2846 CURLE_HTTP2_STREAM error! */
Curl_h2_http_1_1_error(struct Curl_easy * data)2847 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2848 {
2849 if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
2850 int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
2851 return (err == NGHTTP2_HTTP_1_1_REQUIRED);
2852 }
2853 return FALSE;
2854 }
2855
2856 #else /* !USE_NGHTTP2 */
2857
2858 /* Satisfy external references even if http2 is not compiled in. */
2859 #include <curl/curl.h>
2860
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)2861 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2862 {
2863 (void) h;
2864 (void) num;
2865 return NULL;
2866 }
2867
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)2868 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2869 {
2870 (void) h;
2871 (void) header;
2872 return NULL;
2873 }
2874
2875 #endif /* USE_NGHTTP2 */
2876