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 a 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(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(!Curl_strcasecompare(check, (const char *)value) &&
1028 ((conn->remote_port != conn->given->defport) ||
1029 !Curl_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 a 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
1280/*
1281 * Initialize nghttp2 for a Curl connection
1282 */
1283static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
1284{
1285 if(!conn->proto.httpc.h2) {
1286 int rc;
1287 nghttp2_session_callbacks *callbacks;
1288
1289 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1290 if(!conn->proto.httpc.inbuf)
1291 return CURLE_OUT_OF_MEMORY;
1292
1293 rc = nghttp2_session_callbacks_new(&callbacks);
1294
1295 if(rc) {
1296 failf(data, "Couldn't initialize nghttp2 callbacks");
1297 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1298 }
1299
1300 /* nghttp2_send_callback */
1301 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1302 /* nghttp2_on_frame_recv_callback */
1303 nghttp2_session_callbacks_set_on_frame_recv_callback
1304 (callbacks, on_frame_recv);
1305 /* nghttp2_on_data_chunk_recv_callback */
1306 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1307 (callbacks, on_data_chunk_recv);
1308 /* nghttp2_on_stream_close_callback */
1309 nghttp2_session_callbacks_set_on_stream_close_callback
1310 (callbacks, on_stream_close);
1311 /* nghttp2_on_begin_headers_callback */
1312 nghttp2_session_callbacks_set_on_begin_headers_callback
1313 (callbacks, on_begin_headers);
1314 /* nghttp2_on_header_callback */
1315 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1316
1317 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1318
1319 /* The nghttp2 session is not yet setup, do it */
1320 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1321
1322 nghttp2_session_callbacks_del(callbacks);
1323
1324 if(rc) {
1325 failf(data, "Couldn't initialize nghttp2");
1326 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1327 }
1328 }
1329 return CURLE_OK;
1330}
1331
1332/*
1333 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1334 */
1335CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1336 struct Curl_easy *data)
1337{
1338 CURLcode result;
1339 ssize_t binlen;
1340 char *base64;
1341 size_t blen;
1342 struct connectdata *conn = data->conn;
1343 struct SingleRequest *k = &data->req;
1344 uint8_t *binsettings = conn->proto.httpc.binsettings;
1345 struct http_conn *httpc = &conn->proto.httpc;
1346
1347 populate_settings(data, httpc);
1348
1349 /* this returns number of bytes it wrote */
1350 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1351 httpc->local_settings,
1352 httpc->local_settings_num);
1353 if(binlen <= 0) {
1354 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1355 Curl_dyn_free(req);
1356 return CURLE_FAILED_INIT;
1357 }
1358 conn->proto.httpc.binlen = binlen;
1359
1360 result = Curl_base64url_encode((const char *)binsettings, binlen,
1361 &base64, &blen);
1362 if(result) {
1363 Curl_dyn_free(req);
1364 return result;
1365 }
1366
1367 result = Curl_dyn_addf(req,
1368 "Connection: Upgrade, HTTP2-Settings\r\n"
1369 "Upgrade: %s\r\n"
1370 "HTTP2-Settings: %s\r\n",
1371 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1372 free(base64);
1373
1374 k->upgr101 = UPGR101_REQUESTED;
1375
1376 return result;
1377}
1378
1379/*
1380 * Returns nonzero if current HTTP/2 session should be closed.
1381 */
1382static int should_close_session(struct http_conn *httpc)
1383{
1384 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1385 !nghttp2_session_want_write(httpc->h2);
1386}
1387
1388/*
1389 * h2_process_pending_input() processes pending input left in
1390 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1391 * This function returns 0 if it succeeds, or -1 and error code will
1392 * be assigned to *err.
1393 */
1394static int h2_process_pending_input(struct Curl_easy *data,
1395 struct http_conn *httpc,
1396 CURLcode *err)
1397{
1398 ssize_t nread;
1399 char *inbuf;
1400 ssize_t rv;
1401
1402 nread = httpc->inbuflen - httpc->nread_inbuf;
1403 inbuf = httpc->inbuf + httpc->nread_inbuf;
1404
1405 set_transfer(httpc, data); /* set the transfer */
1406 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1407 if(rv < 0) {
1408 failf(data,
1409 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1410 "%zd:%s", rv, nghttp2_strerror((int)rv));
1411 *err = CURLE_RECV_ERROR;
1412 return -1;
1413 }
1414
1415 if(nread == rv) {
1416 H2BUGF(infof(data,
1417 "h2_process_pending_input: All data in connection buffer "
1418 "processed"));
1419 httpc->inbuflen = 0;
1420 httpc->nread_inbuf = 0;
1421 }
1422 else {
1423 httpc->nread_inbuf += rv;
1424 H2BUGF(infof(data,
1425 "h2_process_pending_input: %zu bytes left in connection "
1426 "buffer",
1427 httpc->inbuflen - httpc->nread_inbuf));
1428 }
1429
1430 rv = h2_session_send(data, httpc->h2);
1431 if(rv) {
1432 *err = CURLE_SEND_ERROR;
1433 return -1;
1434 }
1435
1436 if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
1437 /* No more requests are allowed in the current session, so
1438 the connection may not be reused. This is set when a
1439 GOAWAY frame has been received or when the limit of stream
1440 identifiers has been reached. */
1441 connclose(data->conn, "http/2: No new requests allowed");
1442 }
1443
1444 if(should_close_session(httpc)) {
1445 struct HTTP *stream = data->req.p.http;
1446 H2BUGF(infof(data,
1447 "h2_process_pending_input: nothing to do in this session"));
1448 if(stream->error)
1449 *err = CURLE_HTTP2;
1450 else {
1451 /* not an error per se, but should still close the connection */
1452 connclose(data->conn, "GOAWAY received");
1453 *err = CURLE_OK;
1454 }
1455 return -1;
1456 }
1457 return 0;
1458}
1459
1460/*
1461 * Called from transfer.c:done_sending when we stop uploading.
1462 */
1463CURLcode Curl_http2_done_sending(struct Curl_easy *data,
1464 struct connectdata *conn)
1465{
1466 CURLcode result = CURLE_OK;
1467
1468 if((conn->handler == &Curl_handler_http2_ssl) ||
1469 (conn->handler == &Curl_handler_http2)) {
1470 /* make sure this is only attempted for HTTP/2 transfers */
1471 struct HTTP *stream = data->req.p.http;
1472 struct http_conn *httpc = &conn->proto.httpc;
1473 nghttp2_session *h2 = httpc->h2;
1474
1475 if(stream->upload_left) {
1476 /* If the stream still thinks there's data left to upload. */
1477
1478 stream->upload_left = 0; /* DONE! */
1479
1480 /* resume sending here to trigger the callback to get called again so
1481 that it can signal EOF to nghttp2 */
1482 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1483 (void)h2_process_pending_input(data, httpc, &result);
1484 }
1485
1486 /* If nghttp2 still has pending frames unsent */
1487 if(nghttp2_session_want_write(h2)) {
1488 struct SingleRequest *k = &data->req;
1489 int rv;
1490
1491 H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
1492
1493 /* and attempt to send the pending frames */
1494 rv = h2_session_send(data, h2);
1495 if(rv)
1496 result = CURLE_SEND_ERROR;
1497
1498 if(nghttp2_session_want_write(h2)) {
1499 /* re-set KEEP_SEND to make sure we are called again */
1500 k->keepon |= KEEP_SEND;
1501 }
1502 }
1503 }
1504 return result;
1505}
1506
1507static ssize_t http2_handle_stream_close(struct connectdata *conn,
1508 struct Curl_easy *data,
1509 struct HTTP *stream, CURLcode *err)
1510{
1511 struct http_conn *httpc = &conn->proto.httpc;
1512
1513 if(httpc->pause_stream_id == stream->stream_id) {
1514 httpc->pause_stream_id = 0;
1515 }
1516
1517 drained_transfer(data, httpc);
1518
1519 if(httpc->pause_stream_id == 0) {
1520 if(h2_process_pending_input(data, httpc, err) != 0) {
1521 return -1;
1522 }
1523 }
1524
1525 DEBUGASSERT(data->state.drain == 0);
1526
1527 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1528 stream->closed = FALSE;
1529 if(stream->error == NGHTTP2_REFUSED_STREAM) {
1530 H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
1531 stream->stream_id));
1532 connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1533 data->state.refused_stream = TRUE;
1534 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1535 return -1;
1536 }
1537 else if(stream->error != NGHTTP2_NO_ERROR) {
1538 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1539 stream->stream_id, nghttp2_http2_strerror(stream->error),
1540 stream->error);
1541 *err = CURLE_HTTP2_STREAM;
1542 return -1;
1543 }
1544
1545 if(!stream->bodystarted) {
1546 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1547 " all response header fields, treated as error",
1548 stream->stream_id);
1549 *err = CURLE_HTTP2_STREAM;
1550 return -1;
1551 }
1552
1553 if(Curl_dyn_len(&stream->trailer_recvbuf)) {
1554 char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
1555 char *lf;
1556
1557 do {
1558 size_t len = 0;
1559 CURLcode result;
1560 /* each trailer line ends with a newline */
1561 lf = strchr(trailp, '\n');
1562 if(!lf)
1563 break;
1564 len = lf + 1 - trailp;
1565
1566 Curl_debug(data, CURLINFO_HEADER_IN, trailp, len);
1567 /* pass the trailers one by one to the callback */
1568 result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len);
1569 if(result) {
1570 *err = result;
1571 return -1;
1572 }
1573 trailp = ++lf;
1574 } while(lf);
1575 }
1576
1577 stream->close_handled = TRUE;
1578
1579 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
1580 return 0;
1581}
1582
1583/*
1584 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1585 * and dependency to the peer. It also stores the updated values in the state
1586 * struct.
1587 */
1588
1589static void h2_pri_spec(struct Curl_easy *data,
1590 nghttp2_priority_spec *pri_spec)
1591{
1592 struct HTTP *depstream = (data->set.stream_depends_on?
1593 data->set.stream_depends_on->req.p.http:NULL);
1594 int32_t depstream_id = depstream? depstream->stream_id:0;
1595 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1596 data->set.stream_depends_e);
1597 data->state.stream_weight = data->set.stream_weight;
1598 data->state.stream_depends_e = data->set.stream_depends_e;
1599 data->state.stream_depends_on = data->set.stream_depends_on;
1600}
1601
1602/*
1603 * h2_session_send() checks if there's been an update in the priority /
1604 * dependency settings and if so it submits a PRIORITY frame with the updated
1605 * info.
1606 */
1607static int h2_session_send(struct Curl_easy *data,
1608 nghttp2_session *h2)
1609{
1610 struct HTTP *stream = data->req.p.http;
1611 struct http_conn *httpc = &data->conn->proto.httpc;
1612 set_transfer(httpc, data);
1613 if((data->set.stream_weight != data->state.stream_weight) ||
1614 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1615 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1616 /* send new weight and/or dependency */
1617 nghttp2_priority_spec pri_spec;
1618 int rv;
1619
1620 h2_pri_spec(data, &pri_spec);
1621
1622 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
1623 stream->stream_id, data));
1624 DEBUGASSERT(stream->stream_id != -1);
1625 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1626 &pri_spec);
1627 if(rv)
1628 return rv;
1629 }
1630
1631 return nghttp2_session_send(h2);
1632}
1633
1634static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
1635 char *mem, size_t len, CURLcode *err)
1636{
1637 ssize_t nread;
1638 struct connectdata *conn = data->conn;
1639 struct http_conn *httpc = &conn->proto.httpc;
1640 struct HTTP *stream = data->req.p.http;
1641
1642 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1643
1644 if(should_close_session(httpc)) {
1645 H2BUGF(infof(data,
1646 "http2_recv: nothing to do in this session"));
1647 if(conn->bits.close) {
1648 /* already marked for closure, return OK and we're done */
1649 *err = CURLE_OK;
1650 return 0;
1651 }
1652 *err = CURLE_HTTP2;
1653 return -1;
1654 }
1655
1656 /* Nullify here because we call nghttp2_session_send() and they
1657 might refer to the old buffer. */
1658 stream->upload_mem = NULL;
1659 stream->upload_len = 0;
1660
1661 /*
1662 * At this point 'stream' is just in the Curl_easy the connection
1663 * identifies as its owner at this time.
1664 */
1665
1666 if(stream->bodystarted &&
1667 stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) {
1668 /* If there is header data pending for this stream to return, do that */
1669 size_t left =
1670 Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
1671 size_t ncopy = CURLMIN(len, left);
1672 memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
1673 stream->nread_header_recvbuf, ncopy);
1674 stream->nread_header_recvbuf += ncopy;
1675
1676 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
1677 (int)ncopy));
1678 return ncopy;
1679 }
1680
1681 H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
1682 data, stream->stream_id,
1683 nghttp2_session_get_local_window_size(httpc->h2),
1684 nghttp2_session_get_stream_local_window_size(httpc->h2,
1685 stream->stream_id)
1686 ));
1687
1688 if((data->state.drain) && stream->memlen) {
1689 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
1690 stream->memlen, stream->stream_id,
1691 stream->mem, mem));
1692 if(mem != stream->mem) {
1693 /* if we didn't get the same buffer this time, we must move the data to
1694 the beginning */
1695 memmove(mem, stream->mem, stream->memlen);
1696 stream->len = len - stream->memlen;
1697 stream->mem = mem;
1698 }
1699 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1700 /* We have paused nghttp2, but we have no pause data (see
1701 on_data_chunk_recv). */
1702 httpc->pause_stream_id = 0;
1703 if(h2_process_pending_input(data, httpc, err) != 0) {
1704 return -1;
1705 }
1706 }
1707 }
1708 else if(stream->pausedata) {
1709 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1710 nread = CURLMIN(len, stream->pauselen);
1711 memcpy(mem, stream->pausedata, nread);
1712
1713 stream->pausedata += nread;
1714 stream->pauselen -= nread;
1715
1716 if(stream->pauselen == 0) {
1717 H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
1718 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1719 httpc->pause_stream_id = 0;
1720
1721 stream->pausedata = NULL;
1722 stream->pauselen = 0;
1723
1724 /* When NGHTTP2_ERR_PAUSE is returned from
1725 data_source_read_callback, we might not process DATA frame
1726 fully. Calling nghttp2_session_mem_recv() again will
1727 continue to process DATA frame, but if there is no incoming
1728 frames, then we have to call it again with 0-length data.
1729 Without this, on_stream_close callback will not be called,
1730 and stream could be hanged. */
1731 if(h2_process_pending_input(data, httpc, err) != 0) {
1732 return -1;
1733 }
1734 }
1735 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
1736 nread, stream->stream_id));
1737 return nread;
1738 }
1739 else if(httpc->pause_stream_id) {
1740 /* If a stream paused nghttp2_session_mem_recv previously, and has
1741 not processed all data, it still refers to the buffer in
1742 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1743 overwrite that buffer. To avoid that situation, just return
1744 here with CURLE_AGAIN. This could be busy loop since data in
1745 socket is not read. But it seems that usually streams are
1746 notified with its drain property, and socket is read again
1747 quickly. */
1748 if(stream->closed)
1749 /* closed overrides paused */
1750 return 0;
1751 H2BUGF(infof(data, "stream %u is paused, pause id: %u",
1752 stream->stream_id, httpc->pause_stream_id));
1753 *err = CURLE_AGAIN;
1754 return -1;
1755 }
1756 else {
1757 /* remember where to store incoming data for this stream and how big the
1758 buffer is */
1759 stream->mem = mem;
1760 stream->len = len;
1761 stream->memlen = 0;
1762
1763 if(httpc->inbuflen == 0) {
1764 nread = ((Curl_recv *)httpc->recv_underlying)(
1765 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
1766
1767 if(nread == -1) {
1768 if(*err != CURLE_AGAIN)
1769 failf(data, "Failed receiving HTTP2 data");
1770 else if(stream->closed)
1771 /* received when the stream was already closed! */
1772 return http2_handle_stream_close(conn, data, stream, err);
1773
1774 return -1;
1775 }
1776
1777 if(nread == 0) {
1778 if(!stream->closed) {
1779 /* This will happen when the server or proxy server is SIGKILLed
1780 during data transfer. We should emit an error since our data
1781 received may be incomplete. */
1782 failf(data, "HTTP/2 stream %u was not closed cleanly before"
1783 " end of the underlying stream",
1784 stream->stream_id);
1785 *err = CURLE_HTTP2_STREAM;
1786 return -1;
1787 }
1788
1789 H2BUGF(infof(data, "end of stream"));
1790 *err = CURLE_OK;
1791 return 0;
1792 }
1793
1794 H2BUGF(infof(data, "nread=%zd", nread));
1795
1796 httpc->inbuflen = nread;
1797
1798 DEBUGASSERT(httpc->nread_inbuf == 0);
1799 }
1800 else {
1801 nread = httpc->inbuflen - httpc->nread_inbuf;
1802 (void)nread; /* silence warning, used in debug */
1803 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
1804 nread));
1805 }
1806
1807 if(h2_process_pending_input(data, httpc, err))
1808 return -1;
1809 }
1810 if(stream->memlen) {
1811 ssize_t retlen = stream->memlen;
1812 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
1813 retlen, stream->stream_id));
1814 stream->memlen = 0;
1815
1816 if(httpc->pause_stream_id == stream->stream_id) {
1817 /* data for this stream is returned now, but this stream caused a pause
1818 already so we need it called again asap */
1819 H2BUGF(infof(data, "Data returned for PAUSED stream %u",
1820 stream->stream_id));
1821 }
1822 else if(!stream->closed) {
1823 drained_transfer(data, httpc);
1824 }
1825 else
1826 /* this stream is closed, trigger a another read ASAP to detect that */
1827 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1828
1829 return retlen;
1830 }
1831 if(stream->closed)
1832 return http2_handle_stream_close(conn, data, stream, err);
1833 *err = CURLE_AGAIN;
1834 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
1835 stream->stream_id));
1836 return -1;
1837}
1838
1839static ssize_t http2_send(struct Curl_easy *data, int sockindex,
1840 const void *mem, size_t len, CURLcode *err)
1841{
1842 /*
1843 * Currently, we send request in this function, but this function is also
1844 * used to send request body. It would be nice to add dedicated function for
1845 * request.
1846 */
1847 int rv;
1848 struct connectdata *conn = data->conn;
1849 struct http_conn *httpc = &conn->proto.httpc;
1850 struct HTTP *stream = data->req.p.http;
1851 nghttp2_nv *nva = NULL;
1852 size_t nheader;
1853 nghttp2_data_provider data_prd;
1854 int32_t stream_id;
1855 nghttp2_session *h2 = httpc->h2;
1856 nghttp2_priority_spec pri_spec;
1857 CURLcode result;
1858 struct h2h3req *hreq;
1859
1860 (void)sockindex;
1861
1862 H2BUGF(infof(data, "http2_send len=%zu", len));
1863
1864 if(stream->stream_id != -1) {
1865 if(stream->close_handled) {
1866 infof(data, "stream %u closed", stream->stream_id);
1867 *err = CURLE_HTTP2_STREAM;
1868 return -1;
1869 }
1870 else if(stream->closed) {
1871 return http2_handle_stream_close(conn, data, stream, err);
1872 }
1873 /* If stream_id != -1, we have dispatched request HEADERS, and now
1874 are going to send or sending request body in DATA frame */
1875 stream->upload_mem = mem;
1876 stream->upload_len = len;
1877 rv = nghttp2_session_resume_data(h2, stream->stream_id);
1878 if(nghttp2_is_fatal(rv)) {
1879 *err = CURLE_SEND_ERROR;
1880 return -1;
1881 }
1882 rv = h2_session_send(data, h2);
1883 if(nghttp2_is_fatal(rv)) {
1884 *err = CURLE_SEND_ERROR;
1885 return -1;
1886 }
1887 len -= stream->upload_len;
1888
1889 /* Nullify here because we call nghttp2_session_send() and they
1890 might refer to the old buffer. */
1891 stream->upload_mem = NULL;
1892 stream->upload_len = 0;
1893
1894 if(should_close_session(httpc)) {
1895 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
1896 *err = CURLE_HTTP2;
1897 return -1;
1898 }
1899
1900 if(stream->upload_left) {
1901 /* we are sure that we have more data to send here. Calling the
1902 following API will make nghttp2_session_want_write() return
1903 nonzero if remote window allows it, which then libcurl checks
1904 socket is writable or not. See http2_perform_getsock(). */
1905 nghttp2_session_resume_data(h2, stream->stream_id);
1906 }
1907
1908#ifdef DEBUG_HTTP2
1909 if(!len) {
1910 infof(data, "http2_send: easy %p (stream %u) win %u/%u",
1911 data, stream->stream_id,
1912 nghttp2_session_get_remote_window_size(httpc->h2),
1913 nghttp2_session_get_stream_remote_window_size(httpc->h2,
1914 stream->stream_id)
1915 );
1916
1917 }
1918 infof(data, "http2_send returns %zu for stream %u", len,
1919 stream->stream_id);
1920#endif
1921 return len;
1922 }
1923
1924 result = Curl_pseudo_headers(data, mem, len, &hreq);
1925 if(result) {
1926 *err = result;
1927 return -1;
1928 }
1929 nheader = hreq->entries;
1930
1931 nva = malloc(sizeof(nghttp2_nv) * nheader);
1932 if(!nva) {
1933 Curl_pseudo_free(hreq);
1934 *err = CURLE_OUT_OF_MEMORY;
1935 return -1;
1936 }
1937 else {
1938 unsigned int i;
1939 for(i = 0; i < nheader; i++) {
1940 nva[i].name = (unsigned char *)hreq->header[i].name;
1941 nva[i].namelen = hreq->header[i].namelen;
1942 nva[i].value = (unsigned char *)hreq->header[i].value;
1943 nva[i].valuelen = hreq->header[i].valuelen;
1944 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1945 }
1946 Curl_pseudo_free(hreq);
1947 }
1948
1949 h2_pri_spec(data, &pri_spec);
1950
1951 H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
1952 nghttp2_session_check_request_allowed(h2), (void *)data));
1953
1954 switch(data->state.httpreq) {
1955 case HTTPREQ_POST:
1956 case HTTPREQ_POST_FORM:
1957 case HTTPREQ_POST_MIME:
1958 case HTTPREQ_PUT:
1959 if(data->state.infilesize != -1)
1960 stream->upload_left = data->state.infilesize;
1961 else
1962 /* data sending without specifying the data amount up front */
1963 stream->upload_left = -1; /* unknown, but not zero */
1964
1965 data_prd.read_callback = data_source_read_callback;
1966 data_prd.source.ptr = NULL;
1967 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1968 &data_prd, data);
1969 break;
1970 default:
1971 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1972 NULL, data);
1973 }
1974
1975 Curl_safefree(nva);
1976
1977 if(stream_id < 0) {
1978 H2BUGF(infof(data,
1979 "http2_send() nghttp2_submit_request error (%s)%u",
1980 nghttp2_strerror(stream_id), stream_id));
1981 *err = CURLE_SEND_ERROR;
1982 return -1;
1983 }
1984
1985 infof(data, "Using Stream ID: %u (easy handle %p)",
1986 stream_id, (void *)data);
1987 stream->stream_id = stream_id;
1988
1989 rv = h2_session_send(data, h2);
1990 if(rv) {
1991 H2BUGF(infof(data,
1992 "http2_send() nghttp2_session_send error (%s)%d",
1993 nghttp2_strerror(rv), rv));
1994
1995 *err = CURLE_SEND_ERROR;
1996 return -1;
1997 }
1998
1999 if(should_close_session(httpc)) {
2000 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
2001 *err = CURLE_HTTP2;
2002 return -1;
2003 }
2004
2005 /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
2006 library calls data_source_read_callback. But only it found that no data
2007 available, so it deferred the DATA transmission. Which means that
2008 nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
2009 results that no writable socket check is performed. To workaround this,
2010 we issue nghttp2_session_resume_data() here to bring back DATA
2011 transmission from deferred state. */
2012 nghttp2_session_resume_data(h2, stream->stream_id);
2013
2014 return len;
2015}
2016
2017CURLcode Curl_http2_setup(struct Curl_easy *data,
2018 struct connectdata *conn)
2019{
2020 CURLcode result;
2021 struct http_conn *httpc = &conn->proto.httpc;
2022 struct HTTP *stream = data->req.p.http;
2023
2024 DEBUGASSERT(data->state.buffer);
2025
2026 stream->stream_id = -1;
2027
2028 Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
2029 Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
2030
2031 stream->upload_left = 0;
2032 stream->upload_mem = NULL;
2033 stream->upload_len = 0;
2034 stream->mem = data->state.buffer;
2035 stream->len = data->set.buffer_size;
2036
2037 multi_connchanged(data->multi);
2038 /* below this point only connection related inits are done, which only needs
2039 to be done once per connection */
2040
2041 if((conn->handler == &Curl_handler_http2_ssl) ||
2042 (conn->handler == &Curl_handler_http2))
2043 return CURLE_OK; /* already done */
2044
2045 if(conn->handler->flags & PROTOPT_SSL)
2046 conn->handler = &Curl_handler_http2_ssl;
2047 else
2048 conn->handler = &Curl_handler_http2;
2049
2050 result = http2_init(data, conn);
2051 if(result) {
2052 Curl_dyn_free(&stream->header_recvbuf);
2053 return result;
2054 }
2055
2056 infof(data, "Using HTTP2, server supports multiplexing");
2057
2058 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2059 conn->httpversion = 20;
2060 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2061
2062 httpc->inbuflen = 0;
2063 httpc->nread_inbuf = 0;
2064
2065 httpc->pause_stream_id = 0;
2066 httpc->drain_total = 0;
2067
2068 return CURLE_OK;
2069}
2070
2071CURLcode Curl_http2_switched(struct Curl_easy *data,
2072 const char *mem, size_t nread)
2073{
2074 CURLcode result;
2075 struct connectdata *conn = data->conn;
2076 struct http_conn *httpc = &conn->proto.httpc;
2077 int rv;
2078 struct HTTP *stream = data->req.p.http;
2079
2080 result = Curl_http2_setup(data, conn);
2081 if(result)
2082 return result;
2083
2084 httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2085 httpc->send_underlying = conn->send[FIRSTSOCKET];
2086 conn->recv[FIRSTSOCKET] = http2_recv;
2087 conn->send[FIRSTSOCKET] = http2_send;
2088
2089 if(data->req.upgr101 == UPGR101_RECEIVED) {
2090 /* stream 1 is opened implicitly on upgrade */
2091 stream->stream_id = 1;
2092 /* queue SETTINGS frame (again) */
2093 rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
2094 data->state.httpreq == HTTPREQ_HEAD, NULL);
2095 if(rv) {
2096 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
2097 nghttp2_strerror(rv), rv);
2098 return CURLE_HTTP2;
2099 }
2100
2101 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2102 stream->stream_id,
2103 data);
2104 if(rv) {
2105 infof(data, "http/2: failed to set user_data for stream %u",
2106 stream->stream_id);
2107 DEBUGASSERT(0);
2108 }
2109 }
2110 else {
2111 populate_settings(data, httpc);
2112
2113 /* stream ID is unknown at this point */
2114 stream->stream_id = -1;
2115 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2116 httpc->local_settings,
2117 httpc->local_settings_num);
2118 if(rv) {
2119 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2120 nghttp2_strerror(rv), rv);
2121 return CURLE_HTTP2;
2122 }
2123 }
2124
2125 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2126 HTTP2_HUGE_WINDOW_SIZE);
2127 if(rv) {
2128 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2129 nghttp2_strerror(rv), rv);
2130 return CURLE_HTTP2;
2131 }
2132
2133 /* we are going to copy mem to httpc->inbuf. This is required since
2134 mem is part of buffer pointed by stream->mem, and callbacks
2135 called by nghttp2_session_mem_recv() will write stream specific
2136 data into stream->mem, overwriting data already there. */
2137 if(H2_BUFSIZE < nread) {
2138 failf(data, "connection buffer size is too small to store data following "
2139 "HTTP Upgrade response header: buflen=%d, datalen=%zu",
2140 H2_BUFSIZE, nread);
2141 return CURLE_HTTP2;
2142 }
2143
2144 infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
2145 " after upgrade: len=%zu",
2146 nread);
2147
2148 if(nread)
2149 memcpy(httpc->inbuf, mem, nread);
2150
2151 httpc->inbuflen = nread;
2152
2153 DEBUGASSERT(httpc->nread_inbuf == 0);
2154
2155 if(-1 == h2_process_pending_input(data, httpc, &result))
2156 return CURLE_HTTP2;
2157
2158 return CURLE_OK;
2159}
2160
2161CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
2162{
2163 DEBUGASSERT(data);
2164 DEBUGASSERT(data->conn);
2165 /* if it isn't HTTP/2, we're done */
2166 if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
2167 !data->conn->proto.httpc.h2)
2168 return CURLE_OK;
2169#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2170 else {
2171 struct HTTP *stream = data->req.p.http;
2172 struct http_conn *httpc = &data->conn->proto.httpc;
2173 uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
2174 int rv = nghttp2_session_set_local_window_size(httpc->h2,
2175 NGHTTP2_FLAG_NONE,
2176 stream->stream_id,
2177 window);
2178 if(rv) {
2179 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2180 nghttp2_strerror(rv), rv);
2181 return CURLE_HTTP2;
2182 }
2183
2184 /* make sure the window update gets sent */
2185 rv = h2_session_send(data, httpc->h2);
2186 if(rv)
2187 return CURLE_SEND_ERROR;
2188
2189 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2190 window, stream->stream_id));
2191
2192#ifdef DEBUGBUILD
2193 {
2194 /* read out the stream local window again */
2195 uint32_t window2 =
2196 nghttp2_session_get_stream_local_window_size(httpc->h2,
2197 stream->stream_id);
2198 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2199 window2, stream->stream_id));
2200 }
2201#endif
2202 }
2203#endif
2204 return CURLE_OK;
2205}
2206
2207CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2208 struct Curl_easy *child,
2209 bool exclusive)
2210{
2211 if(parent) {
2212 struct Curl_http2_dep **tail;
2213 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2214 if(!dep)
2215 return CURLE_OUT_OF_MEMORY;
2216 dep->data = child;
2217
2218 if(parent->set.stream_dependents && exclusive) {
2219 struct Curl_http2_dep *node = parent->set.stream_dependents;
2220 while(node) {
2221 node->data->set.stream_depends_on = child;
2222 node = node->next;
2223 }
2224
2225 tail = &child->set.stream_dependents;
2226 while(*tail)
2227 tail = &(*tail)->next;
2228
2229 DEBUGASSERT(!*tail);
2230 *tail = parent->set.stream_dependents;
2231 parent->set.stream_dependents = 0;
2232 }
2233
2234 tail = &parent->set.stream_dependents;
2235 while(*tail) {
2236 (*tail)->data->set.stream_depends_e = FALSE;
2237 tail = &(*tail)->next;
2238 }
2239
2240 DEBUGASSERT(!*tail);
2241 *tail = dep;
2242 }
2243
2244 child->set.stream_depends_on = parent;
2245 child->set.stream_depends_e = exclusive;
2246 return CURLE_OK;
2247}
2248
2249void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2250{
2251 struct Curl_http2_dep *last = 0;
2252 struct Curl_http2_dep *data = parent->set.stream_dependents;
2253 DEBUGASSERT(child->set.stream_depends_on == parent);
2254
2255 while(data && data->data != child) {
2256 last = data;
2257 data = data->next;
2258 }
2259
2260 DEBUGASSERT(data);
2261
2262 if(data) {
2263 if(last) {
2264 last->next = data->next;
2265 }
2266 else {
2267 parent->set.stream_dependents = data->next;
2268 }
2269 free(data);
2270 }
2271
2272 child->set.stream_depends_on = 0;
2273 child->set.stream_depends_e = FALSE;
2274}
2275
2276void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2277{
2278 while(data->set.stream_dependents) {
2279 struct Curl_easy *tmp = data->set.stream_dependents->data;
2280 Curl_http2_remove_child(data, tmp);
2281 if(data->set.stream_depends_on)
2282 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2283 }
2284
2285 if(data->set.stream_depends_on)
2286 Curl_http2_remove_child(data->set.stream_depends_on, data);
2287}
2288
2289/* Only call this function for a transfer that already got a HTTP/2
2290 CURLE_HTTP2_STREAM error! */
2291bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2292{
2293 struct HTTP *stream = data->req.p.http;
2294 return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
2295}
2296
2297#else /* !USE_NGHTTP2 */
2298
2299/* Satisfy external references even if http2 is not compiled in. */
2300#include <curl/curl.h>
2301
2302char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2303{
2304 (void) h;
2305 (void) num;
2306 return NULL;
2307}
2308
2309char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2310{
2311 (void) h;
2312 (void) header;
2313 return NULL;
2314}
2315
2316#endif /* USE_NGHTTP2 */
2317