Coverage Report

Created: 2023-03-26 06:11

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