VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/http2.c@ 98929

Last change on this file since 98929 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

  • Property svn:eol-style set to native
File size: 71.5 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, 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 "connect.h"
39#include "strtoofft.h"
40#include "strdup.h"
41#include "transfer.h"
42#include "dynbuf.h"
43#include "h2h3.h"
44#include "headers.h"
45/* The last 3 #include files should be in this order */
46#include "curl_printf.h"
47#include "curl_memory.h"
48#include "memdebug.h"
49
50#define H2_BUFSIZE 32768
51
52#if (NGHTTP2_VERSION_NUM < 0x010c00)
53#error too old nghttp2 version, upgrade!
54#endif
55
56#ifdef CURL_DISABLE_VERBOSE_STRINGS
57#define nghttp2_session_callbacks_set_error_callback(x,y)
58#endif
59
60#if (NGHTTP2_VERSION_NUM >= 0x010c00)
61#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
62#endif
63
64#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
65
66#ifdef DEBUG_HTTP2
67#define H2BUGF(x) x
68#else
69#define H2BUGF(x) do { } while(0)
70#endif
71
72static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
73 char *mem, size_t len, CURLcode *err);
74static bool http2_connisdead(struct Curl_easy *data,
75 struct connectdata *conn);
76static int h2_session_send(struct Curl_easy *data,
77 nghttp2_session *h2);
78static int h2_process_pending_input(struct Curl_easy *data,
79 struct http_conn *httpc,
80 CURLcode *err);
81
82/*
83 * Curl_http2_init_state() is called when the easy handle is created and
84 * allows for HTTP/2 specific init of state.
85 */
86void Curl_http2_init_state(struct UrlState *state)
87{
88 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
89}
90
91/*
92 * Curl_http2_init_userset() is called when the easy handle is created and
93 * allows for HTTP/2 specific user-set fields.
94 */
95void Curl_http2_init_userset(struct UserDefined *set)
96{
97 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
98}
99
100static int http2_getsock(struct Curl_easy *data,
101 struct connectdata *conn,
102 curl_socket_t *sock)
103{
104 const struct http_conn *c = &conn->proto.httpc;
105 struct SingleRequest *k = &data->req;
106 int bitmap = GETSOCK_BLANK;
107 struct HTTP *stream = data->req.p.http;
108
109 sock[0] = conn->sock[FIRSTSOCKET];
110
111 if(!(k->keepon & KEEP_RECV_PAUSE))
112 /* Unless paused - in an HTTP/2 connection we can basically always get a
113 frame so we should always be ready for one */
114 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
115
116 /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
117 there's a window to send data in */
118 if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
119 nghttp2_session_want_write(c->h2)) &&
120 (nghttp2_session_get_remote_window_size(c->h2) &&
121 nghttp2_session_get_stream_remote_window_size(c->h2,
122 stream->stream_id)))
123 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
124
125 return bitmap;
126}
127
128/*
129 * http2_stream_free() free HTTP2 stream related data
130 */
131static void http2_stream_free(struct HTTP *http)
132{
133 if(http) {
134 Curl_dyn_free(&http->header_recvbuf);
135 for(; http->push_headers_used > 0; --http->push_headers_used) {
136 free(http->push_headers[http->push_headers_used - 1]);
137 }
138 free(http->push_headers);
139 http->push_headers = NULL;
140 }
141}
142
143/*
144 * Disconnects *a* connection used for HTTP/2. It might be an old one from the
145 * connection cache and not the "main" one. Don't touch the easy handle!
146 */
147
148static CURLcode http2_disconnect(struct Curl_easy *data,
149 struct connectdata *conn,
150 bool dead_connection)
151{
152 struct http_conn *c = &conn->proto.httpc;
153 (void)dead_connection;
154#ifndef DEBUG_HTTP2
155 (void)data;
156#endif
157
158 H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
159
160 nghttp2_session_del(c->h2);
161 Curl_safefree(c->inbuf);
162
163 H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
164
165 return CURLE_OK;
166}
167
168/*
169 * The server may send us data at any point (e.g. PING frames). Therefore,
170 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
171 *
172 * Instead, if it is readable, run Curl_connalive() to peek at the socket
173 * and distinguish between closed and data.
174 */
175static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
176{
177 int sval;
178 bool dead = TRUE;
179
180 if(conn->bits.close)
181 return TRUE;
182
183 sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
184 if(sval == 0) {
185 /* timeout */
186 dead = FALSE;
187 }
188 else if(sval & CURL_CSELECT_ERR) {
189 /* socket is in an error state */
190 dead = TRUE;
191 }
192 else if(sval & CURL_CSELECT_IN) {
193 /* readable with no error. could still be closed */
194 dead = !Curl_connalive(data, conn);
195 if(!dead) {
196 /* This happens before we've sent off a request and the connection is
197 not in use by any other transfer, there shouldn't be any data here,
198 only "protocol frames" */
199 CURLcode result;
200 struct http_conn *httpc = &conn->proto.httpc;
201 ssize_t nread = -1;
202 if(httpc->recv_underlying)
203 /* if called "too early", this pointer isn't setup yet! */
204 nread = ((Curl_recv *)httpc->recv_underlying)(
205 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
206 if(nread != -1) {
207 H2BUGF(infof(data,
208 "%d bytes stray data read before trying h2 connection",
209 (int)nread));
210 httpc->nread_inbuf = 0;
211 httpc->inbuflen = nread;
212 if(h2_process_pending_input(data, httpc, &result) < 0)
213 /* immediate error, considered dead */
214 dead = TRUE;
215 }
216 else
217 /* the read failed so let's say this is dead anyway */
218 dead = TRUE;
219 }
220 }
221
222 return dead;
223}
224
225/*
226 * Set the transfer that is currently using this HTTP/2 connection.
227 */
228static void set_transfer(struct http_conn *c,
229 struct Curl_easy *data)
230{
231 c->trnsfr = data;
232}
233
234/*
235 * Get the transfer that is currently using this HTTP/2 connection.
236 */
237static struct Curl_easy *get_transfer(struct http_conn *c)
238{
239 DEBUGASSERT(c && c->trnsfr);
240 return c->trnsfr;
241}
242
243static unsigned int http2_conncheck(struct Curl_easy *data,
244 struct connectdata *conn,
245 unsigned int checks_to_perform)
246{
247 unsigned int ret_val = CONNRESULT_NONE;
248 struct http_conn *c = &conn->proto.httpc;
249 int rc;
250 bool send_frames = false;
251
252 if(checks_to_perform & CONNCHECK_ISDEAD) {
253 if(http2_connisdead(data, conn))
254 ret_val |= CONNRESULT_DEAD;
255 }
256
257 if(checks_to_perform & CONNCHECK_KEEPALIVE) {
258 struct curltime now = Curl_now();
259 timediff_t elapsed = Curl_timediff(now, conn->keepalive);
260
261 if(elapsed > data->set.upkeep_interval_ms) {
262 /* Perform an HTTP/2 PING */
263 rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
264 if(!rc) {
265 /* Successfully added a PING frame to the session. Need to flag this
266 so the frame is sent. */
267 send_frames = true;
268 }
269 else {
270 failf(data, "nghttp2_submit_ping() failed: %s(%d)",
271 nghttp2_strerror(rc), rc);
272 }
273
274 conn->keepalive = now;
275 }
276 }
277
278 if(send_frames) {
279 set_transfer(c, data); /* set the transfer */
280 rc = nghttp2_session_send(c->h2);
281 if(rc)
282 failf(data, "nghttp2_session_send() failed: %s(%d)",
283 nghttp2_strerror(rc), rc);
284 }
285
286 return ret_val;
287}
288
289/* called from http_setup_conn */
290void Curl_http2_setup_req(struct Curl_easy *data)
291{
292 struct HTTP *http = data->req.p.http;
293 http->bodystarted = FALSE;
294 http->status_code = -1;
295 http->pausedata = NULL;
296 http->pauselen = 0;
297 http->closed = FALSE;
298 http->close_handled = FALSE;
299 http->mem = NULL;
300 http->len = 0;
301 http->memlen = 0;
302 http->error = NGHTTP2_NO_ERROR;
303}
304
305/* called from http_setup_conn */
306void Curl_http2_setup_conn(struct connectdata *conn)
307{
308 conn->proto.httpc.settings.max_concurrent_streams =
309 DEFAULT_MAX_CONCURRENT_STREAMS;
310}
311
312/*
313 * HTTP2 handler interface. This isn't added to the general list of protocols
314 * but will be used at run-time when the protocol is dynamically switched from
315 * HTTP to HTTP2.
316 */
317static const struct Curl_handler Curl_handler_http2 = {
318 "HTTP", /* scheme */
319 ZERO_NULL, /* setup_connection */
320 Curl_http, /* do_it */
321 Curl_http_done, /* done */
322 ZERO_NULL, /* do_more */
323 ZERO_NULL, /* connect_it */
324 ZERO_NULL, /* connecting */
325 ZERO_NULL, /* doing */
326 http2_getsock, /* proto_getsock */
327 http2_getsock, /* doing_getsock */
328 ZERO_NULL, /* domore_getsock */
329 http2_getsock, /* perform_getsock */
330 http2_disconnect, /* disconnect */
331 ZERO_NULL, /* readwrite */
332 http2_conncheck, /* connection_check */
333 ZERO_NULL, /* attach connection */
334 PORT_HTTP, /* defport */
335 CURLPROTO_HTTP, /* protocol */
336 CURLPROTO_HTTP, /* family */
337 PROTOPT_STREAM /* flags */
338};
339
340static const struct Curl_handler Curl_handler_http2_ssl = {
341 "HTTPS", /* scheme */
342 ZERO_NULL, /* setup_connection */
343 Curl_http, /* do_it */
344 Curl_http_done, /* done */
345 ZERO_NULL, /* do_more */
346 ZERO_NULL, /* connect_it */
347 ZERO_NULL, /* connecting */
348 ZERO_NULL, /* doing */
349 http2_getsock, /* proto_getsock */
350 http2_getsock, /* doing_getsock */
351 ZERO_NULL, /* domore_getsock */
352 http2_getsock, /* perform_getsock */
353 http2_disconnect, /* disconnect */
354 ZERO_NULL, /* readwrite */
355 http2_conncheck, /* connection_check */
356 ZERO_NULL, /* attach connection */
357 PORT_HTTP, /* defport */
358 CURLPROTO_HTTPS, /* protocol */
359 CURLPROTO_HTTP, /* family */
360 PROTOPT_SSL | PROTOPT_STREAM /* flags */
361};
362
363/*
364 * Store nghttp2 version info in this buffer.
365 */
366void Curl_http2_ver(char *p, size_t len)
367{
368 nghttp2_info *h2 = nghttp2_version(0);
369 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
370}
371
372/*
373 * The implementation of nghttp2_send_callback type. Here we write |data| with
374 * size |length| to the network and return the number of bytes actually
375 * written. See the documentation of nghttp2_send_callback for the details.
376 */
377static ssize_t send_callback(nghttp2_session *h2,
378 const uint8_t *mem, size_t length, int flags,
379 void *userp)
380{
381 struct connectdata *conn = (struct connectdata *)userp;
382 struct http_conn *c = &conn->proto.httpc;
383 struct Curl_easy *data = get_transfer(c);
384 ssize_t written;
385 CURLcode result = CURLE_OK;
386
387 (void)h2;
388 (void)flags;
389
390 if(!c->send_underlying)
391 /* called before setup properly! */
392 return NGHTTP2_ERR_CALLBACK_FAILURE;
393
394 written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
395 mem, length, &result);
396
397 if(result == CURLE_AGAIN) {
398 return NGHTTP2_ERR_WOULDBLOCK;
399 }
400
401 if(written == -1) {
402 failf(data, "Failed sending HTTP2 data");
403 return NGHTTP2_ERR_CALLBACK_FAILURE;
404 }
405
406 if(!written)
407 return NGHTTP2_ERR_WOULDBLOCK;
408
409 return written;
410}
411
412
413/* We pass a pointer to this struct in the push callback, but the contents of
414 the struct are hidden from the user. */
415struct curl_pushheaders {
416 struct Curl_easy *data;
417 const nghttp2_push_promise *frame;
418};
419
420/*
421 * push header access function. Only to be used from within the push callback
422 */
423char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
424{
425 /* Verify that we got a good easy handle in the push header struct, mostly to
426 detect rubbish input fast(er). */
427 if(!h || !GOOD_EASY_HANDLE(h->data))
428 return NULL;
429 else {
430 struct HTTP *stream = h->data->req.p.http;
431 if(num < stream->push_headers_used)
432 return stream->push_headers[num];
433 }
434 return NULL;
435}
436
437/*
438 * push header access function. Only to be used from within the push callback
439 */
440char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
441{
442 /* Verify that we got a good easy handle in the push header struct,
443 mostly to detect rubbish input fast(er). Also empty header name
444 is just a rubbish too. We have to allow ":" at the beginning of
445 the header, but header == ":" must be rejected. If we have ':' in
446 the middle of header, it could be matched in middle of the value,
447 this is because we do prefix match.*/
448 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
449 !strcmp(header, ":") || strchr(header + 1, ':'))
450 return NULL;
451 else {
452 struct HTTP *stream = h->data->req.p.http;
453 size_t len = strlen(header);
454 size_t i;
455 for(i = 0; i<stream->push_headers_used; i++) {
456 if(!strncmp(header, stream->push_headers[i], len)) {
457 /* sub-match, make sure that it is followed by a colon */
458 if(stream->push_headers[i][len] != ':')
459 continue;
460 return &stream->push_headers[i][len + 1];
461 }
462 }
463 }
464 return NULL;
465}
466
467/*
468 * This specific transfer on this connection has been "drained".
469 */
470static void drained_transfer(struct Curl_easy *data,
471 struct http_conn *httpc)
472{
473 DEBUGASSERT(httpc->drain_total >= data->state.drain);
474 httpc->drain_total -= data->state.drain;
475 data->state.drain = 0;
476}
477
478/*
479 * Mark this transfer to get "drained".
480 */
481static void drain_this(struct Curl_easy *data,
482 struct http_conn *httpc)
483{
484 data->state.drain++;
485 httpc->drain_total++;
486 DEBUGASSERT(httpc->drain_total >= data->state.drain);
487}
488
489static struct Curl_easy *duphandle(struct Curl_easy *data)
490{
491 struct Curl_easy *second = curl_easy_duphandle(data);
492 if(second) {
493 /* setup the request struct */
494 struct HTTP *http = calloc(1, sizeof(struct HTTP));
495 if(!http) {
496 (void)Curl_close(&second);
497 }
498 else {
499 second->req.p.http = http;
500 Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
501 Curl_http2_setup_req(second);
502 second->state.stream_weight = data->state.stream_weight;
503 }
504 }
505 return second;
506}
507
508static int set_transfer_url(struct Curl_easy *data,
509 struct curl_pushheaders *hp)
510{
511 const char *v;
512 CURLUcode uc;
513 char *url = NULL;
514 int rc = 0;
515 CURLU *u = curl_url();
516
517 if(!u)
518 return 5;
519
520 v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME);
521 if(v) {
522 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
523 if(uc) {
524 rc = 1;
525 goto fail;
526 }
527 }
528
529 v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY);
530 if(v) {
531 uc = curl_url_set(u, CURLUPART_HOST, v, 0);
532 if(uc) {
533 rc = 2;
534 goto fail;
535 }
536 }
537
538 v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH);
539 if(v) {
540 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
541 if(uc) {
542 rc = 3;
543 goto fail;
544 }
545 }
546
547 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
548 if(uc)
549 rc = 4;
550 fail:
551 curl_url_cleanup(u);
552 if(rc)
553 return rc;
554
555 if(data->state.url_alloc)
556 free(data->state.url);
557 data->state.url_alloc = TRUE;
558 data->state.url = url;
559 return 0;
560}
561
562static int push_promise(struct Curl_easy *data,
563 struct connectdata *conn,
564 const nghttp2_push_promise *frame)
565{
566 int rv; /* one of the CURL_PUSH_* defines */
567 H2BUGF(infof(data, "PUSH_PROMISE received, stream %u",
568 frame->promised_stream_id));
569 if(data->multi->push_cb) {
570 struct HTTP *stream;
571 struct HTTP *newstream;
572 struct curl_pushheaders heads;
573 CURLMcode rc;
574 struct http_conn *httpc;
575 size_t i;
576 /* clone the parent */
577 struct Curl_easy *newhandle = duphandle(data);
578 if(!newhandle) {
579 infof(data, "failed to duplicate handle");
580 rv = CURL_PUSH_DENY; /* FAIL HARD */
581 goto fail;
582 }
583
584 heads.data = data;
585 heads.frame = frame;
586 /* ask the application */
587 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
588
589 stream = data->req.p.http;
590 if(!stream) {
591 failf(data, "Internal NULL stream");
592 (void)Curl_close(&newhandle);
593 rv = CURL_PUSH_DENY;
594 goto fail;
595 }
596
597 rv = set_transfer_url(newhandle, &heads);
598 if(rv) {
599 (void)Curl_close(&newhandle);
600 rv = CURL_PUSH_DENY;
601 goto fail;
602 }
603
604 Curl_set_in_callback(data, true);
605 rv = data->multi->push_cb(data, newhandle,
606 stream->push_headers_used, &heads,
607 data->multi->push_userp);
608 Curl_set_in_callback(data, false);
609
610 /* free the headers again */
611 for(i = 0; i<stream->push_headers_used; i++)
612 free(stream->push_headers[i]);
613 free(stream->push_headers);
614 stream->push_headers = NULL;
615 stream->push_headers_used = 0;
616
617 if(rv) {
618 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
619 /* denied, kill off the new handle again */
620 http2_stream_free(newhandle->req.p.http);
621 newhandle->req.p.http = NULL;
622 (void)Curl_close(&newhandle);
623 goto fail;
624 }
625
626 newstream = newhandle->req.p.http;
627 newstream->stream_id = frame->promised_stream_id;
628 newhandle->req.maxdownload = -1;
629 newhandle->req.size = -1;
630
631 /* approved, add to the multi handle and immediately switch to PERFORM
632 state with the given connection !*/
633 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
634 if(rc) {
635 infof(data, "failed to add handle to multi");
636 http2_stream_free(newhandle->req.p.http);
637 newhandle->req.p.http = NULL;
638 Curl_close(&newhandle);
639 rv = CURL_PUSH_DENY;
640 goto fail;
641 }
642
643 httpc = &conn->proto.httpc;
644 rv = nghttp2_session_set_stream_user_data(httpc->h2,
645 frame->promised_stream_id,
646 newhandle);
647 if(rv) {
648 infof(data, "failed to set user_data for stream %u",
649 frame->promised_stream_id);
650 DEBUGASSERT(0);
651 rv = CURL_PUSH_DENY;
652 goto fail;
653 }
654 Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
655 Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
656 }
657 else {
658 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
659 rv = CURL_PUSH_DENY;
660 }
661 fail:
662 return rv;
663}
664
665/*
666 * multi_connchanged() is called to tell that there is a connection in
667 * this multi handle that has changed state (multiplexing become possible, the
668 * number of allowed streams changed or similar), and a subsequent use of this
669 * multi handle should move CONNECT_PEND handles back to CONNECT to have them
670 * retry.
671 */
672static void multi_connchanged(struct Curl_multi *multi)
673{
674 multi->recheckstate = TRUE;
675}
676
677static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
678 void *userp)
679{
680 struct connectdata *conn = (struct connectdata *)userp;
681 struct http_conn *httpc = &conn->proto.httpc;
682 struct Curl_easy *data_s = NULL;
683 struct HTTP *stream = NULL;
684 struct Curl_easy *data = get_transfer(httpc);
685 int rv;
686 size_t left, ncopy;
687 int32_t stream_id = frame->hd.stream_id;
688 CURLcode result;
689
690 if(!stream_id) {
691 /* stream ID zero is for connection-oriented stuff */
692 if(frame->hd.type == NGHTTP2_SETTINGS) {
693 uint32_t max_conn = httpc->settings.max_concurrent_streams;
694 H2BUGF(infof(data, "Got SETTINGS"));
695 httpc->settings.max_concurrent_streams =
696 nghttp2_session_get_remote_settings(
697 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
698 httpc->settings.enable_push =
699 nghttp2_session_get_remote_settings(
700 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
701 H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
702 httpc->settings.max_concurrent_streams));
703 H2BUGF(infof(data, "ENABLE_PUSH == %s",
704 httpc->settings.enable_push?"TRUE":"false"));
705 if(max_conn != httpc->settings.max_concurrent_streams) {
706 /* only signal change if the value actually changed */
707 infof(data,
708 "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
709 httpc->settings.max_concurrent_streams);
710 multi_connchanged(data->multi);
711 }
712 }
713 return 0;
714 }
715 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
716 if(!data_s) {
717 H2BUGF(infof(data,
718 "No Curl_easy associated with stream: %u",
719 stream_id));
720 return 0;
721 }
722
723 stream = data_s->req.p.http;
724 if(!stream) {
725 H2BUGF(infof(data_s, "No proto pointer for stream: %u",
726 stream_id));
727 return NGHTTP2_ERR_CALLBACK_FAILURE;
728 }
729
730 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
731 frame->hd.type, stream_id));
732
733 switch(frame->hd.type) {
734 case NGHTTP2_DATA:
735 /* If body started on this stream, then receiving DATA is illegal. */
736 if(!stream->bodystarted) {
737 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
738 stream_id, NGHTTP2_PROTOCOL_ERROR);
739
740 if(nghttp2_is_fatal(rv)) {
741 return NGHTTP2_ERR_CALLBACK_FAILURE;
742 }
743 }
744 break;
745 case NGHTTP2_HEADERS:
746 if(stream->bodystarted) {
747 /* Only valid HEADERS after body started is trailer HEADERS. We
748 buffer them in on_header callback. */
749 break;
750 }
751
752 /* nghttp2 guarantees that :status is received, and we store it to
753 stream->status_code. Fuzzing has proven this can still be reached
754 without status code having been set. */
755 if(stream->status_code == -1)
756 return NGHTTP2_ERR_CALLBACK_FAILURE;
757
758 /* Only final status code signals the end of header */
759 if(stream->status_code / 100 != 1) {
760 stream->bodystarted = TRUE;
761 stream->status_code = -1;
762 }
763
764 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
765 if(result)
766 return NGHTTP2_ERR_CALLBACK_FAILURE;
767
768 left = Curl_dyn_len(&stream->header_recvbuf) -
769 stream->nread_header_recvbuf;
770 ncopy = CURLMIN(stream->len, left);
771
772 memcpy(&stream->mem[stream->memlen],
773 Curl_dyn_ptr(&stream->header_recvbuf) +
774 stream->nread_header_recvbuf,
775 ncopy);
776 stream->nread_header_recvbuf += ncopy;
777
778 DEBUGASSERT(stream->mem);
779 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
780 ncopy, stream_id, stream->mem));
781
782 stream->len -= ncopy;
783 stream->memlen += ncopy;
784
785 drain_this(data_s, httpc);
786 /* if we receive data for another handle, wake that up */
787 if(get_transfer(httpc) != data_s)
788 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
789 break;
790 case NGHTTP2_PUSH_PROMISE:
791 rv = push_promise(data_s, conn, &frame->push_promise);
792 if(rv) { /* deny! */
793 int h2;
794 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
795 h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
796 frame->push_promise.promised_stream_id,
797 NGHTTP2_CANCEL);
798 if(nghttp2_is_fatal(h2))
799 return NGHTTP2_ERR_CALLBACK_FAILURE;
800 else if(rv == CURL_PUSH_ERROROUT) {
801 DEBUGF(infof(data_s, "Fail the parent stream (too)"));
802 return NGHTTP2_ERR_CALLBACK_FAILURE;
803 }
804 }
805 break;
806 default:
807 H2BUGF(infof(data_s, "Got frame type %x for stream %u",
808 frame->hd.type, stream_id));
809 break;
810 }
811 return 0;
812}
813
814static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
815 int32_t stream_id,
816 const uint8_t *mem, size_t len, void *userp)
817{
818 struct HTTP *stream;
819 struct Curl_easy *data_s;
820 size_t nread;
821 struct connectdata *conn = (struct connectdata *)userp;
822 struct http_conn *httpc = &conn->proto.httpc;
823 (void)session;
824 (void)flags;
825
826 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
827
828 /* get the stream from the hash based on Stream ID */
829 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
830 if(!data_s) {
831 /* Receiving a Stream ID not in the hash should not happen - unless
832 we have aborted a transfer artificially and there were more data
833 in the pipeline. Silently ignore. */
834 H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n",
835 stream_id));
836 return 0;
837 }
838
839 stream = data_s->req.p.http;
840 if(!stream)
841 return NGHTTP2_ERR_CALLBACK_FAILURE;
842
843 nread = CURLMIN(stream->len, len);
844 memcpy(&stream->mem[stream->memlen], mem, nread);
845
846 stream->len -= nread;
847 stream->memlen += nread;
848
849 drain_this(data_s, &conn->proto.httpc);
850
851 /* if we receive data for another handle, wake that up */
852 if(get_transfer(httpc) != data_s)
853 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
854
855 H2BUGF(infof(data_s, "%zu data received for stream %u "
856 "(%zu left in buffer %p, total %zu)",
857 nread, stream_id,
858 stream->len, stream->mem,
859 stream->memlen));
860
861 if(nread < len) {
862 stream->pausedata = mem + nread;
863 stream->pauselen = len - nread;
864 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
865 ", stream %u",
866 len - nread, stream_id));
867 data_s->conn->proto.httpc.pause_stream_id = stream_id;
868
869 return NGHTTP2_ERR_PAUSE;
870 }
871
872 /* pause execution of nghttp2 if we received data for another handle
873 in order to process them first. */
874 if(get_transfer(httpc) != data_s) {
875 data_s->conn->proto.httpc.pause_stream_id = stream_id;
876
877 return NGHTTP2_ERR_PAUSE;
878 }
879
880 return 0;
881}
882
883static int on_stream_close(nghttp2_session *session, int32_t stream_id,
884 uint32_t error_code, void *userp)
885{
886 struct Curl_easy *data_s;
887 struct HTTP *stream;
888 struct connectdata *conn = (struct connectdata *)userp;
889 int rv;
890 (void)session;
891 (void)stream_id;
892
893 if(stream_id) {
894 struct http_conn *httpc;
895 /* get the stream from the hash based on Stream ID, stream ID zero is for
896 connection-oriented stuff */
897 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
898 if(!data_s) {
899 /* We could get stream ID not in the hash. For example, if we
900 decided to reject stream (e.g., PUSH_PROMISE). */
901 return 0;
902 }
903 H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
904 nghttp2_http2_strerror(error_code), error_code, stream_id));
905 stream = data_s->req.p.http;
906 if(!stream)
907 return NGHTTP2_ERR_CALLBACK_FAILURE;
908
909 stream->closed = TRUE;
910 httpc = &conn->proto.httpc;
911 drain_this(data_s, httpc);
912 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
913 stream->error = error_code;
914
915 /* remove the entry from the hash as the stream is now gone */
916 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
917 if(rv) {
918 infof(data_s, "http/2: failed to clear user_data for stream %u",
919 stream_id);
920 DEBUGASSERT(0);
921 }
922 if(stream_id == httpc->pause_stream_id) {
923 H2BUGF(infof(data_s, "Stopped the pause stream"));
924 httpc->pause_stream_id = 0;
925 }
926 H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
927 stream->stream_id = 0; /* cleared */
928 }
929 return 0;
930}
931
932static int on_begin_headers(nghttp2_session *session,
933 const nghttp2_frame *frame, void *userp)
934{
935 struct HTTP *stream;
936 struct Curl_easy *data_s = NULL;
937 (void)userp;
938
939 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
940 if(!data_s) {
941 return 0;
942 }
943
944 H2BUGF(infof(data_s, "on_begin_headers() was called"));
945
946 if(frame->hd.type != NGHTTP2_HEADERS) {
947 return 0;
948 }
949
950 stream = data_s->req.p.http;
951 if(!stream || !stream->bodystarted) {
952 return 0;
953 }
954
955 return 0;
956}
957
958/* Decode HTTP status code. Returns -1 if no valid status code was
959 decoded. */
960static int decode_status_code(const uint8_t *value, size_t len)
961{
962 int i;
963 int res;
964
965 if(len != 3) {
966 return -1;
967 }
968
969 res = 0;
970
971 for(i = 0; i < 3; ++i) {
972 char c = value[i];
973
974 if(c < '0' || c > '9') {
975 return -1;
976 }
977
978 res *= 10;
979 res += c - '0';
980 }
981
982 return res;
983}
984
985/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
986static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
987 const uint8_t *name, size_t namelen,
988 const uint8_t *value, size_t valuelen,
989 uint8_t flags,
990 void *userp)
991{
992 struct HTTP *stream;
993 struct Curl_easy *data_s;
994 int32_t stream_id = frame->hd.stream_id;
995 struct connectdata *conn = (struct connectdata *)userp;
996 struct http_conn *httpc = &conn->proto.httpc;
997 CURLcode result;
998 (void)flags;
999
1000 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1001
1002 /* get the stream from the hash based on Stream ID */
1003 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1004 if(!data_s)
1005 /* Receiving a Stream ID not in the hash should not happen, this is an
1006 internal error more than anything else! */
1007 return NGHTTP2_ERR_CALLBACK_FAILURE;
1008
1009 stream = data_s->req.p.http;
1010 if(!stream) {
1011 failf(data_s, "Internal NULL stream");
1012 return NGHTTP2_ERR_CALLBACK_FAILURE;
1013 }
1014
1015 /* Store received PUSH_PROMISE headers to be used when the subsequent
1016 PUSH_PROMISE callback comes */
1017 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
1018 char *h;
1019
1020 if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
1021 /* pseudo headers are lower case */
1022 int rc = 0;
1023 char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
1024 if(!check)
1025 /* no memory */
1026 return NGHTTP2_ERR_CALLBACK_FAILURE;
1027 if(!strcasecompare(check, (const char *)value) &&
1028 ((conn->remote_port != conn->given->defport) ||
1029 !strcasecompare(conn->host.name, (const char *)value))) {
1030 /* This is push is not for the same authority that was asked for in
1031 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
1032 * PUSH_PROMISE for which the server is not authoritative as a stream
1033 * error of type PROTOCOL_ERROR."
1034 */
1035 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1036 stream_id, NGHTTP2_PROTOCOL_ERROR);
1037 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
1038 }
1039 free(check);
1040 if(rc)
1041 return rc;
1042 }
1043
1044 if(!stream->push_headers) {
1045 stream->push_headers_alloc = 10;
1046 stream->push_headers = malloc(stream->push_headers_alloc *
1047 sizeof(char *));
1048 if(!stream->push_headers)
1049 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1050 stream->push_headers_used = 0;
1051 }
1052 else if(stream->push_headers_used ==
1053 stream->push_headers_alloc) {
1054 char **headp;
1055 if(stream->push_headers_alloc > 1000) {
1056 /* this is beyond crazy many headers, bail out */
1057 failf(data_s, "Too many PUSH_PROMISE headers");
1058 Curl_safefree(stream->push_headers);
1059 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1060 }
1061 stream->push_headers_alloc *= 2;
1062 headp = Curl_saferealloc(stream->push_headers,
1063 stream->push_headers_alloc * sizeof(char *));
1064 if(!headp) {
1065 stream->push_headers = NULL;
1066 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1067 }
1068 stream->push_headers = headp;
1069 }
1070 h = aprintf("%s:%s", name, value);
1071 if(h)
1072 stream->push_headers[stream->push_headers_used++] = h;
1073 return 0;
1074 }
1075
1076 if(stream->bodystarted) {
1077 /* This is a trailer */
1078 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
1079 value));
1080 result = Curl_dyn_addf(&stream->trailer_recvbuf,
1081 "%.*s: %.*s\r\n", namelen, name,
1082 valuelen, value);
1083 if(result)
1084 return NGHTTP2_ERR_CALLBACK_FAILURE;
1085
1086 return 0;
1087 }
1088
1089 if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 &&
1090 memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) {
1091 /* nghttp2 guarantees :status is received first and only once, and
1092 value is 3 digits status code, and decode_status_code always
1093 succeeds. */
1094 char buffer[32];
1095 stream->status_code = decode_status_code(value, valuelen);
1096 DEBUGASSERT(stream->status_code != -1);
1097 msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r",
1098 stream->status_code);
1099 result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
1100 if(result)
1101 return NGHTTP2_ERR_CALLBACK_FAILURE;
1102 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 "));
1103 if(result)
1104 return NGHTTP2_ERR_CALLBACK_FAILURE;
1105 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1106 if(result)
1107 return NGHTTP2_ERR_CALLBACK_FAILURE;
1108 /* the space character after the status code is mandatory */
1109 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n"));
1110 if(result)
1111 return NGHTTP2_ERR_CALLBACK_FAILURE;
1112 /* if we receive data for another handle, wake that up */
1113 if(get_transfer(httpc) != data_s)
1114 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1115
1116 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
1117 stream->status_code, data_s));
1118 return 0;
1119 }
1120
1121 /* nghttp2 guarantees that namelen > 0, and :status was already
1122 received, and this is not pseudo-header field . */
1123 /* convert to an HTTP1-style header */
1124 result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen);
1125 if(result)
1126 return NGHTTP2_ERR_CALLBACK_FAILURE;
1127 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": "));
1128 if(result)
1129 return NGHTTP2_ERR_CALLBACK_FAILURE;
1130 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1131 if(result)
1132 return NGHTTP2_ERR_CALLBACK_FAILURE;
1133 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
1134 if(result)
1135 return NGHTTP2_ERR_CALLBACK_FAILURE;
1136 /* if we receive data for another handle, wake that up */
1137 if(get_transfer(httpc) != data_s)
1138 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1139
1140 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
1141 value));
1142
1143 return 0; /* 0 is successful */
1144}
1145
1146static ssize_t data_source_read_callback(nghttp2_session *session,
1147 int32_t stream_id,
1148 uint8_t *buf, size_t length,
1149 uint32_t *data_flags,
1150 nghttp2_data_source *source,
1151 void *userp)
1152{
1153 struct Curl_easy *data_s;
1154 struct HTTP *stream = NULL;
1155 size_t nread;
1156 (void)source;
1157 (void)userp;
1158
1159 if(stream_id) {
1160 /* get the stream from the hash based on Stream ID, stream ID zero is for
1161 connection-oriented stuff */
1162 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1163 if(!data_s)
1164 /* Receiving a Stream ID not in the hash should not happen, this is an
1165 internal error more than anything else! */
1166 return NGHTTP2_ERR_CALLBACK_FAILURE;
1167
1168 stream = data_s->req.p.http;
1169 if(!stream)
1170 return NGHTTP2_ERR_CALLBACK_FAILURE;
1171 }
1172 else
1173 return NGHTTP2_ERR_INVALID_ARGUMENT;
1174
1175 nread = CURLMIN(stream->upload_len, length);
1176 if(nread > 0) {
1177 memcpy(buf, stream->upload_mem, nread);
1178 stream->upload_mem += nread;
1179 stream->upload_len -= nread;
1180 if(data_s->state.infilesize != -1)
1181 stream->upload_left -= nread;
1182 }
1183
1184 if(stream->upload_left == 0)
1185 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1186 else if(nread == 0)
1187 return NGHTTP2_ERR_DEFERRED;
1188
1189 H2BUGF(infof(data_s, "data_source_read_callback: "
1190 "returns %zu bytes stream %u",
1191 nread, stream_id));
1192
1193 return nread;
1194}
1195
1196#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1197static int error_callback(nghttp2_session *session,
1198 const char *msg,
1199 size_t len,
1200 void *userp)
1201{
1202 (void)session;
1203 (void)msg;
1204 (void)len;
1205 (void)userp;
1206 return 0;
1207}
1208#endif
1209
1210static void populate_settings(struct Curl_easy *data,
1211 struct http_conn *httpc)
1212{
1213 nghttp2_settings_entry *iv = httpc->local_settings;
1214
1215 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1216 iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
1217
1218 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1219 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1220
1221 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1222 iv[2].value = data->multi->push_cb != NULL;
1223
1224 httpc->local_settings_num = 3;
1225}
1226
1227void Curl_http2_done(struct Curl_easy *data, bool premature)
1228{
1229 struct HTTP *http = data->req.p.http;
1230 struct http_conn *httpc = &data->conn->proto.httpc;
1231
1232 /* there might be allocated resources done before this got the 'h2' pointer
1233 setup */
1234 Curl_dyn_free(&http->header_recvbuf);
1235 Curl_dyn_free(&http->trailer_recvbuf);
1236 if(http->push_headers) {
1237 /* if they weren't used and then freed before */
1238 for(; http->push_headers_used > 0; --http->push_headers_used) {
1239 free(http->push_headers[http->push_headers_used - 1]);
1240 }
1241 free(http->push_headers);
1242 http->push_headers = NULL;
1243 }
1244
1245 if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
1246 !httpc->h2) /* not HTTP/2 ? */
1247 return;
1248
1249 /* do this before the reset handling, as that might clear ->stream_id */
1250 if(http->stream_id == httpc->pause_stream_id) {
1251 H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id));
1252 httpc->pause_stream_id = 0;
1253 }
1254 if(premature || (!http->closed && http->stream_id)) {
1255 /* RST_STREAM */
1256 set_transfer(httpc, data); /* set the transfer */
1257 H2BUGF(infof(data, "RST stream %u", http->stream_id));
1258 if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
1259 http->stream_id, NGHTTP2_STREAM_CLOSED))
1260 (void)nghttp2_session_send(httpc->h2);
1261 }
1262
1263 if(data->state.drain)
1264 drained_transfer(data, httpc);
1265
1266 /* -1 means unassigned and 0 means cleared */
1267 if(http->stream_id > 0) {
1268 int rv = nghttp2_session_set_stream_user_data(httpc->h2,
1269 http->stream_id, 0);
1270 if(rv) {
1271 infof(data, "http/2: failed to clear user_data for stream %u",
1272 http->stream_id);
1273 DEBUGASSERT(0);
1274 }
1275 set_transfer(httpc, NULL);
1276 http->stream_id = 0;
1277 }
1278}
1279
1280static int client_new(struct connectdata *conn,
1281 nghttp2_session_callbacks *callbacks)
1282{
1283#if NGHTTP2_VERSION_NUM < 0x013200
1284 /* before 1.50.0 */
1285 return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1286#else
1287 nghttp2_option *o;
1288 int rc = nghttp2_option_new(&o);
1289 if(rc)
1290 return rc;
1291 /* turn off RFC 9113 leading and trailing white spaces validation against
1292 HTTP field value. */
1293 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
1294 rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn,
1295 o);
1296 nghttp2_option_del(o);
1297 return rc;
1298#endif
1299}
1300
1301/*
1302 * Initialize nghttp2 for a Curl connection
1303 */
1304static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
1305{
1306 if(!conn->proto.httpc.h2) {
1307 int rc;
1308 nghttp2_session_callbacks *callbacks;
1309
1310 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1311 if(!conn->proto.httpc.inbuf)
1312 return CURLE_OUT_OF_MEMORY;
1313
1314 rc = nghttp2_session_callbacks_new(&callbacks);
1315
1316 if(rc) {
1317 failf(data, "Couldn't initialize nghttp2 callbacks");
1318 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1319 }
1320
1321 /* nghttp2_send_callback */
1322 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1323 /* nghttp2_on_frame_recv_callback */
1324 nghttp2_session_callbacks_set_on_frame_recv_callback
1325 (callbacks, on_frame_recv);
1326 /* nghttp2_on_data_chunk_recv_callback */
1327 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1328 (callbacks, on_data_chunk_recv);
1329 /* nghttp2_on_stream_close_callback */
1330 nghttp2_session_callbacks_set_on_stream_close_callback
1331 (callbacks, on_stream_close);
1332 /* nghttp2_on_begin_headers_callback */
1333 nghttp2_session_callbacks_set_on_begin_headers_callback
1334 (callbacks, on_begin_headers);
1335 /* nghttp2_on_header_callback */
1336 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1337
1338 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1339
1340 /* The nghttp2 session is not yet setup, do it */
1341 rc = client_new(conn, callbacks);
1342
1343 nghttp2_session_callbacks_del(callbacks);
1344
1345 if(rc) {
1346 failf(data, "Couldn't initialize nghttp2");
1347 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1348 }
1349 }
1350 return CURLE_OK;
1351}
1352
1353/*
1354 * Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
1355 */
1356CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1357 struct Curl_easy *data)
1358{
1359 CURLcode result;
1360 ssize_t binlen;
1361 char *base64;
1362 size_t blen;
1363 struct connectdata *conn = data->conn;
1364 struct SingleRequest *k = &data->req;
1365 uint8_t *binsettings = conn->proto.httpc.binsettings;
1366 struct http_conn *httpc = &conn->proto.httpc;
1367
1368 populate_settings(data, httpc);
1369
1370 /* this returns number of bytes it wrote */
1371 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1372 httpc->local_settings,
1373 httpc->local_settings_num);
1374 if(binlen <= 0) {
1375 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1376 Curl_dyn_free(req);
1377 return CURLE_FAILED_INIT;
1378 }
1379 conn->proto.httpc.binlen = binlen;
1380
1381 result = Curl_base64url_encode((const char *)binsettings, binlen,
1382 &base64, &blen);
1383 if(result) {
1384 Curl_dyn_free(req);
1385 return result;
1386 }
1387
1388 result = Curl_dyn_addf(req,
1389 "Connection: Upgrade, HTTP2-Settings\r\n"
1390 "Upgrade: %s\r\n"
1391 "HTTP2-Settings: %s\r\n",
1392 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1393 free(base64);
1394
1395 k->upgr101 = UPGR101_H2;
1396
1397 return result;
1398}
1399
1400/*
1401 * Returns nonzero if current HTTP/2 session should be closed.
1402 */
1403static int should_close_session(struct http_conn *httpc)
1404{
1405 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1406 !nghttp2_session_want_write(httpc->h2);
1407}
1408
1409/*
1410 * h2_process_pending_input() processes pending input left in
1411 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1412 * This function returns 0 if it succeeds, or -1 and error code will
1413 * be assigned to *err.
1414 */
1415static int h2_process_pending_input(struct Curl_easy *data,
1416 struct http_conn *httpc,
1417 CURLcode *err)
1418{
1419 ssize_t nread;
1420 char *inbuf;
1421 ssize_t rv;
1422
1423 nread = httpc->inbuflen - httpc->nread_inbuf;
1424 inbuf = httpc->inbuf + httpc->nread_inbuf;
1425
1426 set_transfer(httpc, data); /* set the transfer */
1427 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1428 if(rv < 0) {
1429 failf(data,
1430 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1431 "%zd:%s", rv, nghttp2_strerror((int)rv));
1432 *err = CURLE_RECV_ERROR;
1433 return -1;
1434 }
1435
1436 if(nread == rv) {
1437 H2BUGF(infof(data,
1438 "h2_process_pending_input: All data in connection buffer "
1439 "processed"));
1440 httpc->inbuflen = 0;
1441 httpc->nread_inbuf = 0;
1442 }
1443 else {
1444 httpc->nread_inbuf += rv;
1445 H2BUGF(infof(data,
1446 "h2_process_pending_input: %zu bytes left in connection "
1447 "buffer",
1448 httpc->inbuflen - httpc->nread_inbuf));
1449 }
1450
1451 rv = h2_session_send(data, httpc->h2);
1452 if(rv) {
1453 *err = CURLE_SEND_ERROR;
1454 return -1;
1455 }
1456
1457 if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
1458 /* No more requests are allowed in the current session, so
1459 the connection may not be reused. This is set when a
1460 GOAWAY frame has been received or when the limit of stream
1461 identifiers has been reached. */
1462 connclose(data->conn, "http/2: No new requests allowed");
1463 }
1464
1465 if(should_close_session(httpc)) {
1466 struct HTTP *stream = data->req.p.http;
1467 H2BUGF(infof(data,
1468 "h2_process_pending_input: nothing to do in this session"));
1469 if(stream->error)
1470 *err = CURLE_HTTP2;
1471 else {
1472 /* not an error per se, but should still close the connection */
1473 connclose(data->conn, "GOAWAY received");
1474 *err = CURLE_OK;
1475 }
1476 return -1;
1477 }
1478 return 0;
1479}
1480
1481/*
1482 * Called from transfer.c:done_sending when we stop uploading.
1483 */
1484CURLcode Curl_http2_done_sending(struct Curl_easy *data,
1485 struct connectdata *conn)
1486{
1487 CURLcode result = CURLE_OK;
1488
1489 if((conn->handler == &Curl_handler_http2_ssl) ||
1490 (conn->handler == &Curl_handler_http2)) {
1491 /* make sure this is only attempted for HTTP/2 transfers */
1492 struct HTTP *stream = data->req.p.http;
1493 struct http_conn *httpc = &conn->proto.httpc;
1494 nghttp2_session *h2 = httpc->h2;
1495
1496 if(stream->upload_left) {
1497 /* If the stream still thinks there's data left to upload. */
1498
1499 stream->upload_left = 0; /* DONE! */
1500
1501 /* resume sending here to trigger the callback to get called again so
1502 that it can signal EOF to nghttp2 */
1503 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1504 (void)h2_process_pending_input(data, httpc, &result);
1505 }
1506
1507 /* If nghttp2 still has pending frames unsent */
1508 if(nghttp2_session_want_write(h2)) {
1509 struct SingleRequest *k = &data->req;
1510 int rv;
1511
1512 H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
1513
1514 /* and attempt to send the pending frames */
1515 rv = h2_session_send(data, h2);
1516 if(rv)
1517 result = CURLE_SEND_ERROR;
1518
1519 if(nghttp2_session_want_write(h2)) {
1520 /* re-set KEEP_SEND to make sure we are called again */
1521 k->keepon |= KEEP_SEND;
1522 }
1523 }
1524 }
1525 return result;
1526}
1527
1528static ssize_t http2_handle_stream_close(struct connectdata *conn,
1529 struct Curl_easy *data,
1530 struct HTTP *stream, CURLcode *err)
1531{
1532 struct http_conn *httpc = &conn->proto.httpc;
1533
1534 if(httpc->pause_stream_id == stream->stream_id) {
1535 httpc->pause_stream_id = 0;
1536 }
1537
1538 drained_transfer(data, httpc);
1539
1540 if(httpc->pause_stream_id == 0) {
1541 if(h2_process_pending_input(data, httpc, err) != 0) {
1542 return -1;
1543 }
1544 }
1545
1546 DEBUGASSERT(data->state.drain == 0);
1547
1548 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1549 stream->closed = FALSE;
1550 if(stream->error == NGHTTP2_REFUSED_STREAM) {
1551 H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
1552 stream->stream_id));
1553 connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1554 data->state.refused_stream = TRUE;
1555 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1556 return -1;
1557 }
1558 else if(stream->error != NGHTTP2_NO_ERROR) {
1559 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1560 stream->stream_id, nghttp2_http2_strerror(stream->error),
1561 stream->error);
1562 *err = CURLE_HTTP2_STREAM;
1563 return -1;
1564 }
1565
1566 if(!stream->bodystarted) {
1567 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1568 " all response header fields, treated as error",
1569 stream->stream_id);
1570 *err = CURLE_HTTP2_STREAM;
1571 return -1;
1572 }
1573
1574 if(Curl_dyn_len(&stream->trailer_recvbuf)) {
1575 char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
1576 char *lf;
1577
1578 do {
1579 size_t len = 0;
1580 CURLcode result;
1581 /* each trailer line ends with a newline */
1582 lf = strchr(trailp, '\n');
1583 if(!lf)
1584 break;
1585 len = lf + 1 - trailp;
1586
1587 Curl_debug(data, CURLINFO_HEADER_IN, trailp, len);
1588 /* pass the trailers one by one to the callback */
1589 result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len);
1590 if(result) {
1591 *err = result;
1592 return -1;
1593 }
1594 trailp = ++lf;
1595 } while(lf);
1596 }
1597
1598 stream->close_handled = TRUE;
1599
1600 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
1601 return 0;
1602}
1603
1604/*
1605 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1606 * and dependency to the peer. It also stores the updated values in the state
1607 * struct.
1608 */
1609
1610static void h2_pri_spec(struct Curl_easy *data,
1611 nghttp2_priority_spec *pri_spec)
1612{
1613 struct HTTP *depstream = (data->set.stream_depends_on?
1614 data->set.stream_depends_on->req.p.http:NULL);
1615 int32_t depstream_id = depstream? depstream->stream_id:0;
1616 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1617 data->set.stream_depends_e);
1618 data->state.stream_weight = data->set.stream_weight;
1619 data->state.stream_depends_e = data->set.stream_depends_e;
1620 data->state.stream_depends_on = data->set.stream_depends_on;
1621}
1622
1623/*
1624 * h2_session_send() checks if there's been an update in the priority /
1625 * dependency settings and if so it submits a PRIORITY frame with the updated
1626 * info.
1627 */
1628static int h2_session_send(struct Curl_easy *data,
1629 nghttp2_session *h2)
1630{
1631 struct HTTP *stream = data->req.p.http;
1632 struct http_conn *httpc = &data->conn->proto.httpc;
1633 set_transfer(httpc, data);
1634 if((data->set.stream_weight != data->state.stream_weight) ||
1635 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1636 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1637 /* send new weight and/or dependency */
1638 nghttp2_priority_spec pri_spec;
1639 int rv;
1640
1641 h2_pri_spec(data, &pri_spec);
1642
1643 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
1644 stream->stream_id, data));
1645 DEBUGASSERT(stream->stream_id != -1);
1646 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1647 &pri_spec);
1648 if(rv)
1649 return rv;
1650 }
1651
1652 return nghttp2_session_send(h2);
1653}
1654
1655static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
1656 char *mem, size_t len, CURLcode *err)
1657{
1658 ssize_t nread;
1659 struct connectdata *conn = data->conn;
1660 struct http_conn *httpc = &conn->proto.httpc;
1661 struct HTTP *stream = data->req.p.http;
1662
1663 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1664
1665 if(should_close_session(httpc)) {
1666 H2BUGF(infof(data,
1667 "http2_recv: nothing to do in this session"));
1668 if(conn->bits.close) {
1669 /* already marked for closure, return OK and we're done */
1670 *err = CURLE_OK;
1671 return 0;
1672 }
1673 *err = CURLE_HTTP2;
1674 return -1;
1675 }
1676
1677 /* Nullify here because we call nghttp2_session_send() and they
1678 might refer to the old buffer. */
1679 stream->upload_mem = NULL;
1680 stream->upload_len = 0;
1681
1682 /*
1683 * At this point 'stream' is just in the Curl_easy the connection
1684 * identifies as its owner at this time.
1685 */
1686
1687 if(stream->bodystarted &&
1688 stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) {
1689 /* If there is header data pending for this stream to return, do that */
1690 size_t left =
1691 Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
1692 size_t ncopy = CURLMIN(len, left);
1693 memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
1694 stream->nread_header_recvbuf, ncopy);
1695 stream->nread_header_recvbuf += ncopy;
1696
1697 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
1698 (int)ncopy));
1699 return ncopy;
1700 }
1701
1702 H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
1703 data, stream->stream_id,
1704 nghttp2_session_get_local_window_size(httpc->h2),
1705 nghttp2_session_get_stream_local_window_size(httpc->h2,
1706 stream->stream_id)
1707 ));
1708
1709 if((data->state.drain) && stream->memlen) {
1710 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
1711 stream->memlen, stream->stream_id,
1712 stream->mem, mem));
1713 if(mem != stream->mem) {
1714 /* if we didn't get the same buffer this time, we must move the data to
1715 the beginning */
1716 memmove(mem, stream->mem, stream->memlen);
1717 stream->len = len - stream->memlen;
1718 stream->mem = mem;
1719 }
1720 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1721 /* We have paused nghttp2, but we have no pause data (see
1722 on_data_chunk_recv). */
1723 httpc->pause_stream_id = 0;
1724 if(h2_process_pending_input(data, httpc, err) != 0) {
1725 return -1;
1726 }
1727 }
1728 }
1729 else if(stream->pausedata) {
1730 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1731 nread = CURLMIN(len, stream->pauselen);
1732 memcpy(mem, stream->pausedata, nread);
1733
1734 stream->pausedata += nread;
1735 stream->pauselen -= nread;
1736
1737 if(stream->pauselen == 0) {
1738 H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
1739 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1740 httpc->pause_stream_id = 0;
1741
1742 stream->pausedata = NULL;
1743 stream->pauselen = 0;
1744
1745 /* When NGHTTP2_ERR_PAUSE is returned from
1746 data_source_read_callback, we might not process DATA frame
1747 fully. Calling nghttp2_session_mem_recv() again will
1748 continue to process DATA frame, but if there is no incoming
1749 frames, then we have to call it again with 0-length data.
1750 Without this, on_stream_close callback will not be called,
1751 and stream could be hanged. */
1752 if(h2_process_pending_input(data, httpc, err) != 0) {
1753 return -1;
1754 }
1755 }
1756 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
1757 nread, stream->stream_id));
1758 return nread;
1759 }
1760 else if(httpc->pause_stream_id) {
1761 /* If a stream paused nghttp2_session_mem_recv previously, and has
1762 not processed all data, it still refers to the buffer in
1763 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1764 overwrite that buffer. To avoid that situation, just return
1765 here with CURLE_AGAIN. This could be busy loop since data in
1766 socket is not read. But it seems that usually streams are
1767 notified with its drain property, and socket is read again
1768 quickly. */
1769 if(stream->closed)
1770 /* closed overrides paused */
1771 return 0;
1772 H2BUGF(infof(data, "stream %u is paused, pause id: %u",
1773 stream->stream_id, httpc->pause_stream_id));
1774 *err = CURLE_AGAIN;
1775 return -1;
1776 }
1777 else {
1778 /* remember where to store incoming data for this stream and how big the
1779 buffer is */
1780 stream->mem = mem;
1781 stream->len = len;
1782 stream->memlen = 0;
1783
1784 if(httpc->inbuflen == 0) {
1785 nread = ((Curl_recv *)httpc->recv_underlying)(
1786 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
1787
1788 if(nread == -1) {
1789 if(*err != CURLE_AGAIN)
1790 failf(data, "Failed receiving HTTP2 data");
1791 else if(stream->closed)
1792 /* received when the stream was already closed! */
1793 return http2_handle_stream_close(conn, data, stream, err);
1794
1795 return -1;
1796 }
1797
1798 if(nread == 0) {
1799 if(!stream->closed) {
1800 /* This will happen when the server or proxy server is SIGKILLed
1801 during data transfer. We should emit an error since our data
1802 received may be incomplete. */
1803 failf(data, "HTTP/2 stream %u was not closed cleanly before"
1804 " end of the underlying stream",
1805 stream->stream_id);
1806 *err = CURLE_HTTP2_STREAM;
1807 return -1;
1808 }
1809
1810 H2BUGF(infof(data, "end of stream"));
1811 *err = CURLE_OK;
1812 return 0;
1813 }
1814
1815 H2BUGF(infof(data, "nread=%zd", nread));
1816
1817 httpc->inbuflen = nread;
1818
1819 DEBUGASSERT(httpc->nread_inbuf == 0);
1820 }
1821 else {
1822 nread = httpc->inbuflen - httpc->nread_inbuf;
1823 (void)nread; /* silence warning, used in debug */
1824 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
1825 nread));
1826 }
1827
1828 if(h2_process_pending_input(data, httpc, err))
1829 return -1;
1830 }
1831 if(stream->memlen) {
1832 ssize_t retlen = stream->memlen;
1833 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
1834 retlen, stream->stream_id));
1835 stream->memlen = 0;
1836
1837 if(httpc->pause_stream_id == stream->stream_id) {
1838 /* data for this stream is returned now, but this stream caused a pause
1839 already so we need it called again asap */
1840 H2BUGF(infof(data, "Data returned for PAUSED stream %u",
1841 stream->stream_id));
1842 }
1843 else if(!stream->closed) {
1844 drained_transfer(data, httpc);
1845 }
1846 else
1847 /* this stream is closed, trigger a another read ASAP to detect that */
1848 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1849
1850 return retlen;
1851 }
1852 if(stream->closed)
1853 return http2_handle_stream_close(conn, data, stream, err);
1854 *err = CURLE_AGAIN;
1855 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
1856 stream->stream_id));
1857 return -1;
1858}
1859
1860static ssize_t http2_send(struct Curl_easy *data, int sockindex,
1861 const void *mem, size_t len, CURLcode *err)
1862{
1863 /*
1864 * Currently, we send request in this function, but this function is also
1865 * used to send request body. It would be nice to add dedicated function for
1866 * request.
1867 */
1868 int rv;
1869 struct connectdata *conn = data->conn;
1870 struct http_conn *httpc = &conn->proto.httpc;
1871 struct HTTP *stream = data->req.p.http;
1872 nghttp2_nv *nva = NULL;
1873 size_t nheader;
1874 nghttp2_data_provider data_prd;
1875 int32_t stream_id;
1876 nghttp2_session *h2 = httpc->h2;
1877 nghttp2_priority_spec pri_spec;
1878 CURLcode result;
1879 struct h2h3req *hreq;
1880
1881 (void)sockindex;
1882
1883 H2BUGF(infof(data, "http2_send len=%zu", len));
1884
1885 if(stream->stream_id != -1) {
1886 if(stream->close_handled) {
1887 infof(data, "stream %u closed", stream->stream_id);
1888 *err = CURLE_HTTP2_STREAM;
1889 return -1;
1890 }
1891 else if(stream->closed) {
1892 return http2_handle_stream_close(conn, data, stream, err);
1893 }
1894 /* If stream_id != -1, we have dispatched request HEADERS, and now
1895 are going to send or sending request body in DATA frame */
1896 stream->upload_mem = mem;
1897 stream->upload_len = len;
1898 rv = nghttp2_session_resume_data(h2, stream->stream_id);
1899 if(nghttp2_is_fatal(rv)) {
1900 *err = CURLE_SEND_ERROR;
1901 return -1;
1902 }
1903 rv = h2_session_send(data, h2);
1904 if(nghttp2_is_fatal(rv)) {
1905 *err = CURLE_SEND_ERROR;
1906 return -1;
1907 }
1908 len -= stream->upload_len;
1909
1910 /* Nullify here because we call nghttp2_session_send() and they
1911 might refer to the old buffer. */
1912 stream->upload_mem = NULL;
1913 stream->upload_len = 0;
1914
1915 if(should_close_session(httpc)) {
1916 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
1917 *err = CURLE_HTTP2;
1918 return -1;
1919 }
1920
1921 if(stream->upload_left) {
1922 /* we are sure that we have more data to send here. Calling the
1923 following API will make nghttp2_session_want_write() return
1924 nonzero if remote window allows it, which then libcurl checks
1925 socket is writable or not. See http2_perform_getsock(). */
1926 nghttp2_session_resume_data(h2, stream->stream_id);
1927 }
1928
1929#ifdef DEBUG_HTTP2
1930 if(!len) {
1931 infof(data, "http2_send: easy %p (stream %u) win %u/%u",
1932 data, stream->stream_id,
1933 nghttp2_session_get_remote_window_size(httpc->h2),
1934 nghttp2_session_get_stream_remote_window_size(httpc->h2,
1935 stream->stream_id)
1936 );
1937
1938 }
1939 infof(data, "http2_send returns %zu for stream %u", len,
1940 stream->stream_id);
1941#endif
1942 return len;
1943 }
1944
1945 result = Curl_pseudo_headers(data, mem, len, &hreq);
1946 if(result) {
1947 *err = result;
1948 return -1;
1949 }
1950 nheader = hreq->entries;
1951
1952 nva = malloc(sizeof(nghttp2_nv) * nheader);
1953 if(!nva) {
1954 Curl_pseudo_free(hreq);
1955 *err = CURLE_OUT_OF_MEMORY;
1956 return -1;
1957 }
1958 else {
1959 unsigned int i;
1960 for(i = 0; i < nheader; i++) {
1961 nva[i].name = (unsigned char *)hreq->header[i].name;
1962 nva[i].namelen = hreq->header[i].namelen;
1963 nva[i].value = (unsigned char *)hreq->header[i].value;
1964 nva[i].valuelen = hreq->header[i].valuelen;
1965 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1966 }
1967 Curl_pseudo_free(hreq);
1968 }
1969
1970 h2_pri_spec(data, &pri_spec);
1971
1972 H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
1973 nghttp2_session_check_request_allowed(h2), (void *)data));
1974
1975 switch(data->state.httpreq) {
1976 case HTTPREQ_POST:
1977 case HTTPREQ_POST_FORM:
1978 case HTTPREQ_POST_MIME:
1979 case HTTPREQ_PUT:
1980 if(data->state.infilesize != -1)
1981 stream->upload_left = data->state.infilesize;
1982 else
1983 /* data sending without specifying the data amount up front */
1984 stream->upload_left = -1; /* unknown, but not zero */
1985
1986 data_prd.read_callback = data_source_read_callback;
1987 data_prd.source.ptr = NULL;
1988 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1989 &data_prd, data);
1990 break;
1991 default:
1992 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1993 NULL, data);
1994 }
1995
1996 Curl_safefree(nva);
1997
1998 if(stream_id < 0) {
1999 H2BUGF(infof(data,
2000 "http2_send() nghttp2_submit_request error (%s)%u",
2001 nghttp2_strerror(stream_id), stream_id));
2002 *err = CURLE_SEND_ERROR;
2003 return -1;
2004 }
2005
2006 infof(data, "Using Stream ID: %u (easy handle %p)",
2007 stream_id, (void *)data);
2008 stream->stream_id = stream_id;
2009
2010 rv = h2_session_send(data, h2);
2011 if(rv) {
2012 H2BUGF(infof(data,
2013 "http2_send() nghttp2_session_send error (%s)%d",
2014 nghttp2_strerror(rv), rv));
2015
2016 *err = CURLE_SEND_ERROR;
2017 return -1;
2018 }
2019
2020 if(should_close_session(httpc)) {
2021 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
2022 *err = CURLE_HTTP2;
2023 return -1;
2024 }
2025
2026 /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
2027 library calls data_source_read_callback. But only it found that no data
2028 available, so it deferred the DATA transmission. Which means that
2029 nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
2030 results that no writable socket check is performed. To workaround this,
2031 we issue nghttp2_session_resume_data() here to bring back DATA
2032 transmission from deferred state. */
2033 nghttp2_session_resume_data(h2, stream->stream_id);
2034
2035 return len;
2036}
2037
2038CURLcode Curl_http2_setup(struct Curl_easy *data,
2039 struct connectdata *conn)
2040{
2041 CURLcode result;
2042 struct http_conn *httpc = &conn->proto.httpc;
2043 struct HTTP *stream = data->req.p.http;
2044
2045 DEBUGASSERT(data->state.buffer);
2046
2047 stream->stream_id = -1;
2048
2049 Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
2050 Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
2051
2052 stream->upload_left = 0;
2053 stream->upload_mem = NULL;
2054 stream->upload_len = 0;
2055 stream->mem = data->state.buffer;
2056 stream->len = data->set.buffer_size;
2057
2058 multi_connchanged(data->multi);
2059 /* below this point only connection related inits are done, which only needs
2060 to be done once per connection */
2061
2062 if((conn->handler == &Curl_handler_http2_ssl) ||
2063 (conn->handler == &Curl_handler_http2))
2064 return CURLE_OK; /* already done */
2065
2066 if(conn->handler->flags & PROTOPT_SSL)
2067 conn->handler = &Curl_handler_http2_ssl;
2068 else
2069 conn->handler = &Curl_handler_http2;
2070
2071 result = http2_init(data, conn);
2072 if(result) {
2073 Curl_dyn_free(&stream->header_recvbuf);
2074 return result;
2075 }
2076
2077 infof(data, "Using HTTP2, server supports multiplexing");
2078
2079 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2080 conn->httpversion = 20;
2081 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2082
2083 httpc->inbuflen = 0;
2084 httpc->nread_inbuf = 0;
2085
2086 httpc->pause_stream_id = 0;
2087 httpc->drain_total = 0;
2088
2089 return CURLE_OK;
2090}
2091
2092CURLcode Curl_http2_switched(struct Curl_easy *data,
2093 const char *mem, size_t nread)
2094{
2095 CURLcode result;
2096 struct connectdata *conn = data->conn;
2097 struct http_conn *httpc = &conn->proto.httpc;
2098 int rv;
2099 struct HTTP *stream = data->req.p.http;
2100
2101 result = Curl_http2_setup(data, conn);
2102 if(result)
2103 return result;
2104
2105 httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2106 httpc->send_underlying = conn->send[FIRSTSOCKET];
2107 conn->recv[FIRSTSOCKET] = http2_recv;
2108 conn->send[FIRSTSOCKET] = http2_send;
2109
2110 if(data->req.upgr101 == UPGR101_RECEIVED) {
2111 /* stream 1 is opened implicitly on upgrade */
2112 stream->stream_id = 1;
2113 /* queue SETTINGS frame (again) */
2114 rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
2115 data->state.httpreq == HTTPREQ_HEAD, NULL);
2116 if(rv) {
2117 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
2118 nghttp2_strerror(rv), rv);
2119 return CURLE_HTTP2;
2120 }
2121
2122 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2123 stream->stream_id,
2124 data);
2125 if(rv) {
2126 infof(data, "http/2: failed to set user_data for stream %u",
2127 stream->stream_id);
2128 DEBUGASSERT(0);
2129 }
2130 }
2131 else {
2132 populate_settings(data, httpc);
2133
2134 /* stream ID is unknown at this point */
2135 stream->stream_id = -1;
2136 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2137 httpc->local_settings,
2138 httpc->local_settings_num);
2139 if(rv) {
2140 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2141 nghttp2_strerror(rv), rv);
2142 return CURLE_HTTP2;
2143 }
2144 }
2145
2146 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2147 HTTP2_HUGE_WINDOW_SIZE);
2148 if(rv) {
2149 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2150 nghttp2_strerror(rv), rv);
2151 return CURLE_HTTP2;
2152 }
2153
2154 /* we are going to copy mem to httpc->inbuf. This is required since
2155 mem is part of buffer pointed by stream->mem, and callbacks
2156 called by nghttp2_session_mem_recv() will write stream specific
2157 data into stream->mem, overwriting data already there. */
2158 if(H2_BUFSIZE < nread) {
2159 failf(data, "connection buffer size is too small to store data following "
2160 "HTTP Upgrade response header: buflen=%d, datalen=%zu",
2161 H2_BUFSIZE, nread);
2162 return CURLE_HTTP2;
2163 }
2164
2165 infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
2166 " after upgrade: len=%zu",
2167 nread);
2168
2169 if(nread)
2170 memcpy(httpc->inbuf, mem, nread);
2171
2172 httpc->inbuflen = nread;
2173
2174 DEBUGASSERT(httpc->nread_inbuf == 0);
2175
2176 if(-1 == h2_process_pending_input(data, httpc, &result))
2177 return CURLE_HTTP2;
2178
2179 return CURLE_OK;
2180}
2181
2182CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
2183{
2184 DEBUGASSERT(data);
2185 DEBUGASSERT(data->conn);
2186 /* if it isn't HTTP/2, we're done */
2187 if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
2188 !data->conn->proto.httpc.h2)
2189 return CURLE_OK;
2190#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2191 else {
2192 struct HTTP *stream = data->req.p.http;
2193 struct http_conn *httpc = &data->conn->proto.httpc;
2194 uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
2195 int rv = nghttp2_session_set_local_window_size(httpc->h2,
2196 NGHTTP2_FLAG_NONE,
2197 stream->stream_id,
2198 window);
2199 if(rv) {
2200 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2201 nghttp2_strerror(rv), rv);
2202 return CURLE_HTTP2;
2203 }
2204
2205 /* make sure the window update gets sent */
2206 rv = h2_session_send(data, httpc->h2);
2207 if(rv)
2208 return CURLE_SEND_ERROR;
2209
2210 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2211 window, stream->stream_id));
2212
2213#ifdef DEBUGBUILD
2214 {
2215 /* read out the stream local window again */
2216 uint32_t window2 =
2217 nghttp2_session_get_stream_local_window_size(httpc->h2,
2218 stream->stream_id);
2219 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2220 window2, stream->stream_id));
2221 }
2222#endif
2223 }
2224#endif
2225 return CURLE_OK;
2226}
2227
2228CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2229 struct Curl_easy *child,
2230 bool exclusive)
2231{
2232 if(parent) {
2233 struct Curl_http2_dep **tail;
2234 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2235 if(!dep)
2236 return CURLE_OUT_OF_MEMORY;
2237 dep->data = child;
2238
2239 if(parent->set.stream_dependents && exclusive) {
2240 struct Curl_http2_dep *node = parent->set.stream_dependents;
2241 while(node) {
2242 node->data->set.stream_depends_on = child;
2243 node = node->next;
2244 }
2245
2246 tail = &child->set.stream_dependents;
2247 while(*tail)
2248 tail = &(*tail)->next;
2249
2250 DEBUGASSERT(!*tail);
2251 *tail = parent->set.stream_dependents;
2252 parent->set.stream_dependents = 0;
2253 }
2254
2255 tail = &parent->set.stream_dependents;
2256 while(*tail) {
2257 (*tail)->data->set.stream_depends_e = FALSE;
2258 tail = &(*tail)->next;
2259 }
2260
2261 DEBUGASSERT(!*tail);
2262 *tail = dep;
2263 }
2264
2265 child->set.stream_depends_on = parent;
2266 child->set.stream_depends_e = exclusive;
2267 return CURLE_OK;
2268}
2269
2270void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2271{
2272 struct Curl_http2_dep *last = 0;
2273 struct Curl_http2_dep *data = parent->set.stream_dependents;
2274 DEBUGASSERT(child->set.stream_depends_on == parent);
2275
2276 while(data && data->data != child) {
2277 last = data;
2278 data = data->next;
2279 }
2280
2281 DEBUGASSERT(data);
2282
2283 if(data) {
2284 if(last) {
2285 last->next = data->next;
2286 }
2287 else {
2288 parent->set.stream_dependents = data->next;
2289 }
2290 free(data);
2291 }
2292
2293 child->set.stream_depends_on = 0;
2294 child->set.stream_depends_e = FALSE;
2295}
2296
2297void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2298{
2299 while(data->set.stream_dependents) {
2300 struct Curl_easy *tmp = data->set.stream_dependents->data;
2301 Curl_http2_remove_child(data, tmp);
2302 if(data->set.stream_depends_on)
2303 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2304 }
2305
2306 if(data->set.stream_depends_on)
2307 Curl_http2_remove_child(data->set.stream_depends_on, data);
2308}
2309
2310/* Only call this function for a transfer that already got an HTTP/2
2311 CURLE_HTTP2_STREAM error! */
2312bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2313{
2314 struct HTTP *stream = data->req.p.http;
2315 return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
2316}
2317
2318#else /* !USE_NGHTTP2 */
2319
2320/* Satisfy external references even if http2 is not compiled in. */
2321#include <curl/curl.h>
2322
2323char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2324{
2325 (void) h;
2326 (void) num;
2327 return NULL;
2328}
2329
2330char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2331{
2332 (void) h;
2333 (void) header;
2334 return NULL;
2335}
2336
2337#endif /* USE_NGHTTP2 */
Note: See TracBrowser for help on using the repository browser.

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