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