VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/http2.c

最後變更 在這個檔案是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette