1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
28 #include <nghttp2/nghttp2.h>
34 #include "curl_base64.h"
39 #include "strtoofft.h"
45 /* The last 3 #include files should be in this order */
46 #include "curl_printf.h"
47 #include "curl_memory.h"
50 #define H2_BUFSIZE 32768
52 #if (NGHTTP2_VERSION_NUM < 0x010c00)
53 #error too old nghttp2 version, upgrade!
56 #ifdef CURL_DISABLE_VERBOSE_STRINGS
57 #define nghttp2_session_callbacks_set_error_callback(x,y)
60 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
61 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
64 #define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
69 #define H2BUGF(x) do { } while(0)
72 static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
73 char *mem, size_t len, CURLcode *err);
74 static bool http2_connisdead(struct Curl_easy *data,
75 struct connectdata *conn);
76 static int h2_session_send(struct Curl_easy *data,
78 static int h2_process_pending_input(struct Curl_easy *data,
79 struct http_conn *httpc,
83 * Curl_http2_init_state() is called when the easy handle is created and
84 * allows for HTTP/2 specific init of state.
86 void Curl_http2_init_state(struct UrlState *state)
88 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
92 * Curl_http2_init_userset() is called when the easy handle is created and
93 * allows for HTTP/2 specific user-set fields.
95 void Curl_http2_init_userset(struct UserDefined *set)
97 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
100 static int http2_getsock(struct Curl_easy *data,
101 struct connectdata *conn,
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;
109 sock[0] = conn->sock[FIRSTSOCKET];
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);
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,
123 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
129 * http2_stream_free() free HTTP2 stream related data
131 static void http2_stream_free(struct HTTP *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]);
138 free(http->push_headers);
139 http->push_headers = NULL;
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!
148 static CURLcode http2_disconnect(struct Curl_easy *data,
149 struct connectdata *conn,
150 bool dead_connection)
152 struct http_conn *c = &conn->proto.httpc;
153 (void)dead_connection;
158 H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
160 nghttp2_session_del(c->h2);
161 Curl_safefree(c->inbuf);
163 H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
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.
172 * Instead, if it is readable, run Curl_connalive() to peek at the socket
173 * and distinguish between closed and data.
175 static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
183 sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
188 else if(sval & CURL_CSELECT_ERR) {
189 /* socket is in an error state */
192 else if(sval & CURL_CSELECT_IN) {
193 /* readable with no error. could still be closed */
194 dead = !Curl_connalive(conn);
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" */
200 struct http_conn *httpc = &conn->proto.httpc;
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);
208 "%d bytes stray data read before trying h2 connection",
210 httpc->nread_inbuf = 0;
211 httpc->inbuflen = nread;
212 if(h2_process_pending_input(data, httpc, &result) < 0)
213 /* immediate error, considered dead */
217 /* the read failed so let's say this is dead anyway */
226 * Set the transfer that is currently using this HTTP/2 connection.
228 static void set_transfer(struct http_conn *c,
229 struct Curl_easy *data)
235 * Get the transfer that is currently using this HTTP/2 connection.
237 static struct Curl_easy *get_transfer(struct http_conn *c)
239 DEBUGASSERT(c && c->trnsfr);
243 static unsigned int http2_conncheck(struct Curl_easy *data,
244 struct connectdata *conn,
245 unsigned int checks_to_perform)
247 unsigned int ret_val = CONNRESULT_NONE;
248 struct http_conn *c = &conn->proto.httpc;
250 bool send_frames = false;
252 if(checks_to_perform & CONNCHECK_ISDEAD) {
253 if(http2_connisdead(data, conn))
254 ret_val |= CONNRESULT_DEAD;
257 if(checks_to_perform & CONNCHECK_KEEPALIVE) {
258 struct curltime now = Curl_now();
259 timediff_t elapsed = Curl_timediff(now, conn->keepalive);
261 if(elapsed > data->set.upkeep_interval_ms) {
262 /* Perform an HTTP/2 PING */
263 rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
265 /* Successfully added a PING frame to the session. Need to flag this
266 so the frame is sent. */
270 failf(data, "nghttp2_submit_ping() failed: %s(%d)",
271 nghttp2_strerror(rc), rc);
274 conn->keepalive = now;
279 set_transfer(c, data); /* set the transfer */
280 rc = nghttp2_session_send(c->h2);
282 failf(data, "nghttp2_session_send() failed: %s(%d)",
283 nghttp2_strerror(rc), rc);
289 /* called from http_setup_conn */
290 void Curl_http2_setup_req(struct Curl_easy *data)
292 struct HTTP *http = data->req.p.http;
293 http->bodystarted = FALSE;
294 http->status_code = -1;
295 http->pausedata = NULL;
297 http->closed = FALSE;
298 http->close_handled = FALSE;
302 http->error = NGHTTP2_NO_ERROR;
305 /* called from http_setup_conn */
306 void Curl_http2_setup_conn(struct connectdata *conn)
308 conn->proto.httpc.settings.max_concurrent_streams =
309 DEFAULT_MAX_CONCURRENT_STREAMS;
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
317 static const struct Curl_handler Curl_handler_http2 = {
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 */
340 static 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 */
364 * Store nghttp2 version info in this buffer.
366 void Curl_http2_ver(char *p, size_t len)
368 nghttp2_info *h2 = nghttp2_version(0);
369 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
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.
377 static ssize_t send_callback(nghttp2_session *h2,
378 const uint8_t *mem, size_t length, int flags,
381 struct connectdata *conn = (struct connectdata *)userp;
382 struct http_conn *c = &conn->proto.httpc;
383 struct Curl_easy *data = get_transfer(c);
385 CURLcode result = CURLE_OK;
390 if(!c->send_underlying)
391 /* called before setup properly! */
392 return NGHTTP2_ERR_CALLBACK_FAILURE;
394 written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
395 mem, length, &result);
397 if(result == CURLE_AGAIN) {
398 return NGHTTP2_ERR_WOULDBLOCK;
402 failf(data, "Failed sending HTTP2 data");
403 return NGHTTP2_ERR_CALLBACK_FAILURE;
407 return NGHTTP2_ERR_WOULDBLOCK;
413 /* We pass a pointer to this struct in the push callback, but the contents of
414 the struct are hidden from the user. */
415 struct curl_pushheaders {
416 struct Curl_easy *data;
417 const nghttp2_push_promise *frame;
421 * push header access function. Only to be used from within the push callback
423 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
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))
430 struct HTTP *stream = h->data->req.p.http;
431 if(num < stream->push_headers_used)
432 return stream->push_headers[num];
438 * push header access function. Only to be used from within the push callback
440 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
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, ':'))
452 struct HTTP *stream = h->data->req.p.http;
453 size_t len = strlen(header);
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] != ':')
460 return &stream->push_headers[i][len + 1];
468 * This specific transfer on this connection has been "drained".
470 static void drained_transfer(struct Curl_easy *data,
471 struct http_conn *httpc)
473 DEBUGASSERT(httpc->drain_total >= data->state.drain);
474 httpc->drain_total -= data->state.drain;
475 data->state.drain = 0;
479 * Mark this transfer to get "drained".
481 static void drain_this(struct Curl_easy *data,
482 struct http_conn *httpc)
485 httpc->drain_total++;
486 DEBUGASSERT(httpc->drain_total >= data->state.drain);
489 static struct Curl_easy *duphandle(struct Curl_easy *data)
491 struct Curl_easy *second = curl_easy_duphandle(data);
493 /* setup the request struct */
494 struct HTTP *http = calloc(1, sizeof(struct HTTP));
496 (void)Curl_close(&second);
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;
508 static int set_transfer_url(struct Curl_easy *data,
509 struct curl_pushheaders *hp)
515 CURLU *u = curl_url();
520 v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME);
522 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
529 v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY);
531 uc = curl_url_set(u, CURLUPART_HOST, v, 0);
538 v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH);
540 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
547 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
555 if(data->state.url_alloc)
556 free(data->state.url);
557 data->state.url_alloc = TRUE;
558 data->state.url = url;
562 static int push_promise(struct Curl_easy *data,
563 struct connectdata *conn,
564 const nghttp2_push_promise *frame)
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) {
571 struct HTTP *newstream;
572 struct curl_pushheaders heads;
574 struct http_conn *httpc;
576 /* clone the parent */
577 struct Curl_easy *newhandle = duphandle(data);
579 infof(data, "failed to duplicate handle");
580 rv = CURL_PUSH_DENY; /* FAIL HARD */
586 /* ask the application */
587 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
589 stream = data->req.p.http;
591 failf(data, "Internal NULL stream");
592 (void)Curl_close(&newhandle);
597 rv = set_transfer_url(newhandle, &heads);
599 (void)Curl_close(&newhandle);
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);
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;
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);
626 newstream = newhandle->req.p.http;
627 newstream->stream_id = frame->promised_stream_id;
628 newhandle->req.maxdownload = -1;
629 newhandle->req.size = -1;
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);
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);
643 httpc = &conn->proto.httpc;
644 rv = nghttp2_session_set_stream_user_data(httpc->h2,
645 frame->promised_stream_id,
648 infof(data, "failed to set user_data for stream %u",
649 frame->promised_stream_id);
654 Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
655 Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
658 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
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
672 static void multi_connchanged(struct Curl_multi *multi)
674 multi->recheckstate = TRUE;
677 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
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);
687 int32_t stream_id = frame->hd.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 */
708 "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
709 httpc->settings.max_concurrent_streams);
710 multi_connchanged(data->multi);
715 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
718 "No Curl_easy associated with stream: %u",
723 stream = data_s->req.p.http;
725 H2BUGF(infof(data_s, "No proto pointer for stream: %u",
727 return NGHTTP2_ERR_CALLBACK_FAILURE;
730 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
731 frame->hd.type, stream_id));
733 switch(frame->hd.type) {
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);
740 if(nghttp2_is_fatal(rv)) {
741 return NGHTTP2_ERR_CALLBACK_FAILURE;
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. */
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;
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;
764 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
766 return NGHTTP2_ERR_CALLBACK_FAILURE;
768 left = Curl_dyn_len(&stream->header_recvbuf) -
769 stream->nread_header_recvbuf;
770 ncopy = CURLMIN(stream->len, left);
772 memcpy(&stream->mem[stream->memlen],
773 Curl_dyn_ptr(&stream->header_recvbuf) +
774 stream->nread_header_recvbuf,
776 stream->nread_header_recvbuf += ncopy;
778 DEBUGASSERT(stream->mem);
779 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
780 ncopy, stream_id, stream->mem));
782 stream->len -= ncopy;
783 stream->memlen += ncopy;
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);
790 case NGHTTP2_PUSH_PROMISE:
791 rv = push_promise(data_s, conn, &frame->push_promise);
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,
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;
807 H2BUGF(infof(data_s, "Got frame type %x for stream %u",
808 frame->hd.type, stream_id));
814 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
816 const uint8_t *mem, size_t len, void *userp)
819 struct Curl_easy *data_s;
821 struct connectdata *conn = (struct connectdata *)userp;
822 struct http_conn *httpc = &conn->proto.httpc;
826 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
828 /* get the stream from the hash based on Stream ID */
829 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
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",
839 stream = data_s->req.p.http;
841 return NGHTTP2_ERR_CALLBACK_FAILURE;
843 nread = CURLMIN(stream->len, len);
844 memcpy(&stream->mem[stream->memlen], mem, nread);
846 stream->len -= nread;
847 stream->memlen += nread;
849 drain_this(data_s, &conn->proto.httpc);
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);
855 H2BUGF(infof(data_s, "%zu data received for stream %u "
856 "(%zu left in buffer %p, total %zu)",
858 stream->len, stream->mem,
862 stream->pausedata = mem + nread;
863 stream->pauselen = len - nread;
864 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
866 len - nread, stream_id));
867 data_s->conn->proto.httpc.pause_stream_id = stream_id;
869 return NGHTTP2_ERR_PAUSE;
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;
877 return NGHTTP2_ERR_PAUSE;
883 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
884 uint32_t error_code, void *userp)
886 struct Curl_easy *data_s;
888 struct connectdata *conn = (struct connectdata *)userp;
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);
899 /* We could get stream ID not in the hash. For example, if we
900 decided to reject stream (e.g., PUSH_PROMISE). */
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;
907 return NGHTTP2_ERR_CALLBACK_FAILURE;
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;
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);
918 infof(data_s, "http/2: failed to clear user_data for stream %u",
922 if(stream_id == httpc->pause_stream_id) {
923 H2BUGF(infof(data_s, "Stopped the pause stream"));
924 httpc->pause_stream_id = 0;
926 H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
927 stream->stream_id = 0; /* cleared */
932 static int on_begin_headers(nghttp2_session *session,
933 const nghttp2_frame *frame, void *userp)
936 struct Curl_easy *data_s = NULL;
939 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
944 H2BUGF(infof(data_s, "on_begin_headers() was called"));
946 if(frame->hd.type != NGHTTP2_HEADERS) {
950 stream = data_s->req.p.http;
951 if(!stream || !stream->bodystarted) {
958 /* Decode HTTP status code. Returns -1 if no valid status code was
960 static int decode_status_code(const uint8_t *value, size_t len)
971 for(i = 0; i < 3; ++i) {
974 if(c < '0' || c > '9') {
985 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
986 static 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,
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;
1000 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1002 /* get the stream from the hash based on Stream ID */
1003 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
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;
1009 stream = data_s->req.p.http;
1011 failf(data_s, "Internal NULL stream");
1012 return NGHTTP2_ERR_CALLBACK_FAILURE;
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) {
1020 if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
1021 /* pseudo headers are lower case */
1023 char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
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."
1035 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1036 stream_id, NGHTTP2_PROTOCOL_ERROR);
1037 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
1044 if(!stream->push_headers) {
1045 stream->push_headers_alloc = 10;
1046 stream->push_headers = malloc(stream->push_headers_alloc *
1048 if(!stream->push_headers)
1049 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1050 stream->push_headers_used = 0;
1052 else if(stream->push_headers_used ==
1053 stream->push_headers_alloc) {
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;
1061 stream->push_headers_alloc *= 2;
1062 headp = Curl_saferealloc(stream->push_headers,
1063 stream->push_headers_alloc * sizeof(char *));
1065 stream->push_headers = NULL;
1066 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1068 stream->push_headers = headp;
1070 h = aprintf("%s:%s", name, value);
1072 stream->push_headers[stream->push_headers_used++] = h;
1076 if(stream->bodystarted) {
1077 /* This is a trailer */
1078 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
1080 result = Curl_dyn_addf(&stream->trailer_recvbuf,
1081 "%.*s: %.*s\r\n", namelen, name,
1084 return NGHTTP2_ERR_CALLBACK_FAILURE;
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
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);
1101 return NGHTTP2_ERR_CALLBACK_FAILURE;
1102 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 "));
1104 return NGHTTP2_ERR_CALLBACK_FAILURE;
1105 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
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"));
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);
1116 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
1117 stream->status_code, data_s));
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);
1126 return NGHTTP2_ERR_CALLBACK_FAILURE;
1127 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": "));
1129 return NGHTTP2_ERR_CALLBACK_FAILURE;
1130 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1132 return NGHTTP2_ERR_CALLBACK_FAILURE;
1133 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
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);
1140 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
1143 return 0; /* 0 is successful */
1146 static ssize_t data_source_read_callback(nghttp2_session *session,
1148 uint8_t *buf, size_t length,
1149 uint32_t *data_flags,
1150 nghttp2_data_source *source,
1153 struct Curl_easy *data_s;
1154 struct HTTP *stream = NULL;
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);
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;
1168 stream = data_s->req.p.http;
1170 return NGHTTP2_ERR_CALLBACK_FAILURE;
1173 return NGHTTP2_ERR_INVALID_ARGUMENT;
1175 nread = CURLMIN(stream->upload_len, length);
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;
1184 if(stream->upload_left == 0)
1185 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1187 return NGHTTP2_ERR_DEFERRED;
1189 H2BUGF(infof(data_s, "data_source_read_callback: "
1190 "returns %zu bytes stream %u",
1196 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1197 static int error_callback(nghttp2_session *session,
1210 static void populate_settings(struct Curl_easy *data,
1211 struct http_conn *httpc)
1213 nghttp2_settings_entry *iv = httpc->local_settings;
1215 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1216 iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
1218 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1219 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1221 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1222 iv[2].value = data->multi->push_cb != NULL;
1224 httpc->local_settings_num = 3;
1227 void Curl_http2_done(struct Curl_easy *data, bool premature)
1229 struct HTTP *http = data->req.p.http;
1230 struct http_conn *httpc = &data->conn->proto.httpc;
1232 /* there might be allocated resources done before this got the 'h2' pointer
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]);
1241 free(http->push_headers);
1242 http->push_headers = NULL;
1245 if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
1246 !httpc->h2) /* not HTTP/2 ? */
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;
1254 if(premature || (!http->closed && http->stream_id)) {
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);
1263 if(data->state.drain)
1264 drained_transfer(data, httpc);
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);
1271 infof(data, "http/2: failed to clear user_data for stream %u",
1275 set_transfer(httpc, NULL);
1276 http->stream_id = 0;
1280 static int client_new(struct connectdata *conn,
1281 nghttp2_session_callbacks *callbacks)
1283 #if NGHTTP2_VERSION_NUM < 0x013200
1285 return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1288 int rc = nghttp2_option_new(&o);
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,
1296 nghttp2_option_del(o);
1302 * Initialize nghttp2 for a Curl connection
1304 static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
1306 if(!conn->proto.httpc.h2) {
1308 nghttp2_session_callbacks *callbacks;
1310 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1311 if(!conn->proto.httpc.inbuf)
1312 return CURLE_OUT_OF_MEMORY;
1314 rc = nghttp2_session_callbacks_new(&callbacks);
1317 failf(data, "Couldn't initialize nghttp2 callbacks");
1318 return CURLE_OUT_OF_MEMORY; /* most likely at least */
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);
1338 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1340 /* The nghttp2 session is not yet setup, do it */
1341 rc = client_new(conn, callbacks);
1343 nghttp2_session_callbacks_del(callbacks);
1346 failf(data, "Couldn't initialize nghttp2");
1347 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1354 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1356 CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1357 struct Curl_easy *data)
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;
1368 populate_settings(data, httpc);
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);
1375 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1377 return CURLE_FAILED_INIT;
1379 conn->proto.httpc.binlen = binlen;
1381 result = Curl_base64url_encode((const char *)binsettings, binlen,
1388 result = Curl_dyn_addf(req,
1389 "Connection: Upgrade, HTTP2-Settings\r\n"
1391 "HTTP2-Settings: %s\r\n",
1392 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1395 k->upgr101 = UPGR101_H2;
1401 * Returns nonzero if current HTTP/2 session should be closed.
1403 static int should_close_session(struct http_conn *httpc)
1405 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1406 !nghttp2_session_want_write(httpc->h2);
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.
1415 static int h2_process_pending_input(struct Curl_easy *data,
1416 struct http_conn *httpc,
1423 nread = httpc->inbuflen - httpc->nread_inbuf;
1424 inbuf = httpc->inbuf + httpc->nread_inbuf;
1426 set_transfer(httpc, data); /* set the transfer */
1427 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1430 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1431 "%zd:%s", rv, nghttp2_strerror((int)rv));
1432 *err = CURLE_RECV_ERROR;
1438 "h2_process_pending_input: All data in connection buffer "
1440 httpc->inbuflen = 0;
1441 httpc->nread_inbuf = 0;
1444 httpc->nread_inbuf += rv;
1446 "h2_process_pending_input: %zu bytes left in connection "
1448 httpc->inbuflen - httpc->nread_inbuf));
1451 rv = h2_session_send(data, httpc->h2);
1453 *err = CURLE_SEND_ERROR;
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");
1465 if(should_close_session(httpc)) {
1466 struct HTTP *stream = data->req.p.http;
1468 "h2_process_pending_input: nothing to do in this session"));
1472 /* not an error per se, but should still close the connection */
1473 connclose(data->conn, "GOAWAY received");
1482 * Called from transfer.c:done_sending when we stop uploading.
1484 CURLcode Curl_http2_done_sending(struct Curl_easy *data,
1485 struct connectdata *conn)
1487 CURLcode result = CURLE_OK;
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;
1496 if(stream->upload_left) {
1497 /* If the stream still thinks there's data left to upload. */
1499 stream->upload_left = 0; /* DONE! */
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);
1507 /* If nghttp2 still has pending frames unsent */
1508 if(nghttp2_session_want_write(h2)) {
1509 struct SingleRequest *k = &data->req;
1512 H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
1514 /* and attempt to send the pending frames */
1515 rv = h2_session_send(data, h2);
1517 result = CURLE_SEND_ERROR;
1519 if(nghttp2_session_want_write(h2)) {
1520 /* re-set KEEP_SEND to make sure we are called again */
1521 k->keepon |= KEEP_SEND;
1528 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1529 struct Curl_easy *data,
1530 struct HTTP *stream, CURLcode *err)
1532 struct http_conn *httpc = &conn->proto.httpc;
1534 if(httpc->pause_stream_id == stream->stream_id) {
1535 httpc->pause_stream_id = 0;
1538 drained_transfer(data, httpc);
1540 if(httpc->pause_stream_id == 0) {
1541 if(h2_process_pending_input(data, httpc, err) != 0) {
1546 DEBUGASSERT(data->state.drain == 0);
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 */
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),
1562 *err = CURLE_HTTP2_STREAM;
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",
1570 *err = CURLE_HTTP2_STREAM;
1574 if(Curl_dyn_len(&stream->trailer_recvbuf)) {
1575 char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
1581 /* each trailer line ends with a newline */
1582 lf = strchr(trailp, '\n');
1585 len = lf + 1 - trailp;
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);
1598 stream->close_handled = TRUE;
1600 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
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
1610 static void h2_pri_spec(struct Curl_easy *data,
1611 nghttp2_priority_spec *pri_spec)
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;
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
1628 static int h2_session_send(struct Curl_easy *data,
1629 nghttp2_session *h2)
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;
1641 h2_pri_spec(data, &pri_spec);
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,
1652 return nghttp2_session_send(h2);
1655 static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
1656 char *mem, size_t len, CURLcode *err)
1659 struct connectdata *conn = data->conn;
1660 struct http_conn *httpc = &conn->proto.httpc;
1661 struct HTTP *stream = data->req.p.http;
1663 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1665 if(should_close_session(httpc)) {
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 */
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;
1683 * At this point 'stream' is just in the Curl_easy the connection
1684 * identifies as its owner at this time.
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 */
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;
1697 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
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,
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,
1713 if(mem != stream->mem) {
1714 /* if we didn't get the same buffer this time, we must move the data to
1716 memmove(mem, stream->mem, stream->memlen);
1717 stream->len = len - stream->memlen;
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) {
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);
1734 stream->pausedata += nread;
1735 stream->pauselen -= nread;
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;
1742 stream->pausedata = NULL;
1743 stream->pauselen = 0;
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) {
1756 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
1757 nread, stream->stream_id));
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
1770 /* closed overrides paused */
1772 H2BUGF(infof(data, "stream %u is paused, pause id: %u",
1773 stream->stream_id, httpc->pause_stream_id));
1778 /* remember where to store incoming data for this stream and how big the
1784 if(httpc->inbuflen == 0) {
1785 nread = ((Curl_recv *)httpc->recv_underlying)(
1786 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
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);
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",
1806 *err = CURLE_HTTP2_STREAM;
1810 H2BUGF(infof(data, "end of stream"));
1815 H2BUGF(infof(data, "nread=%zd", nread));
1817 httpc->inbuflen = nread;
1819 DEBUGASSERT(httpc->nread_inbuf == 0);
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",
1828 if(h2_process_pending_input(data, httpc, err))
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));
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));
1843 else if(!stream->closed) {
1844 drained_transfer(data, httpc);
1847 /* this stream is closed, trigger a another read ASAP to detect that */
1848 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1853 return http2_handle_stream_close(conn, data, stream, err);
1855 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
1856 stream->stream_id));
1860 static ssize_t http2_send(struct Curl_easy *data, int sockindex,
1861 const void *mem, size_t len, CURLcode *err)
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
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;
1874 nghttp2_data_provider data_prd;
1876 nghttp2_session *h2 = httpc->h2;
1877 nghttp2_priority_spec pri_spec;
1879 struct h2h3req *hreq;
1883 H2BUGF(infof(data, "http2_send len=%zu", len));
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;
1891 else if(stream->closed) {
1892 return http2_handle_stream_close(conn, data, stream, err);
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;
1903 rv = h2_session_send(data, h2);
1904 if(nghttp2_is_fatal(rv)) {
1905 *err = CURLE_SEND_ERROR;
1908 len -= stream->upload_len;
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;
1915 if(should_close_session(httpc)) {
1916 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
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);
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,
1939 infof(data, "http2_send returns %zu for stream %u", len,
1945 result = Curl_pseudo_headers(data, mem, len, &hreq);
1950 nheader = hreq->entries;
1952 nva = malloc(sizeof(nghttp2_nv) * nheader);
1954 Curl_pseudo_free(hreq);
1955 *err = CURLE_OUT_OF_MEMORY;
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;
1967 Curl_pseudo_free(hreq);
1970 h2_pri_spec(data, &pri_spec);
1972 H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
1973 nghttp2_session_check_request_allowed(h2), (void *)data));
1975 switch(data->state.httpreq) {
1977 case HTTPREQ_POST_FORM:
1978 case HTTPREQ_POST_MIME:
1980 if(data->state.infilesize != -1)
1981 stream->upload_left = data->state.infilesize;
1983 /* data sending without specifying the data amount up front */
1984 stream->upload_left = -1; /* unknown, but not zero */
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,
1992 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2000 "http2_send() nghttp2_submit_request error (%s)%u",
2001 nghttp2_strerror(stream_id), stream_id));
2002 *err = CURLE_SEND_ERROR;
2006 infof(data, "Using Stream ID: %u (easy handle %p)",
2007 stream_id, (void *)data);
2008 stream->stream_id = stream_id;
2010 rv = h2_session_send(data, h2);
2013 "http2_send() nghttp2_session_send error (%s)%d",
2014 nghttp2_strerror(rv), rv));
2016 *err = CURLE_SEND_ERROR;
2020 if(should_close_session(httpc)) {
2021 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
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);
2038 CURLcode Curl_http2_setup(struct Curl_easy *data,
2039 struct connectdata *conn)
2042 struct http_conn *httpc = &conn->proto.httpc;
2043 struct HTTP *stream = data->req.p.http;
2045 DEBUGASSERT(data->state.buffer);
2047 stream->stream_id = -1;
2049 Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
2050 Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
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;
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 */
2062 if((conn->handler == &Curl_handler_http2_ssl) ||
2063 (conn->handler == &Curl_handler_http2))
2064 return CURLE_OK; /* already done */
2066 if(conn->handler->flags & PROTOPT_SSL)
2067 conn->handler = &Curl_handler_http2_ssl;
2069 conn->handler = &Curl_handler_http2;
2071 result = http2_init(data, conn);
2073 Curl_dyn_free(&stream->header_recvbuf);
2077 infof(data, "Using HTTP2, server supports multiplexing");
2079 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2080 conn->httpversion = 20;
2081 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2083 httpc->inbuflen = 0;
2084 httpc->nread_inbuf = 0;
2086 httpc->pause_stream_id = 0;
2087 httpc->drain_total = 0;
2092 CURLcode Curl_http2_switched(struct Curl_easy *data,
2093 const char *mem, size_t nread)
2096 struct connectdata *conn = data->conn;
2097 struct http_conn *httpc = &conn->proto.httpc;
2099 struct HTTP *stream = data->req.p.http;
2101 result = Curl_http2_setup(data, conn);
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;
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);
2117 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
2118 nghttp2_strerror(rv), rv);
2122 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2126 infof(data, "http/2: failed to set user_data for stream %u",
2132 populate_settings(data, httpc);
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);
2140 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2141 nghttp2_strerror(rv), rv);
2146 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2147 HTTP2_HUGE_WINDOW_SIZE);
2149 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2150 nghttp2_strerror(rv), rv);
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",
2165 infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
2166 " after upgrade: len=%zu",
2170 memcpy(httpc->inbuf, mem, nread);
2172 httpc->inbuflen = nread;
2174 DEBUGASSERT(httpc->nread_inbuf == 0);
2176 if(-1 == h2_process_pending_input(data, httpc, &result))
2182 CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
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)
2190 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
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,
2200 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2201 nghttp2_strerror(rv), rv);
2205 /* make sure the window update gets sent */
2206 rv = h2_session_send(data, httpc->h2);
2208 return CURLE_SEND_ERROR;
2210 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2211 window, stream->stream_id));
2215 /* read out the stream local window again */
2217 nghttp2_session_get_stream_local_window_size(httpc->h2,
2219 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2220 window2, stream->stream_id));
2228 CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2229 struct Curl_easy *child,
2233 struct Curl_http2_dep **tail;
2234 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2236 return CURLE_OUT_OF_MEMORY;
2239 if(parent->set.stream_dependents && exclusive) {
2240 struct Curl_http2_dep *node = parent->set.stream_dependents;
2242 node->data->set.stream_depends_on = child;
2246 tail = &child->set.stream_dependents;
2248 tail = &(*tail)->next;
2250 DEBUGASSERT(!*tail);
2251 *tail = parent->set.stream_dependents;
2252 parent->set.stream_dependents = 0;
2255 tail = &parent->set.stream_dependents;
2257 (*tail)->data->set.stream_depends_e = FALSE;
2258 tail = &(*tail)->next;
2261 DEBUGASSERT(!*tail);
2265 child->set.stream_depends_on = parent;
2266 child->set.stream_depends_e = exclusive;
2270 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
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);
2276 while(data && data->data != child) {
2285 last->next = data->next;
2288 parent->set.stream_dependents = data->next;
2293 child->set.stream_depends_on = 0;
2294 child->set.stream_depends_e = FALSE;
2297 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
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);
2306 if(data->set.stream_depends_on)
2307 Curl_http2_remove_child(data->set.stream_depends_on, data);
2310 /* Only call this function for a transfer that already got a HTTP/2
2311 CURLE_HTTP2_STREAM error! */
2312 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2314 struct HTTP *stream = data->req.p.http;
2315 return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
2318 #else /* !USE_NGHTTP2 */
2320 /* Satisfy external references even if http2 is not compiled in. */
2321 #include <curl/curl.h>
2323 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2330 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2337 #endif /* USE_NGHTTP2 */