113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifdef USE_NGHTTP2
2813498266Sopenharmony_ci#include <stdint.h>
2913498266Sopenharmony_ci#include <nghttp2/nghttp2.h>
3013498266Sopenharmony_ci#include "urldata.h"
3113498266Sopenharmony_ci#include "bufq.h"
3213498266Sopenharmony_ci#include "http1.h"
3313498266Sopenharmony_ci#include "http2.h"
3413498266Sopenharmony_ci#include "http.h"
3513498266Sopenharmony_ci#include "sendf.h"
3613498266Sopenharmony_ci#include "select.h"
3713498266Sopenharmony_ci#include "curl_base64.h"
3813498266Sopenharmony_ci#include "strcase.h"
3913498266Sopenharmony_ci#include "multiif.h"
4013498266Sopenharmony_ci#include "url.h"
4113498266Sopenharmony_ci#include "urlapi-int.h"
4213498266Sopenharmony_ci#include "cfilters.h"
4313498266Sopenharmony_ci#include "connect.h"
4413498266Sopenharmony_ci#include "rand.h"
4513498266Sopenharmony_ci#include "strtoofft.h"
4613498266Sopenharmony_ci#include "strdup.h"
4713498266Sopenharmony_ci#include "transfer.h"
4813498266Sopenharmony_ci#include "dynbuf.h"
4913498266Sopenharmony_ci#include "headers.h"
5013498266Sopenharmony_ci/* The last 3 #include files should be in this order */
5113498266Sopenharmony_ci#include "curl_printf.h"
5213498266Sopenharmony_ci#include "curl_memory.h"
5313498266Sopenharmony_ci#include "memdebug.h"
5413498266Sopenharmony_ci
5513498266Sopenharmony_ci#if (NGHTTP2_VERSION_NUM < 0x010c00)
5613498266Sopenharmony_ci#error too old nghttp2 version, upgrade!
5713498266Sopenharmony_ci#endif
5813498266Sopenharmony_ci
5913498266Sopenharmony_ci#ifdef CURL_DISABLE_VERBOSE_STRINGS
6013498266Sopenharmony_ci#define nghttp2_session_callbacks_set_error_callback(x,y)
6113498266Sopenharmony_ci#endif
6213498266Sopenharmony_ci
6313498266Sopenharmony_ci#if (NGHTTP2_VERSION_NUM >= 0x010c00)
6413498266Sopenharmony_ci#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
6513498266Sopenharmony_ci#endif
6613498266Sopenharmony_ci
6713498266Sopenharmony_ci
6813498266Sopenharmony_ci/* buffer dimensioning:
6913498266Sopenharmony_ci * use 16K as chunk size, as that fits H2 DATA frames well */
7013498266Sopenharmony_ci#define H2_CHUNK_SIZE           (16 * 1024)
7113498266Sopenharmony_ci/* this is how much we want "in flight" for a stream */
7213498266Sopenharmony_ci#define H2_STREAM_WINDOW_SIZE   (10 * 1024 * 1024)
7313498266Sopenharmony_ci/* on receiving from TLS, we prep for holding a full stream window */
7413498266Sopenharmony_ci#define H2_NW_RECV_CHUNKS       (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
7513498266Sopenharmony_ci/* on send into TLS, we just want to accumulate small frames */
7613498266Sopenharmony_ci#define H2_NW_SEND_CHUNKS       1
7713498266Sopenharmony_ci/* stream recv/send chunks are a result of window / chunk sizes */
7813498266Sopenharmony_ci#define H2_STREAM_RECV_CHUNKS   (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
7913498266Sopenharmony_ci/* keep smaller stream upload buffer (default h2 window size) to have
8013498266Sopenharmony_ci * our progress bars and "upload done" reporting closer to reality */
8113498266Sopenharmony_ci#define H2_STREAM_SEND_CHUNKS   ((64 * 1024) / H2_CHUNK_SIZE)
8213498266Sopenharmony_ci/* spare chunks we keep for a full window */
8313498266Sopenharmony_ci#define H2_STREAM_POOL_SPARES   (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
8413498266Sopenharmony_ci
8513498266Sopenharmony_ci/* We need to accommodate the max number of streams with their window
8613498266Sopenharmony_ci * sizes on the overall connection. Streams might become PAUSED which
8713498266Sopenharmony_ci * will block their received QUOTA in the connection window. And if we
8813498266Sopenharmony_ci * run out of space, the server is blocked from sending us any data.
8913498266Sopenharmony_ci * See #10988 for an issue with this. */
9013498266Sopenharmony_ci#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE)
9113498266Sopenharmony_ci
9213498266Sopenharmony_ci#define H2_SETTINGS_IV_LEN  3
9313498266Sopenharmony_ci#define H2_BINSETTINGS_LEN 80
9413498266Sopenharmony_ci
9513498266Sopenharmony_cistatic int populate_settings(nghttp2_settings_entry *iv,
9613498266Sopenharmony_ci                             struct Curl_easy *data)
9713498266Sopenharmony_ci{
9813498266Sopenharmony_ci  iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
9913498266Sopenharmony_ci  iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
10013498266Sopenharmony_ci
10113498266Sopenharmony_ci  iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
10213498266Sopenharmony_ci  iv[1].value = H2_STREAM_WINDOW_SIZE;
10313498266Sopenharmony_ci
10413498266Sopenharmony_ci  iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
10513498266Sopenharmony_ci  iv[2].value = data->multi->push_cb != NULL;
10613498266Sopenharmony_ci
10713498266Sopenharmony_ci  return 3;
10813498266Sopenharmony_ci}
10913498266Sopenharmony_ci
11013498266Sopenharmony_cistatic ssize_t populate_binsettings(uint8_t *binsettings,
11113498266Sopenharmony_ci                                    struct Curl_easy *data)
11213498266Sopenharmony_ci{
11313498266Sopenharmony_ci  nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
11413498266Sopenharmony_ci  int ivlen;
11513498266Sopenharmony_ci
11613498266Sopenharmony_ci  ivlen = populate_settings(iv, data);
11713498266Sopenharmony_ci  /* this returns number of bytes it wrote or a negative number on error. */
11813498266Sopenharmony_ci  return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
11913498266Sopenharmony_ci                                       iv, ivlen);
12013498266Sopenharmony_ci}
12113498266Sopenharmony_ci
12213498266Sopenharmony_cistruct cf_h2_ctx {
12313498266Sopenharmony_ci  nghttp2_session *h2;
12413498266Sopenharmony_ci  uint32_t max_concurrent_streams;
12513498266Sopenharmony_ci  /* The easy handle used in the current filter call, cleared at return */
12613498266Sopenharmony_ci  struct cf_call_data call_data;
12713498266Sopenharmony_ci
12813498266Sopenharmony_ci  struct bufq inbufq;           /* network input */
12913498266Sopenharmony_ci  struct bufq outbufq;          /* network output */
13013498266Sopenharmony_ci  struct bufc_pool stream_bufcp; /* spares for stream buffers */
13113498266Sopenharmony_ci
13213498266Sopenharmony_ci  size_t drain_total; /* sum of all stream's UrlState drain */
13313498266Sopenharmony_ci  int32_t goaway_error;
13413498266Sopenharmony_ci  int32_t last_stream_id;
13513498266Sopenharmony_ci  BIT(conn_closed);
13613498266Sopenharmony_ci  BIT(goaway);
13713498266Sopenharmony_ci  BIT(enable_push);
13813498266Sopenharmony_ci  BIT(nw_out_blocked);
13913498266Sopenharmony_ci};
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci/* How to access `call_data` from a cf_h2 filter */
14213498266Sopenharmony_ci#undef CF_CTX_CALL_DATA
14313498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf)  \
14413498266Sopenharmony_ci  ((struct cf_h2_ctx *)(cf)->ctx)->call_data
14513498266Sopenharmony_ci
14613498266Sopenharmony_cistatic void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
14713498266Sopenharmony_ci{
14813498266Sopenharmony_ci  struct cf_call_data save = ctx->call_data;
14913498266Sopenharmony_ci
15013498266Sopenharmony_ci  if(ctx->h2) {
15113498266Sopenharmony_ci    nghttp2_session_del(ctx->h2);
15213498266Sopenharmony_ci  }
15313498266Sopenharmony_ci  Curl_bufq_free(&ctx->inbufq);
15413498266Sopenharmony_ci  Curl_bufq_free(&ctx->outbufq);
15513498266Sopenharmony_ci  Curl_bufcp_free(&ctx->stream_bufcp);
15613498266Sopenharmony_ci  memset(ctx, 0, sizeof(*ctx));
15713498266Sopenharmony_ci  ctx->call_data = save;
15813498266Sopenharmony_ci}
15913498266Sopenharmony_ci
16013498266Sopenharmony_cistatic void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
16113498266Sopenharmony_ci{
16213498266Sopenharmony_ci  if(ctx) {
16313498266Sopenharmony_ci    cf_h2_ctx_clear(ctx);
16413498266Sopenharmony_ci    free(ctx);
16513498266Sopenharmony_ci  }
16613498266Sopenharmony_ci}
16713498266Sopenharmony_ci
16813498266Sopenharmony_cistatic CURLcode h2_progress_egress(struct Curl_cfilter *cf,
16913498266Sopenharmony_ci                                  struct Curl_easy *data);
17013498266Sopenharmony_ci
17113498266Sopenharmony_ci/**
17213498266Sopenharmony_ci * All about the H3 internals of a stream
17313498266Sopenharmony_ci */
17413498266Sopenharmony_cistruct stream_ctx {
17513498266Sopenharmony_ci  /*********** for HTTP/2 we store stream-local data here *************/
17613498266Sopenharmony_ci  int32_t id; /* HTTP/2 protocol identifier for stream */
17713498266Sopenharmony_ci  struct bufq recvbuf; /* response buffer */
17813498266Sopenharmony_ci  struct bufq sendbuf; /* request buffer */
17913498266Sopenharmony_ci  struct h1_req_parser h1; /* parsing the request */
18013498266Sopenharmony_ci  struct dynhds resp_trailers; /* response trailer fields */
18113498266Sopenharmony_ci  size_t resp_hds_len; /* amount of response header bytes in recvbuf */
18213498266Sopenharmony_ci  size_t upload_blocked_len;
18313498266Sopenharmony_ci  curl_off_t upload_left; /* number of request bytes left to upload */
18413498266Sopenharmony_ci
18513498266Sopenharmony_ci  char **push_headers;       /* allocated array */
18613498266Sopenharmony_ci  size_t push_headers_used;  /* number of entries filled in */
18713498266Sopenharmony_ci  size_t push_headers_alloc; /* number of entries allocated */
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci  int status_code; /* HTTP response status code */
19013498266Sopenharmony_ci  uint32_t error; /* stream error code */
19113498266Sopenharmony_ci  uint32_t local_window_size; /* the local recv window size */
19213498266Sopenharmony_ci  bool resp_hds_complete; /* we have a complete, final response */
19313498266Sopenharmony_ci  bool closed; /* TRUE on stream close */
19413498266Sopenharmony_ci  bool reset;  /* TRUE on stream reset */
19513498266Sopenharmony_ci  bool close_handled; /* TRUE if stream closure is handled by libcurl */
19613498266Sopenharmony_ci  bool bodystarted;
19713498266Sopenharmony_ci  bool send_closed; /* transfer is done sending, we might have still
19813498266Sopenharmony_ci                        buffered data in stream->sendbuf to upload. */
19913498266Sopenharmony_ci};
20013498266Sopenharmony_ci
20113498266Sopenharmony_ci#define H2_STREAM_CTX(d)    ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
20213498266Sopenharmony_ci                             ((struct HTTP *)(d)->req.p.http)->h2_ctx \
20313498266Sopenharmony_ci                               : NULL))
20413498266Sopenharmony_ci#define H2_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h2_ctx
20513498266Sopenharmony_ci#define H2_STREAM_ID(d)     (H2_STREAM_CTX(d)? \
20613498266Sopenharmony_ci                             H2_STREAM_CTX(d)->id : -2)
20713498266Sopenharmony_ci
20813498266Sopenharmony_ci/*
20913498266Sopenharmony_ci * Mark this transfer to get "drained".
21013498266Sopenharmony_ci */
21113498266Sopenharmony_cistatic void drain_stream(struct Curl_cfilter *cf,
21213498266Sopenharmony_ci                         struct Curl_easy *data,
21313498266Sopenharmony_ci                         struct stream_ctx *stream)
21413498266Sopenharmony_ci{
21513498266Sopenharmony_ci  unsigned char bits;
21613498266Sopenharmony_ci
21713498266Sopenharmony_ci  (void)cf;
21813498266Sopenharmony_ci  bits = CURL_CSELECT_IN;
21913498266Sopenharmony_ci  if(!stream->send_closed &&
22013498266Sopenharmony_ci     (stream->upload_left || stream->upload_blocked_len))
22113498266Sopenharmony_ci    bits |= CURL_CSELECT_OUT;
22213498266Sopenharmony_ci  if(data->state.select_bits != bits) {
22313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
22413498266Sopenharmony_ci                stream->id, bits);
22513498266Sopenharmony_ci    data->state.select_bits = bits;
22613498266Sopenharmony_ci    Curl_expire(data, 0, EXPIRE_RUN_NOW);
22713498266Sopenharmony_ci  }
22813498266Sopenharmony_ci}
22913498266Sopenharmony_ci
23013498266Sopenharmony_cistatic CURLcode http2_data_setup(struct Curl_cfilter *cf,
23113498266Sopenharmony_ci                                 struct Curl_easy *data,
23213498266Sopenharmony_ci                                 struct stream_ctx **pstream)
23313498266Sopenharmony_ci{
23413498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
23513498266Sopenharmony_ci  struct stream_ctx *stream;
23613498266Sopenharmony_ci
23713498266Sopenharmony_ci  (void)cf;
23813498266Sopenharmony_ci  DEBUGASSERT(data);
23913498266Sopenharmony_ci  if(!data->req.p.http) {
24013498266Sopenharmony_ci    failf(data, "initialization failure, transfer not http initialized");
24113498266Sopenharmony_ci    return CURLE_FAILED_INIT;
24213498266Sopenharmony_ci  }
24313498266Sopenharmony_ci  stream = H2_STREAM_CTX(data);
24413498266Sopenharmony_ci  if(stream) {
24513498266Sopenharmony_ci    *pstream = stream;
24613498266Sopenharmony_ci    return CURLE_OK;
24713498266Sopenharmony_ci  }
24813498266Sopenharmony_ci
24913498266Sopenharmony_ci  stream = calloc(1, sizeof(*stream));
25013498266Sopenharmony_ci  if(!stream)
25113498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
25213498266Sopenharmony_ci
25313498266Sopenharmony_ci  stream->id = -1;
25413498266Sopenharmony_ci  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
25513498266Sopenharmony_ci                  H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
25613498266Sopenharmony_ci  Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
25713498266Sopenharmony_ci                  H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
25813498266Sopenharmony_ci  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
25913498266Sopenharmony_ci  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
26013498266Sopenharmony_ci  stream->resp_hds_len = 0;
26113498266Sopenharmony_ci  stream->bodystarted = FALSE;
26213498266Sopenharmony_ci  stream->status_code = -1;
26313498266Sopenharmony_ci  stream->closed = FALSE;
26413498266Sopenharmony_ci  stream->close_handled = FALSE;
26513498266Sopenharmony_ci  stream->error = NGHTTP2_NO_ERROR;
26613498266Sopenharmony_ci  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
26713498266Sopenharmony_ci  stream->upload_left = 0;
26813498266Sopenharmony_ci
26913498266Sopenharmony_ci  H2_STREAM_LCTX(data) = stream;
27013498266Sopenharmony_ci  *pstream = stream;
27113498266Sopenharmony_ci  return CURLE_OK;
27213498266Sopenharmony_ci}
27313498266Sopenharmony_ci
27413498266Sopenharmony_cistatic void free_push_headers(struct stream_ctx *stream)
27513498266Sopenharmony_ci{
27613498266Sopenharmony_ci  size_t i;
27713498266Sopenharmony_ci  for(i = 0; i<stream->push_headers_used; i++)
27813498266Sopenharmony_ci    free(stream->push_headers[i]);
27913498266Sopenharmony_ci  Curl_safefree(stream->push_headers);
28013498266Sopenharmony_ci  stream->push_headers_used = 0;
28113498266Sopenharmony_ci}
28213498266Sopenharmony_ci
28313498266Sopenharmony_cistatic void http2_data_done(struct Curl_cfilter *cf,
28413498266Sopenharmony_ci                            struct Curl_easy *data, bool premature)
28513498266Sopenharmony_ci{
28613498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
28713498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
28813498266Sopenharmony_ci
28913498266Sopenharmony_ci  DEBUGASSERT(ctx);
29013498266Sopenharmony_ci  (void)premature;
29113498266Sopenharmony_ci  if(!stream)
29213498266Sopenharmony_ci    return;
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci  if(ctx->h2) {
29513498266Sopenharmony_ci    bool flush_egress = FALSE;
29613498266Sopenharmony_ci    /* returns error if stream not known, which is fine here */
29713498266Sopenharmony_ci    (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci    if(!stream->closed && stream->id > 0) {
30013498266Sopenharmony_ci      /* RST_STREAM */
30113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
30213498266Sopenharmony_ci                  stream->id);
30313498266Sopenharmony_ci      stream->closed = TRUE;
30413498266Sopenharmony_ci      stream->reset = TRUE;
30513498266Sopenharmony_ci      stream->send_closed = TRUE;
30613498266Sopenharmony_ci      nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
30713498266Sopenharmony_ci                                stream->id, NGHTTP2_STREAM_CLOSED);
30813498266Sopenharmony_ci      flush_egress = TRUE;
30913498266Sopenharmony_ci    }
31013498266Sopenharmony_ci    if(!Curl_bufq_is_empty(&stream->recvbuf)) {
31113498266Sopenharmony_ci      /* Anything in the recvbuf is still being counted
31213498266Sopenharmony_ci       * in stream and connection window flow control. Need
31313498266Sopenharmony_ci       * to free that space or the connection window might get
31413498266Sopenharmony_ci       * exhausted eventually. */
31513498266Sopenharmony_ci      nghttp2_session_consume(ctx->h2, stream->id,
31613498266Sopenharmony_ci                              Curl_bufq_len(&stream->recvbuf));
31713498266Sopenharmony_ci      /* give WINDOW_UPATE a chance to be sent, but ignore any error */
31813498266Sopenharmony_ci      flush_egress = TRUE;
31913498266Sopenharmony_ci    }
32013498266Sopenharmony_ci
32113498266Sopenharmony_ci    if(flush_egress)
32213498266Sopenharmony_ci      nghttp2_session_send(ctx->h2);
32313498266Sopenharmony_ci  }
32413498266Sopenharmony_ci
32513498266Sopenharmony_ci  Curl_bufq_free(&stream->sendbuf);
32613498266Sopenharmony_ci  Curl_bufq_free(&stream->recvbuf);
32713498266Sopenharmony_ci  Curl_h1_req_parse_free(&stream->h1);
32813498266Sopenharmony_ci  Curl_dynhds_free(&stream->resp_trailers);
32913498266Sopenharmony_ci  free_push_headers(stream);
33013498266Sopenharmony_ci  free(stream);
33113498266Sopenharmony_ci  H2_STREAM_LCTX(data) = NULL;
33213498266Sopenharmony_ci}
33313498266Sopenharmony_ci
33413498266Sopenharmony_cistatic int h2_client_new(struct Curl_cfilter *cf,
33513498266Sopenharmony_ci                         nghttp2_session_callbacks *cbs)
33613498266Sopenharmony_ci{
33713498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
33813498266Sopenharmony_ci  nghttp2_option *o;
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  int rc = nghttp2_option_new(&o);
34113498266Sopenharmony_ci  if(rc)
34213498266Sopenharmony_ci    return rc;
34313498266Sopenharmony_ci  /* We handle window updates ourself to enforce buffer limits */
34413498266Sopenharmony_ci  nghttp2_option_set_no_auto_window_update(o, 1);
34513498266Sopenharmony_ci#if NGHTTP2_VERSION_NUM >= 0x013200
34613498266Sopenharmony_ci  /* with 1.50.0 */
34713498266Sopenharmony_ci  /* turn off RFC 9113 leading and trailing white spaces validation against
34813498266Sopenharmony_ci     HTTP field value. */
34913498266Sopenharmony_ci  nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
35013498266Sopenharmony_ci#endif
35113498266Sopenharmony_ci  rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
35213498266Sopenharmony_ci  nghttp2_option_del(o);
35313498266Sopenharmony_ci  return rc;
35413498266Sopenharmony_ci}
35513498266Sopenharmony_ci
35613498266Sopenharmony_cistatic ssize_t nw_in_reader(void *reader_ctx,
35713498266Sopenharmony_ci                              unsigned char *buf, size_t buflen,
35813498266Sopenharmony_ci                              CURLcode *err)
35913498266Sopenharmony_ci{
36013498266Sopenharmony_ci  struct Curl_cfilter *cf = reader_ctx;
36113498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
36213498266Sopenharmony_ci
36313498266Sopenharmony_ci  return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
36413498266Sopenharmony_ci}
36513498266Sopenharmony_ci
36613498266Sopenharmony_cistatic ssize_t nw_out_writer(void *writer_ctx,
36713498266Sopenharmony_ci                             const unsigned char *buf, size_t buflen,
36813498266Sopenharmony_ci                             CURLcode *err)
36913498266Sopenharmony_ci{
37013498266Sopenharmony_ci  struct Curl_cfilter *cf = writer_ctx;
37113498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
37213498266Sopenharmony_ci
37313498266Sopenharmony_ci  if(data) {
37413498266Sopenharmony_ci    ssize_t nwritten = Curl_conn_cf_send(cf->next, data,
37513498266Sopenharmony_ci                                         (const char *)buf, buflen, err);
37613498266Sopenharmony_ci    if(nwritten > 0)
37713498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
37813498266Sopenharmony_ci    return nwritten;
37913498266Sopenharmony_ci  }
38013498266Sopenharmony_ci  return 0;
38113498266Sopenharmony_ci}
38213498266Sopenharmony_ci
38313498266Sopenharmony_cistatic ssize_t send_callback(nghttp2_session *h2,
38413498266Sopenharmony_ci                             const uint8_t *mem, size_t length, int flags,
38513498266Sopenharmony_ci                             void *userp);
38613498266Sopenharmony_cistatic int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
38713498266Sopenharmony_ci                         void *userp);
38813498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
38913498266Sopenharmony_cistatic int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
39013498266Sopenharmony_ci                         void *userp);
39113498266Sopenharmony_ci#endif
39213498266Sopenharmony_cistatic int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
39313498266Sopenharmony_ci                              int32_t stream_id,
39413498266Sopenharmony_ci                              const uint8_t *mem, size_t len, void *userp);
39513498266Sopenharmony_cistatic int on_stream_close(nghttp2_session *session, int32_t stream_id,
39613498266Sopenharmony_ci                           uint32_t error_code, void *userp);
39713498266Sopenharmony_cistatic int on_begin_headers(nghttp2_session *session,
39813498266Sopenharmony_ci                            const nghttp2_frame *frame, void *userp);
39913498266Sopenharmony_cistatic int on_header(nghttp2_session *session, const nghttp2_frame *frame,
40013498266Sopenharmony_ci                     const uint8_t *name, size_t namelen,
40113498266Sopenharmony_ci                     const uint8_t *value, size_t valuelen,
40213498266Sopenharmony_ci                     uint8_t flags,
40313498266Sopenharmony_ci                     void *userp);
40413498266Sopenharmony_cistatic int error_callback(nghttp2_session *session, const char *msg,
40513498266Sopenharmony_ci                          size_t len, void *userp);
40613498266Sopenharmony_ci
40713498266Sopenharmony_ci/*
40813498266Sopenharmony_ci * Initialize the cfilter context
40913498266Sopenharmony_ci */
41013498266Sopenharmony_cistatic CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
41113498266Sopenharmony_ci                               struct Curl_easy *data,
41213498266Sopenharmony_ci                               bool via_h1_upgrade)
41313498266Sopenharmony_ci{
41413498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
41513498266Sopenharmony_ci  struct stream_ctx *stream;
41613498266Sopenharmony_ci  CURLcode result = CURLE_OUT_OF_MEMORY;
41713498266Sopenharmony_ci  int rc;
41813498266Sopenharmony_ci  nghttp2_session_callbacks *cbs = NULL;
41913498266Sopenharmony_ci
42013498266Sopenharmony_ci  DEBUGASSERT(!ctx->h2);
42113498266Sopenharmony_ci  Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
42213498266Sopenharmony_ci  Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
42313498266Sopenharmony_ci  Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
42413498266Sopenharmony_ci  ctx->last_stream_id = 2147483647;
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci  rc = nghttp2_session_callbacks_new(&cbs);
42713498266Sopenharmony_ci  if(rc) {
42813498266Sopenharmony_ci    failf(data, "Couldn't initialize nghttp2 callbacks");
42913498266Sopenharmony_ci    goto out;
43013498266Sopenharmony_ci  }
43113498266Sopenharmony_ci
43213498266Sopenharmony_ci  nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
43313498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
43413498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
43513498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
43613498266Sopenharmony_ci#endif
43713498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
43813498266Sopenharmony_ci    cbs, on_data_chunk_recv);
43913498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
44013498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_begin_headers_callback(
44113498266Sopenharmony_ci    cbs, on_begin_headers);
44213498266Sopenharmony_ci  nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
44313498266Sopenharmony_ci  nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
44413498266Sopenharmony_ci
44513498266Sopenharmony_ci  /* The nghttp2 session is not yet setup, do it */
44613498266Sopenharmony_ci  rc = h2_client_new(cf, cbs);
44713498266Sopenharmony_ci  if(rc) {
44813498266Sopenharmony_ci    failf(data, "Couldn't initialize nghttp2");
44913498266Sopenharmony_ci    goto out;
45013498266Sopenharmony_ci  }
45113498266Sopenharmony_ci  ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
45213498266Sopenharmony_ci
45313498266Sopenharmony_ci  if(via_h1_upgrade) {
45413498266Sopenharmony_ci    /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
45513498266Sopenharmony_ci     * in the H1 request and we upgrade from there. This stream
45613498266Sopenharmony_ci     * is opened implicitly as #1. */
45713498266Sopenharmony_ci    uint8_t binsettings[H2_BINSETTINGS_LEN];
45813498266Sopenharmony_ci    ssize_t binlen; /* length of the binsettings data */
45913498266Sopenharmony_ci
46013498266Sopenharmony_ci    binlen = populate_binsettings(binsettings, data);
46113498266Sopenharmony_ci    if(binlen <= 0) {
46213498266Sopenharmony_ci      failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
46313498266Sopenharmony_ci      result = CURLE_FAILED_INIT;
46413498266Sopenharmony_ci      goto out;
46513498266Sopenharmony_ci    }
46613498266Sopenharmony_ci
46713498266Sopenharmony_ci    result = http2_data_setup(cf, data, &stream);
46813498266Sopenharmony_ci    if(result)
46913498266Sopenharmony_ci      goto out;
47013498266Sopenharmony_ci    DEBUGASSERT(stream);
47113498266Sopenharmony_ci    stream->id = 1;
47213498266Sopenharmony_ci    /* queue SETTINGS frame (again) */
47313498266Sopenharmony_ci    rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
47413498266Sopenharmony_ci                                  data->state.httpreq == HTTPREQ_HEAD,
47513498266Sopenharmony_ci                                  NULL);
47613498266Sopenharmony_ci    if(rc) {
47713498266Sopenharmony_ci      failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
47813498266Sopenharmony_ci            nghttp2_strerror(rc), rc);
47913498266Sopenharmony_ci      result = CURLE_HTTP2;
48013498266Sopenharmony_ci      goto out;
48113498266Sopenharmony_ci    }
48213498266Sopenharmony_ci
48313498266Sopenharmony_ci    rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
48413498266Sopenharmony_ci                                              data);
48513498266Sopenharmony_ci    if(rc) {
48613498266Sopenharmony_ci      infof(data, "http/2: failed to set user_data for stream %u",
48713498266Sopenharmony_ci            stream->id);
48813498266Sopenharmony_ci      DEBUGASSERT(0);
48913498266Sopenharmony_ci    }
49013498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "created session via Upgrade");
49113498266Sopenharmony_ci  }
49213498266Sopenharmony_ci  else {
49313498266Sopenharmony_ci    nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
49413498266Sopenharmony_ci    int ivlen;
49513498266Sopenharmony_ci
49613498266Sopenharmony_ci    ivlen = populate_settings(iv, data);
49713498266Sopenharmony_ci    rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
49813498266Sopenharmony_ci                                 iv, ivlen);
49913498266Sopenharmony_ci    if(rc) {
50013498266Sopenharmony_ci      failf(data, "nghttp2_submit_settings() failed: %s(%d)",
50113498266Sopenharmony_ci            nghttp2_strerror(rc), rc);
50213498266Sopenharmony_ci      result = CURLE_HTTP2;
50313498266Sopenharmony_ci      goto out;
50413498266Sopenharmony_ci    }
50513498266Sopenharmony_ci  }
50613498266Sopenharmony_ci
50713498266Sopenharmony_ci  rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
50813498266Sopenharmony_ci                                             HTTP2_HUGE_WINDOW_SIZE);
50913498266Sopenharmony_ci  if(rc) {
51013498266Sopenharmony_ci    failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
51113498266Sopenharmony_ci          nghttp2_strerror(rc), rc);
51213498266Sopenharmony_ci    result = CURLE_HTTP2;
51313498266Sopenharmony_ci    goto out;
51413498266Sopenharmony_ci  }
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci  /* all set, traffic will be send on connect */
51713498266Sopenharmony_ci  result = CURLE_OK;
51813498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[0] created h2 session%s",
51913498266Sopenharmony_ci              via_h1_upgrade? " (via h1 upgrade)" : "");
52013498266Sopenharmony_ci
52113498266Sopenharmony_ciout:
52213498266Sopenharmony_ci  if(cbs)
52313498266Sopenharmony_ci    nghttp2_session_callbacks_del(cbs);
52413498266Sopenharmony_ci  return result;
52513498266Sopenharmony_ci}
52613498266Sopenharmony_ci
52713498266Sopenharmony_ci/*
52813498266Sopenharmony_ci * Returns nonzero if current HTTP/2 session should be closed.
52913498266Sopenharmony_ci */
53013498266Sopenharmony_cistatic int should_close_session(struct cf_h2_ctx *ctx)
53113498266Sopenharmony_ci{
53213498266Sopenharmony_ci  return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) &&
53313498266Sopenharmony_ci    !nghttp2_session_want_write(ctx->h2);
53413498266Sopenharmony_ci}
53513498266Sopenharmony_ci
53613498266Sopenharmony_ci/*
53713498266Sopenharmony_ci * Processes pending input left in network input buffer.
53813498266Sopenharmony_ci * This function returns 0 if it succeeds, or -1 and error code will
53913498266Sopenharmony_ci * be assigned to *err.
54013498266Sopenharmony_ci */
54113498266Sopenharmony_cistatic int h2_process_pending_input(struct Curl_cfilter *cf,
54213498266Sopenharmony_ci                                    struct Curl_easy *data,
54313498266Sopenharmony_ci                                    CURLcode *err)
54413498266Sopenharmony_ci{
54513498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
54613498266Sopenharmony_ci  const unsigned char *buf;
54713498266Sopenharmony_ci  size_t blen;
54813498266Sopenharmony_ci  ssize_t rv;
54913498266Sopenharmony_ci
55013498266Sopenharmony_ci  while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) {
55113498266Sopenharmony_ci
55213498266Sopenharmony_ci    rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
55313498266Sopenharmony_ci    if(rv < 0) {
55413498266Sopenharmony_ci      failf(data,
55513498266Sopenharmony_ci            "process_pending_input: nghttp2_session_mem_recv() returned "
55613498266Sopenharmony_ci            "%zd:%s", rv, nghttp2_strerror((int)rv));
55713498266Sopenharmony_ci      *err = CURLE_RECV_ERROR;
55813498266Sopenharmony_ci      return -1;
55913498266Sopenharmony_ci    }
56013498266Sopenharmony_ci    Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
56113498266Sopenharmony_ci    if(Curl_bufq_is_empty(&ctx->inbufq)) {
56213498266Sopenharmony_ci      break;
56313498266Sopenharmony_ci    }
56413498266Sopenharmony_ci    else {
56513498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left "
56613498266Sopenharmony_ci                  "in connection buffer", Curl_bufq_len(&ctx->inbufq));
56713498266Sopenharmony_ci    }
56813498266Sopenharmony_ci  }
56913498266Sopenharmony_ci
57013498266Sopenharmony_ci  if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
57113498266Sopenharmony_ci    /* No more requests are allowed in the current session, so
57213498266Sopenharmony_ci       the connection may not be reused. This is set when a
57313498266Sopenharmony_ci       GOAWAY frame has been received or when the limit of stream
57413498266Sopenharmony_ci       identifiers has been reached. */
57513498266Sopenharmony_ci    connclose(cf->conn, "http/2: No new requests allowed");
57613498266Sopenharmony_ci  }
57713498266Sopenharmony_ci
57813498266Sopenharmony_ci  return 0;
57913498266Sopenharmony_ci}
58013498266Sopenharmony_ci
58113498266Sopenharmony_ci/*
58213498266Sopenharmony_ci * The server may send us data at any point (e.g. PING frames). Therefore,
58313498266Sopenharmony_ci * we cannot assume that an HTTP/2 socket is dead just because it is readable.
58413498266Sopenharmony_ci *
58513498266Sopenharmony_ci * Check the lower filters first and, if successful, peek at the socket
58613498266Sopenharmony_ci * and distinguish between closed and data.
58713498266Sopenharmony_ci */
58813498266Sopenharmony_cistatic bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
58913498266Sopenharmony_ci                              bool *input_pending)
59013498266Sopenharmony_ci{
59113498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
59213498266Sopenharmony_ci  bool alive = TRUE;
59313498266Sopenharmony_ci
59413498266Sopenharmony_ci  *input_pending = FALSE;
59513498266Sopenharmony_ci  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
59613498266Sopenharmony_ci    return FALSE;
59713498266Sopenharmony_ci
59813498266Sopenharmony_ci  if(*input_pending) {
59913498266Sopenharmony_ci    /* This happens before we've sent off a request and the connection is
60013498266Sopenharmony_ci       not in use by any other transfer, there shouldn't be any data here,
60113498266Sopenharmony_ci       only "protocol frames" */
60213498266Sopenharmony_ci    CURLcode result;
60313498266Sopenharmony_ci    ssize_t nread = -1;
60413498266Sopenharmony_ci
60513498266Sopenharmony_ci    *input_pending = FALSE;
60613498266Sopenharmony_ci    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
60713498266Sopenharmony_ci    if(nread != -1) {
60813498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying "
60913498266Sopenharmony_ci                  "h2 connection", nread);
61013498266Sopenharmony_ci      if(h2_process_pending_input(cf, data, &result) < 0)
61113498266Sopenharmony_ci        /* immediate error, considered dead */
61213498266Sopenharmony_ci        alive = FALSE;
61313498266Sopenharmony_ci      else {
61413498266Sopenharmony_ci        alive = !should_close_session(ctx);
61513498266Sopenharmony_ci      }
61613498266Sopenharmony_ci    }
61713498266Sopenharmony_ci    else if(result != CURLE_AGAIN) {
61813498266Sopenharmony_ci      /* the read failed so let's say this is dead anyway */
61913498266Sopenharmony_ci      alive = FALSE;
62013498266Sopenharmony_ci    }
62113498266Sopenharmony_ci  }
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci  return alive;
62413498266Sopenharmony_ci}
62513498266Sopenharmony_ci
62613498266Sopenharmony_cistatic CURLcode http2_send_ping(struct Curl_cfilter *cf,
62713498266Sopenharmony_ci                                struct Curl_easy *data)
62813498266Sopenharmony_ci{
62913498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
63013498266Sopenharmony_ci  int rc;
63113498266Sopenharmony_ci
63213498266Sopenharmony_ci  rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL);
63313498266Sopenharmony_ci  if(rc) {
63413498266Sopenharmony_ci    failf(data, "nghttp2_submit_ping() failed: %s(%d)",
63513498266Sopenharmony_ci          nghttp2_strerror(rc), rc);
63613498266Sopenharmony_ci   return CURLE_HTTP2;
63713498266Sopenharmony_ci  }
63813498266Sopenharmony_ci
63913498266Sopenharmony_ci  rc = nghttp2_session_send(ctx->h2);
64013498266Sopenharmony_ci  if(rc) {
64113498266Sopenharmony_ci    failf(data, "nghttp2_session_send() failed: %s(%d)",
64213498266Sopenharmony_ci          nghttp2_strerror(rc), rc);
64313498266Sopenharmony_ci    return CURLE_SEND_ERROR;
64413498266Sopenharmony_ci  }
64513498266Sopenharmony_ci  return CURLE_OK;
64613498266Sopenharmony_ci}
64713498266Sopenharmony_ci
64813498266Sopenharmony_ci/*
64913498266Sopenharmony_ci * Store nghttp2 version info in this buffer.
65013498266Sopenharmony_ci */
65113498266Sopenharmony_civoid Curl_http2_ver(char *p, size_t len)
65213498266Sopenharmony_ci{
65313498266Sopenharmony_ci  nghttp2_info *h2 = nghttp2_version(0);
65413498266Sopenharmony_ci  (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
65513498266Sopenharmony_ci}
65613498266Sopenharmony_ci
65713498266Sopenharmony_cistatic CURLcode nw_out_flush(struct Curl_cfilter *cf,
65813498266Sopenharmony_ci                             struct Curl_easy *data)
65913498266Sopenharmony_ci{
66013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
66113498266Sopenharmony_ci  ssize_t nwritten;
66213498266Sopenharmony_ci  CURLcode result;
66313498266Sopenharmony_ci
66413498266Sopenharmony_ci  (void)data;
66513498266Sopenharmony_ci  if(Curl_bufq_is_empty(&ctx->outbufq))
66613498266Sopenharmony_ci    return CURLE_OK;
66713498266Sopenharmony_ci
66813498266Sopenharmony_ci  nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
66913498266Sopenharmony_ci  if(nwritten < 0) {
67013498266Sopenharmony_ci    if(result == CURLE_AGAIN) {
67113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
67213498266Sopenharmony_ci                  Curl_bufq_len(&ctx->outbufq));
67313498266Sopenharmony_ci      ctx->nw_out_blocked = 1;
67413498266Sopenharmony_ci    }
67513498266Sopenharmony_ci    return result;
67613498266Sopenharmony_ci  }
67713498266Sopenharmony_ci  return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
67813498266Sopenharmony_ci}
67913498266Sopenharmony_ci
68013498266Sopenharmony_ci/*
68113498266Sopenharmony_ci * The implementation of nghttp2_send_callback type. Here we write |data| with
68213498266Sopenharmony_ci * size |length| to the network and return the number of bytes actually
68313498266Sopenharmony_ci * written. See the documentation of nghttp2_send_callback for the details.
68413498266Sopenharmony_ci */
68513498266Sopenharmony_cistatic ssize_t send_callback(nghttp2_session *h2,
68613498266Sopenharmony_ci                             const uint8_t *buf, size_t blen, int flags,
68713498266Sopenharmony_ci                             void *userp)
68813498266Sopenharmony_ci{
68913498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
69013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
69113498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
69213498266Sopenharmony_ci  ssize_t nwritten;
69313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
69413498266Sopenharmony_ci
69513498266Sopenharmony_ci  (void)h2;
69613498266Sopenharmony_ci  (void)flags;
69713498266Sopenharmony_ci  DEBUGASSERT(data);
69813498266Sopenharmony_ci
69913498266Sopenharmony_ci  nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
70013498266Sopenharmony_ci                                  nw_out_writer, cf, &result);
70113498266Sopenharmony_ci  if(nwritten < 0) {
70213498266Sopenharmony_ci    if(result == CURLE_AGAIN) {
70313498266Sopenharmony_ci      ctx->nw_out_blocked = 1;
70413498266Sopenharmony_ci      return NGHTTP2_ERR_WOULDBLOCK;
70513498266Sopenharmony_ci    }
70613498266Sopenharmony_ci    failf(data, "Failed sending HTTP2 data");
70713498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
70813498266Sopenharmony_ci  }
70913498266Sopenharmony_ci
71013498266Sopenharmony_ci  if(!nwritten) {
71113498266Sopenharmony_ci    ctx->nw_out_blocked = 1;
71213498266Sopenharmony_ci    return NGHTTP2_ERR_WOULDBLOCK;
71313498266Sopenharmony_ci  }
71413498266Sopenharmony_ci  return nwritten;
71513498266Sopenharmony_ci}
71613498266Sopenharmony_ci
71713498266Sopenharmony_ci
71813498266Sopenharmony_ci/* We pass a pointer to this struct in the push callback, but the contents of
71913498266Sopenharmony_ci   the struct are hidden from the user. */
72013498266Sopenharmony_cistruct curl_pushheaders {
72113498266Sopenharmony_ci  struct Curl_easy *data;
72213498266Sopenharmony_ci  const nghttp2_push_promise *frame;
72313498266Sopenharmony_ci};
72413498266Sopenharmony_ci
72513498266Sopenharmony_ci/*
72613498266Sopenharmony_ci * push header access function. Only to be used from within the push callback
72713498266Sopenharmony_ci */
72813498266Sopenharmony_cichar *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
72913498266Sopenharmony_ci{
73013498266Sopenharmony_ci  /* Verify that we got a good easy handle in the push header struct, mostly to
73113498266Sopenharmony_ci     detect rubbish input fast(er). */
73213498266Sopenharmony_ci  if(!h || !GOOD_EASY_HANDLE(h->data))
73313498266Sopenharmony_ci    return NULL;
73413498266Sopenharmony_ci  else {
73513498266Sopenharmony_ci    struct stream_ctx *stream = H2_STREAM_CTX(h->data);
73613498266Sopenharmony_ci    if(stream && num < stream->push_headers_used)
73713498266Sopenharmony_ci      return stream->push_headers[num];
73813498266Sopenharmony_ci  }
73913498266Sopenharmony_ci  return NULL;
74013498266Sopenharmony_ci}
74113498266Sopenharmony_ci
74213498266Sopenharmony_ci/*
74313498266Sopenharmony_ci * push header access function. Only to be used from within the push callback
74413498266Sopenharmony_ci */
74513498266Sopenharmony_cichar *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
74613498266Sopenharmony_ci{
74713498266Sopenharmony_ci  struct stream_ctx *stream;
74813498266Sopenharmony_ci  size_t len;
74913498266Sopenharmony_ci  size_t i;
75013498266Sopenharmony_ci  /* Verify that we got a good easy handle in the push header struct,
75113498266Sopenharmony_ci     mostly to detect rubbish input fast(er). Also empty header name
75213498266Sopenharmony_ci     is just a rubbish too. We have to allow ":" at the beginning of
75313498266Sopenharmony_ci     the header, but header == ":" must be rejected. If we have ':' in
75413498266Sopenharmony_ci     the middle of header, it could be matched in middle of the value,
75513498266Sopenharmony_ci     this is because we do prefix match.*/
75613498266Sopenharmony_ci  if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
75713498266Sopenharmony_ci     !strcmp(header, ":") || strchr(header + 1, ':'))
75813498266Sopenharmony_ci    return NULL;
75913498266Sopenharmony_ci
76013498266Sopenharmony_ci  stream = H2_STREAM_CTX(h->data);
76113498266Sopenharmony_ci  if(!stream)
76213498266Sopenharmony_ci    return NULL;
76313498266Sopenharmony_ci
76413498266Sopenharmony_ci  len = strlen(header);
76513498266Sopenharmony_ci  for(i = 0; i<stream->push_headers_used; i++) {
76613498266Sopenharmony_ci    if(!strncmp(header, stream->push_headers[i], len)) {
76713498266Sopenharmony_ci      /* sub-match, make sure that it is followed by a colon */
76813498266Sopenharmony_ci      if(stream->push_headers[i][len] != ':')
76913498266Sopenharmony_ci        continue;
77013498266Sopenharmony_ci      return &stream->push_headers[i][len + 1];
77113498266Sopenharmony_ci    }
77213498266Sopenharmony_ci  }
77313498266Sopenharmony_ci  return NULL;
77413498266Sopenharmony_ci}
77513498266Sopenharmony_ci
77613498266Sopenharmony_cistatic struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
77713498266Sopenharmony_ci                                      struct Curl_easy *data)
77813498266Sopenharmony_ci{
77913498266Sopenharmony_ci  struct Curl_easy *second = curl_easy_duphandle(data);
78013498266Sopenharmony_ci  if(second) {
78113498266Sopenharmony_ci    /* setup the request struct */
78213498266Sopenharmony_ci    struct HTTP *http = calloc(1, sizeof(struct HTTP));
78313498266Sopenharmony_ci    if(!http) {
78413498266Sopenharmony_ci      (void)Curl_close(&second);
78513498266Sopenharmony_ci    }
78613498266Sopenharmony_ci    else {
78713498266Sopenharmony_ci      struct stream_ctx *second_stream;
78813498266Sopenharmony_ci
78913498266Sopenharmony_ci      second->req.p.http = http;
79013498266Sopenharmony_ci      http2_data_setup(cf, second, &second_stream);
79113498266Sopenharmony_ci      second->state.priority.weight = data->state.priority.weight;
79213498266Sopenharmony_ci    }
79313498266Sopenharmony_ci  }
79413498266Sopenharmony_ci  return second;
79513498266Sopenharmony_ci}
79613498266Sopenharmony_ci
79713498266Sopenharmony_cistatic int set_transfer_url(struct Curl_easy *data,
79813498266Sopenharmony_ci                            struct curl_pushheaders *hp)
79913498266Sopenharmony_ci{
80013498266Sopenharmony_ci  const char *v;
80113498266Sopenharmony_ci  CURLUcode uc;
80213498266Sopenharmony_ci  char *url = NULL;
80313498266Sopenharmony_ci  int rc = 0;
80413498266Sopenharmony_ci  CURLU *u = curl_url();
80513498266Sopenharmony_ci
80613498266Sopenharmony_ci  if(!u)
80713498266Sopenharmony_ci    return 5;
80813498266Sopenharmony_ci
80913498266Sopenharmony_ci  v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
81013498266Sopenharmony_ci  if(v) {
81113498266Sopenharmony_ci    uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
81213498266Sopenharmony_ci    if(uc) {
81313498266Sopenharmony_ci      rc = 1;
81413498266Sopenharmony_ci      goto fail;
81513498266Sopenharmony_ci    }
81613498266Sopenharmony_ci  }
81713498266Sopenharmony_ci
81813498266Sopenharmony_ci  v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
81913498266Sopenharmony_ci  if(v) {
82013498266Sopenharmony_ci    uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER);
82113498266Sopenharmony_ci    if(uc) {
82213498266Sopenharmony_ci      rc = 2;
82313498266Sopenharmony_ci      goto fail;
82413498266Sopenharmony_ci    }
82513498266Sopenharmony_ci  }
82613498266Sopenharmony_ci
82713498266Sopenharmony_ci  v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH);
82813498266Sopenharmony_ci  if(v) {
82913498266Sopenharmony_ci    uc = curl_url_set(u, CURLUPART_PATH, v, 0);
83013498266Sopenharmony_ci    if(uc) {
83113498266Sopenharmony_ci      rc = 3;
83213498266Sopenharmony_ci      goto fail;
83313498266Sopenharmony_ci    }
83413498266Sopenharmony_ci  }
83513498266Sopenharmony_ci
83613498266Sopenharmony_ci  uc = curl_url_get(u, CURLUPART_URL, &url, 0);
83713498266Sopenharmony_ci  if(uc)
83813498266Sopenharmony_ci    rc = 4;
83913498266Sopenharmony_cifail:
84013498266Sopenharmony_ci  curl_url_cleanup(u);
84113498266Sopenharmony_ci  if(rc)
84213498266Sopenharmony_ci    return rc;
84313498266Sopenharmony_ci
84413498266Sopenharmony_ci  if(data->state.url_alloc)
84513498266Sopenharmony_ci    free(data->state.url);
84613498266Sopenharmony_ci  data->state.url_alloc = TRUE;
84713498266Sopenharmony_ci  data->state.url = url;
84813498266Sopenharmony_ci  return 0;
84913498266Sopenharmony_ci}
85013498266Sopenharmony_ci
85113498266Sopenharmony_cistatic void discard_newhandle(struct Curl_cfilter *cf,
85213498266Sopenharmony_ci                              struct Curl_easy *newhandle)
85313498266Sopenharmony_ci{
85413498266Sopenharmony_ci  if(!newhandle->req.p.http) {
85513498266Sopenharmony_ci    http2_data_done(cf, newhandle, TRUE);
85613498266Sopenharmony_ci    newhandle->req.p.http = NULL;
85713498266Sopenharmony_ci  }
85813498266Sopenharmony_ci  (void)Curl_close(&newhandle);
85913498266Sopenharmony_ci}
86013498266Sopenharmony_ci
86113498266Sopenharmony_cistatic int push_promise(struct Curl_cfilter *cf,
86213498266Sopenharmony_ci                        struct Curl_easy *data,
86313498266Sopenharmony_ci                        const nghttp2_push_promise *frame)
86413498266Sopenharmony_ci{
86513498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
86613498266Sopenharmony_ci  int rv; /* one of the CURL_PUSH_* defines */
86713498266Sopenharmony_ci
86813498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
86913498266Sopenharmony_ci              frame->promised_stream_id);
87013498266Sopenharmony_ci  if(data->multi->push_cb) {
87113498266Sopenharmony_ci    struct stream_ctx *stream;
87213498266Sopenharmony_ci    struct stream_ctx *newstream;
87313498266Sopenharmony_ci    struct curl_pushheaders heads;
87413498266Sopenharmony_ci    CURLMcode rc;
87513498266Sopenharmony_ci    CURLcode result;
87613498266Sopenharmony_ci    /* clone the parent */
87713498266Sopenharmony_ci    struct Curl_easy *newhandle = h2_duphandle(cf, data);
87813498266Sopenharmony_ci    if(!newhandle) {
87913498266Sopenharmony_ci      infof(data, "failed to duplicate handle");
88013498266Sopenharmony_ci      rv = CURL_PUSH_DENY; /* FAIL HARD */
88113498266Sopenharmony_ci      goto fail;
88213498266Sopenharmony_ci    }
88313498266Sopenharmony_ci
88413498266Sopenharmony_ci    heads.data = data;
88513498266Sopenharmony_ci    heads.frame = frame;
88613498266Sopenharmony_ci    /* ask the application */
88713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
88813498266Sopenharmony_ci
88913498266Sopenharmony_ci    stream = H2_STREAM_CTX(data);
89013498266Sopenharmony_ci    if(!stream) {
89113498266Sopenharmony_ci      failf(data, "Internal NULL stream");
89213498266Sopenharmony_ci      discard_newhandle(cf, newhandle);
89313498266Sopenharmony_ci      rv = CURL_PUSH_DENY;
89413498266Sopenharmony_ci      goto fail;
89513498266Sopenharmony_ci    }
89613498266Sopenharmony_ci
89713498266Sopenharmony_ci    rv = set_transfer_url(newhandle, &heads);
89813498266Sopenharmony_ci    if(rv) {
89913498266Sopenharmony_ci      discard_newhandle(cf, newhandle);
90013498266Sopenharmony_ci      rv = CURL_PUSH_DENY;
90113498266Sopenharmony_ci      goto fail;
90213498266Sopenharmony_ci    }
90313498266Sopenharmony_ci
90413498266Sopenharmony_ci    result = http2_data_setup(cf, newhandle, &newstream);
90513498266Sopenharmony_ci    if(result) {
90613498266Sopenharmony_ci      failf(data, "error setting up stream: %d", result);
90713498266Sopenharmony_ci      discard_newhandle(cf, newhandle);
90813498266Sopenharmony_ci      rv = CURL_PUSH_DENY;
90913498266Sopenharmony_ci      goto fail;
91013498266Sopenharmony_ci    }
91113498266Sopenharmony_ci    DEBUGASSERT(stream);
91213498266Sopenharmony_ci
91313498266Sopenharmony_ci    Curl_set_in_callback(data, true);
91413498266Sopenharmony_ci    rv = data->multi->push_cb(data, newhandle,
91513498266Sopenharmony_ci                              stream->push_headers_used, &heads,
91613498266Sopenharmony_ci                              data->multi->push_userp);
91713498266Sopenharmony_ci    Curl_set_in_callback(data, false);
91813498266Sopenharmony_ci
91913498266Sopenharmony_ci    /* free the headers again */
92013498266Sopenharmony_ci    free_push_headers(stream);
92113498266Sopenharmony_ci
92213498266Sopenharmony_ci    if(rv) {
92313498266Sopenharmony_ci      DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
92413498266Sopenharmony_ci      /* denied, kill off the new handle again */
92513498266Sopenharmony_ci      discard_newhandle(cf, newhandle);
92613498266Sopenharmony_ci      goto fail;
92713498266Sopenharmony_ci    }
92813498266Sopenharmony_ci
92913498266Sopenharmony_ci    newstream->id = frame->promised_stream_id;
93013498266Sopenharmony_ci    newhandle->req.maxdownload = -1;
93113498266Sopenharmony_ci    newhandle->req.size = -1;
93213498266Sopenharmony_ci
93313498266Sopenharmony_ci    /* approved, add to the multi handle and immediately switch to PERFORM
93413498266Sopenharmony_ci       state with the given connection !*/
93513498266Sopenharmony_ci    rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
93613498266Sopenharmony_ci    if(rc) {
93713498266Sopenharmony_ci      infof(data, "failed to add handle to multi");
93813498266Sopenharmony_ci      discard_newhandle(cf, newhandle);
93913498266Sopenharmony_ci      rv = CURL_PUSH_DENY;
94013498266Sopenharmony_ci      goto fail;
94113498266Sopenharmony_ci    }
94213498266Sopenharmony_ci
94313498266Sopenharmony_ci    rv = nghttp2_session_set_stream_user_data(ctx->h2,
94413498266Sopenharmony_ci                                              newstream->id,
94513498266Sopenharmony_ci                                              newhandle);
94613498266Sopenharmony_ci    if(rv) {
94713498266Sopenharmony_ci      infof(data, "failed to set user_data for stream %u",
94813498266Sopenharmony_ci            newstream->id);
94913498266Sopenharmony_ci      DEBUGASSERT(0);
95013498266Sopenharmony_ci      rv = CURL_PUSH_DENY;
95113498266Sopenharmony_ci      goto fail;
95213498266Sopenharmony_ci    }
95313498266Sopenharmony_ci  }
95413498266Sopenharmony_ci  else {
95513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
95613498266Sopenharmony_ci    rv = CURL_PUSH_DENY;
95713498266Sopenharmony_ci  }
95813498266Sopenharmony_cifail:
95913498266Sopenharmony_ci  return rv;
96013498266Sopenharmony_ci}
96113498266Sopenharmony_ci
96213498266Sopenharmony_cistatic CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
96313498266Sopenharmony_ci                                  struct Curl_easy *data,
96413498266Sopenharmony_ci                                  const char *buf, size_t blen)
96513498266Sopenharmony_ci{
96613498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
96713498266Sopenharmony_ci  ssize_t nwritten;
96813498266Sopenharmony_ci  CURLcode result;
96913498266Sopenharmony_ci
97013498266Sopenharmony_ci  (void)cf;
97113498266Sopenharmony_ci  nwritten = Curl_bufq_write(&stream->recvbuf,
97213498266Sopenharmony_ci                             (const unsigned char *)buf, blen, &result);
97313498266Sopenharmony_ci  if(nwritten < 0)
97413498266Sopenharmony_ci    return result;
97513498266Sopenharmony_ci  stream->resp_hds_len += (size_t)nwritten;
97613498266Sopenharmony_ci  DEBUGASSERT((size_t)nwritten == blen);
97713498266Sopenharmony_ci  return CURLE_OK;
97813498266Sopenharmony_ci}
97913498266Sopenharmony_ci
98013498266Sopenharmony_cistatic CURLcode on_stream_frame(struct Curl_cfilter *cf,
98113498266Sopenharmony_ci                                struct Curl_easy *data,
98213498266Sopenharmony_ci                                const nghttp2_frame *frame)
98313498266Sopenharmony_ci{
98413498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
98513498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
98613498266Sopenharmony_ci  int32_t stream_id = frame->hd.stream_id;
98713498266Sopenharmony_ci  CURLcode result;
98813498266Sopenharmony_ci  size_t rbuflen;
98913498266Sopenharmony_ci  int rv;
99013498266Sopenharmony_ci
99113498266Sopenharmony_ci  if(!stream) {
99213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id);
99313498266Sopenharmony_ci    return CURLE_FAILED_INIT;
99413498266Sopenharmony_ci  }
99513498266Sopenharmony_ci
99613498266Sopenharmony_ci  switch(frame->hd.type) {
99713498266Sopenharmony_ci  case NGHTTP2_DATA:
99813498266Sopenharmony_ci    rbuflen = Curl_bufq_len(&stream->recvbuf);
99913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d",
100013498266Sopenharmony_ci                stream_id, rbuflen,
100113498266Sopenharmony_ci                nghttp2_session_get_stream_effective_recv_data_length(
100213498266Sopenharmony_ci                  ctx->h2, stream->id),
100313498266Sopenharmony_ci                nghttp2_session_get_stream_effective_local_window_size(
100413498266Sopenharmony_ci                  ctx->h2, stream->id));
100513498266Sopenharmony_ci    /* If !body started on this stream, then receiving DATA is illegal. */
100613498266Sopenharmony_ci    if(!stream->bodystarted) {
100713498266Sopenharmony_ci      rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
100813498266Sopenharmony_ci                                     stream_id, NGHTTP2_PROTOCOL_ERROR);
100913498266Sopenharmony_ci
101013498266Sopenharmony_ci      if(nghttp2_is_fatal(rv)) {
101113498266Sopenharmony_ci        return CURLE_RECV_ERROR;
101213498266Sopenharmony_ci      }
101313498266Sopenharmony_ci    }
101413498266Sopenharmony_ci    if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
101513498266Sopenharmony_ci      drain_stream(cf, data, stream);
101613498266Sopenharmony_ci    }
101713498266Sopenharmony_ci    else if(rbuflen > stream->local_window_size) {
101813498266Sopenharmony_ci      int32_t wsize = nghttp2_session_get_stream_local_window_size(
101913498266Sopenharmony_ci                        ctx->h2, stream->id);
102013498266Sopenharmony_ci      if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) {
102113498266Sopenharmony_ci        /* H2 flow control is not absolute, as the server might not have the
102213498266Sopenharmony_ci         * same view, yet. When we receive more than we want, we enforce
102313498266Sopenharmony_ci         * the local window size again to make nghttp2 send WINDOW_UPATEs
102413498266Sopenharmony_ci         * accordingly. */
102513498266Sopenharmony_ci        nghttp2_session_set_local_window_size(ctx->h2,
102613498266Sopenharmony_ci                                              NGHTTP2_FLAG_NONE,
102713498266Sopenharmony_ci                                              stream->id,
102813498266Sopenharmony_ci                                              stream->local_window_size);
102913498266Sopenharmony_ci      }
103013498266Sopenharmony_ci    }
103113498266Sopenharmony_ci    break;
103213498266Sopenharmony_ci  case NGHTTP2_HEADERS:
103313498266Sopenharmony_ci    if(stream->bodystarted) {
103413498266Sopenharmony_ci      /* Only valid HEADERS after body started is trailer HEADERS.  We
103513498266Sopenharmony_ci         buffer them in on_header callback. */
103613498266Sopenharmony_ci      break;
103713498266Sopenharmony_ci    }
103813498266Sopenharmony_ci
103913498266Sopenharmony_ci    /* nghttp2 guarantees that :status is received, and we store it to
104013498266Sopenharmony_ci       stream->status_code. Fuzzing has proven this can still be reached
104113498266Sopenharmony_ci       without status code having been set. */
104213498266Sopenharmony_ci    if(stream->status_code == -1)
104313498266Sopenharmony_ci      return CURLE_RECV_ERROR;
104413498266Sopenharmony_ci
104513498266Sopenharmony_ci    /* Only final status code signals the end of header */
104613498266Sopenharmony_ci    if(stream->status_code / 100 != 1) {
104713498266Sopenharmony_ci      stream->bodystarted = TRUE;
104813498266Sopenharmony_ci      stream->status_code = -1;
104913498266Sopenharmony_ci    }
105013498266Sopenharmony_ci
105113498266Sopenharmony_ci    result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
105213498266Sopenharmony_ci    if(result)
105313498266Sopenharmony_ci      return result;
105413498266Sopenharmony_ci
105513498266Sopenharmony_ci    if(stream->status_code / 100 != 1) {
105613498266Sopenharmony_ci      stream->resp_hds_complete = TRUE;
105713498266Sopenharmony_ci    }
105813498266Sopenharmony_ci    drain_stream(cf, data, stream);
105913498266Sopenharmony_ci    break;
106013498266Sopenharmony_ci  case NGHTTP2_PUSH_PROMISE:
106113498266Sopenharmony_ci    rv = push_promise(cf, data, &frame->push_promise);
106213498266Sopenharmony_ci    if(rv) { /* deny! */
106313498266Sopenharmony_ci      DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
106413498266Sopenharmony_ci      rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
106513498266Sopenharmony_ci                                     frame->push_promise.promised_stream_id,
106613498266Sopenharmony_ci                                     NGHTTP2_CANCEL);
106713498266Sopenharmony_ci      if(nghttp2_is_fatal(rv))
106813498266Sopenharmony_ci        return CURLE_SEND_ERROR;
106913498266Sopenharmony_ci      else if(rv == CURL_PUSH_ERROROUT) {
107013498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received",
107113498266Sopenharmony_ci                    stream_id);
107213498266Sopenharmony_ci        return CURLE_RECV_ERROR;
107313498266Sopenharmony_ci      }
107413498266Sopenharmony_ci    }
107513498266Sopenharmony_ci    break;
107613498266Sopenharmony_ci  case NGHTTP2_RST_STREAM:
107713498266Sopenharmony_ci    stream->closed = TRUE;
107813498266Sopenharmony_ci    if(frame->rst_stream.error_code) {
107913498266Sopenharmony_ci      stream->reset = TRUE;
108013498266Sopenharmony_ci    }
108113498266Sopenharmony_ci    stream->send_closed = TRUE;
108213498266Sopenharmony_ci    drain_stream(cf, data, stream);
108313498266Sopenharmony_ci    break;
108413498266Sopenharmony_ci  case NGHTTP2_WINDOW_UPDATE:
108513498266Sopenharmony_ci    if(CURL_WANT_SEND(data)) {
108613498266Sopenharmony_ci      drain_stream(cf, data, stream);
108713498266Sopenharmony_ci    }
108813498266Sopenharmony_ci    break;
108913498266Sopenharmony_ci  default:
109013498266Sopenharmony_ci    break;
109113498266Sopenharmony_ci  }
109213498266Sopenharmony_ci  return CURLE_OK;
109313498266Sopenharmony_ci}
109413498266Sopenharmony_ci
109513498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
109613498266Sopenharmony_cistatic int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
109713498266Sopenharmony_ci{
109813498266Sopenharmony_ci  switch(frame->hd.type) {
109913498266Sopenharmony_ci    case NGHTTP2_DATA: {
110013498266Sopenharmony_ci      return msnprintf(buffer, blen,
110113498266Sopenharmony_ci                       "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
110213498266Sopenharmony_ci                       (int)frame->hd.length,
110313498266Sopenharmony_ci                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
110413498266Sopenharmony_ci                       (int)frame->data.padlen);
110513498266Sopenharmony_ci    }
110613498266Sopenharmony_ci    case NGHTTP2_HEADERS: {
110713498266Sopenharmony_ci      return msnprintf(buffer, blen,
110813498266Sopenharmony_ci                       "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
110913498266Sopenharmony_ci                       (int)frame->hd.length,
111013498266Sopenharmony_ci                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
111113498266Sopenharmony_ci                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
111213498266Sopenharmony_ci    }
111313498266Sopenharmony_ci    case NGHTTP2_PRIORITY: {
111413498266Sopenharmony_ci      return msnprintf(buffer, blen,
111513498266Sopenharmony_ci                       "FRAME[PRIORITY, len=%d, flags=%d]",
111613498266Sopenharmony_ci                       (int)frame->hd.length, frame->hd.flags);
111713498266Sopenharmony_ci    }
111813498266Sopenharmony_ci    case NGHTTP2_RST_STREAM: {
111913498266Sopenharmony_ci      return msnprintf(buffer, blen,
112013498266Sopenharmony_ci                       "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
112113498266Sopenharmony_ci                       (int)frame->hd.length, frame->hd.flags,
112213498266Sopenharmony_ci                       frame->rst_stream.error_code);
112313498266Sopenharmony_ci    }
112413498266Sopenharmony_ci    case NGHTTP2_SETTINGS: {
112513498266Sopenharmony_ci      if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
112613498266Sopenharmony_ci        return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
112713498266Sopenharmony_ci      }
112813498266Sopenharmony_ci      return msnprintf(buffer, blen,
112913498266Sopenharmony_ci                       "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
113013498266Sopenharmony_ci    }
113113498266Sopenharmony_ci    case NGHTTP2_PUSH_PROMISE: {
113213498266Sopenharmony_ci      return msnprintf(buffer, blen,
113313498266Sopenharmony_ci                       "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
113413498266Sopenharmony_ci                       (int)frame->hd.length,
113513498266Sopenharmony_ci                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
113613498266Sopenharmony_ci    }
113713498266Sopenharmony_ci    case NGHTTP2_PING: {
113813498266Sopenharmony_ci      return msnprintf(buffer, blen,
113913498266Sopenharmony_ci                       "FRAME[PING, len=%d, ack=%d]",
114013498266Sopenharmony_ci                       (int)frame->hd.length,
114113498266Sopenharmony_ci                       frame->hd.flags&NGHTTP2_FLAG_ACK);
114213498266Sopenharmony_ci    }
114313498266Sopenharmony_ci    case NGHTTP2_GOAWAY: {
114413498266Sopenharmony_ci      char scratch[128];
114513498266Sopenharmony_ci      size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
114613498266Sopenharmony_ci        size_t len = (frame->goaway.opaque_data_len < s_len)?
114713498266Sopenharmony_ci                      frame->goaway.opaque_data_len : s_len-1;
114813498266Sopenharmony_ci        if(len)
114913498266Sopenharmony_ci          memcpy(scratch, frame->goaway.opaque_data, len);
115013498266Sopenharmony_ci        scratch[len] = '\0';
115113498266Sopenharmony_ci        return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
115213498266Sopenharmony_ci                         "last_stream=%d]", frame->goaway.error_code,
115313498266Sopenharmony_ci                         scratch, frame->goaway.last_stream_id);
115413498266Sopenharmony_ci    }
115513498266Sopenharmony_ci    case NGHTTP2_WINDOW_UPDATE: {
115613498266Sopenharmony_ci      return msnprintf(buffer, blen,
115713498266Sopenharmony_ci                       "FRAME[WINDOW_UPDATE, incr=%d]",
115813498266Sopenharmony_ci                       frame->window_update.window_size_increment);
115913498266Sopenharmony_ci    }
116013498266Sopenharmony_ci    default:
116113498266Sopenharmony_ci      return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
116213498266Sopenharmony_ci                       frame->hd.type, (int)frame->hd.length,
116313498266Sopenharmony_ci                       frame->hd.flags);
116413498266Sopenharmony_ci  }
116513498266Sopenharmony_ci}
116613498266Sopenharmony_ci
116713498266Sopenharmony_cistatic int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
116813498266Sopenharmony_ci                         void *userp)
116913498266Sopenharmony_ci{
117013498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
117113498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
117213498266Sopenharmony_ci
117313498266Sopenharmony_ci  (void)session;
117413498266Sopenharmony_ci  DEBUGASSERT(data);
117513498266Sopenharmony_ci  if(data && Curl_trc_cf_is_verbose(cf, data)) {
117613498266Sopenharmony_ci    char buffer[256];
117713498266Sopenharmony_ci    int len;
117813498266Sopenharmony_ci    len = fr_print(frame, buffer, sizeof(buffer)-1);
117913498266Sopenharmony_ci    buffer[len] = 0;
118013498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
118113498266Sopenharmony_ci  }
118213498266Sopenharmony_ci  return 0;
118313498266Sopenharmony_ci}
118413498266Sopenharmony_ci#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
118513498266Sopenharmony_ci
118613498266Sopenharmony_cistatic int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
118713498266Sopenharmony_ci                         void *userp)
118813498266Sopenharmony_ci{
118913498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
119013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
119113498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s;
119213498266Sopenharmony_ci  int32_t stream_id = frame->hd.stream_id;
119313498266Sopenharmony_ci
119413498266Sopenharmony_ci  DEBUGASSERT(data);
119513498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
119613498266Sopenharmony_ci  if(Curl_trc_cf_is_verbose(cf, data)) {
119713498266Sopenharmony_ci    char buffer[256];
119813498266Sopenharmony_ci    int len;
119913498266Sopenharmony_ci    len = fr_print(frame, buffer, sizeof(buffer)-1);
120013498266Sopenharmony_ci    buffer[len] = 0;
120113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
120213498266Sopenharmony_ci  }
120313498266Sopenharmony_ci#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
120413498266Sopenharmony_ci
120513498266Sopenharmony_ci  if(!stream_id) {
120613498266Sopenharmony_ci    /* stream ID zero is for connection-oriented stuff */
120713498266Sopenharmony_ci    DEBUGASSERT(data);
120813498266Sopenharmony_ci    switch(frame->hd.type) {
120913498266Sopenharmony_ci    case NGHTTP2_SETTINGS: {
121013498266Sopenharmony_ci      if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
121113498266Sopenharmony_ci        uint32_t max_conn = ctx->max_concurrent_streams;
121213498266Sopenharmony_ci        ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
121313498266Sopenharmony_ci            session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
121413498266Sopenharmony_ci        ctx->enable_push = nghttp2_session_get_remote_settings(
121513498266Sopenharmony_ci            session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
121613498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d",
121713498266Sopenharmony_ci                    ctx->max_concurrent_streams);
121813498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s",
121913498266Sopenharmony_ci                    ctx->enable_push ? "TRUE" : "false");
122013498266Sopenharmony_ci        if(data && max_conn != ctx->max_concurrent_streams) {
122113498266Sopenharmony_ci          /* only signal change if the value actually changed */
122213498266Sopenharmony_ci          CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u",
122313498266Sopenharmony_ci                      ctx->max_concurrent_streams);
122413498266Sopenharmony_ci          Curl_multi_connchanged(data->multi);
122513498266Sopenharmony_ci        }
122613498266Sopenharmony_ci        /* Since the initial stream window is 64K, a request might be on HOLD,
122713498266Sopenharmony_ci         * due to exhaustion. The (initial) SETTINGS may announce a much larger
122813498266Sopenharmony_ci         * window and *assume* that we treat this like a WINDOW_UPDATE. Some
122913498266Sopenharmony_ci         * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
123013498266Sopenharmony_ci         * To be safe, we UNHOLD a stream in order not to stall. */
123113498266Sopenharmony_ci        if(CURL_WANT_SEND(data)) {
123213498266Sopenharmony_ci          struct stream_ctx *stream = H2_STREAM_CTX(data);
123313498266Sopenharmony_ci          if(stream)
123413498266Sopenharmony_ci            drain_stream(cf, data, stream);
123513498266Sopenharmony_ci        }
123613498266Sopenharmony_ci      }
123713498266Sopenharmony_ci      break;
123813498266Sopenharmony_ci    }
123913498266Sopenharmony_ci    case NGHTTP2_GOAWAY:
124013498266Sopenharmony_ci      ctx->goaway = TRUE;
124113498266Sopenharmony_ci      ctx->goaway_error = frame->goaway.error_code;
124213498266Sopenharmony_ci      ctx->last_stream_id = frame->goaway.last_stream_id;
124313498266Sopenharmony_ci      if(data) {
124413498266Sopenharmony_ci        infof(data, "received GOAWAY, error=%d, last_stream=%u",
124513498266Sopenharmony_ci                    ctx->goaway_error, ctx->last_stream_id);
124613498266Sopenharmony_ci        Curl_multi_connchanged(data->multi);
124713498266Sopenharmony_ci      }
124813498266Sopenharmony_ci      break;
124913498266Sopenharmony_ci    default:
125013498266Sopenharmony_ci      break;
125113498266Sopenharmony_ci    }
125213498266Sopenharmony_ci    return 0;
125313498266Sopenharmony_ci  }
125413498266Sopenharmony_ci
125513498266Sopenharmony_ci  data_s = nghttp2_session_get_stream_user_data(session, stream_id);
125613498266Sopenharmony_ci  if(!data_s) {
125713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id);
125813498266Sopenharmony_ci    return 0;
125913498266Sopenharmony_ci  }
126013498266Sopenharmony_ci
126113498266Sopenharmony_ci  return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
126213498266Sopenharmony_ci}
126313498266Sopenharmony_ci
126413498266Sopenharmony_cistatic int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
126513498266Sopenharmony_ci                              int32_t stream_id,
126613498266Sopenharmony_ci                              const uint8_t *mem, size_t len, void *userp)
126713498266Sopenharmony_ci{
126813498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
126913498266Sopenharmony_ci  struct stream_ctx *stream;
127013498266Sopenharmony_ci  struct Curl_easy *data_s;
127113498266Sopenharmony_ci  ssize_t nwritten;
127213498266Sopenharmony_ci  CURLcode result;
127313498266Sopenharmony_ci  (void)flags;
127413498266Sopenharmony_ci
127513498266Sopenharmony_ci  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
127613498266Sopenharmony_ci  DEBUGASSERT(CF_DATA_CURRENT(cf));
127713498266Sopenharmony_ci
127813498266Sopenharmony_ci  /* get the stream from the hash based on Stream ID */
127913498266Sopenharmony_ci  data_s = nghttp2_session_get_stream_user_data(session, stream_id);
128013498266Sopenharmony_ci  if(!data_s) {
128113498266Sopenharmony_ci    /* Receiving a Stream ID not in the hash should not happen - unless
128213498266Sopenharmony_ci       we have aborted a transfer artificially and there were more data
128313498266Sopenharmony_ci       in the pipeline. Silently ignore. */
128413498266Sopenharmony_ci    CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown",
128513498266Sopenharmony_ci                stream_id);
128613498266Sopenharmony_ci    /* consumed explicitly as no one will read it */
128713498266Sopenharmony_ci    nghttp2_session_consume(session, stream_id, len);
128813498266Sopenharmony_ci    return 0;
128913498266Sopenharmony_ci  }
129013498266Sopenharmony_ci
129113498266Sopenharmony_ci  stream = H2_STREAM_CTX(data_s);
129213498266Sopenharmony_ci  if(!stream)
129313498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
129413498266Sopenharmony_ci
129513498266Sopenharmony_ci  nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result);
129613498266Sopenharmony_ci  if(nwritten < 0) {
129713498266Sopenharmony_ci    if(result != CURLE_AGAIN)
129813498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
129913498266Sopenharmony_ci
130013498266Sopenharmony_ci    nwritten = 0;
130113498266Sopenharmony_ci  }
130213498266Sopenharmony_ci
130313498266Sopenharmony_ci  /* if we receive data for another handle, wake that up */
130413498266Sopenharmony_ci  drain_stream(cf, data_s, stream);
130513498266Sopenharmony_ci
130613498266Sopenharmony_ci  DEBUGASSERT((size_t)nwritten == len);
130713498266Sopenharmony_ci  return 0;
130813498266Sopenharmony_ci}
130913498266Sopenharmony_ci
131013498266Sopenharmony_cistatic int on_stream_close(nghttp2_session *session, int32_t stream_id,
131113498266Sopenharmony_ci                           uint32_t error_code, void *userp)
131213498266Sopenharmony_ci{
131313498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
131413498266Sopenharmony_ci  struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
131513498266Sopenharmony_ci  struct stream_ctx *stream;
131613498266Sopenharmony_ci  int rv;
131713498266Sopenharmony_ci  (void)session;
131813498266Sopenharmony_ci
131913498266Sopenharmony_ci  DEBUGASSERT(call_data);
132013498266Sopenharmony_ci  /* get the stream from the hash based on Stream ID, stream ID zero is for
132113498266Sopenharmony_ci     connection-oriented stuff */
132213498266Sopenharmony_ci  data_s = stream_id?
132313498266Sopenharmony_ci             nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
132413498266Sopenharmony_ci  if(!data_s) {
132513498266Sopenharmony_ci    CURL_TRC_CF(call_data, cf,
132613498266Sopenharmony_ci                "[%d] on_stream_close, no easy set on stream", stream_id);
132713498266Sopenharmony_ci    return 0;
132813498266Sopenharmony_ci  }
132913498266Sopenharmony_ci  if(!GOOD_EASY_HANDLE(data_s)) {
133013498266Sopenharmony_ci    /* nghttp2 still has an easy registered for the stream which has
133113498266Sopenharmony_ci     * been freed be libcurl. This points to a code path that does not
133213498266Sopenharmony_ci     * trigger DONE or DETACH events as it must. */
133313498266Sopenharmony_ci    CURL_TRC_CF(call_data, cf,
133413498266Sopenharmony_ci                "[%d] on_stream_close, not a GOOD easy on stream", stream_id);
133513498266Sopenharmony_ci    (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
133613498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
133713498266Sopenharmony_ci  }
133813498266Sopenharmony_ci  stream = H2_STREAM_CTX(data_s);
133913498266Sopenharmony_ci  if(!stream) {
134013498266Sopenharmony_ci    CURL_TRC_CF(data_s, cf,
134113498266Sopenharmony_ci                "[%d] on_stream_close, GOOD easy but no stream", stream_id);
134213498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
134313498266Sopenharmony_ci  }
134413498266Sopenharmony_ci
134513498266Sopenharmony_ci  stream->closed = TRUE;
134613498266Sopenharmony_ci  stream->error = error_code;
134713498266Sopenharmony_ci  if(stream->error) {
134813498266Sopenharmony_ci    stream->reset = TRUE;
134913498266Sopenharmony_ci    stream->send_closed = TRUE;
135013498266Sopenharmony_ci  }
135113498266Sopenharmony_ci
135213498266Sopenharmony_ci  if(stream->error)
135313498266Sopenharmony_ci    CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
135413498266Sopenharmony_ci              stream_id, nghttp2_http2_strerror(error_code), error_code);
135513498266Sopenharmony_ci  else
135613498266Sopenharmony_ci    CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
135713498266Sopenharmony_ci  drain_stream(cf, data_s, stream);
135813498266Sopenharmony_ci
135913498266Sopenharmony_ci  /* remove `data_s` from the nghttp2 stream */
136013498266Sopenharmony_ci  rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
136113498266Sopenharmony_ci  if(rv) {
136213498266Sopenharmony_ci    infof(data_s, "http/2: failed to clear user_data for stream %u",
136313498266Sopenharmony_ci          stream_id);
136413498266Sopenharmony_ci    DEBUGASSERT(0);
136513498266Sopenharmony_ci  }
136613498266Sopenharmony_ci  return 0;
136713498266Sopenharmony_ci}
136813498266Sopenharmony_ci
136913498266Sopenharmony_cistatic int on_begin_headers(nghttp2_session *session,
137013498266Sopenharmony_ci                            const nghttp2_frame *frame, void *userp)
137113498266Sopenharmony_ci{
137213498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
137313498266Sopenharmony_ci  struct stream_ctx *stream;
137413498266Sopenharmony_ci  struct Curl_easy *data_s = NULL;
137513498266Sopenharmony_ci
137613498266Sopenharmony_ci  (void)cf;
137713498266Sopenharmony_ci  data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
137813498266Sopenharmony_ci  if(!data_s) {
137913498266Sopenharmony_ci    return 0;
138013498266Sopenharmony_ci  }
138113498266Sopenharmony_ci
138213498266Sopenharmony_ci  if(frame->hd.type != NGHTTP2_HEADERS) {
138313498266Sopenharmony_ci    return 0;
138413498266Sopenharmony_ci  }
138513498266Sopenharmony_ci
138613498266Sopenharmony_ci  stream = H2_STREAM_CTX(data_s);
138713498266Sopenharmony_ci  if(!stream || !stream->bodystarted) {
138813498266Sopenharmony_ci    return 0;
138913498266Sopenharmony_ci  }
139013498266Sopenharmony_ci
139113498266Sopenharmony_ci  return 0;
139213498266Sopenharmony_ci}
139313498266Sopenharmony_ci
139413498266Sopenharmony_ci/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
139513498266Sopenharmony_cistatic int on_header(nghttp2_session *session, const nghttp2_frame *frame,
139613498266Sopenharmony_ci                     const uint8_t *name, size_t namelen,
139713498266Sopenharmony_ci                     const uint8_t *value, size_t valuelen,
139813498266Sopenharmony_ci                     uint8_t flags,
139913498266Sopenharmony_ci                     void *userp)
140013498266Sopenharmony_ci{
140113498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
140213498266Sopenharmony_ci  struct stream_ctx *stream;
140313498266Sopenharmony_ci  struct Curl_easy *data_s;
140413498266Sopenharmony_ci  int32_t stream_id = frame->hd.stream_id;
140513498266Sopenharmony_ci  CURLcode result;
140613498266Sopenharmony_ci  (void)flags;
140713498266Sopenharmony_ci
140813498266Sopenharmony_ci  DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
140913498266Sopenharmony_ci
141013498266Sopenharmony_ci  /* get the stream from the hash based on Stream ID */
141113498266Sopenharmony_ci  data_s = nghttp2_session_get_stream_user_data(session, stream_id);
141213498266Sopenharmony_ci  if(!data_s)
141313498266Sopenharmony_ci    /* Receiving a Stream ID not in the hash should not happen, this is an
141413498266Sopenharmony_ci       internal error more than anything else! */
141513498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
141613498266Sopenharmony_ci
141713498266Sopenharmony_ci  stream = H2_STREAM_CTX(data_s);
141813498266Sopenharmony_ci  if(!stream) {
141913498266Sopenharmony_ci    failf(data_s, "Internal NULL stream");
142013498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
142113498266Sopenharmony_ci  }
142213498266Sopenharmony_ci
142313498266Sopenharmony_ci  /* Store received PUSH_PROMISE headers to be used when the subsequent
142413498266Sopenharmony_ci     PUSH_PROMISE callback comes */
142513498266Sopenharmony_ci  if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
142613498266Sopenharmony_ci    char *h;
142713498266Sopenharmony_ci
142813498266Sopenharmony_ci    if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) {
142913498266Sopenharmony_ci      /* pseudo headers are lower case */
143013498266Sopenharmony_ci      int rc = 0;
143113498266Sopenharmony_ci      char *check = aprintf("%s:%d", cf->conn->host.name,
143213498266Sopenharmony_ci                            cf->conn->remote_port);
143313498266Sopenharmony_ci      if(!check)
143413498266Sopenharmony_ci        /* no memory */
143513498266Sopenharmony_ci        return NGHTTP2_ERR_CALLBACK_FAILURE;
143613498266Sopenharmony_ci      if(!strcasecompare(check, (const char *)value) &&
143713498266Sopenharmony_ci         ((cf->conn->remote_port != cf->conn->given->defport) ||
143813498266Sopenharmony_ci          !strcasecompare(cf->conn->host.name, (const char *)value))) {
143913498266Sopenharmony_ci        /* This is push is not for the same authority that was asked for in
144013498266Sopenharmony_ci         * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
144113498266Sopenharmony_ci         * PUSH_PROMISE for which the server is not authoritative as a stream
144213498266Sopenharmony_ci         * error of type PROTOCOL_ERROR."
144313498266Sopenharmony_ci         */
144413498266Sopenharmony_ci        (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
144513498266Sopenharmony_ci                                        stream_id, NGHTTP2_PROTOCOL_ERROR);
144613498266Sopenharmony_ci        rc = NGHTTP2_ERR_CALLBACK_FAILURE;
144713498266Sopenharmony_ci      }
144813498266Sopenharmony_ci      free(check);
144913498266Sopenharmony_ci      if(rc)
145013498266Sopenharmony_ci        return rc;
145113498266Sopenharmony_ci    }
145213498266Sopenharmony_ci
145313498266Sopenharmony_ci    if(!stream->push_headers) {
145413498266Sopenharmony_ci      stream->push_headers_alloc = 10;
145513498266Sopenharmony_ci      stream->push_headers = malloc(stream->push_headers_alloc *
145613498266Sopenharmony_ci                                    sizeof(char *));
145713498266Sopenharmony_ci      if(!stream->push_headers)
145813498266Sopenharmony_ci        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
145913498266Sopenharmony_ci      stream->push_headers_used = 0;
146013498266Sopenharmony_ci    }
146113498266Sopenharmony_ci    else if(stream->push_headers_used ==
146213498266Sopenharmony_ci            stream->push_headers_alloc) {
146313498266Sopenharmony_ci      char **headp;
146413498266Sopenharmony_ci      if(stream->push_headers_alloc > 1000) {
146513498266Sopenharmony_ci        /* this is beyond crazy many headers, bail out */
146613498266Sopenharmony_ci        failf(data_s, "Too many PUSH_PROMISE headers");
146713498266Sopenharmony_ci        free_push_headers(stream);
146813498266Sopenharmony_ci        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
146913498266Sopenharmony_ci      }
147013498266Sopenharmony_ci      stream->push_headers_alloc *= 2;
147113498266Sopenharmony_ci      headp = realloc(stream->push_headers,
147213498266Sopenharmony_ci                      stream->push_headers_alloc * sizeof(char *));
147313498266Sopenharmony_ci      if(!headp) {
147413498266Sopenharmony_ci        free_push_headers(stream);
147513498266Sopenharmony_ci        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
147613498266Sopenharmony_ci      }
147713498266Sopenharmony_ci      stream->push_headers = headp;
147813498266Sopenharmony_ci    }
147913498266Sopenharmony_ci    h = aprintf("%s:%s", name, value);
148013498266Sopenharmony_ci    if(h)
148113498266Sopenharmony_ci      stream->push_headers[stream->push_headers_used++] = h;
148213498266Sopenharmony_ci    return 0;
148313498266Sopenharmony_ci  }
148413498266Sopenharmony_ci
148513498266Sopenharmony_ci  if(stream->bodystarted) {
148613498266Sopenharmony_ci    /* This is a trailer */
148713498266Sopenharmony_ci    CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
148813498266Sopenharmony_ci                stream->id, (int)namelen, name, (int)valuelen, value);
148913498266Sopenharmony_ci    result = Curl_dynhds_add(&stream->resp_trailers,
149013498266Sopenharmony_ci                             (const char *)name, namelen,
149113498266Sopenharmony_ci                             (const char *)value, valuelen);
149213498266Sopenharmony_ci    if(result)
149313498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
149413498266Sopenharmony_ci
149513498266Sopenharmony_ci    return 0;
149613498266Sopenharmony_ci  }
149713498266Sopenharmony_ci
149813498266Sopenharmony_ci  if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
149913498266Sopenharmony_ci     memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
150013498266Sopenharmony_ci    /* nghttp2 guarantees :status is received first and only once. */
150113498266Sopenharmony_ci    char buffer[32];
150213498266Sopenharmony_ci    result = Curl_http_decode_status(&stream->status_code,
150313498266Sopenharmony_ci                                     (const char *)value, valuelen);
150413498266Sopenharmony_ci    if(result)
150513498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
150613498266Sopenharmony_ci    msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
150713498266Sopenharmony_ci              stream->status_code);
150813498266Sopenharmony_ci    result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
150913498266Sopenharmony_ci    if(result)
151013498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
151113498266Sopenharmony_ci    result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
151213498266Sopenharmony_ci    if(result)
151313498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
151413498266Sopenharmony_ci    result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
151513498266Sopenharmony_ci    if(result)
151613498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
151713498266Sopenharmony_ci    /* the space character after the status code is mandatory */
151813498266Sopenharmony_ci    result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
151913498266Sopenharmony_ci    if(result)
152013498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
152113498266Sopenharmony_ci    /* if we receive data for another handle, wake that up */
152213498266Sopenharmony_ci    if(CF_DATA_CURRENT(cf) != data_s)
152313498266Sopenharmony_ci      Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
152413498266Sopenharmony_ci
152513498266Sopenharmony_ci    CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
152613498266Sopenharmony_ci                stream->id, stream->status_code);
152713498266Sopenharmony_ci    return 0;
152813498266Sopenharmony_ci  }
152913498266Sopenharmony_ci
153013498266Sopenharmony_ci  /* nghttp2 guarantees that namelen > 0, and :status was already
153113498266Sopenharmony_ci     received, and this is not pseudo-header field . */
153213498266Sopenharmony_ci  /* convert to an HTTP1-style header */
153313498266Sopenharmony_ci  result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
153413498266Sopenharmony_ci  if(result)
153513498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
153613498266Sopenharmony_ci  result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
153713498266Sopenharmony_ci  if(result)
153813498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
153913498266Sopenharmony_ci  result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
154013498266Sopenharmony_ci  if(result)
154113498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
154213498266Sopenharmony_ci  result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
154313498266Sopenharmony_ci  if(result)
154413498266Sopenharmony_ci    return NGHTTP2_ERR_CALLBACK_FAILURE;
154513498266Sopenharmony_ci  /* if we receive data for another handle, wake that up */
154613498266Sopenharmony_ci  if(CF_DATA_CURRENT(cf) != data_s)
154713498266Sopenharmony_ci    Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
154813498266Sopenharmony_ci
154913498266Sopenharmony_ci  CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
155013498266Sopenharmony_ci              stream->id, (int)namelen, name, (int)valuelen, value);
155113498266Sopenharmony_ci
155213498266Sopenharmony_ci  return 0; /* 0 is successful */
155313498266Sopenharmony_ci}
155413498266Sopenharmony_ci
155513498266Sopenharmony_cistatic ssize_t req_body_read_callback(nghttp2_session *session,
155613498266Sopenharmony_ci                                      int32_t stream_id,
155713498266Sopenharmony_ci                                      uint8_t *buf, size_t length,
155813498266Sopenharmony_ci                                      uint32_t *data_flags,
155913498266Sopenharmony_ci                                      nghttp2_data_source *source,
156013498266Sopenharmony_ci                                      void *userp)
156113498266Sopenharmony_ci{
156213498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
156313498266Sopenharmony_ci  struct Curl_easy *data_s;
156413498266Sopenharmony_ci  struct stream_ctx *stream = NULL;
156513498266Sopenharmony_ci  CURLcode result;
156613498266Sopenharmony_ci  ssize_t nread;
156713498266Sopenharmony_ci  (void)source;
156813498266Sopenharmony_ci
156913498266Sopenharmony_ci  (void)cf;
157013498266Sopenharmony_ci  if(stream_id) {
157113498266Sopenharmony_ci    /* get the stream from the hash based on Stream ID, stream ID zero is for
157213498266Sopenharmony_ci       connection-oriented stuff */
157313498266Sopenharmony_ci    data_s = nghttp2_session_get_stream_user_data(session, stream_id);
157413498266Sopenharmony_ci    if(!data_s)
157513498266Sopenharmony_ci      /* Receiving a Stream ID not in the hash should not happen, this is an
157613498266Sopenharmony_ci         internal error more than anything else! */
157713498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
157813498266Sopenharmony_ci
157913498266Sopenharmony_ci    stream = H2_STREAM_CTX(data_s);
158013498266Sopenharmony_ci    if(!stream)
158113498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
158213498266Sopenharmony_ci  }
158313498266Sopenharmony_ci  else
158413498266Sopenharmony_ci    return NGHTTP2_ERR_INVALID_ARGUMENT;
158513498266Sopenharmony_ci
158613498266Sopenharmony_ci  nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result);
158713498266Sopenharmony_ci  if(nread < 0) {
158813498266Sopenharmony_ci    if(result != CURLE_AGAIN)
158913498266Sopenharmony_ci      return NGHTTP2_ERR_CALLBACK_FAILURE;
159013498266Sopenharmony_ci    nread = 0;
159113498266Sopenharmony_ci  }
159213498266Sopenharmony_ci
159313498266Sopenharmony_ci  if(nread > 0 && stream->upload_left != -1)
159413498266Sopenharmony_ci    stream->upload_left -= nread;
159513498266Sopenharmony_ci
159613498266Sopenharmony_ci  CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%"
159713498266Sopenharmony_ci              CURL_FORMAT_CURL_OFF_T " -> %zd, %d",
159813498266Sopenharmony_ci              stream_id, length, stream->upload_left, nread, result);
159913498266Sopenharmony_ci
160013498266Sopenharmony_ci  if(stream->upload_left == 0)
160113498266Sopenharmony_ci    *data_flags = NGHTTP2_DATA_FLAG_EOF;
160213498266Sopenharmony_ci  else if(nread == 0)
160313498266Sopenharmony_ci    return NGHTTP2_ERR_DEFERRED;
160413498266Sopenharmony_ci
160513498266Sopenharmony_ci  return nread;
160613498266Sopenharmony_ci}
160713498266Sopenharmony_ci
160813498266Sopenharmony_ci#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
160913498266Sopenharmony_cistatic int error_callback(nghttp2_session *session,
161013498266Sopenharmony_ci                          const char *msg,
161113498266Sopenharmony_ci                          size_t len,
161213498266Sopenharmony_ci                          void *userp)
161313498266Sopenharmony_ci{
161413498266Sopenharmony_ci  struct Curl_cfilter *cf = userp;
161513498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
161613498266Sopenharmony_ci  (void)session;
161713498266Sopenharmony_ci  failf(data, "%.*s", (int)len, msg);
161813498266Sopenharmony_ci  return 0;
161913498266Sopenharmony_ci}
162013498266Sopenharmony_ci#endif
162113498266Sopenharmony_ci
162213498266Sopenharmony_ci/*
162313498266Sopenharmony_ci * Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
162413498266Sopenharmony_ci */
162513498266Sopenharmony_ciCURLcode Curl_http2_request_upgrade(struct dynbuf *req,
162613498266Sopenharmony_ci                                    struct Curl_easy *data)
162713498266Sopenharmony_ci{
162813498266Sopenharmony_ci  CURLcode result;
162913498266Sopenharmony_ci  char *base64;
163013498266Sopenharmony_ci  size_t blen;
163113498266Sopenharmony_ci  struct SingleRequest *k = &data->req;
163213498266Sopenharmony_ci  uint8_t binsettings[H2_BINSETTINGS_LEN];
163313498266Sopenharmony_ci  ssize_t binlen; /* length of the binsettings data */
163413498266Sopenharmony_ci
163513498266Sopenharmony_ci  binlen = populate_binsettings(binsettings, data);
163613498266Sopenharmony_ci  if(binlen <= 0) {
163713498266Sopenharmony_ci    failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
163813498266Sopenharmony_ci    Curl_dyn_free(req);
163913498266Sopenharmony_ci    return CURLE_FAILED_INIT;
164013498266Sopenharmony_ci  }
164113498266Sopenharmony_ci
164213498266Sopenharmony_ci  result = Curl_base64url_encode((const char *)binsettings, binlen,
164313498266Sopenharmony_ci                                 &base64, &blen);
164413498266Sopenharmony_ci  if(result) {
164513498266Sopenharmony_ci    Curl_dyn_free(req);
164613498266Sopenharmony_ci    return result;
164713498266Sopenharmony_ci  }
164813498266Sopenharmony_ci
164913498266Sopenharmony_ci  result = Curl_dyn_addf(req,
165013498266Sopenharmony_ci                         "Connection: Upgrade, HTTP2-Settings\r\n"
165113498266Sopenharmony_ci                         "Upgrade: %s\r\n"
165213498266Sopenharmony_ci                         "HTTP2-Settings: %s\r\n",
165313498266Sopenharmony_ci                         NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
165413498266Sopenharmony_ci  free(base64);
165513498266Sopenharmony_ci
165613498266Sopenharmony_ci  k->upgr101 = UPGR101_H2;
165713498266Sopenharmony_ci
165813498266Sopenharmony_ci  return result;
165913498266Sopenharmony_ci}
166013498266Sopenharmony_ci
166113498266Sopenharmony_cistatic CURLcode http2_data_done_send(struct Curl_cfilter *cf,
166213498266Sopenharmony_ci                                     struct Curl_easy *data)
166313498266Sopenharmony_ci{
166413498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
166513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
166613498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
166713498266Sopenharmony_ci
166813498266Sopenharmony_ci  if(!ctx || !ctx->h2 || !stream)
166913498266Sopenharmony_ci    goto out;
167013498266Sopenharmony_ci
167113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%d] data done send", stream->id);
167213498266Sopenharmony_ci  if(!stream->send_closed) {
167313498266Sopenharmony_ci    stream->send_closed = TRUE;
167413498266Sopenharmony_ci    if(stream->upload_left) {
167513498266Sopenharmony_ci      /* we now know that everything that is buffered is all there is. */
167613498266Sopenharmony_ci      stream->upload_left = Curl_bufq_len(&stream->sendbuf);
167713498266Sopenharmony_ci      /* resume sending here to trigger the callback to get called again so
167813498266Sopenharmony_ci         that it can signal EOF to nghttp2 */
167913498266Sopenharmony_ci      (void)nghttp2_session_resume_data(ctx->h2, stream->id);
168013498266Sopenharmony_ci      drain_stream(cf, data, stream);
168113498266Sopenharmony_ci    }
168213498266Sopenharmony_ci  }
168313498266Sopenharmony_ci
168413498266Sopenharmony_ciout:
168513498266Sopenharmony_ci  return result;
168613498266Sopenharmony_ci}
168713498266Sopenharmony_ci
168813498266Sopenharmony_cistatic ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
168913498266Sopenharmony_ci                                         struct Curl_easy *data,
169013498266Sopenharmony_ci                                         struct stream_ctx *stream,
169113498266Sopenharmony_ci                                         CURLcode *err)
169213498266Sopenharmony_ci{
169313498266Sopenharmony_ci  ssize_t rv = 0;
169413498266Sopenharmony_ci
169513498266Sopenharmony_ci  if(stream->error == NGHTTP2_REFUSED_STREAM) {
169613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
169713498266Sopenharmony_ci                "connection", stream->id);
169813498266Sopenharmony_ci    connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
169913498266Sopenharmony_ci    data->state.refused_stream = TRUE;
170013498266Sopenharmony_ci    *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
170113498266Sopenharmony_ci    return -1;
170213498266Sopenharmony_ci  }
170313498266Sopenharmony_ci  else if(stream->error != NGHTTP2_NO_ERROR) {
170413498266Sopenharmony_ci    failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
170513498266Sopenharmony_ci          stream->id, nghttp2_http2_strerror(stream->error),
170613498266Sopenharmony_ci          stream->error);
170713498266Sopenharmony_ci    *err = CURLE_HTTP2_STREAM;
170813498266Sopenharmony_ci    return -1;
170913498266Sopenharmony_ci  }
171013498266Sopenharmony_ci  else if(stream->reset) {
171113498266Sopenharmony_ci    failf(data, "HTTP/2 stream %u was reset", stream->id);
171213498266Sopenharmony_ci    *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
171313498266Sopenharmony_ci    return -1;
171413498266Sopenharmony_ci  }
171513498266Sopenharmony_ci
171613498266Sopenharmony_ci  if(!stream->bodystarted) {
171713498266Sopenharmony_ci    failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
171813498266Sopenharmony_ci          " all response header fields, treated as error",
171913498266Sopenharmony_ci          stream->id);
172013498266Sopenharmony_ci    *err = CURLE_HTTP2_STREAM;
172113498266Sopenharmony_ci    return -1;
172213498266Sopenharmony_ci  }
172313498266Sopenharmony_ci
172413498266Sopenharmony_ci  if(Curl_dynhds_count(&stream->resp_trailers)) {
172513498266Sopenharmony_ci    struct dynhds_entry *e;
172613498266Sopenharmony_ci    struct dynbuf dbuf;
172713498266Sopenharmony_ci    size_t i;
172813498266Sopenharmony_ci
172913498266Sopenharmony_ci    *err = CURLE_OK;
173013498266Sopenharmony_ci    Curl_dyn_init(&dbuf, DYN_TRAILERS);
173113498266Sopenharmony_ci    for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
173213498266Sopenharmony_ci      e = Curl_dynhds_getn(&stream->resp_trailers, i);
173313498266Sopenharmony_ci      if(!e)
173413498266Sopenharmony_ci        break;
173513498266Sopenharmony_ci      Curl_dyn_reset(&dbuf);
173613498266Sopenharmony_ci      *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
173713498266Sopenharmony_ci                          (int)e->namelen, e->name,
173813498266Sopenharmony_ci                          (int)e->valuelen, e->value);
173913498266Sopenharmony_ci      if(*err)
174013498266Sopenharmony_ci        break;
174113498266Sopenharmony_ci      Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
174213498266Sopenharmony_ci                 Curl_dyn_len(&dbuf));
174313498266Sopenharmony_ci      *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
174413498266Sopenharmony_ci                               Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
174513498266Sopenharmony_ci      if(*err)
174613498266Sopenharmony_ci        break;
174713498266Sopenharmony_ci    }
174813498266Sopenharmony_ci    Curl_dyn_free(&dbuf);
174913498266Sopenharmony_ci    if(*err)
175013498266Sopenharmony_ci      goto out;
175113498266Sopenharmony_ci  }
175213498266Sopenharmony_ci
175313498266Sopenharmony_ci  stream->close_handled = TRUE;
175413498266Sopenharmony_ci  *err = CURLE_OK;
175513498266Sopenharmony_ci  rv = 0;
175613498266Sopenharmony_ci
175713498266Sopenharmony_ciout:
175813498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
175913498266Sopenharmony_ci  return rv;
176013498266Sopenharmony_ci}
176113498266Sopenharmony_ci
176213498266Sopenharmony_cistatic int sweight_wanted(const struct Curl_easy *data)
176313498266Sopenharmony_ci{
176413498266Sopenharmony_ci  /* 0 weight is not set by user and we take the nghttp2 default one */
176513498266Sopenharmony_ci  return data->set.priority.weight?
176613498266Sopenharmony_ci    data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
176713498266Sopenharmony_ci}
176813498266Sopenharmony_ci
176913498266Sopenharmony_cistatic int sweight_in_effect(const struct Curl_easy *data)
177013498266Sopenharmony_ci{
177113498266Sopenharmony_ci  /* 0 weight is not set by user and we take the nghttp2 default one */
177213498266Sopenharmony_ci  return data->state.priority.weight?
177313498266Sopenharmony_ci    data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
177413498266Sopenharmony_ci}
177513498266Sopenharmony_ci
177613498266Sopenharmony_ci/*
177713498266Sopenharmony_ci * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
177813498266Sopenharmony_ci * and dependency to the peer. It also stores the updated values in the state
177913498266Sopenharmony_ci * struct.
178013498266Sopenharmony_ci */
178113498266Sopenharmony_ci
178213498266Sopenharmony_cistatic void h2_pri_spec(struct Curl_easy *data,
178313498266Sopenharmony_ci                        nghttp2_priority_spec *pri_spec)
178413498266Sopenharmony_ci{
178513498266Sopenharmony_ci  struct Curl_data_priority *prio = &data->set.priority;
178613498266Sopenharmony_ci  struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
178713498266Sopenharmony_ci  int32_t depstream_id = depstream? depstream->id:0;
178813498266Sopenharmony_ci  nghttp2_priority_spec_init(pri_spec, depstream_id,
178913498266Sopenharmony_ci                             sweight_wanted(data),
179013498266Sopenharmony_ci                             data->set.priority.exclusive);
179113498266Sopenharmony_ci  data->state.priority = *prio;
179213498266Sopenharmony_ci}
179313498266Sopenharmony_ci
179413498266Sopenharmony_ci/*
179513498266Sopenharmony_ci * Check if there's been an update in the priority /
179613498266Sopenharmony_ci * dependency settings and if so it submits a PRIORITY frame with the updated
179713498266Sopenharmony_ci * info.
179813498266Sopenharmony_ci * Flush any out data pending in the network buffer.
179913498266Sopenharmony_ci */
180013498266Sopenharmony_cistatic CURLcode h2_progress_egress(struct Curl_cfilter *cf,
180113498266Sopenharmony_ci                                  struct Curl_easy *data)
180213498266Sopenharmony_ci{
180313498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
180413498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
180513498266Sopenharmony_ci  int rv = 0;
180613498266Sopenharmony_ci
180713498266Sopenharmony_ci  if(stream && stream->id > 0 &&
180813498266Sopenharmony_ci     ((sweight_wanted(data) != sweight_in_effect(data)) ||
180913498266Sopenharmony_ci      (data->set.priority.exclusive != data->state.priority.exclusive) ||
181013498266Sopenharmony_ci      (data->set.priority.parent != data->state.priority.parent)) ) {
181113498266Sopenharmony_ci    /* send new weight and/or dependency */
181213498266Sopenharmony_ci    nghttp2_priority_spec pri_spec;
181313498266Sopenharmony_ci
181413498266Sopenharmony_ci    h2_pri_spec(data, &pri_spec);
181513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
181613498266Sopenharmony_ci    DEBUGASSERT(stream->id != -1);
181713498266Sopenharmony_ci    rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
181813498266Sopenharmony_ci                                 stream->id, &pri_spec);
181913498266Sopenharmony_ci    if(rv)
182013498266Sopenharmony_ci      goto out;
182113498266Sopenharmony_ci  }
182213498266Sopenharmony_ci
182313498266Sopenharmony_ci  ctx->nw_out_blocked = 0;
182413498266Sopenharmony_ci  while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
182513498266Sopenharmony_ci    rv = nghttp2_session_send(ctx->h2);
182613498266Sopenharmony_ci
182713498266Sopenharmony_ciout:
182813498266Sopenharmony_ci  if(nghttp2_is_fatal(rv)) {
182913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d",
183013498266Sopenharmony_ci                nghttp2_strerror(rv), rv);
183113498266Sopenharmony_ci    return CURLE_SEND_ERROR;
183213498266Sopenharmony_ci  }
183313498266Sopenharmony_ci  return nw_out_flush(cf, data);
183413498266Sopenharmony_ci}
183513498266Sopenharmony_ci
183613498266Sopenharmony_cistatic ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
183713498266Sopenharmony_ci                           struct stream_ctx *stream,
183813498266Sopenharmony_ci                           char *buf, size_t len, CURLcode *err)
183913498266Sopenharmony_ci{
184013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
184113498266Sopenharmony_ci  ssize_t nread = -1;
184213498266Sopenharmony_ci
184313498266Sopenharmony_ci  *err = CURLE_AGAIN;
184413498266Sopenharmony_ci  if(!Curl_bufq_is_empty(&stream->recvbuf)) {
184513498266Sopenharmony_ci    nread = Curl_bufq_read(&stream->recvbuf,
184613498266Sopenharmony_ci                           (unsigned char *)buf, len, err);
184713498266Sopenharmony_ci    if(nread < 0)
184813498266Sopenharmony_ci      goto out;
184913498266Sopenharmony_ci    DEBUGASSERT(nread > 0);
185013498266Sopenharmony_ci  }
185113498266Sopenharmony_ci
185213498266Sopenharmony_ci  if(nread < 0) {
185313498266Sopenharmony_ci    if(stream->closed) {
185413498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
185513498266Sopenharmony_ci      nread = http2_handle_stream_close(cf, data, stream, err);
185613498266Sopenharmony_ci    }
185713498266Sopenharmony_ci    else if(stream->reset ||
185813498266Sopenharmony_ci            (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
185913498266Sopenharmony_ci            (ctx->goaway && ctx->last_stream_id < stream->id)) {
186013498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
186113498266Sopenharmony_ci      *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
186213498266Sopenharmony_ci      nread = -1;
186313498266Sopenharmony_ci    }
186413498266Sopenharmony_ci  }
186513498266Sopenharmony_ci  else if(nread == 0) {
186613498266Sopenharmony_ci    *err = CURLE_AGAIN;
186713498266Sopenharmony_ci    nread = -1;
186813498266Sopenharmony_ci  }
186913498266Sopenharmony_ci
187013498266Sopenharmony_ciout:
187113498266Sopenharmony_ci  if(nread < 0 && *err != CURLE_AGAIN)
187213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
187313498266Sopenharmony_ci                stream->id, len, nread, *err);
187413498266Sopenharmony_ci  return nread;
187513498266Sopenharmony_ci}
187613498266Sopenharmony_ci
187713498266Sopenharmony_cistatic CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
187813498266Sopenharmony_ci                                    struct Curl_easy *data)
187913498266Sopenharmony_ci{
188013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
188113498266Sopenharmony_ci  struct stream_ctx *stream;
188213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
188313498266Sopenharmony_ci  ssize_t nread;
188413498266Sopenharmony_ci
188513498266Sopenharmony_ci  /* Process network input buffer fist */
188613498266Sopenharmony_ci  if(!Curl_bufq_is_empty(&ctx->inbufq)) {
188713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
188813498266Sopenharmony_ci                Curl_bufq_len(&ctx->inbufq));
188913498266Sopenharmony_ci    if(h2_process_pending_input(cf, data, &result) < 0)
189013498266Sopenharmony_ci      return result;
189113498266Sopenharmony_ci  }
189213498266Sopenharmony_ci
189313498266Sopenharmony_ci  /* Receive data from the "lower" filters, e.g. network until
189413498266Sopenharmony_ci   * it is time to stop due to connection close or us not processing
189513498266Sopenharmony_ci   * all network input */
189613498266Sopenharmony_ci  while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
189713498266Sopenharmony_ci    stream = H2_STREAM_CTX(data);
189813498266Sopenharmony_ci    if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) {
189913498266Sopenharmony_ci      /* We would like to abort here and stop processing, so that
190013498266Sopenharmony_ci       * the transfer loop can handle the data/close here. However,
190113498266Sopenharmony_ci       * this may leave data in underlying buffers that will not
190213498266Sopenharmony_ci       * be consumed. */
190313498266Sopenharmony_ci      if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
190413498266Sopenharmony_ci        break;
190513498266Sopenharmony_ci    }
190613498266Sopenharmony_ci
190713498266Sopenharmony_ci    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
190813498266Sopenharmony_ci    if(nread < 0) {
190913498266Sopenharmony_ci      if(result != CURLE_AGAIN) {
191013498266Sopenharmony_ci        failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
191113498266Sopenharmony_ci              curl_easy_strerror(result));
191213498266Sopenharmony_ci        return result;
191313498266Sopenharmony_ci      }
191413498266Sopenharmony_ci      break;
191513498266Sopenharmony_ci    }
191613498266Sopenharmony_ci    else if(nread == 0) {
191713498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[0] ingress: connection closed");
191813498266Sopenharmony_ci      ctx->conn_closed = TRUE;
191913498266Sopenharmony_ci      break;
192013498266Sopenharmony_ci    }
192113498266Sopenharmony_ci    else {
192213498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes",
192313498266Sopenharmony_ci                  nread);
192413498266Sopenharmony_ci    }
192513498266Sopenharmony_ci
192613498266Sopenharmony_ci    if(h2_process_pending_input(cf, data, &result))
192713498266Sopenharmony_ci      return result;
192813498266Sopenharmony_ci  }
192913498266Sopenharmony_ci
193013498266Sopenharmony_ci  if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
193113498266Sopenharmony_ci    connclose(cf->conn, "GOAWAY received");
193213498266Sopenharmony_ci  }
193313498266Sopenharmony_ci
193413498266Sopenharmony_ci  return CURLE_OK;
193513498266Sopenharmony_ci}
193613498266Sopenharmony_ci
193713498266Sopenharmony_cistatic ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
193813498266Sopenharmony_ci                          char *buf, size_t len, CURLcode *err)
193913498266Sopenharmony_ci{
194013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
194113498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
194213498266Sopenharmony_ci  ssize_t nread = -1;
194313498266Sopenharmony_ci  CURLcode result;
194413498266Sopenharmony_ci  struct cf_call_data save;
194513498266Sopenharmony_ci
194613498266Sopenharmony_ci  if(!stream) {
194713498266Sopenharmony_ci    /* Abnormal call sequence: either this transfer has never opened a stream
194813498266Sopenharmony_ci     * (unlikely) or the transfer has been done, cleaned up its resources, but
194913498266Sopenharmony_ci     * a read() is called anyway. It is not clear what the calling sequence
195013498266Sopenharmony_ci     * is for such a case. */
195113498266Sopenharmony_ci    failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
195213498266Sopenharmony_ci          "or already cleared", (ssize_t)data->id,
195313498266Sopenharmony_ci          (ssize_t)cf->conn->connection_id);
195413498266Sopenharmony_ci    *err = CURLE_HTTP2;
195513498266Sopenharmony_ci    return -1;
195613498266Sopenharmony_ci  }
195713498266Sopenharmony_ci
195813498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
195913498266Sopenharmony_ci
196013498266Sopenharmony_ci  nread = stream_recv(cf, data, stream, buf, len, err);
196113498266Sopenharmony_ci  if(nread < 0 && *err != CURLE_AGAIN)
196213498266Sopenharmony_ci    goto out;
196313498266Sopenharmony_ci
196413498266Sopenharmony_ci  if(nread < 0) {
196513498266Sopenharmony_ci    *err = h2_progress_ingress(cf, data);
196613498266Sopenharmony_ci    if(*err)
196713498266Sopenharmony_ci      goto out;
196813498266Sopenharmony_ci
196913498266Sopenharmony_ci    nread = stream_recv(cf, data, stream, buf, len, err);
197013498266Sopenharmony_ci  }
197113498266Sopenharmony_ci
197213498266Sopenharmony_ci  if(nread > 0) {
197313498266Sopenharmony_ci    size_t data_consumed = (size_t)nread;
197413498266Sopenharmony_ci    /* Now that we transferred this to the upper layer, we report
197513498266Sopenharmony_ci     * the actual amount of DATA consumed to the H2 session, so
197613498266Sopenharmony_ci     * that it adjusts stream flow control */
197713498266Sopenharmony_ci    if(stream->resp_hds_len >= data_consumed) {
197813498266Sopenharmony_ci      stream->resp_hds_len -= data_consumed;  /* no DATA */
197913498266Sopenharmony_ci    }
198013498266Sopenharmony_ci    else {
198113498266Sopenharmony_ci      if(stream->resp_hds_len) {
198213498266Sopenharmony_ci        data_consumed -= stream->resp_hds_len;
198313498266Sopenharmony_ci        stream->resp_hds_len = 0;
198413498266Sopenharmony_ci      }
198513498266Sopenharmony_ci      if(data_consumed) {
198613498266Sopenharmony_ci        nghttp2_session_consume(ctx->h2, stream->id, data_consumed);
198713498266Sopenharmony_ci      }
198813498266Sopenharmony_ci    }
198913498266Sopenharmony_ci
199013498266Sopenharmony_ci    if(stream->closed) {
199113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
199213498266Sopenharmony_ci      drain_stream(cf, data, stream);
199313498266Sopenharmony_ci    }
199413498266Sopenharmony_ci  }
199513498266Sopenharmony_ci
199613498266Sopenharmony_ciout:
199713498266Sopenharmony_ci  result = h2_progress_egress(cf, data);
199813498266Sopenharmony_ci  if(result == CURLE_AGAIN) {
199913498266Sopenharmony_ci    /* pending data to send, need to be called again. Ideally, we'd
200013498266Sopenharmony_ci     * monitor the socket for POLLOUT, but we might not be in SENDING
200113498266Sopenharmony_ci     * transfer state any longer and are unable to make this happen.
200213498266Sopenharmony_ci     */
200313498266Sopenharmony_ci    drain_stream(cf, data, stream);
200413498266Sopenharmony_ci  }
200513498266Sopenharmony_ci  else if(result) {
200613498266Sopenharmony_ci    *err = result;
200713498266Sopenharmony_ci    nread = -1;
200813498266Sopenharmony_ci  }
200913498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
201013498266Sopenharmony_ci              "buffered=%zu, window=%d/%d, connection %d/%d",
201113498266Sopenharmony_ci              stream->id, len, nread, *err,
201213498266Sopenharmony_ci              Curl_bufq_len(&stream->recvbuf),
201313498266Sopenharmony_ci              nghttp2_session_get_stream_effective_recv_data_length(
201413498266Sopenharmony_ci                ctx->h2, stream->id),
201513498266Sopenharmony_ci              nghttp2_session_get_stream_effective_local_window_size(
201613498266Sopenharmony_ci                ctx->h2, stream->id),
201713498266Sopenharmony_ci              nghttp2_session_get_local_window_size(ctx->h2),
201813498266Sopenharmony_ci              HTTP2_HUGE_WINDOW_SIZE);
201913498266Sopenharmony_ci
202013498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
202113498266Sopenharmony_ci  return nread;
202213498266Sopenharmony_ci}
202313498266Sopenharmony_ci
202413498266Sopenharmony_cistatic ssize_t h2_submit(struct stream_ctx **pstream,
202513498266Sopenharmony_ci                         struct Curl_cfilter *cf, struct Curl_easy *data,
202613498266Sopenharmony_ci                         const void *buf, size_t len, CURLcode *err)
202713498266Sopenharmony_ci{
202813498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
202913498266Sopenharmony_ci  struct stream_ctx *stream = NULL;
203013498266Sopenharmony_ci  struct dynhds h2_headers;
203113498266Sopenharmony_ci  nghttp2_nv *nva = NULL;
203213498266Sopenharmony_ci  const void *body = NULL;
203313498266Sopenharmony_ci  size_t nheader, bodylen, i;
203413498266Sopenharmony_ci  nghttp2_data_provider data_prd;
203513498266Sopenharmony_ci  int32_t stream_id;
203613498266Sopenharmony_ci  nghttp2_priority_spec pri_spec;
203713498266Sopenharmony_ci  ssize_t nwritten;
203813498266Sopenharmony_ci
203913498266Sopenharmony_ci  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
204013498266Sopenharmony_ci
204113498266Sopenharmony_ci  *err = http2_data_setup(cf, data, &stream);
204213498266Sopenharmony_ci  if(*err) {
204313498266Sopenharmony_ci    nwritten = -1;
204413498266Sopenharmony_ci    goto out;
204513498266Sopenharmony_ci  }
204613498266Sopenharmony_ci
204713498266Sopenharmony_ci  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
204813498266Sopenharmony_ci  if(nwritten < 0)
204913498266Sopenharmony_ci    goto out;
205013498266Sopenharmony_ci  if(!stream->h1.done) {
205113498266Sopenharmony_ci    /* need more data */
205213498266Sopenharmony_ci    goto out;
205313498266Sopenharmony_ci  }
205413498266Sopenharmony_ci  DEBUGASSERT(stream->h1.req);
205513498266Sopenharmony_ci
205613498266Sopenharmony_ci  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
205713498266Sopenharmony_ci  if(*err) {
205813498266Sopenharmony_ci    nwritten = -1;
205913498266Sopenharmony_ci    goto out;
206013498266Sopenharmony_ci  }
206113498266Sopenharmony_ci  /* no longer needed */
206213498266Sopenharmony_ci  Curl_h1_req_parse_free(&stream->h1);
206313498266Sopenharmony_ci
206413498266Sopenharmony_ci  nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
206513498266Sopenharmony_ci  if(!nva) {
206613498266Sopenharmony_ci    *err = CURLE_OUT_OF_MEMORY;
206713498266Sopenharmony_ci    nwritten = -1;
206813498266Sopenharmony_ci    goto out;
206913498266Sopenharmony_ci  }
207013498266Sopenharmony_ci
207113498266Sopenharmony_ci  h2_pri_spec(data, &pri_spec);
207213498266Sopenharmony_ci  if(!nghttp2_session_check_request_allowed(ctx->h2))
207313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
207413498266Sopenharmony_ci
207513498266Sopenharmony_ci  switch(data->state.httpreq) {
207613498266Sopenharmony_ci  case HTTPREQ_POST:
207713498266Sopenharmony_ci  case HTTPREQ_POST_FORM:
207813498266Sopenharmony_ci  case HTTPREQ_POST_MIME:
207913498266Sopenharmony_ci  case HTTPREQ_PUT:
208013498266Sopenharmony_ci    if(data->state.infilesize != -1)
208113498266Sopenharmony_ci      stream->upload_left = data->state.infilesize;
208213498266Sopenharmony_ci    else
208313498266Sopenharmony_ci      /* data sending without specifying the data amount up front */
208413498266Sopenharmony_ci      stream->upload_left = -1; /* unknown */
208513498266Sopenharmony_ci
208613498266Sopenharmony_ci    data_prd.read_callback = req_body_read_callback;
208713498266Sopenharmony_ci    data_prd.source.ptr = NULL;
208813498266Sopenharmony_ci    stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
208913498266Sopenharmony_ci                                       &data_prd, data);
209013498266Sopenharmony_ci    break;
209113498266Sopenharmony_ci  default:
209213498266Sopenharmony_ci    stream->upload_left = 0; /* no request body */
209313498266Sopenharmony_ci    stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
209413498266Sopenharmony_ci                                       NULL, data);
209513498266Sopenharmony_ci  }
209613498266Sopenharmony_ci
209713498266Sopenharmony_ci  if(stream_id < 0) {
209813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
209913498266Sopenharmony_ci                nghttp2_strerror(stream_id), stream_id);
210013498266Sopenharmony_ci    *err = CURLE_SEND_ERROR;
210113498266Sopenharmony_ci    nwritten = -1;
210213498266Sopenharmony_ci    goto out;
210313498266Sopenharmony_ci  }
210413498266Sopenharmony_ci
210513498266Sopenharmony_ci#define MAX_ACC 60000  /* <64KB to account for some overhead */
210613498266Sopenharmony_ci  if(Curl_trc_is_verbose(data)) {
210713498266Sopenharmony_ci    size_t acc = 0;
210813498266Sopenharmony_ci
210913498266Sopenharmony_ci    infof(data, "[HTTP/2] [%d] OPENED stream for %s",
211013498266Sopenharmony_ci          stream_id, data->state.url);
211113498266Sopenharmony_ci    for(i = 0; i < nheader; ++i) {
211213498266Sopenharmony_ci      acc += nva[i].namelen + nva[i].valuelen;
211313498266Sopenharmony_ci
211413498266Sopenharmony_ci      infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
211513498266Sopenharmony_ci            (int)nva[i].namelen, nva[i].name,
211613498266Sopenharmony_ci            (int)nva[i].valuelen, nva[i].value);
211713498266Sopenharmony_ci    }
211813498266Sopenharmony_ci
211913498266Sopenharmony_ci    if(acc > MAX_ACC) {
212013498266Sopenharmony_ci      infof(data, "[HTTP/2] Warning: The cumulative length of all "
212113498266Sopenharmony_ci            "headers exceeds %d bytes and that could cause the "
212213498266Sopenharmony_ci            "stream to be rejected.", MAX_ACC);
212313498266Sopenharmony_ci    }
212413498266Sopenharmony_ci  }
212513498266Sopenharmony_ci
212613498266Sopenharmony_ci  stream->id = stream_id;
212713498266Sopenharmony_ci  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
212813498266Sopenharmony_ci  if(data->set.max_recv_speed) {
212913498266Sopenharmony_ci    /* We are asked to only receive `max_recv_speed` bytes per second.
213013498266Sopenharmony_ci     * Let's limit our stream window size around that, otherwise the server
213113498266Sopenharmony_ci     * will send in large bursts only. We make the window 50% larger to
213213498266Sopenharmony_ci     * allow for data in flight and avoid stalling. */
213313498266Sopenharmony_ci    curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
213413498266Sopenharmony_ci    n += CURLMAX((n/2), 1);
213513498266Sopenharmony_ci    if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
213613498266Sopenharmony_ci       n < (UINT_MAX / H2_CHUNK_SIZE)) {
213713498266Sopenharmony_ci      stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
213813498266Sopenharmony_ci    }
213913498266Sopenharmony_ci  }
214013498266Sopenharmony_ci
214113498266Sopenharmony_ci  body = (const char *)buf + nwritten;
214213498266Sopenharmony_ci  bodylen = len - nwritten;
214313498266Sopenharmony_ci
214413498266Sopenharmony_ci  if(bodylen) {
214513498266Sopenharmony_ci    /* We have request body to send in DATA frame */
214613498266Sopenharmony_ci    ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
214713498266Sopenharmony_ci    if(n < 0) {
214813498266Sopenharmony_ci      *err = CURLE_SEND_ERROR;
214913498266Sopenharmony_ci      nwritten = -1;
215013498266Sopenharmony_ci      goto out;
215113498266Sopenharmony_ci    }
215213498266Sopenharmony_ci    nwritten += n;
215313498266Sopenharmony_ci  }
215413498266Sopenharmony_ci
215513498266Sopenharmony_ciout:
215613498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
215713498266Sopenharmony_ci              stream? stream->id : -1, nwritten, *err);
215813498266Sopenharmony_ci  Curl_safefree(nva);
215913498266Sopenharmony_ci  *pstream = stream;
216013498266Sopenharmony_ci  Curl_dynhds_free(&h2_headers);
216113498266Sopenharmony_ci  return nwritten;
216213498266Sopenharmony_ci}
216313498266Sopenharmony_ci
216413498266Sopenharmony_cistatic ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
216513498266Sopenharmony_ci                          const void *buf, size_t len, CURLcode *err)
216613498266Sopenharmony_ci{
216713498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
216813498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
216913498266Sopenharmony_ci  struct cf_call_data save;
217013498266Sopenharmony_ci  int rv;
217113498266Sopenharmony_ci  ssize_t nwritten;
217213498266Sopenharmony_ci  CURLcode result;
217313498266Sopenharmony_ci  int blocked = 0, was_blocked = 0;
217413498266Sopenharmony_ci
217513498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
217613498266Sopenharmony_ci
217713498266Sopenharmony_ci  if(stream && stream->id != -1) {
217813498266Sopenharmony_ci    if(stream->upload_blocked_len) {
217913498266Sopenharmony_ci      /* the data in `buf` has already been submitted or added to the
218013498266Sopenharmony_ci       * buffers, but have been EAGAINed on the last invocation. */
218113498266Sopenharmony_ci      /* TODO: this assertion triggers in OSSFuzz runs and it is not
218213498266Sopenharmony_ci       * clear why. Disable for now to let OSSFuzz continue its tests. */
218313498266Sopenharmony_ci      DEBUGASSERT(len >= stream->upload_blocked_len);
218413498266Sopenharmony_ci      if(len < stream->upload_blocked_len) {
218513498266Sopenharmony_ci        /* Did we get called again with a smaller `len`? This should not
218613498266Sopenharmony_ci         * happen. We are not prepared to handle that. */
218713498266Sopenharmony_ci        failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)",
218813498266Sopenharmony_ci              len, stream->upload_blocked_len);
218913498266Sopenharmony_ci        *err = CURLE_HTTP2;
219013498266Sopenharmony_ci        nwritten = -1;
219113498266Sopenharmony_ci        goto out;
219213498266Sopenharmony_ci      }
219313498266Sopenharmony_ci      nwritten = (ssize_t)stream->upload_blocked_len;
219413498266Sopenharmony_ci      stream->upload_blocked_len = 0;
219513498266Sopenharmony_ci      was_blocked = 1;
219613498266Sopenharmony_ci    }
219713498266Sopenharmony_ci    else if(stream->closed) {
219813498266Sopenharmony_ci      if(stream->resp_hds_complete) {
219913498266Sopenharmony_ci        /* Server decided to close the stream after having sent us a findl
220013498266Sopenharmony_ci         * response. This is valid if it is not interested in the request
220113498266Sopenharmony_ci         * body. This happens on 30x or 40x responses.
220213498266Sopenharmony_ci         * We silently discard the data sent, since this is not a transport
220313498266Sopenharmony_ci         * error situation. */
220413498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "[%d] discarding data"
220513498266Sopenharmony_ci                    "on closed stream with response", stream->id);
220613498266Sopenharmony_ci        *err = CURLE_OK;
220713498266Sopenharmony_ci        nwritten = (ssize_t)len;
220813498266Sopenharmony_ci        goto out;
220913498266Sopenharmony_ci      }
221013498266Sopenharmony_ci      infof(data, "stream %u closed", stream->id);
221113498266Sopenharmony_ci      *err = CURLE_SEND_ERROR;
221213498266Sopenharmony_ci      nwritten = -1;
221313498266Sopenharmony_ci      goto out;
221413498266Sopenharmony_ci    }
221513498266Sopenharmony_ci    else {
221613498266Sopenharmony_ci      /* If stream_id != -1, we have dispatched request HEADERS and
221713498266Sopenharmony_ci       * optionally request body, and now are going to send or sending
221813498266Sopenharmony_ci       * more request body in DATA frame */
221913498266Sopenharmony_ci      nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
222013498266Sopenharmony_ci      if(nwritten < 0 && *err != CURLE_AGAIN)
222113498266Sopenharmony_ci        goto out;
222213498266Sopenharmony_ci    }
222313498266Sopenharmony_ci
222413498266Sopenharmony_ci    if(!Curl_bufq_is_empty(&stream->sendbuf)) {
222513498266Sopenharmony_ci      /* req body data is buffered, resume the potentially suspended stream */
222613498266Sopenharmony_ci      rv = nghttp2_session_resume_data(ctx->h2, stream->id);
222713498266Sopenharmony_ci      if(nghttp2_is_fatal(rv)) {
222813498266Sopenharmony_ci        *err = CURLE_SEND_ERROR;
222913498266Sopenharmony_ci        nwritten = -1;
223013498266Sopenharmony_ci        goto out;
223113498266Sopenharmony_ci      }
223213498266Sopenharmony_ci    }
223313498266Sopenharmony_ci  }
223413498266Sopenharmony_ci  else {
223513498266Sopenharmony_ci    nwritten = h2_submit(&stream, cf, data, buf, len, err);
223613498266Sopenharmony_ci    if(nwritten < 0) {
223713498266Sopenharmony_ci      goto out;
223813498266Sopenharmony_ci    }
223913498266Sopenharmony_ci    DEBUGASSERT(stream);
224013498266Sopenharmony_ci  }
224113498266Sopenharmony_ci
224213498266Sopenharmony_ci  /* Call the nghttp2 send loop and flush to write ALL buffered data,
224313498266Sopenharmony_ci   * headers and/or request body completely out to the network */
224413498266Sopenharmony_ci  result = h2_progress_egress(cf, data);
224513498266Sopenharmony_ci  /* if the stream has been closed in egress handling (nghttp2 does that
224613498266Sopenharmony_ci   * when it does not like the headers, for example */
224713498266Sopenharmony_ci  if(stream && stream->closed && !was_blocked) {
224813498266Sopenharmony_ci    infof(data, "stream %u closed", stream->id);
224913498266Sopenharmony_ci    *err = CURLE_SEND_ERROR;
225013498266Sopenharmony_ci    nwritten = -1;
225113498266Sopenharmony_ci    goto out;
225213498266Sopenharmony_ci  }
225313498266Sopenharmony_ci  else if(result == CURLE_AGAIN) {
225413498266Sopenharmony_ci    blocked = 1;
225513498266Sopenharmony_ci  }
225613498266Sopenharmony_ci  else if(result) {
225713498266Sopenharmony_ci    *err = result;
225813498266Sopenharmony_ci    nwritten = -1;
225913498266Sopenharmony_ci    goto out;
226013498266Sopenharmony_ci  }
226113498266Sopenharmony_ci  else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
226213498266Sopenharmony_ci    /* although we wrote everything that nghttp2 wants to send now,
226313498266Sopenharmony_ci     * there is data left in our stream send buffer unwritten. This may
226413498266Sopenharmony_ci     * be due to the stream's HTTP/2 flow window being exhausted. */
226513498266Sopenharmony_ci    blocked = 1;
226613498266Sopenharmony_ci  }
226713498266Sopenharmony_ci
226813498266Sopenharmony_ci  if(stream && blocked && nwritten > 0) {
226913498266Sopenharmony_ci    /* Unable to send all data, due to connection blocked or H2 window
227013498266Sopenharmony_ci     * exhaustion. Data is left in our stream buffer, or nghttp2's internal
227113498266Sopenharmony_ci     * frame buffer or our network out buffer. */
227213498266Sopenharmony_ci    size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
227313498266Sopenharmony_ci                                                                stream->id);
227413498266Sopenharmony_ci    /* Whatever the cause, we need to return CURL_EAGAIN for this call.
227513498266Sopenharmony_ci     * We have unwritten state that needs us being invoked again and EAGAIN
227613498266Sopenharmony_ci     * is the only way to ensure that. */
227713498266Sopenharmony_ci    stream->upload_blocked_len = nwritten;
227813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
227913498266Sopenharmony_ci                "blocked_len=%zu",
228013498266Sopenharmony_ci                stream->id, len,
228113498266Sopenharmony_ci                nghttp2_session_get_remote_window_size(ctx->h2), rwin,
228213498266Sopenharmony_ci                nwritten);
228313498266Sopenharmony_ci    *err = CURLE_AGAIN;
228413498266Sopenharmony_ci    nwritten = -1;
228513498266Sopenharmony_ci    goto out;
228613498266Sopenharmony_ci  }
228713498266Sopenharmony_ci  else if(should_close_session(ctx)) {
228813498266Sopenharmony_ci    /* nghttp2 thinks this session is done. If the stream has not been
228913498266Sopenharmony_ci     * closed, this is an error state for out transfer */
229013498266Sopenharmony_ci    if(stream->closed) {
229113498266Sopenharmony_ci      nwritten = http2_handle_stream_close(cf, data, stream, err);
229213498266Sopenharmony_ci    }
229313498266Sopenharmony_ci    else {
229413498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "send: nothing to do in this session");
229513498266Sopenharmony_ci      *err = CURLE_HTTP2;
229613498266Sopenharmony_ci      nwritten = -1;
229713498266Sopenharmony_ci    }
229813498266Sopenharmony_ci  }
229913498266Sopenharmony_ci
230013498266Sopenharmony_ciout:
230113498266Sopenharmony_ci  if(stream) {
230213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
230313498266Sopenharmony_ci                "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
230413498266Sopenharmony_ci                "h2 windows %d-%d (stream-conn), "
230513498266Sopenharmony_ci                "buffers %zu-%zu (stream-conn)",
230613498266Sopenharmony_ci                stream->id, len, nwritten, *err,
230713498266Sopenharmony_ci                stream->upload_left,
230813498266Sopenharmony_ci                nghttp2_session_get_stream_remote_window_size(
230913498266Sopenharmony_ci                  ctx->h2, stream->id),
231013498266Sopenharmony_ci                nghttp2_session_get_remote_window_size(ctx->h2),
231113498266Sopenharmony_ci                Curl_bufq_len(&stream->sendbuf),
231213498266Sopenharmony_ci                Curl_bufq_len(&ctx->outbufq));
231313498266Sopenharmony_ci  }
231413498266Sopenharmony_ci  else {
231513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
231613498266Sopenharmony_ci                "connection-window=%d, nw_send_buffer(%zu)",
231713498266Sopenharmony_ci                len, nwritten, *err,
231813498266Sopenharmony_ci                nghttp2_session_get_remote_window_size(ctx->h2),
231913498266Sopenharmony_ci                Curl_bufq_len(&ctx->outbufq));
232013498266Sopenharmony_ci  }
232113498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
232213498266Sopenharmony_ci  return nwritten;
232313498266Sopenharmony_ci}
232413498266Sopenharmony_ci
232513498266Sopenharmony_cistatic void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
232613498266Sopenharmony_ci                                 struct Curl_easy *data,
232713498266Sopenharmony_ci                                 struct easy_pollset *ps)
232813498266Sopenharmony_ci{
232913498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
233013498266Sopenharmony_ci  curl_socket_t sock;
233113498266Sopenharmony_ci  bool want_recv, want_send;
233213498266Sopenharmony_ci
233313498266Sopenharmony_ci  if(!ctx->h2)
233413498266Sopenharmony_ci    return;
233513498266Sopenharmony_ci
233613498266Sopenharmony_ci  sock = Curl_conn_cf_get_socket(cf, data);
233713498266Sopenharmony_ci  Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
233813498266Sopenharmony_ci  if(want_recv || want_send) {
233913498266Sopenharmony_ci    struct stream_ctx *stream = H2_STREAM_CTX(data);
234013498266Sopenharmony_ci    struct cf_call_data save;
234113498266Sopenharmony_ci    bool c_exhaust, s_exhaust;
234213498266Sopenharmony_ci
234313498266Sopenharmony_ci    CF_DATA_SAVE(save, cf, data);
234413498266Sopenharmony_ci    c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
234513498266Sopenharmony_ci    s_exhaust = want_send && stream && stream->id >= 0 &&
234613498266Sopenharmony_ci                !nghttp2_session_get_stream_remote_window_size(ctx->h2,
234713498266Sopenharmony_ci                                                               stream->id);
234813498266Sopenharmony_ci    want_recv = (want_recv || c_exhaust || s_exhaust);
234913498266Sopenharmony_ci    want_send = (!s_exhaust && want_send) ||
235013498266Sopenharmony_ci                (!c_exhaust && nghttp2_session_want_write(ctx->h2));
235113498266Sopenharmony_ci
235213498266Sopenharmony_ci    Curl_pollset_set(data, ps, sock, want_recv, want_send);
235313498266Sopenharmony_ci    CF_DATA_RESTORE(cf, save);
235413498266Sopenharmony_ci  }
235513498266Sopenharmony_ci}
235613498266Sopenharmony_ci
235713498266Sopenharmony_cistatic CURLcode cf_h2_connect(struct Curl_cfilter *cf,
235813498266Sopenharmony_ci                              struct Curl_easy *data,
235913498266Sopenharmony_ci                              bool blocking, bool *done)
236013498266Sopenharmony_ci{
236113498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
236213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
236313498266Sopenharmony_ci  struct cf_call_data save;
236413498266Sopenharmony_ci
236513498266Sopenharmony_ci  if(cf->connected) {
236613498266Sopenharmony_ci    *done = TRUE;
236713498266Sopenharmony_ci    return CURLE_OK;
236813498266Sopenharmony_ci  }
236913498266Sopenharmony_ci
237013498266Sopenharmony_ci  /* Connect the lower filters first */
237113498266Sopenharmony_ci  if(!cf->next->connected) {
237213498266Sopenharmony_ci    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
237313498266Sopenharmony_ci    if(result || !*done)
237413498266Sopenharmony_ci      return result;
237513498266Sopenharmony_ci  }
237613498266Sopenharmony_ci
237713498266Sopenharmony_ci  *done = FALSE;
237813498266Sopenharmony_ci
237913498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
238013498266Sopenharmony_ci  if(!ctx->h2) {
238113498266Sopenharmony_ci    result = cf_h2_ctx_init(cf, data, FALSE);
238213498266Sopenharmony_ci    if(result)
238313498266Sopenharmony_ci      goto out;
238413498266Sopenharmony_ci  }
238513498266Sopenharmony_ci
238613498266Sopenharmony_ci  result = h2_progress_ingress(cf, data);
238713498266Sopenharmony_ci  if(result)
238813498266Sopenharmony_ci    goto out;
238913498266Sopenharmony_ci
239013498266Sopenharmony_ci  /* Send out our SETTINGS and ACKs and such. If that blocks, we
239113498266Sopenharmony_ci   * have it buffered and  can count this filter as being connected */
239213498266Sopenharmony_ci  result = h2_progress_egress(cf, data);
239313498266Sopenharmony_ci  if(result == CURLE_AGAIN)
239413498266Sopenharmony_ci    result = CURLE_OK;
239513498266Sopenharmony_ci  else if(result)
239613498266Sopenharmony_ci    goto out;
239713498266Sopenharmony_ci
239813498266Sopenharmony_ci  *done = TRUE;
239913498266Sopenharmony_ci  cf->connected = TRUE;
240013498266Sopenharmony_ci  result = CURLE_OK;
240113498266Sopenharmony_ci
240213498266Sopenharmony_ciout:
240313498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
240413498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
240513498266Sopenharmony_ci  return result;
240613498266Sopenharmony_ci}
240713498266Sopenharmony_ci
240813498266Sopenharmony_cistatic void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
240913498266Sopenharmony_ci{
241013498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
241113498266Sopenharmony_ci
241213498266Sopenharmony_ci  if(ctx) {
241313498266Sopenharmony_ci    struct cf_call_data save;
241413498266Sopenharmony_ci
241513498266Sopenharmony_ci    CF_DATA_SAVE(save, cf, data);
241613498266Sopenharmony_ci    cf_h2_ctx_clear(ctx);
241713498266Sopenharmony_ci    CF_DATA_RESTORE(cf, save);
241813498266Sopenharmony_ci  }
241913498266Sopenharmony_ci  if(cf->next)
242013498266Sopenharmony_ci    cf->next->cft->do_close(cf->next, data);
242113498266Sopenharmony_ci}
242213498266Sopenharmony_ci
242313498266Sopenharmony_cistatic void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
242413498266Sopenharmony_ci{
242513498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
242613498266Sopenharmony_ci
242713498266Sopenharmony_ci  (void)data;
242813498266Sopenharmony_ci  if(ctx) {
242913498266Sopenharmony_ci    cf_h2_ctx_free(ctx);
243013498266Sopenharmony_ci    cf->ctx = NULL;
243113498266Sopenharmony_ci  }
243213498266Sopenharmony_ci}
243313498266Sopenharmony_ci
243413498266Sopenharmony_cistatic CURLcode http2_data_pause(struct Curl_cfilter *cf,
243513498266Sopenharmony_ci                                 struct Curl_easy *data,
243613498266Sopenharmony_ci                                 bool pause)
243713498266Sopenharmony_ci{
243813498266Sopenharmony_ci#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
243913498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
244013498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
244113498266Sopenharmony_ci
244213498266Sopenharmony_ci  DEBUGASSERT(data);
244313498266Sopenharmony_ci  if(ctx && ctx->h2 && stream) {
244413498266Sopenharmony_ci    uint32_t window = pause? 0 : stream->local_window_size;
244513498266Sopenharmony_ci
244613498266Sopenharmony_ci    int rv = nghttp2_session_set_local_window_size(ctx->h2,
244713498266Sopenharmony_ci                                                   NGHTTP2_FLAG_NONE,
244813498266Sopenharmony_ci                                                   stream->id,
244913498266Sopenharmony_ci                                                   window);
245013498266Sopenharmony_ci    if(rv) {
245113498266Sopenharmony_ci      failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
245213498266Sopenharmony_ci            nghttp2_strerror(rv), rv);
245313498266Sopenharmony_ci      return CURLE_HTTP2;
245413498266Sopenharmony_ci    }
245513498266Sopenharmony_ci
245613498266Sopenharmony_ci    if(!pause)
245713498266Sopenharmony_ci      drain_stream(cf, data, stream);
245813498266Sopenharmony_ci
245913498266Sopenharmony_ci    /* attempt to send the window update */
246013498266Sopenharmony_ci    (void)h2_progress_egress(cf, data);
246113498266Sopenharmony_ci
246213498266Sopenharmony_ci    if(!pause) {
246313498266Sopenharmony_ci      /* Unpausing a h2 transfer, requires it to be run again. The server
246413498266Sopenharmony_ci       * may send new DATA on us increasing the flow window, and it may
246513498266Sopenharmony_ci       * not. We may have already buffered and exhausted the new window
246613498266Sopenharmony_ci       * by operating on things in flight during the handling of other
246713498266Sopenharmony_ci       * transfers. */
246813498266Sopenharmony_ci      drain_stream(cf, data, stream);
246913498266Sopenharmony_ci      Curl_expire(data, 0, EXPIRE_RUN_NOW);
247013498266Sopenharmony_ci    }
247113498266Sopenharmony_ci    DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
247213498266Sopenharmony_ci                 window, stream->id));
247313498266Sopenharmony_ci
247413498266Sopenharmony_ci#ifdef DEBUGBUILD
247513498266Sopenharmony_ci    {
247613498266Sopenharmony_ci      /* read out the stream local window again */
247713498266Sopenharmony_ci      uint32_t window2 =
247813498266Sopenharmony_ci        nghttp2_session_get_stream_local_window_size(ctx->h2,
247913498266Sopenharmony_ci                                                     stream->id);
248013498266Sopenharmony_ci      DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
248113498266Sopenharmony_ci                   window2, stream->id));
248213498266Sopenharmony_ci    }
248313498266Sopenharmony_ci#endif
248413498266Sopenharmony_ci  }
248513498266Sopenharmony_ci#endif
248613498266Sopenharmony_ci  return CURLE_OK;
248713498266Sopenharmony_ci}
248813498266Sopenharmony_ci
248913498266Sopenharmony_cistatic CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
249013498266Sopenharmony_ci                            struct Curl_easy *data,
249113498266Sopenharmony_ci                            int event, int arg1, void *arg2)
249213498266Sopenharmony_ci{
249313498266Sopenharmony_ci  CURLcode result = CURLE_OK;
249413498266Sopenharmony_ci  struct cf_call_data save;
249513498266Sopenharmony_ci
249613498266Sopenharmony_ci  (void)arg2;
249713498266Sopenharmony_ci
249813498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
249913498266Sopenharmony_ci  switch(event) {
250013498266Sopenharmony_ci  case CF_CTRL_DATA_SETUP:
250113498266Sopenharmony_ci    break;
250213498266Sopenharmony_ci  case CF_CTRL_DATA_PAUSE:
250313498266Sopenharmony_ci    result = http2_data_pause(cf, data, (arg1 != 0));
250413498266Sopenharmony_ci    break;
250513498266Sopenharmony_ci  case CF_CTRL_DATA_DONE_SEND:
250613498266Sopenharmony_ci    result = http2_data_done_send(cf, data);
250713498266Sopenharmony_ci    break;
250813498266Sopenharmony_ci  case CF_CTRL_DATA_DETACH:
250913498266Sopenharmony_ci    http2_data_done(cf, data, TRUE);
251013498266Sopenharmony_ci    break;
251113498266Sopenharmony_ci  case CF_CTRL_DATA_DONE:
251213498266Sopenharmony_ci    http2_data_done(cf, data, arg1 != 0);
251313498266Sopenharmony_ci    break;
251413498266Sopenharmony_ci  default:
251513498266Sopenharmony_ci    break;
251613498266Sopenharmony_ci  }
251713498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
251813498266Sopenharmony_ci  return result;
251913498266Sopenharmony_ci}
252013498266Sopenharmony_ci
252113498266Sopenharmony_cistatic bool cf_h2_data_pending(struct Curl_cfilter *cf,
252213498266Sopenharmony_ci                               const struct Curl_easy *data)
252313498266Sopenharmony_ci{
252413498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
252513498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
252613498266Sopenharmony_ci
252713498266Sopenharmony_ci  if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
252813498266Sopenharmony_ci            || (stream && !Curl_bufq_is_empty(&stream->sendbuf))
252913498266Sopenharmony_ci            || (stream && !Curl_bufq_is_empty(&stream->recvbuf))))
253013498266Sopenharmony_ci    return TRUE;
253113498266Sopenharmony_ci  return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
253213498266Sopenharmony_ci}
253313498266Sopenharmony_ci
253413498266Sopenharmony_cistatic bool cf_h2_is_alive(struct Curl_cfilter *cf,
253513498266Sopenharmony_ci                           struct Curl_easy *data,
253613498266Sopenharmony_ci                           bool *input_pending)
253713498266Sopenharmony_ci{
253813498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
253913498266Sopenharmony_ci  CURLcode result;
254013498266Sopenharmony_ci  struct cf_call_data save;
254113498266Sopenharmony_ci
254213498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
254313498266Sopenharmony_ci  result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
254413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
254513498266Sopenharmony_ci              result, *input_pending);
254613498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
254713498266Sopenharmony_ci  return result;
254813498266Sopenharmony_ci}
254913498266Sopenharmony_ci
255013498266Sopenharmony_cistatic CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
255113498266Sopenharmony_ci                                 struct Curl_easy *data)
255213498266Sopenharmony_ci{
255313498266Sopenharmony_ci  CURLcode result;
255413498266Sopenharmony_ci  struct cf_call_data save;
255513498266Sopenharmony_ci
255613498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
255713498266Sopenharmony_ci  result = http2_send_ping(cf, data);
255813498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
255913498266Sopenharmony_ci  return result;
256013498266Sopenharmony_ci}
256113498266Sopenharmony_ci
256213498266Sopenharmony_cistatic CURLcode cf_h2_query(struct Curl_cfilter *cf,
256313498266Sopenharmony_ci                            struct Curl_easy *data,
256413498266Sopenharmony_ci                            int query, int *pres1, void *pres2)
256513498266Sopenharmony_ci{
256613498266Sopenharmony_ci  struct cf_h2_ctx *ctx = cf->ctx;
256713498266Sopenharmony_ci  struct cf_call_data save;
256813498266Sopenharmony_ci  size_t effective_max;
256913498266Sopenharmony_ci
257013498266Sopenharmony_ci  switch(query) {
257113498266Sopenharmony_ci  case CF_QUERY_MAX_CONCURRENT:
257213498266Sopenharmony_ci    DEBUGASSERT(pres1);
257313498266Sopenharmony_ci
257413498266Sopenharmony_ci    CF_DATA_SAVE(save, cf, data);
257513498266Sopenharmony_ci    if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
257613498266Sopenharmony_ci      /* the limit is what we have in use right now */
257713498266Sopenharmony_ci      effective_max = CONN_INUSE(cf->conn);
257813498266Sopenharmony_ci    }
257913498266Sopenharmony_ci    else {
258013498266Sopenharmony_ci      effective_max = ctx->max_concurrent_streams;
258113498266Sopenharmony_ci    }
258213498266Sopenharmony_ci    *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
258313498266Sopenharmony_ci    CF_DATA_RESTORE(cf, save);
258413498266Sopenharmony_ci    return CURLE_OK;
258513498266Sopenharmony_ci  default:
258613498266Sopenharmony_ci    break;
258713498266Sopenharmony_ci  }
258813498266Sopenharmony_ci  return cf->next?
258913498266Sopenharmony_ci    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
259013498266Sopenharmony_ci    CURLE_UNKNOWN_OPTION;
259113498266Sopenharmony_ci}
259213498266Sopenharmony_ci
259313498266Sopenharmony_cistruct Curl_cftype Curl_cft_nghttp2 = {
259413498266Sopenharmony_ci  "HTTP/2",
259513498266Sopenharmony_ci  CF_TYPE_MULTIPLEX,
259613498266Sopenharmony_ci  CURL_LOG_LVL_NONE,
259713498266Sopenharmony_ci  cf_h2_destroy,
259813498266Sopenharmony_ci  cf_h2_connect,
259913498266Sopenharmony_ci  cf_h2_close,
260013498266Sopenharmony_ci  Curl_cf_def_get_host,
260113498266Sopenharmony_ci  cf_h2_adjust_pollset,
260213498266Sopenharmony_ci  cf_h2_data_pending,
260313498266Sopenharmony_ci  cf_h2_send,
260413498266Sopenharmony_ci  cf_h2_recv,
260513498266Sopenharmony_ci  cf_h2_cntrl,
260613498266Sopenharmony_ci  cf_h2_is_alive,
260713498266Sopenharmony_ci  cf_h2_keep_alive,
260813498266Sopenharmony_ci  cf_h2_query,
260913498266Sopenharmony_ci};
261013498266Sopenharmony_ci
261113498266Sopenharmony_cistatic CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
261213498266Sopenharmony_ci                                  struct Curl_easy *data,
261313498266Sopenharmony_ci                                  struct connectdata *conn,
261413498266Sopenharmony_ci                                  int sockindex)
261513498266Sopenharmony_ci{
261613498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
261713498266Sopenharmony_ci  struct cf_h2_ctx *ctx;
261813498266Sopenharmony_ci  CURLcode result = CURLE_OUT_OF_MEMORY;
261913498266Sopenharmony_ci
262013498266Sopenharmony_ci  DEBUGASSERT(data->conn);
262113498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
262213498266Sopenharmony_ci  if(!ctx)
262313498266Sopenharmony_ci    goto out;
262413498266Sopenharmony_ci
262513498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
262613498266Sopenharmony_ci  if(result)
262713498266Sopenharmony_ci    goto out;
262813498266Sopenharmony_ci
262913498266Sopenharmony_ci  Curl_conn_cf_add(data, conn, sockindex, cf);
263013498266Sopenharmony_ci  result = CURLE_OK;
263113498266Sopenharmony_ci
263213498266Sopenharmony_ciout:
263313498266Sopenharmony_ci  if(result)
263413498266Sopenharmony_ci    cf_h2_ctx_free(ctx);
263513498266Sopenharmony_ci  *pcf = result? NULL : cf;
263613498266Sopenharmony_ci  return result;
263713498266Sopenharmony_ci}
263813498266Sopenharmony_ci
263913498266Sopenharmony_cistatic CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
264013498266Sopenharmony_ci                                           struct Curl_easy *data)
264113498266Sopenharmony_ci{
264213498266Sopenharmony_ci  struct Curl_cfilter *cf_h2 = NULL;
264313498266Sopenharmony_ci  struct cf_h2_ctx *ctx;
264413498266Sopenharmony_ci  CURLcode result = CURLE_OUT_OF_MEMORY;
264513498266Sopenharmony_ci
264613498266Sopenharmony_ci  (void)data;
264713498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
264813498266Sopenharmony_ci  if(!ctx)
264913498266Sopenharmony_ci    goto out;
265013498266Sopenharmony_ci
265113498266Sopenharmony_ci  result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
265213498266Sopenharmony_ci  if(result)
265313498266Sopenharmony_ci    goto out;
265413498266Sopenharmony_ci
265513498266Sopenharmony_ci  Curl_conn_cf_insert_after(cf, cf_h2);
265613498266Sopenharmony_ci  result = CURLE_OK;
265713498266Sopenharmony_ci
265813498266Sopenharmony_ciout:
265913498266Sopenharmony_ci  if(result)
266013498266Sopenharmony_ci    cf_h2_ctx_free(ctx);
266113498266Sopenharmony_ci  return result;
266213498266Sopenharmony_ci}
266313498266Sopenharmony_ci
266413498266Sopenharmony_cistatic bool Curl_cf_is_http2(struct Curl_cfilter *cf,
266513498266Sopenharmony_ci                             const struct Curl_easy *data)
266613498266Sopenharmony_ci{
266713498266Sopenharmony_ci  (void)data;
266813498266Sopenharmony_ci  for(; cf; cf = cf->next) {
266913498266Sopenharmony_ci    if(cf->cft == &Curl_cft_nghttp2)
267013498266Sopenharmony_ci      return TRUE;
267113498266Sopenharmony_ci    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
267213498266Sopenharmony_ci      return FALSE;
267313498266Sopenharmony_ci  }
267413498266Sopenharmony_ci  return FALSE;
267513498266Sopenharmony_ci}
267613498266Sopenharmony_ci
267713498266Sopenharmony_cibool Curl_conn_is_http2(const struct Curl_easy *data,
267813498266Sopenharmony_ci                        const struct connectdata *conn,
267913498266Sopenharmony_ci                        int sockindex)
268013498266Sopenharmony_ci{
268113498266Sopenharmony_ci  return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
268213498266Sopenharmony_ci}
268313498266Sopenharmony_ci
268413498266Sopenharmony_cibool Curl_http2_may_switch(struct Curl_easy *data,
268513498266Sopenharmony_ci                           struct connectdata *conn,
268613498266Sopenharmony_ci                           int sockindex)
268713498266Sopenharmony_ci{
268813498266Sopenharmony_ci  (void)sockindex;
268913498266Sopenharmony_ci  if(!Curl_conn_is_http2(data, conn, sockindex) &&
269013498266Sopenharmony_ci     data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
269113498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY
269213498266Sopenharmony_ci    if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
269313498266Sopenharmony_ci      /* We don't support HTTP/2 proxies yet. Also it's debatable
269413498266Sopenharmony_ci         whether or not this setting should apply to HTTP/2 proxies. */
269513498266Sopenharmony_ci      infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
269613498266Sopenharmony_ci      return FALSE;
269713498266Sopenharmony_ci    }
269813498266Sopenharmony_ci#endif
269913498266Sopenharmony_ci    return TRUE;
270013498266Sopenharmony_ci  }
270113498266Sopenharmony_ci  return FALSE;
270213498266Sopenharmony_ci}
270313498266Sopenharmony_ci
270413498266Sopenharmony_ciCURLcode Curl_http2_switch(struct Curl_easy *data,
270513498266Sopenharmony_ci                           struct connectdata *conn, int sockindex)
270613498266Sopenharmony_ci{
270713498266Sopenharmony_ci  struct Curl_cfilter *cf;
270813498266Sopenharmony_ci  CURLcode result;
270913498266Sopenharmony_ci
271013498266Sopenharmony_ci  DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
271113498266Sopenharmony_ci  DEBUGF(infof(data, "switching to HTTP/2"));
271213498266Sopenharmony_ci
271313498266Sopenharmony_ci  result = http2_cfilter_add(&cf, data, conn, sockindex);
271413498266Sopenharmony_ci  if(result)
271513498266Sopenharmony_ci    return result;
271613498266Sopenharmony_ci
271713498266Sopenharmony_ci  result = cf_h2_ctx_init(cf, data, FALSE);
271813498266Sopenharmony_ci  if(result)
271913498266Sopenharmony_ci    return result;
272013498266Sopenharmony_ci
272113498266Sopenharmony_ci  conn->httpversion = 20; /* we know we're on HTTP/2 now */
272213498266Sopenharmony_ci  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
272313498266Sopenharmony_ci  conn->bundle->multiuse = BUNDLE_MULTIPLEX;
272413498266Sopenharmony_ci  Curl_multi_connchanged(data->multi);
272513498266Sopenharmony_ci
272613498266Sopenharmony_ci  if(cf->next) {
272713498266Sopenharmony_ci    bool done;
272813498266Sopenharmony_ci    return Curl_conn_cf_connect(cf, data, FALSE, &done);
272913498266Sopenharmony_ci  }
273013498266Sopenharmony_ci  return CURLE_OK;
273113498266Sopenharmony_ci}
273213498266Sopenharmony_ci
273313498266Sopenharmony_ciCURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
273413498266Sopenharmony_ci{
273513498266Sopenharmony_ci  struct Curl_cfilter *cf_h2;
273613498266Sopenharmony_ci  CURLcode result;
273713498266Sopenharmony_ci
273813498266Sopenharmony_ci  DEBUGASSERT(!Curl_cf_is_http2(cf, data));
273913498266Sopenharmony_ci
274013498266Sopenharmony_ci  result = http2_cfilter_insert_after(cf, data);
274113498266Sopenharmony_ci  if(result)
274213498266Sopenharmony_ci    return result;
274313498266Sopenharmony_ci
274413498266Sopenharmony_ci  cf_h2 = cf->next;
274513498266Sopenharmony_ci  result = cf_h2_ctx_init(cf_h2, data, FALSE);
274613498266Sopenharmony_ci  if(result)
274713498266Sopenharmony_ci    return result;
274813498266Sopenharmony_ci
274913498266Sopenharmony_ci  cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
275013498266Sopenharmony_ci  cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
275113498266Sopenharmony_ci  cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
275213498266Sopenharmony_ci  Curl_multi_connchanged(data->multi);
275313498266Sopenharmony_ci
275413498266Sopenharmony_ci  if(cf_h2->next) {
275513498266Sopenharmony_ci    bool done;
275613498266Sopenharmony_ci    return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
275713498266Sopenharmony_ci  }
275813498266Sopenharmony_ci  return CURLE_OK;
275913498266Sopenharmony_ci}
276013498266Sopenharmony_ci
276113498266Sopenharmony_ciCURLcode Curl_http2_upgrade(struct Curl_easy *data,
276213498266Sopenharmony_ci                            struct connectdata *conn, int sockindex,
276313498266Sopenharmony_ci                            const char *mem, size_t nread)
276413498266Sopenharmony_ci{
276513498266Sopenharmony_ci  struct Curl_cfilter *cf;
276613498266Sopenharmony_ci  struct cf_h2_ctx *ctx;
276713498266Sopenharmony_ci  CURLcode result;
276813498266Sopenharmony_ci
276913498266Sopenharmony_ci  DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
277013498266Sopenharmony_ci  DEBUGF(infof(data, "upgrading to HTTP/2"));
277113498266Sopenharmony_ci  DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
277213498266Sopenharmony_ci
277313498266Sopenharmony_ci  result = http2_cfilter_add(&cf, data, conn, sockindex);
277413498266Sopenharmony_ci  if(result)
277513498266Sopenharmony_ci    return result;
277613498266Sopenharmony_ci
277713498266Sopenharmony_ci  DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
277813498266Sopenharmony_ci  ctx = cf->ctx;
277913498266Sopenharmony_ci
278013498266Sopenharmony_ci  result = cf_h2_ctx_init(cf, data, TRUE);
278113498266Sopenharmony_ci  if(result)
278213498266Sopenharmony_ci    return result;
278313498266Sopenharmony_ci
278413498266Sopenharmony_ci  if(nread > 0) {
278513498266Sopenharmony_ci    /* Remaining data from the protocol switch reply is already using
278613498266Sopenharmony_ci     * the switched protocol, ie. HTTP/2. We add that to the network
278713498266Sopenharmony_ci     * inbufq. */
278813498266Sopenharmony_ci    ssize_t copied;
278913498266Sopenharmony_ci
279013498266Sopenharmony_ci    copied = Curl_bufq_write(&ctx->inbufq,
279113498266Sopenharmony_ci                             (const unsigned char *)mem, nread, &result);
279213498266Sopenharmony_ci    if(copied < 0) {
279313498266Sopenharmony_ci      failf(data, "error on copying HTTP Upgrade response: %d", result);
279413498266Sopenharmony_ci      return CURLE_RECV_ERROR;
279513498266Sopenharmony_ci    }
279613498266Sopenharmony_ci    if((size_t)copied < nread) {
279713498266Sopenharmony_ci      failf(data, "connection buffer size could not take all data "
279813498266Sopenharmony_ci            "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
279913498266Sopenharmony_ci            copied, nread);
280013498266Sopenharmony_ci      return CURLE_HTTP2;
280113498266Sopenharmony_ci    }
280213498266Sopenharmony_ci    infof(data, "Copied HTTP/2 data in stream buffer to connection buffer"
280313498266Sopenharmony_ci          " after upgrade: len=%zu", nread);
280413498266Sopenharmony_ci  }
280513498266Sopenharmony_ci
280613498266Sopenharmony_ci  conn->httpversion = 20; /* we know we're on HTTP/2 now */
280713498266Sopenharmony_ci  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
280813498266Sopenharmony_ci  conn->bundle->multiuse = BUNDLE_MULTIPLEX;
280913498266Sopenharmony_ci  Curl_multi_connchanged(data->multi);
281013498266Sopenharmony_ci
281113498266Sopenharmony_ci  if(cf->next) {
281213498266Sopenharmony_ci    bool done;
281313498266Sopenharmony_ci    return Curl_conn_cf_connect(cf, data, FALSE, &done);
281413498266Sopenharmony_ci  }
281513498266Sopenharmony_ci  return CURLE_OK;
281613498266Sopenharmony_ci}
281713498266Sopenharmony_ci
281813498266Sopenharmony_ci/* Only call this function for a transfer that already got an HTTP/2
281913498266Sopenharmony_ci   CURLE_HTTP2_STREAM error! */
282013498266Sopenharmony_cibool Curl_h2_http_1_1_error(struct Curl_easy *data)
282113498266Sopenharmony_ci{
282213498266Sopenharmony_ci  struct stream_ctx *stream = H2_STREAM_CTX(data);
282313498266Sopenharmony_ci  return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
282413498266Sopenharmony_ci}
282513498266Sopenharmony_ci
282613498266Sopenharmony_ci#else /* !USE_NGHTTP2 */
282713498266Sopenharmony_ci
282813498266Sopenharmony_ci/* Satisfy external references even if http2 is not compiled in. */
282913498266Sopenharmony_ci#include <curl/curl.h>
283013498266Sopenharmony_ci
283113498266Sopenharmony_cichar *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
283213498266Sopenharmony_ci{
283313498266Sopenharmony_ci  (void) h;
283413498266Sopenharmony_ci  (void) num;
283513498266Sopenharmony_ci  return NULL;
283613498266Sopenharmony_ci}
283713498266Sopenharmony_ci
283813498266Sopenharmony_cichar *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
283913498266Sopenharmony_ci{
284013498266Sopenharmony_ci  (void) h;
284113498266Sopenharmony_ci  (void) header;
284213498266Sopenharmony_ci  return NULL;
284313498266Sopenharmony_ci}
284413498266Sopenharmony_ci
284513498266Sopenharmony_ci#endif /* USE_NGHTTP2 */
2846