1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2020, 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.haxx.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"
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
45 #define H2_BUFSIZE 32768
47 #if (NGHTTP2_VERSION_NUM < 0x010c00)
48 #error too old nghttp2 version, upgrade!
51 #ifdef CURL_DISABLE_VERBOSE_STRINGS
52 #define nghttp2_session_callbacks_set_error_callback(x,y)
55 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
56 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
59 #define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
64 #define H2BUGF(x) do { } while(0)
68 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
69 char *mem, size_t len, CURLcode *err);
70 static bool http2_connisdead(struct connectdata *conn);
71 static int h2_session_send(struct Curl_easy *data,
73 static int h2_process_pending_input(struct connectdata *conn,
74 struct http_conn *httpc,
78 * Curl_http2_init_state() is called when the easy handle is created and
79 * allows for HTTP/2 specific init of state.
81 void Curl_http2_init_state(struct UrlState *state)
83 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
87 * Curl_http2_init_userset() is called when the easy handle is created and
88 * allows for HTTP/2 specific user-set fields.
90 void Curl_http2_init_userset(struct UserDefined *set)
92 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
95 static int http2_perform_getsock(const struct connectdata *conn,
98 const struct http_conn *c = &conn->proto.httpc;
99 struct SingleRequest *k = &conn->data->req;
100 int bitmap = GETSOCK_BLANK;
102 sock[0] = conn->sock[FIRSTSOCKET];
104 /* in a HTTP/2 connection we can basically always get a frame so we should
105 always be ready for one */
106 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
108 /* we're still uploading or the HTTP/2 layer wants to send data */
109 if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
110 nghttp2_session_want_write(c->h2))
111 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
116 static int http2_getsock(struct connectdata *conn,
117 curl_socket_t *socks)
119 return http2_perform_getsock(conn, socks);
123 * http2_stream_free() free HTTP2 stream related data
125 static void http2_stream_free(struct HTTP *http)
128 Curl_dyn_free(&http->header_recvbuf);
129 for(; http->push_headers_used > 0; --http->push_headers_used) {
130 free(http->push_headers[http->push_headers_used - 1]);
132 free(http->push_headers);
133 http->push_headers = NULL;
138 * Disconnects *a* connection used for HTTP/2. It might be an old one from the
139 * connection cache and not the "main" one. Don't touch the easy handle!
142 static CURLcode http2_disconnect(struct connectdata *conn,
143 bool dead_connection)
145 struct http_conn *c = &conn->proto.httpc;
146 (void)dead_connection;
148 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
150 nghttp2_session_del(c->h2);
151 Curl_safefree(c->inbuf);
153 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
159 * The server may send us data at any point (e.g. PING frames). Therefore,
160 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
162 * Instead, if it is readable, run Curl_connalive() to peek at the socket
163 * and distinguish between closed and data.
165 static bool http2_connisdead(struct connectdata *conn)
173 sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
178 else if(sval & CURL_CSELECT_ERR) {
179 /* socket is in an error state */
182 else if(sval & CURL_CSELECT_IN) {
183 /* readable with no error. could still be closed */
184 dead = !Curl_connalive(conn);
186 /* This happens before we've sent off a request and the connection is
187 not in use by any other transfer, there shouldn't be any data here,
188 only "protocol frames" */
190 struct http_conn *httpc = &conn->proto.httpc;
192 if(httpc->recv_underlying)
193 /* if called "too early", this pointer isn't setup yet! */
194 nread = ((Curl_recv *)httpc->recv_underlying)(
195 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
198 "%d bytes stray data read before trying h2 connection\n",
200 httpc->nread_inbuf = 0;
201 httpc->inbuflen = nread;
202 (void)h2_process_pending_input(conn, httpc, &result);
205 /* the read failed so let's say this is dead anyway */
213 static unsigned int http2_conncheck(struct connectdata *check,
214 unsigned int checks_to_perform)
216 unsigned int ret_val = CONNRESULT_NONE;
217 struct http_conn *c = &check->proto.httpc;
219 bool send_frames = false;
221 if(checks_to_perform & CONNCHECK_ISDEAD) {
222 if(http2_connisdead(check))
223 ret_val |= CONNRESULT_DEAD;
226 if(checks_to_perform & CONNCHECK_KEEPALIVE) {
227 struct curltime now = Curl_now();
228 timediff_t elapsed = Curl_timediff(now, check->keepalive);
230 if(elapsed > check->upkeep_interval_ms) {
231 /* Perform an HTTP/2 PING */
232 rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
234 /* Successfully added a PING frame to the session. Need to flag this
235 so the frame is sent. */
239 failf(check->data, "nghttp2_submit_ping() failed: %s(%d)",
240 nghttp2_strerror(rc), rc);
243 check->keepalive = now;
248 rc = nghttp2_session_send(c->h2);
250 failf(check->data, "nghttp2_session_send() failed: %s(%d)",
251 nghttp2_strerror(rc), rc);
257 /* called from http_setup_conn */
258 void Curl_http2_setup_req(struct Curl_easy *data)
260 struct HTTP *http = data->req.protop;
261 http->bodystarted = FALSE;
262 http->status_code = -1;
263 http->pausedata = NULL;
265 http->closed = FALSE;
266 http->close_handled = FALSE;
272 /* called from http_setup_conn */
273 void Curl_http2_setup_conn(struct connectdata *conn)
275 conn->proto.httpc.settings.max_concurrent_streams =
276 DEFAULT_MAX_CONCURRENT_STREAMS;
277 conn->proto.httpc.error_code = NGHTTP2_NO_ERROR;
281 * HTTP2 handler interface. This isn't added to the general list of protocols
282 * but will be used at run-time when the protocol is dynamically switched from
285 static const struct Curl_handler Curl_handler_http2 = {
287 ZERO_NULL, /* setup_connection */
288 Curl_http, /* do_it */
289 Curl_http_done, /* done */
290 ZERO_NULL, /* do_more */
291 ZERO_NULL, /* connect_it */
292 ZERO_NULL, /* connecting */
293 ZERO_NULL, /* doing */
294 http2_getsock, /* proto_getsock */
295 http2_getsock, /* doing_getsock */
296 ZERO_NULL, /* domore_getsock */
297 http2_perform_getsock, /* perform_getsock */
298 http2_disconnect, /* disconnect */
299 ZERO_NULL, /* readwrite */
300 http2_conncheck, /* connection_check */
301 PORT_HTTP, /* defport */
302 CURLPROTO_HTTP, /* protocol */
303 CURLPROTO_HTTP, /* family */
304 PROTOPT_STREAM /* flags */
307 static const struct Curl_handler Curl_handler_http2_ssl = {
308 "HTTPS", /* scheme */
309 ZERO_NULL, /* setup_connection */
310 Curl_http, /* do_it */
311 Curl_http_done, /* done */
312 ZERO_NULL, /* do_more */
313 ZERO_NULL, /* connect_it */
314 ZERO_NULL, /* connecting */
315 ZERO_NULL, /* doing */
316 http2_getsock, /* proto_getsock */
317 http2_getsock, /* doing_getsock */
318 ZERO_NULL, /* domore_getsock */
319 http2_perform_getsock, /* perform_getsock */
320 http2_disconnect, /* disconnect */
321 ZERO_NULL, /* readwrite */
322 http2_conncheck, /* connection_check */
323 PORT_HTTP, /* defport */
324 CURLPROTO_HTTPS, /* protocol */
325 CURLPROTO_HTTP, /* family */
326 PROTOPT_SSL | PROTOPT_STREAM /* flags */
330 * Store nghttp2 version info in this buffer, Prefix with a space. Return
331 * total length written.
333 int Curl_http2_ver(char *p, size_t len)
335 nghttp2_info *h2 = nghttp2_version(0);
336 return msnprintf(p, len, "nghttp2/%s", h2->version_str);
340 * The implementation of nghttp2_send_callback type. Here we write |data| with
341 * size |length| to the network and return the number of bytes actually
342 * written. See the documentation of nghttp2_send_callback for the details.
344 static ssize_t send_callback(nghttp2_session *h2,
345 const uint8_t *data, size_t length, int flags,
348 struct connectdata *conn = (struct connectdata *)userp;
349 struct http_conn *c = &conn->proto.httpc;
351 CURLcode result = CURLE_OK;
356 if(!c->send_underlying)
357 /* called before setup properly! */
358 return NGHTTP2_ERR_CALLBACK_FAILURE;
360 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
361 data, length, &result);
363 if(result == CURLE_AGAIN) {
364 return NGHTTP2_ERR_WOULDBLOCK;
368 failf(conn->data, "Failed sending HTTP2 data");
369 return NGHTTP2_ERR_CALLBACK_FAILURE;
373 return NGHTTP2_ERR_WOULDBLOCK;
379 /* We pass a pointer to this struct in the push callback, but the contents of
380 the struct are hidden from the user. */
381 struct curl_pushheaders {
382 struct Curl_easy *data;
383 const nghttp2_push_promise *frame;
387 * push header access function. Only to be used from within the push callback
389 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
391 /* Verify that we got a good easy handle in the push header struct, mostly to
392 detect rubbish input fast(er). */
393 if(!h || !GOOD_EASY_HANDLE(h->data))
396 struct HTTP *stream = h->data->req.protop;
397 if(num < stream->push_headers_used)
398 return stream->push_headers[num];
404 * push header access function. Only to be used from within the push callback
406 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
408 /* Verify that we got a good easy handle in the push header struct,
409 mostly to detect rubbish input fast(er). Also empty header name
410 is just a rubbish too. We have to allow ":" at the beginning of
411 the header, but header == ":" must be rejected. If we have ':' in
412 the middle of header, it could be matched in middle of the value,
413 this is because we do prefix match.*/
414 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
415 !strcmp(header, ":") || strchr(header + 1, ':'))
418 struct HTTP *stream = h->data->req.protop;
419 size_t len = strlen(header);
421 for(i = 0; i<stream->push_headers_used; i++) {
422 if(!strncmp(header, stream->push_headers[i], len)) {
423 /* sub-match, make sure that it is followed by a colon */
424 if(stream->push_headers[i][len] != ':')
426 return &stream->push_headers[i][len + 1];
434 * This specific transfer on this connection has been "drained".
436 static void drained_transfer(struct Curl_easy *data,
437 struct http_conn *httpc)
439 DEBUGASSERT(httpc->drain_total >= data->state.drain);
440 httpc->drain_total -= data->state.drain;
441 data->state.drain = 0;
445 * Mark this transfer to get "drained".
447 static void drain_this(struct Curl_easy *data,
448 struct http_conn *httpc)
451 httpc->drain_total++;
452 DEBUGASSERT(httpc->drain_total >= data->state.drain);
455 static struct Curl_easy *duphandle(struct Curl_easy *data)
457 struct Curl_easy *second = curl_easy_duphandle(data);
459 /* setup the request struct */
460 struct HTTP *http = calloc(1, sizeof(struct HTTP));
462 (void)Curl_close(&second);
465 second->req.protop = http;
466 Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
467 Curl_http2_setup_req(second);
468 second->state.stream_weight = data->state.stream_weight;
474 static int set_transfer_url(struct Curl_easy *data,
475 struct curl_pushheaders *hp)
478 CURLU *u = curl_url();
482 v = curl_pushheader_byname(hp, ":scheme");
484 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
489 v = curl_pushheader_byname(hp, ":authority");
491 uc = curl_url_set(u, CURLUPART_HOST, v, 0);
496 v = curl_pushheader_byname(hp, ":path");
498 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
503 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
508 if(data->change.url_alloc)
509 free(data->change.url);
510 data->change.url_alloc = TRUE;
511 data->change.url = url;
515 static int push_promise(struct Curl_easy *data,
516 struct connectdata *conn,
517 const nghttp2_push_promise *frame)
519 int rv; /* one of the CURL_PUSH_* defines */
520 H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
521 frame->promised_stream_id));
522 if(data->multi->push_cb) {
524 struct HTTP *newstream;
525 struct curl_pushheaders heads;
527 struct http_conn *httpc;
529 /* clone the parent */
530 struct Curl_easy *newhandle = duphandle(data);
532 infof(data, "failed to duplicate handle\n");
533 rv = CURL_PUSH_DENY; /* FAIL HARD */
539 /* ask the application */
540 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
542 stream = data->req.protop;
544 failf(data, "Internal NULL stream!\n");
545 (void)Curl_close(&newhandle);
550 rv = set_transfer_url(newhandle, &heads);
556 Curl_set_in_callback(data, true);
557 rv = data->multi->push_cb(data, newhandle,
558 stream->push_headers_used, &heads,
559 data->multi->push_userp);
560 Curl_set_in_callback(data, false);
562 /* free the headers again */
563 for(i = 0; i<stream->push_headers_used; i++)
564 free(stream->push_headers[i]);
565 free(stream->push_headers);
566 stream->push_headers = NULL;
567 stream->push_headers_used = 0;
570 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
571 /* denied, kill off the new handle again */
572 http2_stream_free(newhandle->req.protop);
573 newhandle->req.protop = NULL;
574 (void)Curl_close(&newhandle);
578 newstream = newhandle->req.protop;
579 newstream->stream_id = frame->promised_stream_id;
580 newhandle->req.maxdownload = -1;
581 newhandle->req.size = -1;
583 /* approved, add to the multi handle and immediately switch to PERFORM
584 state with the given connection !*/
585 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
587 infof(data, "failed to add handle to multi\n");
588 http2_stream_free(newhandle->req.protop);
589 newhandle->req.protop = NULL;
590 Curl_close(&newhandle);
595 httpc = &conn->proto.httpc;
596 rv = nghttp2_session_set_stream_user_data(httpc->h2,
597 frame->promised_stream_id,
600 infof(data, "failed to set user_data for stream %d\n",
601 frame->promised_stream_id);
608 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
616 * multi_connchanged() is called to tell that there is a connection in
617 * this multi handle that has changed state (multiplexing become possible, the
618 * number of allowed streams changed or similar), and a subsequent use of this
619 * multi handle should move CONNECT_PEND handles back to CONNECT to have them
622 static void multi_connchanged(struct Curl_multi *multi)
624 multi->recheckstate = TRUE;
627 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
630 struct connectdata *conn = (struct connectdata *)userp;
631 struct http_conn *httpc = &conn->proto.httpc;
632 struct Curl_easy *data_s = NULL;
633 struct HTTP *stream = NULL;
636 int32_t stream_id = frame->hd.stream_id;
640 /* stream ID zero is for connection-oriented stuff */
641 if(frame->hd.type == NGHTTP2_SETTINGS) {
642 uint32_t max_conn = httpc->settings.max_concurrent_streams;
643 H2BUGF(infof(conn->data, "Got SETTINGS\n"));
644 httpc->settings.max_concurrent_streams =
645 nghttp2_session_get_remote_settings(
646 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
647 httpc->settings.enable_push =
648 nghttp2_session_get_remote_settings(
649 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
650 H2BUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
651 httpc->settings.max_concurrent_streams));
652 H2BUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
653 httpc->settings.enable_push?"TRUE":"false"));
654 if(max_conn != httpc->settings.max_concurrent_streams) {
655 /* only signal change if the value actually changed */
657 "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
658 httpc->settings.max_concurrent_streams);
659 multi_connchanged(conn->data->multi);
664 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
666 H2BUGF(infof(conn->data,
667 "No Curl_easy associated with stream: %x\n",
672 stream = data_s->req.protop;
674 H2BUGF(infof(data_s, "No proto pointer for stream: %x\n",
676 return NGHTTP2_ERR_CALLBACK_FAILURE;
679 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
680 frame->hd.type, stream_id));
682 switch(frame->hd.type) {
684 /* If body started on this stream, then receiving DATA is illegal. */
685 if(!stream->bodystarted) {
686 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
687 stream_id, NGHTTP2_PROTOCOL_ERROR);
689 if(nghttp2_is_fatal(rv)) {
690 return NGHTTP2_ERR_CALLBACK_FAILURE;
694 case NGHTTP2_HEADERS:
695 if(stream->bodystarted) {
696 /* Only valid HEADERS after body started is trailer HEADERS. We
697 buffer them in on_header callback. */
701 /* nghttp2 guarantees that :status is received, and we store it to
702 stream->status_code. Fuzzing has proven this can still be reached
703 without status code having been set. */
704 if(stream->status_code == -1)
705 return NGHTTP2_ERR_CALLBACK_FAILURE;
707 /* Only final status code signals the end of header */
708 if(stream->status_code / 100 != 1) {
709 stream->bodystarted = TRUE;
710 stream->status_code = -1;
713 result = Curl_dyn_add(&stream->header_recvbuf, "\r\n");
715 return NGHTTP2_ERR_CALLBACK_FAILURE;
717 left = Curl_dyn_len(&stream->header_recvbuf) -
718 stream->nread_header_recvbuf;
719 ncopy = CURLMIN(stream->len, left);
721 memcpy(&stream->mem[stream->memlen],
722 Curl_dyn_ptr(&stream->header_recvbuf) +
723 stream->nread_header_recvbuf,
725 stream->nread_header_recvbuf += ncopy;
727 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
728 ncopy, stream_id, stream->mem));
730 stream->len -= ncopy;
731 stream->memlen += ncopy;
733 drain_this(data_s, httpc);
735 /* get the pointer from userp again since it was re-assigned above */
736 struct connectdata *conn_s = (struct connectdata *)userp;
738 /* if we receive data for another handle, wake that up */
739 if(conn_s->data != data_s)
740 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
743 case NGHTTP2_PUSH_PROMISE:
744 rv = push_promise(data_s, conn, &frame->push_promise);
747 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
748 h2 = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
749 frame->push_promise.promised_stream_id,
751 if(nghttp2_is_fatal(h2))
752 return NGHTTP2_ERR_CALLBACK_FAILURE;
753 else if(rv == CURL_PUSH_ERROROUT) {
754 DEBUGF(infof(data_s, "Fail the parent stream (too)\n"));
755 return NGHTTP2_ERR_CALLBACK_FAILURE;
760 H2BUGF(infof(data_s, "Got frame type %x for stream %u!\n",
761 frame->hd.type, stream_id));
767 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
769 const uint8_t *data, size_t len, void *userp)
772 struct Curl_easy *data_s;
774 struct connectdata *conn = (struct connectdata *)userp;
779 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
781 /* get the stream from the hash based on Stream ID */
782 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
784 /* Receiving a Stream ID not in the hash should not happen, this is an
785 internal error more than anything else! */
786 return NGHTTP2_ERR_CALLBACK_FAILURE;
788 stream = data_s->req.protop;
790 return NGHTTP2_ERR_CALLBACK_FAILURE;
792 nread = CURLMIN(stream->len, len);
793 memcpy(&stream->mem[stream->memlen], data, nread);
795 stream->len -= nread;
796 stream->memlen += nread;
798 drain_this(data_s, &conn->proto.httpc);
800 /* if we receive data for another handle, wake that up */
801 if(conn->data != data_s)
802 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
804 H2BUGF(infof(data_s, "%zu data received for stream %u "
805 "(%zu left in buffer %p, total %zu)\n",
807 stream->len, stream->mem,
811 stream->pausedata = data + nread;
812 stream->pauselen = len - nread;
813 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
815 len - nread, stream_id));
816 data_s->conn->proto.httpc.pause_stream_id = stream_id;
818 return NGHTTP2_ERR_PAUSE;
821 /* pause execution of nghttp2 if we received data for another handle
822 in order to process them first. */
823 if(conn->data != data_s) {
824 data_s->conn->proto.httpc.pause_stream_id = stream_id;
826 return NGHTTP2_ERR_PAUSE;
832 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
833 uint32_t error_code, void *userp)
835 struct Curl_easy *data_s;
837 struct connectdata *conn = (struct connectdata *)userp;
843 struct http_conn *httpc;
844 /* get the stream from the hash based on Stream ID, stream ID zero is for
845 connection-oriented stuff */
846 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
848 /* We could get stream ID not in the hash. For example, if we
849 decided to reject stream (e.g., PUSH_PROMISE). */
852 H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
853 nghttp2_http2_strerror(error_code), error_code, stream_id));
854 stream = data_s->req.protop;
856 return NGHTTP2_ERR_CALLBACK_FAILURE;
858 stream->closed = TRUE;
859 httpc = &conn->proto.httpc;
860 drain_this(data_s, httpc);
861 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
862 httpc->error_code = error_code;
864 /* remove the entry from the hash as the stream is now gone */
865 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
867 infof(data_s, "http/2: failed to clear user_data for stream %d!\n",
871 if(stream_id == httpc->pause_stream_id) {
872 H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
873 httpc->pause_stream_id = 0;
875 H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
876 stream->stream_id = 0; /* cleared */
881 static int on_begin_headers(nghttp2_session *session,
882 const nghttp2_frame *frame, void *userp)
885 struct Curl_easy *data_s = NULL;
888 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
893 H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
895 if(frame->hd.type != NGHTTP2_HEADERS) {
899 stream = data_s->req.protop;
900 if(!stream || !stream->bodystarted) {
907 /* Decode HTTP status code. Returns -1 if no valid status code was
909 static int decode_status_code(const uint8_t *value, size_t len)
920 for(i = 0; i < 3; ++i) {
923 if(c < '0' || c > '9') {
934 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
935 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
936 const uint8_t *name, size_t namelen,
937 const uint8_t *value, size_t valuelen,
942 struct Curl_easy *data_s;
943 int32_t stream_id = frame->hd.stream_id;
944 struct connectdata *conn = (struct connectdata *)userp;
948 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
950 /* get the stream from the hash based on Stream ID */
951 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
953 /* Receiving a Stream ID not in the hash should not happen, this is an
954 internal error more than anything else! */
955 return NGHTTP2_ERR_CALLBACK_FAILURE;
957 stream = data_s->req.protop;
959 failf(data_s, "Internal NULL stream! 5\n");
960 return NGHTTP2_ERR_CALLBACK_FAILURE;
963 /* Store received PUSH_PROMISE headers to be used when the subsequent
964 PUSH_PROMISE callback comes */
965 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
968 if(!strcmp(":authority", (const char *)name)) {
969 /* pseudo headers are lower case */
971 char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
974 return NGHTTP2_ERR_CALLBACK_FAILURE;
975 if(!Curl_strcasecompare(check, (const char *)value) &&
976 ((conn->remote_port != conn->given->defport) ||
977 !Curl_strcasecompare(conn->host.name, (const char *)value))) {
978 /* This is push is not for the same authority that was asked for in
979 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
980 * PUSH_PROMISE for which the server is not authoritative as a stream
981 * error of type PROTOCOL_ERROR."
983 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
984 stream_id, NGHTTP2_PROTOCOL_ERROR);
985 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
992 if(!stream->push_headers) {
993 stream->push_headers_alloc = 10;
994 stream->push_headers = malloc(stream->push_headers_alloc *
996 if(!stream->push_headers)
997 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
998 stream->push_headers_used = 0;
1000 else if(stream->push_headers_used ==
1001 stream->push_headers_alloc) {
1003 stream->push_headers_alloc *= 2;
1004 headp = Curl_saferealloc(stream->push_headers,
1005 stream->push_headers_alloc * sizeof(char *));
1007 stream->push_headers = NULL;
1008 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1010 stream->push_headers = headp;
1012 h = aprintf("%s:%s", name, value);
1014 stream->push_headers[stream->push_headers_used++] = h;
1018 if(stream->bodystarted) {
1019 /* This is a trailer */
1020 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
1022 result = Curl_dyn_addf(&stream->trailer_recvbuf,
1023 "%.*s: %.*s\r\n", namelen, name,
1026 return NGHTTP2_ERR_CALLBACK_FAILURE;
1031 if(namelen == sizeof(":status") - 1 &&
1032 memcmp(":status", name, namelen) == 0) {
1033 /* nghttp2 guarantees :status is received first and only once, and
1034 value is 3 digits status code, and decode_status_code always
1036 stream->status_code = decode_status_code(value, valuelen);
1037 DEBUGASSERT(stream->status_code != -1);
1039 result = Curl_dyn_add(&stream->header_recvbuf, "HTTP/2 ");
1041 return NGHTTP2_ERR_CALLBACK_FAILURE;
1042 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1044 return NGHTTP2_ERR_CALLBACK_FAILURE;
1045 /* the space character after the status code is mandatory */
1046 result = Curl_dyn_add(&stream->header_recvbuf, " \r\n");
1048 return NGHTTP2_ERR_CALLBACK_FAILURE;
1049 /* if we receive data for another handle, wake that up */
1050 if(conn->data != data_s)
1051 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1053 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
1054 stream->status_code, data_s));
1058 /* nghttp2 guarantees that namelen > 0, and :status was already
1059 received, and this is not pseudo-header field . */
1060 /* convert to a HTTP1-style header */
1061 result = Curl_dyn_addn(&stream->header_recvbuf, name, namelen);
1063 return NGHTTP2_ERR_CALLBACK_FAILURE;
1064 result = Curl_dyn_add(&stream->header_recvbuf, ": ");
1066 return NGHTTP2_ERR_CALLBACK_FAILURE;
1067 result = Curl_dyn_addn(&stream->header_recvbuf, value, valuelen);
1069 return NGHTTP2_ERR_CALLBACK_FAILURE;
1070 result = Curl_dyn_add(&stream->header_recvbuf, "\r\n");
1072 return NGHTTP2_ERR_CALLBACK_FAILURE;
1073 /* if we receive data for another handle, wake that up */
1074 if(conn->data != data_s)
1075 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1077 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
1080 return 0; /* 0 is successful */
1083 static ssize_t data_source_read_callback(nghttp2_session *session,
1085 uint8_t *buf, size_t length,
1086 uint32_t *data_flags,
1087 nghttp2_data_source *source,
1090 struct Curl_easy *data_s;
1091 struct HTTP *stream = NULL;
1097 /* get the stream from the hash based on Stream ID, stream ID zero is for
1098 connection-oriented stuff */
1099 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1101 /* Receiving a Stream ID not in the hash should not happen, this is an
1102 internal error more than anything else! */
1103 return NGHTTP2_ERR_CALLBACK_FAILURE;
1105 stream = data_s->req.protop;
1107 return NGHTTP2_ERR_CALLBACK_FAILURE;
1110 return NGHTTP2_ERR_INVALID_ARGUMENT;
1112 nread = CURLMIN(stream->upload_len, length);
1114 memcpy(buf, stream->upload_mem, nread);
1115 stream->upload_mem += nread;
1116 stream->upload_len -= nread;
1117 if(data_s->state.infilesize != -1)
1118 stream->upload_left -= nread;
1121 if(stream->upload_left == 0)
1122 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1124 return NGHTTP2_ERR_DEFERRED;
1126 H2BUGF(infof(data_s, "data_source_read_callback: "
1127 "returns %zu bytes stream %u\n",
1133 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1134 static int error_callback(nghttp2_session *session,
1139 struct connectdata *conn = (struct connectdata *)userp;
1141 infof(conn->data, "http2 error: %.*s\n", len, msg);
1146 static void populate_settings(struct connectdata *conn,
1147 struct http_conn *httpc)
1149 nghttp2_settings_entry *iv = httpc->local_settings;
1150 DEBUGASSERT(conn->data);
1152 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1153 iv[0].value = Curl_multi_max_concurrent_streams(conn->data->multi);
1155 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1156 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1158 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1159 iv[2].value = conn->data->multi->push_cb != NULL;
1161 httpc->local_settings_num = 3;
1164 void Curl_http2_done(struct Curl_easy *data, bool premature)
1166 struct HTTP *http = data->req.protop;
1167 struct http_conn *httpc = &data->conn->proto.httpc;
1169 /* there might be allocated resources done before this got the 'h2' pointer
1171 Curl_dyn_free(&http->header_recvbuf);
1172 Curl_dyn_free(&http->trailer_recvbuf);
1173 if(http->push_headers) {
1174 /* if they weren't used and then freed before */
1175 for(; http->push_headers_used > 0; --http->push_headers_used) {
1176 free(http->push_headers[http->push_headers_used - 1]);
1178 free(http->push_headers);
1179 http->push_headers = NULL;
1182 if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
1183 !httpc->h2) /* not HTTP/2 ? */
1188 if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
1189 http->stream_id, NGHTTP2_STREAM_CLOSED))
1190 (void)nghttp2_session_send(httpc->h2);
1192 if(http->stream_id == httpc->pause_stream_id) {
1193 infof(data, "stopped the pause stream!\n");
1194 httpc->pause_stream_id = 0;
1198 if(data->state.drain)
1199 drained_transfer(data, httpc);
1201 /* -1 means unassigned and 0 means cleared */
1202 if(http->stream_id > 0) {
1203 int rv = nghttp2_session_set_stream_user_data(httpc->h2,
1204 http->stream_id, 0);
1206 infof(data, "http/2: failed to clear user_data for stream %d!\n",
1210 http->stream_id = 0;
1215 * Initialize nghttp2 for a Curl connection
1217 static CURLcode http2_init(struct connectdata *conn)
1219 if(!conn->proto.httpc.h2) {
1221 nghttp2_session_callbacks *callbacks;
1223 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1224 if(conn->proto.httpc.inbuf == NULL)
1225 return CURLE_OUT_OF_MEMORY;
1227 rc = nghttp2_session_callbacks_new(&callbacks);
1230 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1231 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1234 /* nghttp2_send_callback */
1235 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1236 /* nghttp2_on_frame_recv_callback */
1237 nghttp2_session_callbacks_set_on_frame_recv_callback
1238 (callbacks, on_frame_recv);
1239 /* nghttp2_on_data_chunk_recv_callback */
1240 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1241 (callbacks, on_data_chunk_recv);
1242 /* nghttp2_on_stream_close_callback */
1243 nghttp2_session_callbacks_set_on_stream_close_callback
1244 (callbacks, on_stream_close);
1245 /* nghttp2_on_begin_headers_callback */
1246 nghttp2_session_callbacks_set_on_begin_headers_callback
1247 (callbacks, on_begin_headers);
1248 /* nghttp2_on_header_callback */
1249 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1251 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1253 /* The nghttp2 session is not yet setup, do it */
1254 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1256 nghttp2_session_callbacks_del(callbacks);
1259 failf(conn->data, "Couldn't initialize nghttp2!");
1260 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1267 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1269 CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1270 struct connectdata *conn)
1276 struct SingleRequest *k = &conn->data->req;
1277 uint8_t *binsettings = conn->proto.httpc.binsettings;
1278 struct http_conn *httpc = &conn->proto.httpc;
1280 populate_settings(conn, httpc);
1282 /* this returns number of bytes it wrote */
1283 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1284 httpc->local_settings,
1285 httpc->local_settings_num);
1287 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1289 return CURLE_FAILED_INIT;
1291 conn->proto.httpc.binlen = binlen;
1293 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1300 result = Curl_dyn_addf(req,
1301 "Connection: Upgrade, HTTP2-Settings\r\n"
1303 "HTTP2-Settings: %s\r\n",
1304 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1307 k->upgr101 = UPGR101_REQUESTED;
1313 * Returns nonzero if current HTTP/2 session should be closed.
1315 static int should_close_session(struct http_conn *httpc)
1317 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1318 !nghttp2_session_want_write(httpc->h2);
1322 * h2_process_pending_input() processes pending input left in
1323 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1324 * This function returns 0 if it succeeds, or -1 and error code will
1325 * be assigned to *err.
1327 static int h2_process_pending_input(struct connectdata *conn,
1328 struct http_conn *httpc,
1334 struct Curl_easy *data = conn->data;
1336 nread = httpc->inbuflen - httpc->nread_inbuf;
1337 inbuf = httpc->inbuf + httpc->nread_inbuf;
1339 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1342 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1343 "%zd:%s\n", rv, nghttp2_strerror((int)rv));
1344 *err = CURLE_RECV_ERROR;
1350 "h2_process_pending_input: All data in connection buffer "
1352 httpc->inbuflen = 0;
1353 httpc->nread_inbuf = 0;
1356 httpc->nread_inbuf += rv;
1358 "h2_process_pending_input: %zu bytes left in connection "
1360 httpc->inbuflen - httpc->nread_inbuf));
1363 rv = h2_session_send(data, httpc->h2);
1365 *err = CURLE_SEND_ERROR;
1369 if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
1370 /* No more requests are allowed in the current session, so
1371 the connection may not be reused. This is set when a
1372 GOAWAY frame has been received or when the limit of stream
1373 identifiers has been reached. */
1374 connclose(conn, "http/2: No new requests allowed");
1377 if(should_close_session(httpc)) {
1379 "h2_process_pending_input: nothing to do in this session\n"));
1380 if(httpc->error_code)
1383 /* not an error per se, but should still close the connection */
1384 connclose(conn, "GOAWAY received");
1393 * Called from transfer.c:done_sending when we stop uploading.
1395 CURLcode Curl_http2_done_sending(struct connectdata *conn)
1397 CURLcode result = CURLE_OK;
1399 if((conn->handler == &Curl_handler_http2_ssl) ||
1400 (conn->handler == &Curl_handler_http2)) {
1401 /* make sure this is only attempted for HTTP/2 transfers */
1403 struct HTTP *stream = conn->data->req.protop;
1405 struct http_conn *httpc = &conn->proto.httpc;
1406 nghttp2_session *h2 = httpc->h2;
1408 if(stream->upload_left) {
1409 /* If the stream still thinks there's data left to upload. */
1411 stream->upload_left = 0; /* DONE! */
1413 /* resume sending here to trigger the callback to get called again so
1414 that it can signal EOF to nghttp2 */
1415 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1417 (void)h2_process_pending_input(conn, httpc, &result);
1420 /* If nghttp2 still has pending frames unsent */
1421 if(nghttp2_session_want_write(h2)) {
1422 struct Curl_easy *data = conn->data;
1423 struct SingleRequest *k = &data->req;
1426 H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)\n", data));
1428 /* re-set KEEP_SEND to make sure we are called again */
1429 k->keepon |= KEEP_SEND;
1431 /* and attempt to send the pending frames */
1432 rv = h2_session_send(data, h2);
1434 result = CURLE_SEND_ERROR;
1440 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1441 struct Curl_easy *data,
1442 struct HTTP *stream, CURLcode *err)
1444 struct http_conn *httpc = &conn->proto.httpc;
1446 if(httpc->pause_stream_id == stream->stream_id) {
1447 httpc->pause_stream_id = 0;
1450 drained_transfer(data, httpc);
1452 if(httpc->pause_stream_id == 0) {
1453 if(h2_process_pending_input(conn, httpc, err) != 0) {
1458 DEBUGASSERT(data->state.drain == 0);
1460 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1461 stream->closed = FALSE;
1462 if(httpc->error_code == NGHTTP2_REFUSED_STREAM) {
1463 H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!\n",
1464 stream->stream_id));
1465 connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1466 data->state.refused_stream = TRUE;
1467 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1470 else if(httpc->error_code != NGHTTP2_NO_ERROR) {
1471 failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
1472 stream->stream_id, nghttp2_http2_strerror(httpc->error_code),
1474 *err = CURLE_HTTP2_STREAM;
1478 if(!stream->bodystarted) {
1479 failf(data, "HTTP/2 stream %d was closed cleanly, but before getting "
1480 " all response header fields, treated as error",
1482 *err = CURLE_HTTP2_STREAM;
1486 if(Curl_dyn_len(&stream->trailer_recvbuf)) {
1487 char *trailp = Curl_dyn_ptr(&stream->trailer_recvbuf);
1493 /* each trailer line ends with a newline */
1494 lf = strchr(trailp, '\n');
1497 len = lf + 1 - trailp;
1499 if(data->set.verbose)
1500 Curl_debug(data, CURLINFO_HEADER_IN, trailp, len);
1501 /* pass the trailers one by one to the callback */
1502 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailp, len);
1511 stream->close_handled = TRUE;
1513 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1518 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1519 * and dependency to the peer. It also stores the updated values in the state
1523 static void h2_pri_spec(struct Curl_easy *data,
1524 nghttp2_priority_spec *pri_spec)
1526 struct HTTP *depstream = (data->set.stream_depends_on?
1527 data->set.stream_depends_on->req.protop:NULL);
1528 int32_t depstream_id = depstream? depstream->stream_id:0;
1529 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1530 data->set.stream_depends_e);
1531 data->state.stream_weight = data->set.stream_weight;
1532 data->state.stream_depends_e = data->set.stream_depends_e;
1533 data->state.stream_depends_on = data->set.stream_depends_on;
1537 * h2_session_send() checks if there's been an update in the priority /
1538 * dependency settings and if so it submits a PRIORITY frame with the updated
1541 static int h2_session_send(struct Curl_easy *data,
1542 nghttp2_session *h2)
1544 struct HTTP *stream = data->req.protop;
1545 if((data->set.stream_weight != data->state.stream_weight) ||
1546 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1547 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1548 /* send new weight and/or dependency */
1549 nghttp2_priority_spec pri_spec;
1552 h2_pri_spec(data, &pri_spec);
1554 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1555 stream->stream_id, data));
1556 DEBUGASSERT(stream->stream_id != -1);
1557 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1563 return nghttp2_session_send(h2);
1566 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1567 char *mem, size_t len, CURLcode *err)
1570 struct http_conn *httpc = &conn->proto.httpc;
1571 struct Curl_easy *data = conn->data;
1572 struct HTTP *stream = data->req.protop;
1574 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1576 if(should_close_session(httpc)) {
1578 "http2_recv: nothing to do in this session\n"));
1579 if(conn->bits.close) {
1580 /* already marked for closure, return OK and we're done */
1588 /* Nullify here because we call nghttp2_session_send() and they
1589 might refer to the old buffer. */
1590 stream->upload_mem = NULL;
1591 stream->upload_len = 0;
1594 * At this point 'stream' is just in the Curl_easy the connection
1595 * identifies as its owner at this time.
1598 if(stream->bodystarted &&
1599 stream->nread_header_recvbuf < Curl_dyn_len(&stream->header_recvbuf)) {
1600 /* If there is header data pending for this stream to return, do that */
1602 Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
1603 size_t ncopy = CURLMIN(len, left);
1604 memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
1605 stream->nread_header_recvbuf, ncopy);
1606 stream->nread_header_recvbuf += ncopy;
1608 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1613 H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u\n",
1614 data, stream->stream_id,
1615 nghttp2_session_get_local_window_size(httpc->h2),
1616 nghttp2_session_get_stream_local_window_size(httpc->h2,
1620 if((data->state.drain) && stream->memlen) {
1621 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1622 stream->memlen, stream->stream_id,
1624 if(mem != stream->mem) {
1625 /* if we didn't get the same buffer this time, we must move the data to
1627 memmove(mem, stream->mem, stream->memlen);
1628 stream->len = len - stream->memlen;
1631 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1632 /* We have paused nghttp2, but we have no pause data (see
1633 on_data_chunk_recv). */
1634 httpc->pause_stream_id = 0;
1635 if(h2_process_pending_input(conn, httpc, err) != 0) {
1640 else if(stream->pausedata) {
1641 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1642 nread = CURLMIN(len, stream->pauselen);
1643 memcpy(mem, stream->pausedata, nread);
1645 stream->pausedata += nread;
1646 stream->pauselen -= nread;
1648 if(stream->pauselen == 0) {
1649 H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1650 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1651 httpc->pause_stream_id = 0;
1653 stream->pausedata = NULL;
1654 stream->pauselen = 0;
1656 /* When NGHTTP2_ERR_PAUSE is returned from
1657 data_source_read_callback, we might not process DATA frame
1658 fully. Calling nghttp2_session_mem_recv() again will
1659 continue to process DATA frame, but if there is no incoming
1660 frames, then we have to call it again with 0-length data.
1661 Without this, on_stream_close callback will not be called,
1662 and stream could be hanged. */
1663 if(h2_process_pending_input(conn, httpc, err) != 0) {
1667 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1668 nread, stream->stream_id));
1671 else if(httpc->pause_stream_id) {
1672 /* If a stream paused nghttp2_session_mem_recv previously, and has
1673 not processed all data, it still refers to the buffer in
1674 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1675 overwrite that buffer. To avoid that situation, just return
1676 here with CURLE_AGAIN. This could be busy loop since data in
1677 socket is not read. But it seems that usually streams are
1678 notified with its drain property, and socket is read again
1681 /* closed overrides paused */
1683 H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
1684 stream->stream_id, httpc->pause_stream_id));
1689 /* remember where to store incoming data for this stream and how big the
1695 if(httpc->inbuflen == 0) {
1696 nread = ((Curl_recv *)httpc->recv_underlying)(
1697 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
1700 if(*err != CURLE_AGAIN)
1701 failf(data, "Failed receiving HTTP2 data");
1702 else if(stream->closed)
1703 /* received when the stream was already closed! */
1704 return http2_handle_stream_close(conn, data, stream, err);
1710 H2BUGF(infof(data, "end of stream\n"));
1715 H2BUGF(infof(data, "nread=%zd\n", nread));
1717 httpc->inbuflen = nread;
1719 DEBUGASSERT(httpc->nread_inbuf == 0);
1722 nread = httpc->inbuflen - httpc->nread_inbuf;
1723 (void)nread; /* silence warning, used in debug */
1724 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1728 if(h2_process_pending_input(conn, httpc, err) != 0)
1731 if(stream->memlen) {
1732 ssize_t retlen = stream->memlen;
1733 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1734 retlen, stream->stream_id));
1737 if(httpc->pause_stream_id == stream->stream_id) {
1738 /* data for this stream is returned now, but this stream caused a pause
1739 already so we need it called again asap */
1740 H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
1741 stream->stream_id));
1743 else if(!stream->closed) {
1744 drained_transfer(data, httpc);
1747 /* this stream is closed, trigger a another read ASAP to detect that */
1748 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1755 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1756 stream->stream_id));
1760 /* Index where :authority header field will appear in request header
1762 #define AUTHORITY_DST_IDX 3
1764 /* USHRT_MAX is 65535 == 0xffff */
1765 #define HEADER_OVERFLOW(x) \
1766 (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
1769 * Check header memory for the token "trailers".
1770 * Parse the tokens as separated by comma and surrounded by whitespace.
1771 * Returns TRUE if found or FALSE if not.
1773 static bool contains_trailers(const char *p, size_t len)
1775 const char *end = p + len;
1777 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1779 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1781 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1782 p += sizeof("trailers") - 1;
1783 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1785 if(p == end || *p == ',')
1788 /* skip to next token */
1789 for(; p != end && *p != ','; ++p)
1798 /* Send header to server */
1800 /* Don't send header to server */
1802 /* Discard header, and replace it with "te: trailers" */
1803 HEADERINST_TE_TRAILERS
1804 } header_instruction;
1806 /* Decides how to treat given header field. */
1807 static header_instruction inspect_header(const char *name, size_t namelen,
1808 const char *value, size_t valuelen) {
1811 if(!strncasecompare("te", name, namelen))
1812 return HEADERINST_FORWARD;
1814 return contains_trailers(value, valuelen) ?
1815 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1817 return strncasecompare("upgrade", name, namelen) ?
1818 HEADERINST_IGNORE : HEADERINST_FORWARD;
1820 return (strncasecompare("connection", name, namelen) ||
1821 strncasecompare("keep-alive", name, namelen)) ?
1822 HEADERINST_IGNORE : HEADERINST_FORWARD;
1824 return strncasecompare("proxy-connection", name, namelen) ?
1825 HEADERINST_IGNORE : HEADERINST_FORWARD;
1827 return strncasecompare("transfer-encoding", name, namelen) ?
1828 HEADERINST_IGNORE : HEADERINST_FORWARD;
1830 return HEADERINST_FORWARD;
1834 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1835 const void *mem, size_t len, CURLcode *err)
1838 * Currently, we send request in this function, but this function is also
1839 * used to send request body. It would be nice to add dedicated function for
1843 struct http_conn *httpc = &conn->proto.httpc;
1844 struct HTTP *stream = conn->data->req.protop;
1845 nghttp2_nv *nva = NULL;
1848 size_t authority_idx;
1849 char *hdbuf = (char *)mem;
1850 char *end, *line_end;
1851 nghttp2_data_provider data_prd;
1853 nghttp2_session *h2 = httpc->h2;
1854 nghttp2_priority_spec pri_spec;
1858 H2BUGF(infof(conn->data, "http2_send len=%zu\n", len));
1860 if(stream->stream_id != -1) {
1861 if(stream->close_handled) {
1862 infof(conn->data, "stream %d closed\n", stream->stream_id);
1863 *err = CURLE_HTTP2_STREAM;
1866 else if(stream->closed) {
1867 return http2_handle_stream_close(conn, conn->data, stream, err);
1869 /* If stream_id != -1, we have dispatched request HEADERS, and now
1870 are going to send or sending request body in DATA frame */
1871 stream->upload_mem = mem;
1872 stream->upload_len = len;
1873 rv = nghttp2_session_resume_data(h2, stream->stream_id);
1874 if(nghttp2_is_fatal(rv)) {
1875 *err = CURLE_SEND_ERROR;
1878 rv = h2_session_send(conn->data, h2);
1879 if(nghttp2_is_fatal(rv)) {
1880 *err = CURLE_SEND_ERROR;
1883 len -= stream->upload_len;
1885 /* Nullify here because we call nghttp2_session_send() and they
1886 might refer to the old buffer. */
1887 stream->upload_mem = NULL;
1888 stream->upload_len = 0;
1890 if(should_close_session(httpc)) {
1891 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1896 if(stream->upload_left) {
1897 /* we are sure that we have more data to send here. Calling the
1898 following API will make nghttp2_session_want_write() return
1899 nonzero if remote window allows it, which then libcurl checks
1900 socket is writable or not. See http2_perform_getsock(). */
1901 nghttp2_session_resume_data(h2, stream->stream_id);
1904 H2BUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1905 stream->stream_id));
1909 /* Calculate number of headers contained in [mem, mem + len) */
1910 /* Here, we assume the curl http code generate *correct* HTTP header
1913 for(i = 1; i < len; ++i) {
1914 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1922 /* We counted additional 2 \r\n in the first and last line. We need 3
1923 new headers: :method, :path and :scheme. Therefore we need one
1926 nva = malloc(sizeof(nghttp2_nv) * nheader);
1928 *err = CURLE_OUT_OF_MEMORY;
1932 /* Extract :method, :path from request line
1933 We do line endings with CRLF so checking for CR is enough */
1934 line_end = memchr(hdbuf, '\r', len);
1938 /* Method does not contain spaces */
1939 end = memchr(hdbuf, ' ', line_end - hdbuf);
1940 if(!end || end == hdbuf)
1942 nva[0].name = (unsigned char *)":method";
1943 nva[0].namelen = strlen((char *)nva[0].name);
1944 nva[0].value = (unsigned char *)hdbuf;
1945 nva[0].valuelen = (size_t)(end - hdbuf);
1946 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1947 if(HEADER_OVERFLOW(nva[0])) {
1948 failf(conn->data, "Failed sending HTTP request: Header overflow");
1954 /* Path may contain spaces so scan backwards */
1956 for(i = (size_t)(line_end - hdbuf); i; --i) {
1957 if(hdbuf[i - 1] == ' ') {
1958 end = &hdbuf[i - 1];
1962 if(!end || end == hdbuf)
1964 nva[1].name = (unsigned char *)":path";
1965 nva[1].namelen = strlen((char *)nva[1].name);
1966 nva[1].value = (unsigned char *)hdbuf;
1967 nva[1].valuelen = (size_t)(end - hdbuf);
1968 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1969 if(HEADER_OVERFLOW(nva[1])) {
1970 failf(conn->data, "Failed sending HTTP request: Header overflow");
1974 nva[2].name = (unsigned char *)":scheme";
1975 nva[2].namelen = strlen((char *)nva[2].name);
1976 if(conn->handler->flags & PROTOPT_SSL)
1977 nva[2].value = (unsigned char *)"https";
1979 nva[2].value = (unsigned char *)"http";
1980 nva[2].valuelen = strlen((char *)nva[2].value);
1981 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1982 if(HEADER_OVERFLOW(nva[2])) {
1983 failf(conn->data, "Failed sending HTTP request: Header overflow");
1989 while(i < nheader) {
1992 hdbuf = line_end + 2;
1994 /* check for next CR, but only within the piece of data left in the given
1996 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
1997 if(!line_end || (line_end == hdbuf))
2000 /* header continuation lines are not supported */
2001 if(*hdbuf == ' ' || *hdbuf == '\t')
2004 for(end = hdbuf; end < line_end && *end != ':'; ++end)
2006 if(end == hdbuf || end == line_end)
2010 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
2012 nva[i].name = (unsigned char *)":authority";
2013 nva[i].namelen = strlen((char *)nva[i].name);
2016 nva[i].namelen = (size_t)(end - hdbuf);
2017 /* Lower case the header name for HTTP/2 */
2018 Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
2019 nva[i].name = (unsigned char *)hdbuf;
2022 while(*hdbuf == ' ' || *hdbuf == '\t')
2026 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
2028 case HEADERINST_IGNORE:
2029 /* skip header fields prohibited by HTTP/2 specification. */
2032 case HEADERINST_TE_TRAILERS:
2033 nva[i].value = (uint8_t*)"trailers";
2034 nva[i].valuelen = sizeof("trailers") - 1;
2037 nva[i].value = (unsigned char *)hdbuf;
2038 nva[i].valuelen = (size_t)(end - hdbuf);
2041 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
2042 if(HEADER_OVERFLOW(nva[i])) {
2043 failf(conn->data, "Failed sending HTTP request: Header overflow");
2049 /* :authority must come before non-pseudo header fields */
2050 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
2051 nghttp2_nv authority = nva[authority_idx];
2052 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
2053 nva[i] = nva[i - 1];
2058 /* Warn stream may be rejected if cumulative length of headers is too large.
2059 It appears nghttp2 will not send a header frame larger than 64KB. */
2060 #define MAX_ACC 60000 /* <64KB to account for some overhead */
2064 for(i = 0; i < nheader; ++i) {
2065 acc += nva[i].namelen + nva[i].valuelen;
2067 H2BUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
2068 nva[i].namelen, nva[i].name,
2069 nva[i].valuelen, nva[i].value));
2073 infof(conn->data, "http2_send: Warning: The cumulative length of all "
2074 "headers exceeds %zu bytes and that could cause the "
2075 "stream to be rejected.\n", MAX_ACC);
2079 h2_pri_spec(conn->data, &pri_spec);
2081 H2BUGF(infof(conn->data, "http2_send request allowed %d (easy handle %p)\n",
2082 nghttp2_session_check_request_allowed(h2), (void *)conn->data));
2084 switch(conn->data->state.httpreq) {
2086 case HTTPREQ_POST_FORM:
2087 case HTTPREQ_POST_MIME:
2089 if(conn->data->state.infilesize != -1)
2090 stream->upload_left = conn->data->state.infilesize;
2092 /* data sending without specifying the data amount up front */
2093 stream->upload_left = -1; /* unknown, but not zero */
2095 data_prd.read_callback = data_source_read_callback;
2096 data_prd.source.ptr = NULL;
2097 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2098 &data_prd, conn->data);
2101 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2108 H2BUGF(infof(conn->data,
2109 "http2_send() nghttp2_submit_request error (%s)%d\n",
2110 nghttp2_strerror(stream_id), stream_id));
2111 *err = CURLE_SEND_ERROR;
2115 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
2116 stream_id, (void *)conn->data);
2117 stream->stream_id = stream_id;
2119 /* this does not call h2_session_send() since there can not have been any
2120 * priority update since the nghttp2_submit_request() call above */
2121 rv = nghttp2_session_send(h2);
2123 H2BUGF(infof(conn->data,
2124 "http2_send() nghttp2_session_send error (%s)%d\n",
2125 nghttp2_strerror(rv), rv));
2127 *err = CURLE_SEND_ERROR;
2131 if(should_close_session(httpc)) {
2132 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
2137 /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
2138 library calls data_source_read_callback. But only it found that no data
2139 available, so it deferred the DATA transmission. Which means that
2140 nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
2141 results that no writable socket check is performed. To workaround this,
2142 we issue nghttp2_session_resume_data() here to bring back DATA
2143 transmission from deferred state. */
2144 nghttp2_session_resume_data(h2, stream->stream_id);
2150 *err = CURLE_SEND_ERROR;
2154 CURLcode Curl_http2_setup(struct connectdata *conn)
2157 struct http_conn *httpc = &conn->proto.httpc;
2158 struct HTTP *stream = conn->data->req.protop;
2160 DEBUGASSERT(conn->data->state.buffer);
2162 stream->stream_id = -1;
2164 Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
2165 Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
2167 if((conn->handler == &Curl_handler_http2_ssl) ||
2168 (conn->handler == &Curl_handler_http2))
2169 return CURLE_OK; /* already done */
2171 if(conn->handler->flags & PROTOPT_SSL)
2172 conn->handler = &Curl_handler_http2_ssl;
2174 conn->handler = &Curl_handler_http2;
2176 result = http2_init(conn);
2178 Curl_dyn_free(&stream->header_recvbuf);
2182 infof(conn->data, "Using HTTP2, server supports multi-use\n");
2183 stream->upload_left = 0;
2184 stream->upload_mem = NULL;
2185 stream->upload_len = 0;
2186 stream->mem = conn->data->state.buffer;
2187 stream->len = conn->data->set.buffer_size;
2189 httpc->inbuflen = 0;
2190 httpc->nread_inbuf = 0;
2192 httpc->pause_stream_id = 0;
2193 httpc->drain_total = 0;
2195 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2196 conn->httpversion = 20;
2197 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2199 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2200 multi_connchanged(conn->data->multi);
2205 CURLcode Curl_http2_switched(struct connectdata *conn,
2206 const char *mem, size_t nread)
2209 struct http_conn *httpc = &conn->proto.httpc;
2211 struct Curl_easy *data = conn->data;
2212 struct HTTP *stream = conn->data->req.protop;
2214 result = Curl_http2_setup(conn);
2218 httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2219 httpc->send_underlying = conn->send[FIRSTSOCKET];
2220 conn->recv[FIRSTSOCKET] = http2_recv;
2221 conn->send[FIRSTSOCKET] = http2_send;
2223 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2224 /* stream 1 is opened implicitly on upgrade */
2225 stream->stream_id = 1;
2226 /* queue SETTINGS frame (again) */
2227 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2228 httpc->binlen, NULL);
2230 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2231 nghttp2_strerror(rv), rv);
2235 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2239 infof(data, "http/2: failed to set user_data for stream %d!\n",
2245 populate_settings(conn, httpc);
2247 /* stream ID is unknown at this point */
2248 stream->stream_id = -1;
2249 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2250 httpc->local_settings,
2251 httpc->local_settings_num);
2253 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2254 nghttp2_strerror(rv), rv);
2259 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2260 HTTP2_HUGE_WINDOW_SIZE);
2262 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2263 nghttp2_strerror(rv), rv);
2267 /* we are going to copy mem to httpc->inbuf. This is required since
2268 mem is part of buffer pointed by stream->mem, and callbacks
2269 called by nghttp2_session_mem_recv() will write stream specific
2270 data into stream->mem, overwriting data already there. */
2271 if(H2_BUFSIZE < nread) {
2272 failf(data, "connection buffer size is too small to store data following "
2273 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2278 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2279 " after upgrade: len=%zu\n",
2283 memcpy(httpc->inbuf, mem, nread);
2285 httpc->inbuflen = nread;
2287 DEBUGASSERT(httpc->nread_inbuf == 0);
2289 if(-1 == h2_process_pending_input(conn, httpc, &result))
2295 CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
2298 DEBUGASSERT(data->conn);
2299 /* if it isn't HTTP/2, we're done */
2300 if(!data->conn->proto.httpc.h2)
2302 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2304 struct HTTP *stream = data->req.protop;
2305 struct http_conn *httpc = &data->conn->proto.httpc;
2306 uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
2307 int rv = nghttp2_session_set_local_window_size(httpc->h2,
2312 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2313 nghttp2_strerror(rv), rv);
2317 /* make sure the window update gets sent */
2318 rv = h2_session_send(data, httpc->h2);
2320 return CURLE_SEND_ERROR;
2322 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u\n",
2323 window, stream->stream_id));
2327 /* read out the stream local window again */
2329 nghttp2_session_get_stream_local_window_size(httpc->h2,
2331 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u\n",
2332 window2, stream->stream_id));
2340 CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2341 struct Curl_easy *child,
2345 struct Curl_http2_dep **tail;
2346 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2348 return CURLE_OUT_OF_MEMORY;
2351 if(parent->set.stream_dependents && exclusive) {
2352 struct Curl_http2_dep *node = parent->set.stream_dependents;
2354 node->data->set.stream_depends_on = child;
2358 tail = &child->set.stream_dependents;
2360 tail = &(*tail)->next;
2362 DEBUGASSERT(!*tail);
2363 *tail = parent->set.stream_dependents;
2364 parent->set.stream_dependents = 0;
2367 tail = &parent->set.stream_dependents;
2369 (*tail)->data->set.stream_depends_e = FALSE;
2370 tail = &(*tail)->next;
2373 DEBUGASSERT(!*tail);
2377 child->set.stream_depends_on = parent;
2378 child->set.stream_depends_e = exclusive;
2382 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2384 struct Curl_http2_dep *last = 0;
2385 struct Curl_http2_dep *data = parent->set.stream_dependents;
2386 DEBUGASSERT(child->set.stream_depends_on == parent);
2388 while(data && data->data != child) {
2397 last->next = data->next;
2400 parent->set.stream_dependents = data->next;
2405 child->set.stream_depends_on = 0;
2406 child->set.stream_depends_e = FALSE;
2409 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2411 while(data->set.stream_dependents) {
2412 struct Curl_easy *tmp = data->set.stream_dependents->data;
2413 Curl_http2_remove_child(data, tmp);
2414 if(data->set.stream_depends_on)
2415 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2418 if(data->set.stream_depends_on)
2419 Curl_http2_remove_child(data->set.stream_depends_on, data);
2422 /* Only call this function for a transfer that already got a HTTP/2
2423 CURLE_HTTP2_STREAM error! */
2424 bool Curl_h2_http_1_1_error(struct connectdata *conn)
2426 struct http_conn *httpc = &conn->proto.httpc;
2427 return (httpc->error_code == NGHTTP2_HTTP_1_1_REQUIRED);
2430 #else /* !USE_NGHTTP2 */
2432 /* Satisfy external references even if http2 is not compiled in. */
2433 #include <curl/curl.h>
2435 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2442 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2449 #endif /* USE_NGHTTP2 */