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 ***************************************************************************/
23 #include "curl_setup.h"
26 #include <nghttp2/nghttp2.h>
32 #include "curl_base64.h"
37 #include "strtoofft.h"
43 /* The last 3 #include files should be in this order */
44 #include "curl_printf.h"
45 #include "curl_memory.h"
48 #define H2_BUFSIZE 32768
50 #if (NGHTTP2_VERSION_NUM < 0x010c00)
51 #error too old nghttp2 version, upgrade!
54 #ifdef CURL_DISABLE_VERBOSE_STRINGS
55 #define nghttp2_session_callbacks_set_error_callback(x,y)
58 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
59 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
62 #define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
67 #define H2BUGF(x) do { } while(0)
70 static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
71 char *mem, size_t len, CURLcode *err);
72 static bool http2_connisdead(struct Curl_easy *data,
73 struct connectdata *conn);
74 static int h2_session_send(struct Curl_easy *data,
76 static int h2_process_pending_input(struct Curl_easy *data,
77 struct http_conn *httpc,
81 * Curl_http2_init_state() is called when the easy handle is created and
82 * allows for HTTP/2 specific init of state.
84 void Curl_http2_init_state(struct UrlState *state)
86 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
90 * Curl_http2_init_userset() is called when the easy handle is created and
91 * allows for HTTP/2 specific user-set fields.
93 void Curl_http2_init_userset(struct UserDefined *set)
95 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
98 static int http2_getsock(struct Curl_easy *data,
99 struct connectdata *conn,
102 const struct http_conn *c = &conn->proto.httpc;
103 struct SingleRequest *k = &data->req;
104 int bitmap = GETSOCK_BLANK;
105 struct HTTP *stream = data->req.p.http;
107 sock[0] = conn->sock[FIRSTSOCKET];
109 if(!(k->keepon & KEEP_RECV_PAUSE))
110 /* Unless paused - in a HTTP/2 connection we can basically always get a
111 frame so we should always be ready for one */
112 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
114 /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
115 there's a window to send data in */
116 if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
117 nghttp2_session_want_write(c->h2)) &&
118 (nghttp2_session_get_remote_window_size(c->h2) &&
119 nghttp2_session_get_stream_remote_window_size(c->h2,
121 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
127 * http2_stream_free() free HTTP2 stream related data
129 static void http2_stream_free(struct HTTP *http)
132 Curl_dyn_free(&http->header_recvbuf);
133 for(; http->push_headers_used > 0; --http->push_headers_used) {
134 free(http->push_headers[http->push_headers_used - 1]);
136 free(http->push_headers);
137 http->push_headers = NULL;
142 * Disconnects *a* connection used for HTTP/2. It might be an old one from the
143 * connection cache and not the "main" one. Don't touch the easy handle!
146 static CURLcode http2_disconnect(struct Curl_easy *data,
147 struct connectdata *conn,
148 bool dead_connection)
150 struct http_conn *c = &conn->proto.httpc;
151 (void)dead_connection;
156 H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
158 nghttp2_session_del(c->h2);
159 Curl_safefree(c->inbuf);
161 H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
167 * The server may send us data at any point (e.g. PING frames). Therefore,
168 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
170 * Instead, if it is readable, run Curl_connalive() to peek at the socket
171 * and distinguish between closed and data.
173 static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
181 sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
186 else if(sval & CURL_CSELECT_ERR) {
187 /* socket is in an error state */
190 else if(sval & CURL_CSELECT_IN) {
191 /* readable with no error. could still be closed */
192 dead = !Curl_connalive(conn);
194 /* This happens before we've sent off a request and the connection is
195 not in use by any other transfer, there shouldn't be any data here,
196 only "protocol frames" */
198 struct http_conn *httpc = &conn->proto.httpc;
200 if(httpc->recv_underlying)
201 /* if called "too early", this pointer isn't setup yet! */
202 nread = ((Curl_recv *)httpc->recv_underlying)(
203 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
206 "%d bytes stray data read before trying h2 connection",
208 httpc->nread_inbuf = 0;
209 httpc->inbuflen = nread;
210 if(h2_process_pending_input(data, httpc, &result) < 0)
211 /* immediate error, considered dead */
215 /* the read failed so let's say this is dead anyway */
224 * Set the transfer that is currently using this HTTP/2 connection.
226 static void set_transfer(struct http_conn *c,
227 struct Curl_easy *data)
233 * Get the transfer that is currently using this HTTP/2 connection.
235 static struct Curl_easy *get_transfer(struct http_conn *c)
237 DEBUGASSERT(c && c->trnsfr);
241 static unsigned int http2_conncheck(struct Curl_easy *data,
242 struct connectdata *conn,
243 unsigned int checks_to_perform)
245 unsigned int ret_val = CONNRESULT_NONE;
246 struct http_conn *c = &conn->proto.httpc;
248 bool send_frames = false;
250 if(checks_to_perform & CONNCHECK_ISDEAD) {
251 if(http2_connisdead(data, conn))
252 ret_val |= CONNRESULT_DEAD;
255 if(checks_to_perform & CONNCHECK_KEEPALIVE) {
256 struct curltime now = Curl_now();
257 timediff_t elapsed = Curl_timediff(now, conn->keepalive);
259 if(elapsed > data->set.upkeep_interval_ms) {
260 /* Perform an HTTP/2 PING */
261 rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
263 /* Successfully added a PING frame to the session. Need to flag this
264 so the frame is sent. */
268 failf(data, "nghttp2_submit_ping() failed: %s(%d)",
269 nghttp2_strerror(rc), rc);
272 conn->keepalive = now;
277 set_transfer(c, data); /* set the transfer */
278 rc = nghttp2_session_send(c->h2);
280 failf(data, "nghttp2_session_send() failed: %s(%d)",
281 nghttp2_strerror(rc), rc);
287 /* called from http_setup_conn */
288 void Curl_http2_setup_req(struct Curl_easy *data)
290 struct HTTP *http = data->req.p.http;
291 http->bodystarted = FALSE;
292 http->status_code = -1;
293 http->pausedata = NULL;
295 http->closed = FALSE;
296 http->close_handled = FALSE;
300 http->error = NGHTTP2_NO_ERROR;
303 /* called from http_setup_conn */
304 void Curl_http2_setup_conn(struct connectdata *conn)
306 conn->proto.httpc.settings.max_concurrent_streams =
307 DEFAULT_MAX_CONCURRENT_STREAMS;
311 * HTTP2 handler interface. This isn't added to the general list of protocols
312 * but will be used at run-time when the protocol is dynamically switched from
315 static const struct Curl_handler Curl_handler_http2 = {
317 ZERO_NULL, /* setup_connection */
318 Curl_http, /* do_it */
319 Curl_http_done, /* done */
320 ZERO_NULL, /* do_more */
321 ZERO_NULL, /* connect_it */
322 ZERO_NULL, /* connecting */
323 ZERO_NULL, /* doing */
324 http2_getsock, /* proto_getsock */
325 http2_getsock, /* doing_getsock */
326 ZERO_NULL, /* domore_getsock */
327 http2_getsock, /* perform_getsock */
328 http2_disconnect, /* disconnect */
329 ZERO_NULL, /* readwrite */
330 http2_conncheck, /* connection_check */
331 ZERO_NULL, /* attach connection */
332 PORT_HTTP, /* defport */
333 CURLPROTO_HTTP, /* protocol */
334 CURLPROTO_HTTP, /* family */
335 PROTOPT_STREAM /* flags */
338 static const struct Curl_handler Curl_handler_http2_ssl = {
339 "HTTPS", /* scheme */
340 ZERO_NULL, /* setup_connection */
341 Curl_http, /* do_it */
342 Curl_http_done, /* done */
343 ZERO_NULL, /* do_more */
344 ZERO_NULL, /* connect_it */
345 ZERO_NULL, /* connecting */
346 ZERO_NULL, /* doing */
347 http2_getsock, /* proto_getsock */
348 http2_getsock, /* doing_getsock */
349 ZERO_NULL, /* domore_getsock */
350 http2_getsock, /* perform_getsock */
351 http2_disconnect, /* disconnect */
352 ZERO_NULL, /* readwrite */
353 http2_conncheck, /* connection_check */
354 ZERO_NULL, /* attach connection */
355 PORT_HTTP, /* defport */
356 CURLPROTO_HTTPS, /* protocol */
357 CURLPROTO_HTTP, /* family */
358 PROTOPT_SSL | PROTOPT_STREAM /* flags */
362 * Store nghttp2 version info in this buffer.
364 void Curl_http2_ver(char *p, size_t len)
366 nghttp2_info *h2 = nghttp2_version(0);
367 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
371 * The implementation of nghttp2_send_callback type. Here we write |data| with
372 * size |length| to the network and return the number of bytes actually
373 * written. See the documentation of nghttp2_send_callback for the details.
375 static ssize_t send_callback(nghttp2_session *h2,
376 const uint8_t *mem, size_t length, int flags,
379 struct connectdata *conn = (struct connectdata *)userp;
380 struct http_conn *c = &conn->proto.httpc;
381 struct Curl_easy *data = get_transfer(c);
383 CURLcode result = CURLE_OK;
388 if(!c->send_underlying)
389 /* called before setup properly! */
390 return NGHTTP2_ERR_CALLBACK_FAILURE;
392 written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
393 mem, length, &result);
395 if(result == CURLE_AGAIN) {
396 return NGHTTP2_ERR_WOULDBLOCK;
400 failf(data, "Failed sending HTTP2 data");
401 return NGHTTP2_ERR_CALLBACK_FAILURE;
405 return NGHTTP2_ERR_WOULDBLOCK;
411 /* We pass a pointer to this struct in the push callback, but the contents of
412 the struct are hidden from the user. */
413 struct curl_pushheaders {
414 struct Curl_easy *data;
415 const nghttp2_push_promise *frame;
419 * push header access function. Only to be used from within the push callback
421 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
423 /* Verify that we got a good easy handle in the push header struct, mostly to
424 detect rubbish input fast(er). */
425 if(!h || !GOOD_EASY_HANDLE(h->data))
428 struct HTTP *stream = h->data->req.p.http;
429 if(num < stream->push_headers_used)
430 return stream->push_headers[num];
436 * push header access function. Only to be used from within the push callback
438 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
440 /* Verify that we got a good easy handle in the push header struct,
441 mostly to detect rubbish input fast(er). Also empty header name
442 is just a rubbish too. We have to allow ":" at the beginning of
443 the header, but header == ":" must be rejected. If we have ':' in
444 the middle of header, it could be matched in middle of the value,
445 this is because we do prefix match.*/
446 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
447 !strcmp(header, ":") || strchr(header + 1, ':'))
450 struct HTTP *stream = h->data->req.p.http;
451 size_t len = strlen(header);
453 for(i = 0; i<stream->push_headers_used; i++) {
454 if(!strncmp(header, stream->push_headers[i], len)) {
455 /* sub-match, make sure that it is followed by a colon */
456 if(stream->push_headers[i][len] != ':')
458 return &stream->push_headers[i][len + 1];
466 * This specific transfer on this connection has been "drained".
468 static void drained_transfer(struct Curl_easy *data,
469 struct http_conn *httpc)
471 DEBUGASSERT(httpc->drain_total >= data->state.drain);
472 httpc->drain_total -= data->state.drain;
473 data->state.drain = 0;
477 * Mark this transfer to get "drained".
479 static void drain_this(struct Curl_easy *data,
480 struct http_conn *httpc)
483 httpc->drain_total++;
484 DEBUGASSERT(httpc->drain_total >= data->state.drain);
487 static struct Curl_easy *duphandle(struct Curl_easy *data)
489 struct Curl_easy *second = curl_easy_duphandle(data);
491 /* setup the request struct */
492 struct HTTP *http = calloc(1, sizeof(struct HTTP));
494 (void)Curl_close(&second);
497 second->req.p.http = http;
498 Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
499 Curl_http2_setup_req(second);
500 second->state.stream_weight = data->state.stream_weight;
506 static int set_transfer_url(struct Curl_easy *data,
507 struct curl_pushheaders *hp)
513 CURLU *u = curl_url();
518 v = curl_pushheader_byname(hp, H2H3_PSEUDO_SCHEME);
520 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
527 v = curl_pushheader_byname(hp, H2H3_PSEUDO_AUTHORITY);
529 uc = curl_url_set(u, CURLUPART_HOST, v, 0);
536 v = curl_pushheader_byname(hp, H2H3_PSEUDO_PATH);
538 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
545 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
553 if(data->state.url_alloc)
554 free(data->state.url);
555 data->state.url_alloc = TRUE;
556 data->state.url = url;
560 static int push_promise(struct Curl_easy *data,
561 struct connectdata *conn,
562 const nghttp2_push_promise *frame)
564 int rv; /* one of the CURL_PUSH_* defines */
565 H2BUGF(infof(data, "PUSH_PROMISE received, stream %u",
566 frame->promised_stream_id));
567 if(data->multi->push_cb) {
569 struct HTTP *newstream;
570 struct curl_pushheaders heads;
572 struct http_conn *httpc;
574 /* clone the parent */
575 struct Curl_easy *newhandle = duphandle(data);
577 infof(data, "failed to duplicate handle");
578 rv = CURL_PUSH_DENY; /* FAIL HARD */
584 /* ask the application */
585 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
587 stream = data->req.p.http;
589 failf(data, "Internal NULL stream");
590 (void)Curl_close(&newhandle);
595 rv = set_transfer_url(newhandle, &heads);
597 (void)Curl_close(&newhandle);
602 Curl_set_in_callback(data, true);
603 rv = data->multi->push_cb(data, newhandle,
604 stream->push_headers_used, &heads,
605 data->multi->push_userp);
606 Curl_set_in_callback(data, false);
608 /* free the headers again */
609 for(i = 0; i<stream->push_headers_used; i++)
610 free(stream->push_headers[i]);
611 free(stream->push_headers);
612 stream->push_headers = NULL;
613 stream->push_headers_used = 0;
616 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
617 /* denied, kill off the new handle again */
618 http2_stream_free(newhandle->req.p.http);
619 newhandle->req.p.http = NULL;
620 (void)Curl_close(&newhandle);
624 newstream = newhandle->req.p.http;
625 newstream->stream_id = frame->promised_stream_id;
626 newhandle->req.maxdownload = -1;
627 newhandle->req.size = -1;
629 /* approved, add to the multi handle and immediately switch to PERFORM
630 state with the given connection !*/
631 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
633 infof(data, "failed to add handle to multi");
634 http2_stream_free(newhandle->req.p.http);
635 newhandle->req.p.http = NULL;
636 Curl_close(&newhandle);
641 httpc = &conn->proto.httpc;
642 rv = nghttp2_session_set_stream_user_data(httpc->h2,
643 frame->promised_stream_id,
646 infof(data, "failed to set user_data for stream %d",
647 frame->promised_stream_id);
652 Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
653 Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
656 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
664 * multi_connchanged() is called to tell that there is a connection in
665 * this multi handle that has changed state (multiplexing become possible, the
666 * number of allowed streams changed or similar), and a subsequent use of this
667 * multi handle should move CONNECT_PEND handles back to CONNECT to have them
670 static void multi_connchanged(struct Curl_multi *multi)
672 multi->recheckstate = TRUE;
675 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
678 struct connectdata *conn = (struct connectdata *)userp;
679 struct http_conn *httpc = &conn->proto.httpc;
680 struct Curl_easy *data_s = NULL;
681 struct HTTP *stream = NULL;
682 struct Curl_easy *data = get_transfer(httpc);
685 int32_t stream_id = frame->hd.stream_id;
689 /* stream ID zero is for connection-oriented stuff */
690 if(frame->hd.type == NGHTTP2_SETTINGS) {
691 uint32_t max_conn = httpc->settings.max_concurrent_streams;
692 H2BUGF(infof(data, "Got SETTINGS"));
693 httpc->settings.max_concurrent_streams =
694 nghttp2_session_get_remote_settings(
695 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
696 httpc->settings.enable_push =
697 nghttp2_session_get_remote_settings(
698 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
699 H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
700 httpc->settings.max_concurrent_streams));
701 H2BUGF(infof(data, "ENABLE_PUSH == %s",
702 httpc->settings.enable_push?"TRUE":"false"));
703 if(max_conn != httpc->settings.max_concurrent_streams) {
704 /* only signal change if the value actually changed */
706 "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
707 httpc->settings.max_concurrent_streams);
708 multi_connchanged(data->multi);
713 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
716 "No Curl_easy associated with stream: %x",
721 stream = data_s->req.p.http;
723 H2BUGF(infof(data_s, "No proto pointer for stream: %x",
725 return NGHTTP2_ERR_CALLBACK_FAILURE;
728 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x",
729 frame->hd.type, stream_id));
731 switch(frame->hd.type) {
733 /* If body started on this stream, then receiving DATA is illegal. */
734 if(!stream->bodystarted) {
735 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
736 stream_id, NGHTTP2_PROTOCOL_ERROR);
738 if(nghttp2_is_fatal(rv)) {
739 return NGHTTP2_ERR_CALLBACK_FAILURE;
743 case NGHTTP2_HEADERS:
744 if(stream->bodystarted) {
745 /* Only valid HEADERS after body started is trailer HEADERS. We
746 buffer them in on_header callback. */
750 /* nghttp2 guarantees that :status is received, and we store it to
751 stream->status_code. Fuzzing has proven this can still be reached
752 without status code having been set. */
753 if(stream->status_code == -1)
754 return NGHTTP2_ERR_CALLBACK_FAILURE;
756 /* Only final status code signals the end of header */
757 if(stream->status_code / 100 != 1) {
758 stream->bodystarted = TRUE;
759 stream->status_code = -1;
762 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
764 return NGHTTP2_ERR_CALLBACK_FAILURE;
766 left = Curl_dyn_len(&stream->header_recvbuf) -
767 stream->nread_header_recvbuf;
768 ncopy = CURLMIN(stream->len, left);
770 memcpy(&stream->mem[stream->memlen],
771 Curl_dyn_ptr(&stream->header_recvbuf) +
772 stream->nread_header_recvbuf,
774 stream->nread_header_recvbuf += ncopy;
776 DEBUGASSERT(stream->mem);
777 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
778 ncopy, stream_id, stream->mem));
780 stream->len -= ncopy;
781 stream->memlen += ncopy;
783 drain_this(data_s, httpc);
784 /* if we receive data for another handle, wake that up */
785 if(get_transfer(httpc) != data_s)
786 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
788 case NGHTTP2_PUSH_PROMISE:
789 rv = push_promise(data_s, conn, &frame->push_promise);
792 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
793 h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
794 frame->push_promise.promised_stream_id,
796 if(nghttp2_is_fatal(h2))
797 return NGHTTP2_ERR_CALLBACK_FAILURE;
798 else if(rv == CURL_PUSH_ERROROUT) {
799 DEBUGF(infof(data_s, "Fail the parent stream (too)"));
800 return NGHTTP2_ERR_CALLBACK_FAILURE;
805 H2BUGF(infof(data_s, "Got frame type %x for stream %u",
806 frame->hd.type, stream_id));
812 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
814 const uint8_t *mem, size_t len, void *userp)
817 struct Curl_easy *data_s;
819 struct connectdata *conn = (struct connectdata *)userp;
820 struct http_conn *httpc = &conn->proto.httpc;
824 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
826 /* get the stream from the hash based on Stream ID */
827 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
829 /* Receiving a Stream ID not in the hash should not happen - unless
830 we have aborted a transfer artificially and there were more data
831 in the pipeline. Silently ignore. */
832 H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n",
837 stream = data_s->req.p.http;
839 return NGHTTP2_ERR_CALLBACK_FAILURE;
841 nread = CURLMIN(stream->len, len);
842 memcpy(&stream->mem[stream->memlen], mem, nread);
844 stream->len -= nread;
845 stream->memlen += nread;
847 drain_this(data_s, &conn->proto.httpc);
849 /* if we receive data for another handle, wake that up */
850 if(get_transfer(httpc) != data_s)
851 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
853 H2BUGF(infof(data_s, "%zu data received for stream %u "
854 "(%zu left in buffer %p, total %zu)",
856 stream->len, stream->mem,
860 stream->pausedata = mem + nread;
861 stream->pauselen = len - nread;
862 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
864 len - nread, stream_id));
865 data_s->conn->proto.httpc.pause_stream_id = stream_id;
867 return NGHTTP2_ERR_PAUSE;
870 /* pause execution of nghttp2 if we received data for another handle
871 in order to process them first. */
872 if(get_transfer(httpc) != data_s) {
873 data_s->conn->proto.httpc.pause_stream_id = stream_id;
875 return NGHTTP2_ERR_PAUSE;
881 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
882 uint32_t error_code, void *userp)
884 struct Curl_easy *data_s;
886 struct connectdata *conn = (struct connectdata *)userp;
892 struct http_conn *httpc;
893 /* get the stream from the hash based on Stream ID, stream ID zero is for
894 connection-oriented stuff */
895 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
897 /* We could get stream ID not in the hash. For example, if we
898 decided to reject stream (e.g., PUSH_PROMISE). */
901 H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
902 nghttp2_http2_strerror(error_code), error_code, stream_id));
903 stream = data_s->req.p.http;
905 return NGHTTP2_ERR_CALLBACK_FAILURE;
907 stream->closed = TRUE;
908 httpc = &conn->proto.httpc;
909 drain_this(data_s, httpc);
910 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
911 stream->error = error_code;
913 /* remove the entry from the hash as the stream is now gone */
914 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
916 infof(data_s, "http/2: failed to clear user_data for stream %d",
920 if(stream_id == httpc->pause_stream_id) {
921 H2BUGF(infof(data_s, "Stopped the pause stream"));
922 httpc->pause_stream_id = 0;
924 H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
925 stream->stream_id = 0; /* cleared */
930 static int on_begin_headers(nghttp2_session *session,
931 const nghttp2_frame *frame, void *userp)
934 struct Curl_easy *data_s = NULL;
937 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
942 H2BUGF(infof(data_s, "on_begin_headers() was called"));
944 if(frame->hd.type != NGHTTP2_HEADERS) {
948 stream = data_s->req.p.http;
949 if(!stream || !stream->bodystarted) {
956 /* Decode HTTP status code. Returns -1 if no valid status code was
958 static int decode_status_code(const uint8_t *value, size_t len)
969 for(i = 0; i < 3; ++i) {
972 if(c < '0' || c > '9') {
983 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
984 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
985 const uint8_t *name, size_t namelen,
986 const uint8_t *value, size_t valuelen,
991 struct Curl_easy *data_s;
992 int32_t stream_id = frame->hd.stream_id;
993 struct connectdata *conn = (struct connectdata *)userp;
994 struct http_conn *httpc = &conn->proto.httpc;
998 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1000 /* get the stream from the hash based on Stream ID */
1001 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1003 /* Receiving a Stream ID not in the hash should not happen, this is an
1004 internal error more than anything else! */
1005 return NGHTTP2_ERR_CALLBACK_FAILURE;
1007 stream = data_s->req.p.http;
1009 failf(data_s, "Internal NULL stream");
1010 return NGHTTP2_ERR_CALLBACK_FAILURE;
1013 /* Store received PUSH_PROMISE headers to be used when the subsequent
1014 PUSH_PROMISE callback comes */
1015 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
1018 if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
1019 /* pseudo headers are lower case */
1021 char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
1024 return NGHTTP2_ERR_CALLBACK_FAILURE;
1025 if(!Curl_strcasecompare(check, (const char *)value) &&
1026 ((conn->remote_port != conn->given->defport) ||
1027 !Curl_strcasecompare(conn->host.name, (const char *)value))) {
1028 /* This is push is not for the same authority that was asked for in
1029 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
1030 * PUSH_PROMISE for which the server is not authoritative as a stream
1031 * error of type PROTOCOL_ERROR."
1033 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1034 stream_id, NGHTTP2_PROTOCOL_ERROR);
1035 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
1042 if(!stream->push_headers) {
1043 stream->push_headers_alloc = 10;
1044 stream->push_headers = malloc(stream->push_headers_alloc *
1046 if(!stream->push_headers)
1047 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1048 stream->push_headers_used = 0;
1050 else if(stream->push_headers_used ==
1051 stream->push_headers_alloc) {
1053 stream->push_headers_alloc *= 2;
1054 headp = Curl_saferealloc(stream->push_headers,
1055 stream->push_headers_alloc * sizeof(char *));
1057 stream->push_headers = NULL;
1058 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1060 stream->push_headers = headp;
1062 h = aprintf("%s:%s", name, value);
1064 stream->push_headers[stream->push_headers_used++] = h;
1068 if(stream->bodystarted) {
1069 /* This is a trailer */
1070 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
1072 result = Curl_dyn_addf(&stream->trailer_recvbuf,
1073 "%.*s: %.*s\r\n", namelen, name,
1076 return NGHTTP2_ERR_CALLBACK_FAILURE;
1081 if(namelen == sizeof(H2H3_PSEUDO_STATUS) - 1 &&
1082 memcmp(H2H3_PSEUDO_STATUS, name, namelen) == 0) {
1083 /* nghttp2 guarantees :status is received first and only once, and
1084 value is 3 digits status code, and decode_status_code always
1087 stream->status_code = decode_status_code(value, valuelen);
1088 DEBUGASSERT(stream->status_code != -1);
1089 msnprintf(buffer, sizeof(buffer), H2H3_PSEUDO_STATUS ":%u\r",
1090 stream->status_code);
1091 result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
1093 return NGHTTP2_ERR_CALLBACK_FAILURE;
1094 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("HTTP/2 "));
1096 return NGHTTP2_ERR_CALLBACK_FAILURE;
1097 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1099 return NGHTTP2_ERR_CALLBACK_FAILURE;
1100 /* the space character after the status code is mandatory */
1101 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(" \r\n"));
1103 return NGHTTP2_ERR_CALLBACK_FAILURE;
1104 /* if we receive data for another handle, wake that up */
1105 if(get_transfer(httpc) != data_s)
1106 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1108 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
1109 stream->status_code, data_s));
1113 /* nghttp2 guarantees that namelen > 0, and :status was already
1114 received, and this is not pseudo-header field . */
1115 /* convert to a HTTP1-style header */
1116 result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen);
1118 return NGHTTP2_ERR_CALLBACK_FAILURE;
1119 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST(": "));
1121 return NGHTTP2_ERR_CALLBACK_FAILURE;
1122 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1124 return NGHTTP2_ERR_CALLBACK_FAILURE;
1125 result = Curl_dyn_addn(&stream->header_recvbuf, STRCONST("\r\n"));
1127 return NGHTTP2_ERR_CALLBACK_FAILURE;
1128 /* if we receive data for another handle, wake that up */
1129 if(get_transfer(httpc) != data_s)
1130 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1132 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
1135 return 0; /* 0 is successful */
1138 static ssize_t data_source_read_callback(nghttp2_session *session,
1140 uint8_t *buf, size_t length,
1141 uint32_t *data_flags,
1142 nghttp2_data_source *source,
1145 struct Curl_easy *data_s;
1146 struct HTTP *stream = NULL;
1152 /* get the stream from the hash based on Stream ID, stream ID zero is for
1153 connection-oriented stuff */
1154 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1156 /* Receiving a Stream ID not in the hash should not happen, this is an
1157 internal error more than anything else! */
1158 return NGHTTP2_ERR_CALLBACK_FAILURE;
1160 stream = data_s->req.p.http;
1162 return NGHTTP2_ERR_CALLBACK_FAILURE;
1165 return NGHTTP2_ERR_INVALID_ARGUMENT;
1167 nread = CURLMIN(stream->upload_len, length);
1169 memcpy(buf, stream->upload_mem, nread);
1170 stream->upload_mem += nread;
1171 stream->upload_len -= nread;
1172 if(data_s->state.infilesize != -1)
1173 stream->upload_left -= nread;
1176 if(stream->upload_left == 0)
1177 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1179 return NGHTTP2_ERR_DEFERRED;
1181 H2BUGF(infof(data_s, "data_source_read_callback: "
1182 "returns %zu bytes stream %u",
1188 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1189 static int error_callback(nghttp2_session *session,
1202 static void populate_settings(struct Curl_easy *data,
1203 struct http_conn *httpc)
1205 nghttp2_settings_entry *iv = httpc->local_settings;
1207 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1208 iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
1210 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1211 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1213 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1214 iv[2].value = data->multi->push_cb != NULL;
1216 httpc->local_settings_num = 3;
1219 void Curl_http2_done(struct Curl_easy *data, bool premature)
1221 struct HTTP *http = data->req.p.http;
1222 struct http_conn *httpc = &data->conn->proto.httpc;
1224 /* there might be allocated resources done before this got the 'h2' pointer
1226 Curl_dyn_free(&http->header_recvbuf);
1227 Curl_dyn_free(&http->trailer_recvbuf);
1228 if(http->push_headers) {
1229 /* if they weren't used and then freed before */
1230 for(; http->push_headers_used > 0; --http->push_headers_used) {
1231 free(http->push_headers[http->push_headers_used - 1]);
1233 free(http->push_headers);
1234 http->push_headers = NULL;
1237 if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
1238 !httpc->h2) /* not HTTP/2 ? */
1241 /* do this before the reset handling, as that might clear ->stream_id */
1242 if(http->stream_id == httpc->pause_stream_id) {
1243 H2BUGF(infof(data, "DONE the pause stream (%x)", http->stream_id));
1244 httpc->pause_stream_id = 0;
1246 if(premature || (!http->closed && http->stream_id)) {
1248 set_transfer(httpc, data); /* set the transfer */
1249 H2BUGF(infof(data, "RST stream %x", http->stream_id));
1250 if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
1251 http->stream_id, NGHTTP2_STREAM_CLOSED))
1252 (void)nghttp2_session_send(httpc->h2);
1255 if(data->state.drain)
1256 drained_transfer(data, httpc);
1258 /* -1 means unassigned and 0 means cleared */
1259 if(http->stream_id > 0) {
1260 int rv = nghttp2_session_set_stream_user_data(httpc->h2,
1261 http->stream_id, 0);
1263 infof(data, "http/2: failed to clear user_data for stream %d",
1267 set_transfer(httpc, NULL);
1268 http->stream_id = 0;
1273 * Initialize nghttp2 for a Curl connection
1275 static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
1277 if(!conn->proto.httpc.h2) {
1279 nghttp2_session_callbacks *callbacks;
1281 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1282 if(!conn->proto.httpc.inbuf)
1283 return CURLE_OUT_OF_MEMORY;
1285 rc = nghttp2_session_callbacks_new(&callbacks);
1288 failf(data, "Couldn't initialize nghttp2 callbacks");
1289 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1292 /* nghttp2_send_callback */
1293 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1294 /* nghttp2_on_frame_recv_callback */
1295 nghttp2_session_callbacks_set_on_frame_recv_callback
1296 (callbacks, on_frame_recv);
1297 /* nghttp2_on_data_chunk_recv_callback */
1298 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1299 (callbacks, on_data_chunk_recv);
1300 /* nghttp2_on_stream_close_callback */
1301 nghttp2_session_callbacks_set_on_stream_close_callback
1302 (callbacks, on_stream_close);
1303 /* nghttp2_on_begin_headers_callback */
1304 nghttp2_session_callbacks_set_on_begin_headers_callback
1305 (callbacks, on_begin_headers);
1306 /* nghttp2_on_header_callback */
1307 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1309 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1311 /* The nghttp2 session is not yet setup, do it */
1312 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1314 nghttp2_session_callbacks_del(callbacks);
1317 failf(data, "Couldn't initialize nghttp2");
1318 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1325 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1327 CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1328 struct Curl_easy *data)
1334 struct connectdata *conn = data->conn;
1335 struct SingleRequest *k = &data->req;
1336 uint8_t *binsettings = conn->proto.httpc.binsettings;
1337 struct http_conn *httpc = &conn->proto.httpc;
1339 populate_settings(data, httpc);
1341 /* this returns number of bytes it wrote */
1342 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1343 httpc->local_settings,
1344 httpc->local_settings_num);
1346 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1348 return CURLE_FAILED_INIT;
1350 conn->proto.httpc.binlen = binlen;
1352 result = Curl_base64url_encode((const char *)binsettings, binlen,
1359 result = Curl_dyn_addf(req,
1360 "Connection: Upgrade, HTTP2-Settings\r\n"
1362 "HTTP2-Settings: %s\r\n",
1363 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1366 k->upgr101 = UPGR101_REQUESTED;
1372 * Returns nonzero if current HTTP/2 session should be closed.
1374 static int should_close_session(struct http_conn *httpc)
1376 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1377 !nghttp2_session_want_write(httpc->h2);
1381 * h2_process_pending_input() processes pending input left in
1382 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1383 * This function returns 0 if it succeeds, or -1 and error code will
1384 * be assigned to *err.
1386 static int h2_process_pending_input(struct Curl_easy *data,
1387 struct http_conn *httpc,
1394 nread = httpc->inbuflen - httpc->nread_inbuf;
1395 inbuf = httpc->inbuf + httpc->nread_inbuf;
1397 set_transfer(httpc, data); /* set the transfer */
1398 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1401 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1402 "%zd:%s", rv, nghttp2_strerror((int)rv));
1403 *err = CURLE_RECV_ERROR;
1409 "h2_process_pending_input: All data in connection buffer "
1411 httpc->inbuflen = 0;
1412 httpc->nread_inbuf = 0;
1415 httpc->nread_inbuf += rv;
1417 "h2_process_pending_input: %zu bytes left in connection "
1419 httpc->inbuflen - httpc->nread_inbuf));
1422 rv = h2_session_send(data, httpc->h2);
1424 *err = CURLE_SEND_ERROR;
1428 if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
1429 /* No more requests are allowed in the current session, so
1430 the connection may not be reused. This is set when a
1431 GOAWAY frame has been received or when the limit of stream
1432 identifiers has been reached. */
1433 connclose(data->conn, "http/2: No new requests allowed");
1436 if(should_close_session(httpc)) {
1437 struct HTTP *stream = data->req.p.http;
1439 "h2_process_pending_input: nothing to do in this session"));
1443 /* not an error per se, but should still close the connection */
1444 connclose(data->conn, "GOAWAY received");
1453 * Called from transfer.c:done_sending when we stop uploading.
1455 CURLcode Curl_http2_done_sending(struct Curl_easy *data,
1456 struct connectdata *conn)
1458 CURLcode result = CURLE_OK;
1460 if((conn->handler == &Curl_handler_http2_ssl) ||
1461 (conn->handler == &Curl_handler_http2)) {
1462 /* make sure this is only attempted for HTTP/2 transfers */
1463 struct HTTP *stream = data->req.p.http;
1464 struct http_conn *httpc = &conn->proto.httpc;
1465 nghttp2_session *h2 = httpc->h2;
1467 if(stream->upload_left) {
1468 /* If the stream still thinks there's data left to upload. */
1470 stream->upload_left = 0; /* DONE! */
1472 /* resume sending here to trigger the callback to get called again so
1473 that it can signal EOF to nghttp2 */
1474 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1475 (void)h2_process_pending_input(data, httpc, &result);
1478 /* If nghttp2 still has pending frames unsent */
1479 if(nghttp2_session_want_write(h2)) {
1480 struct SingleRequest *k = &data->req;
1483 H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
1485 /* and attempt to send the pending frames */
1486 rv = h2_session_send(data, h2);
1488 result = CURLE_SEND_ERROR;
1490 if(nghttp2_session_want_write(h2)) {
1491 /* re-set KEEP_SEND to make sure we are called again */
1492 k->keepon |= KEEP_SEND;
1499 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1500 struct Curl_easy *data,
1501 struct HTTP *stream, CURLcode *err)
1503 struct http_conn *httpc = &conn->proto.httpc;
1505 if(httpc->pause_stream_id == stream->stream_id) {
1506 httpc->pause_stream_id = 0;
1509 drained_transfer(data, httpc);
1511 if(httpc->pause_stream_id == 0) {
1512 if(h2_process_pending_input(data, httpc, err) != 0) {
1517 DEBUGASSERT(data->state.drain == 0);
1519 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1520 stream->closed = FALSE;
1521 if(stream->error == NGHTTP2_REFUSED_STREAM) {
1522 H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection",
1523 stream->stream_id));
1524 connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1525 data->state.refused_stream = TRUE;
1526 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1529 else if(stream->error != NGHTTP2_NO_ERROR) {
1530 failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
1531 stream->stream_id, nghttp2_http2_strerror(stream->error),
1533 *err = CURLE_HTTP2_STREAM;
1537 if(!stream->bodystarted) {
1538 failf(data, "HTTP/2 stream %d was closed cleanly, but before getting "
1539 " all response header fields, treated as error",
1541 *err = CURLE_HTTP2_STREAM;
1545 if(Curl_dyn_len(&stream->trailer_recvbuf)) {
1546 char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
1552 /* each trailer line ends with a newline */
1553 lf = strchr(trailp, '\n');
1556 len = lf + 1 - trailp;
1558 Curl_debug(data, CURLINFO_HEADER_IN, trailp, len);
1559 /* pass the trailers one by one to the callback */
1560 result = Curl_client_write(data, CLIENTWRITE_HEADER, trailp, len);
1569 stream->close_handled = TRUE;
1571 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
1576 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1577 * and dependency to the peer. It also stores the updated values in the state
1581 static void h2_pri_spec(struct Curl_easy *data,
1582 nghttp2_priority_spec *pri_spec)
1584 struct HTTP *depstream = (data->set.stream_depends_on?
1585 data->set.stream_depends_on->req.p.http:NULL);
1586 int32_t depstream_id = depstream? depstream->stream_id:0;
1587 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1588 data->set.stream_depends_e);
1589 data->state.stream_weight = data->set.stream_weight;
1590 data->state.stream_depends_e = data->set.stream_depends_e;
1591 data->state.stream_depends_on = data->set.stream_depends_on;
1595 * h2_session_send() checks if there's been an update in the priority /
1596 * dependency settings and if so it submits a PRIORITY frame with the updated
1599 static int h2_session_send(struct Curl_easy *data,
1600 nghttp2_session *h2)
1602 struct HTTP *stream = data->req.p.http;
1603 struct http_conn *httpc = &data->conn->proto.httpc;
1604 set_transfer(httpc, data);
1605 if((data->set.stream_weight != data->state.stream_weight) ||
1606 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1607 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1608 /* send new weight and/or dependency */
1609 nghttp2_priority_spec pri_spec;
1612 h2_pri_spec(data, &pri_spec);
1614 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
1615 stream->stream_id, data));
1616 DEBUGASSERT(stream->stream_id != -1);
1617 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1623 return nghttp2_session_send(h2);
1626 static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
1627 char *mem, size_t len, CURLcode *err)
1630 struct connectdata *conn = data->conn;
1631 struct http_conn *httpc = &conn->proto.httpc;
1632 struct HTTP *stream = data->req.p.http;
1634 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1636 if(should_close_session(httpc)) {
1638 "http2_recv: nothing to do in this session"));
1639 if(conn->bits.close) {
1640 /* already marked for closure, return OK and we're done */
1648 /* Nullify here because we call nghttp2_session_send() and they
1649 might refer to the old buffer. */
1650 stream->upload_mem = NULL;
1651 stream->upload_len = 0;
1654 * At this point 'stream' is just in the Curl_easy the connection
1655 * identifies as its owner at this time.
1658 if(stream->bodystarted &&
1659 stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) {
1660 /* If there is header data pending for this stream to return, do that */
1662 Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
1663 size_t ncopy = CURLMIN(len, left);
1664 memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
1665 stream->nread_header_recvbuf, ncopy);
1666 stream->nread_header_recvbuf += ncopy;
1668 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
1673 H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
1674 data, stream->stream_id,
1675 nghttp2_session_get_local_window_size(httpc->h2),
1676 nghttp2_session_get_stream_local_window_size(httpc->h2,
1680 if((data->state.drain) && stream->memlen) {
1681 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
1682 stream->memlen, stream->stream_id,
1684 if(mem != stream->mem) {
1685 /* if we didn't get the same buffer this time, we must move the data to
1687 memmove(mem, stream->mem, stream->memlen);
1688 stream->len = len - stream->memlen;
1691 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1692 /* We have paused nghttp2, but we have no pause data (see
1693 on_data_chunk_recv). */
1694 httpc->pause_stream_id = 0;
1695 if(h2_process_pending_input(data, httpc, err) != 0) {
1700 else if(stream->pausedata) {
1701 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1702 nread = CURLMIN(len, stream->pauselen);
1703 memcpy(mem, stream->pausedata, nread);
1705 stream->pausedata += nread;
1706 stream->pauselen -= nread;
1708 if(stream->pauselen == 0) {
1709 H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
1710 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1711 httpc->pause_stream_id = 0;
1713 stream->pausedata = NULL;
1714 stream->pauselen = 0;
1716 /* When NGHTTP2_ERR_PAUSE is returned from
1717 data_source_read_callback, we might not process DATA frame
1718 fully. Calling nghttp2_session_mem_recv() again will
1719 continue to process DATA frame, but if there is no incoming
1720 frames, then we have to call it again with 0-length data.
1721 Without this, on_stream_close callback will not be called,
1722 and stream could be hanged. */
1723 if(h2_process_pending_input(data, httpc, err) != 0) {
1727 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
1728 nread, stream->stream_id));
1731 else if(httpc->pause_stream_id) {
1732 /* If a stream paused nghttp2_session_mem_recv previously, and has
1733 not processed all data, it still refers to the buffer in
1734 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1735 overwrite that buffer. To avoid that situation, just return
1736 here with CURLE_AGAIN. This could be busy loop since data in
1737 socket is not read. But it seems that usually streams are
1738 notified with its drain property, and socket is read again
1741 /* closed overrides paused */
1743 H2BUGF(infof(data, "stream %x is paused, pause id: %x",
1744 stream->stream_id, httpc->pause_stream_id));
1749 /* remember where to store incoming data for this stream and how big the
1755 if(httpc->inbuflen == 0) {
1756 nread = ((Curl_recv *)httpc->recv_underlying)(
1757 data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
1760 if(*err != CURLE_AGAIN)
1761 failf(data, "Failed receiving HTTP2 data");
1762 else if(stream->closed)
1763 /* received when the stream was already closed! */
1764 return http2_handle_stream_close(conn, data, stream, err);
1770 if(!stream->closed) {
1771 /* This will happen when the server or proxy server is SIGKILLed
1772 during data transfer. We should emit an error since our data
1773 received may be incomplete. */
1774 failf(data, "HTTP/2 stream %d was not closed cleanly before"
1775 " end of the underlying stream",
1777 *err = CURLE_HTTP2_STREAM;
1781 H2BUGF(infof(data, "end of stream"));
1786 H2BUGF(infof(data, "nread=%zd", nread));
1788 httpc->inbuflen = nread;
1790 DEBUGASSERT(httpc->nread_inbuf == 0);
1793 nread = httpc->inbuflen - httpc->nread_inbuf;
1794 (void)nread; /* silence warning, used in debug */
1795 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
1799 if(h2_process_pending_input(data, httpc, err))
1802 if(stream->memlen) {
1803 ssize_t retlen = stream->memlen;
1804 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
1805 retlen, stream->stream_id));
1808 if(httpc->pause_stream_id == stream->stream_id) {
1809 /* data for this stream is returned now, but this stream caused a pause
1810 already so we need it called again asap */
1811 H2BUGF(infof(data, "Data returned for PAUSED stream %u",
1812 stream->stream_id));
1814 else if(!stream->closed) {
1815 drained_transfer(data, httpc);
1818 /* this stream is closed, trigger a another read ASAP to detect that */
1819 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1824 return http2_handle_stream_close(conn, data, stream, err);
1826 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
1827 stream->stream_id));
1831 static ssize_t http2_send(struct Curl_easy *data, int sockindex,
1832 const void *mem, size_t len, CURLcode *err)
1835 * Currently, we send request in this function, but this function is also
1836 * used to send request body. It would be nice to add dedicated function for
1840 struct connectdata *conn = data->conn;
1841 struct http_conn *httpc = &conn->proto.httpc;
1842 struct HTTP *stream = data->req.p.http;
1843 nghttp2_nv *nva = NULL;
1845 nghttp2_data_provider data_prd;
1847 nghttp2_session *h2 = httpc->h2;
1848 nghttp2_priority_spec pri_spec;
1850 struct h2h3req *hreq;
1854 H2BUGF(infof(data, "http2_send len=%zu", len));
1856 if(stream->stream_id != -1) {
1857 if(stream->close_handled) {
1858 infof(data, "stream %d closed", stream->stream_id);
1859 *err = CURLE_HTTP2_STREAM;
1862 else if(stream->closed) {
1863 return http2_handle_stream_close(conn, data, stream, err);
1865 /* If stream_id != -1, we have dispatched request HEADERS, and now
1866 are going to send or sending request body in DATA frame */
1867 stream->upload_mem = mem;
1868 stream->upload_len = len;
1869 rv = nghttp2_session_resume_data(h2, stream->stream_id);
1870 if(nghttp2_is_fatal(rv)) {
1871 *err = CURLE_SEND_ERROR;
1874 rv = h2_session_send(data, h2);
1875 if(nghttp2_is_fatal(rv)) {
1876 *err = CURLE_SEND_ERROR;
1879 len -= stream->upload_len;
1881 /* Nullify here because we call nghttp2_session_send() and they
1882 might refer to the old buffer. */
1883 stream->upload_mem = NULL;
1884 stream->upload_len = 0;
1886 if(should_close_session(httpc)) {
1887 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
1892 if(stream->upload_left) {
1893 /* we are sure that we have more data to send here. Calling the
1894 following API will make nghttp2_session_want_write() return
1895 nonzero if remote window allows it, which then libcurl checks
1896 socket is writable or not. See http2_perform_getsock(). */
1897 nghttp2_session_resume_data(h2, stream->stream_id);
1902 infof(data, "http2_send: easy %p (stream %u) win %u/%u",
1903 data, stream->stream_id,
1904 nghttp2_session_get_remote_window_size(httpc->h2),
1905 nghttp2_session_get_stream_remote_window_size(httpc->h2,
1910 infof(data, "http2_send returns %zu for stream %u", len,
1916 result = Curl_pseudo_headers(data, mem, len, &hreq);
1921 nheader = hreq->entries;
1923 nva = malloc(sizeof(nghttp2_nv) * nheader);
1925 Curl_pseudo_free(hreq);
1926 *err = CURLE_OUT_OF_MEMORY;
1931 for(i = 0; i < nheader; i++) {
1932 nva[i].name = (unsigned char *)hreq->header[i].name;
1933 nva[i].namelen = hreq->header[i].namelen;
1934 nva[i].value = (unsigned char *)hreq->header[i].value;
1935 nva[i].valuelen = hreq->header[i].valuelen;
1936 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1938 Curl_pseudo_free(hreq);
1941 h2_pri_spec(data, &pri_spec);
1943 H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
1944 nghttp2_session_check_request_allowed(h2), (void *)data));
1946 switch(data->state.httpreq) {
1948 case HTTPREQ_POST_FORM:
1949 case HTTPREQ_POST_MIME:
1951 if(data->state.infilesize != -1)
1952 stream->upload_left = data->state.infilesize;
1954 /* data sending without specifying the data amount up front */
1955 stream->upload_left = -1; /* unknown, but not zero */
1957 data_prd.read_callback = data_source_read_callback;
1958 data_prd.source.ptr = NULL;
1959 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1963 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1971 "http2_send() nghttp2_submit_request error (%s)%d",
1972 nghttp2_strerror(stream_id), stream_id));
1973 *err = CURLE_SEND_ERROR;
1977 infof(data, "Using Stream ID: %x (easy handle %p)",
1978 stream_id, (void *)data);
1979 stream->stream_id = stream_id;
1981 rv = h2_session_send(data, h2);
1984 "http2_send() nghttp2_session_send error (%s)%d",
1985 nghttp2_strerror(rv), rv));
1987 *err = CURLE_SEND_ERROR;
1991 if(should_close_session(httpc)) {
1992 H2BUGF(infof(data, "http2_send: nothing to do in this session"));
1997 /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
1998 library calls data_source_read_callback. But only it found that no data
1999 available, so it deferred the DATA transmission. Which means that
2000 nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
2001 results that no writable socket check is performed. To workaround this,
2002 we issue nghttp2_session_resume_data() here to bring back DATA
2003 transmission from deferred state. */
2004 nghttp2_session_resume_data(h2, stream->stream_id);
2009 CURLcode Curl_http2_setup(struct Curl_easy *data,
2010 struct connectdata *conn)
2013 struct http_conn *httpc = &conn->proto.httpc;
2014 struct HTTP *stream = data->req.p.http;
2016 DEBUGASSERT(data->state.buffer);
2018 stream->stream_id = -1;
2020 Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
2021 Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
2023 stream->upload_left = 0;
2024 stream->upload_mem = NULL;
2025 stream->upload_len = 0;
2026 stream->mem = data->state.buffer;
2027 stream->len = data->set.buffer_size;
2029 multi_connchanged(data->multi);
2030 /* below this point only connection related inits are done, which only needs
2031 to be done once per connection */
2033 if((conn->handler == &Curl_handler_http2_ssl) ||
2034 (conn->handler == &Curl_handler_http2))
2035 return CURLE_OK; /* already done */
2037 if(conn->handler->flags & PROTOPT_SSL)
2038 conn->handler = &Curl_handler_http2_ssl;
2040 conn->handler = &Curl_handler_http2;
2042 result = http2_init(data, conn);
2044 Curl_dyn_free(&stream->header_recvbuf);
2048 infof(data, "Using HTTP2, server supports multiplexing");
2050 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2051 conn->httpversion = 20;
2052 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2054 httpc->inbuflen = 0;
2055 httpc->nread_inbuf = 0;
2057 httpc->pause_stream_id = 0;
2058 httpc->drain_total = 0;
2063 CURLcode Curl_http2_switched(struct Curl_easy *data,
2064 const char *mem, size_t nread)
2067 struct connectdata *conn = data->conn;
2068 struct http_conn *httpc = &conn->proto.httpc;
2070 struct HTTP *stream = data->req.p.http;
2072 result = Curl_http2_setup(data, conn);
2076 httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2077 httpc->send_underlying = conn->send[FIRSTSOCKET];
2078 conn->recv[FIRSTSOCKET] = http2_recv;
2079 conn->send[FIRSTSOCKET] = http2_send;
2081 if(data->req.upgr101 == UPGR101_RECEIVED) {
2082 /* stream 1 is opened implicitly on upgrade */
2083 stream->stream_id = 1;
2084 /* queue SETTINGS frame (again) */
2085 rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
2086 data->state.httpreq == HTTPREQ_HEAD, NULL);
2088 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
2089 nghttp2_strerror(rv), rv);
2093 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2097 infof(data, "http/2: failed to set user_data for stream %d",
2103 populate_settings(data, httpc);
2105 /* stream ID is unknown at this point */
2106 stream->stream_id = -1;
2107 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2108 httpc->local_settings,
2109 httpc->local_settings_num);
2111 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2112 nghttp2_strerror(rv), rv);
2117 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2118 HTTP2_HUGE_WINDOW_SIZE);
2120 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2121 nghttp2_strerror(rv), rv);
2125 /* we are going to copy mem to httpc->inbuf. This is required since
2126 mem is part of buffer pointed by stream->mem, and callbacks
2127 called by nghttp2_session_mem_recv() will write stream specific
2128 data into stream->mem, overwriting data already there. */
2129 if(H2_BUFSIZE < nread) {
2130 failf(data, "connection buffer size is too small to store data following "
2131 "HTTP Upgrade response header: buflen=%d, datalen=%zu",
2136 infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
2137 " after upgrade: len=%zu",
2141 memcpy(httpc->inbuf, mem, nread);
2143 httpc->inbuflen = nread;
2145 DEBUGASSERT(httpc->nread_inbuf == 0);
2147 if(-1 == h2_process_pending_input(data, httpc, &result))
2153 CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
2156 DEBUGASSERT(data->conn);
2157 /* if it isn't HTTP/2, we're done */
2158 if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
2159 !data->conn->proto.httpc.h2)
2161 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2163 struct HTTP *stream = data->req.p.http;
2164 struct http_conn *httpc = &data->conn->proto.httpc;
2165 uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
2166 int rv = nghttp2_session_set_local_window_size(httpc->h2,
2171 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2172 nghttp2_strerror(rv), rv);
2176 /* make sure the window update gets sent */
2177 rv = h2_session_send(data, httpc->h2);
2179 return CURLE_SEND_ERROR;
2181 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2182 window, stream->stream_id));
2186 /* read out the stream local window again */
2188 nghttp2_session_get_stream_local_window_size(httpc->h2,
2190 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2191 window2, stream->stream_id));
2199 CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2200 struct Curl_easy *child,
2204 struct Curl_http2_dep **tail;
2205 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2207 return CURLE_OUT_OF_MEMORY;
2210 if(parent->set.stream_dependents && exclusive) {
2211 struct Curl_http2_dep *node = parent->set.stream_dependents;
2213 node->data->set.stream_depends_on = child;
2217 tail = &child->set.stream_dependents;
2219 tail = &(*tail)->next;
2221 DEBUGASSERT(!*tail);
2222 *tail = parent->set.stream_dependents;
2223 parent->set.stream_dependents = 0;
2226 tail = &parent->set.stream_dependents;
2228 (*tail)->data->set.stream_depends_e = FALSE;
2229 tail = &(*tail)->next;
2232 DEBUGASSERT(!*tail);
2236 child->set.stream_depends_on = parent;
2237 child->set.stream_depends_e = exclusive;
2241 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2243 struct Curl_http2_dep *last = 0;
2244 struct Curl_http2_dep *data = parent->set.stream_dependents;
2245 DEBUGASSERT(child->set.stream_depends_on == parent);
2247 while(data && data->data != child) {
2256 last->next = data->next;
2259 parent->set.stream_dependents = data->next;
2264 child->set.stream_depends_on = 0;
2265 child->set.stream_depends_e = FALSE;
2268 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2270 while(data->set.stream_dependents) {
2271 struct Curl_easy *tmp = data->set.stream_dependents->data;
2272 Curl_http2_remove_child(data, tmp);
2273 if(data->set.stream_depends_on)
2274 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2277 if(data->set.stream_depends_on)
2278 Curl_http2_remove_child(data->set.stream_depends_on, data);
2281 /* Only call this function for a transfer that already got a HTTP/2
2282 CURLE_HTTP2_STREAM error! */
2283 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2285 struct HTTP *stream = data->req.p.http;
2286 return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
2289 #else /* !USE_NGHTTP2 */
2291 /* Satisfy external references even if http2 is not compiled in. */
2292 #include <curl/curl.h>
2294 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2301 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2308 #endif /* USE_NGHTTP2 */