VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/http2.c@ 99371

Last change on this file since 99371 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

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