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