1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, 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"
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
44 #define MIN(x,y) ((x)<(y)?(x):(y))
46 #if (NGHTTP2_VERSION_NUM < 0x010000)
47 #error too old nghttp2 version, upgrade!
50 #if (NGHTTP2_VERSION_NUM > 0x010800)
51 #define NGHTTP2_HAS_HTTP2_STRERROR 1
54 #if (NGHTTP2_VERSION_NUM >= 0x010900)
55 /* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or
57 #define NGHTTP2_HAS_ERROR_CALLBACK 1
59 #define nghttp2_session_callbacks_set_error_callback(x,y)
62 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
63 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
66 #define HTTP2_HUGE_WINDOW_SIZE (1 << 30)
71 #define H2BUGF(x) do { } WHILE_FALSE
75 * Curl_http2_init_state() is called when the easy handle is created and
76 * allows for HTTP/2 specific init of state.
78 void Curl_http2_init_state(struct UrlState *state)
80 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
84 * Curl_http2_init_userset() is called when the easy handle is created and
85 * allows for HTTP/2 specific user-set fields.
87 void Curl_http2_init_userset(struct UserDefined *set)
89 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
92 static int http2_perform_getsock(const struct connectdata *conn,
93 curl_socket_t *sock, /* points to
99 const struct http_conn *c = &conn->proto.httpc;
100 int bitmap = GETSOCK_BLANK;
103 /* TODO We should check underlying socket state if it is SSL socket
104 because of renegotiation. */
105 sock[0] = conn->sock[FIRSTSOCKET];
107 /* in a HTTP/2 connection we can basically always get a frame so we should
108 always be ready for one */
109 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
111 if(nghttp2_session_want_write(c->h2))
112 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
117 static int http2_getsock(struct connectdata *conn,
118 curl_socket_t *sock, /* points to numsocks
122 return http2_perform_getsock(conn, sock, numsocks);
126 * http2_stream_free() free HTTP2 stream related data
128 static void http2_stream_free(struct HTTP *http)
131 Curl_add_buffer_free(http->header_recvbuf);
132 http->header_recvbuf = NULL; /* clear the pointer */
133 Curl_add_buffer_free(http->trailer_recvbuf);
134 http->trailer_recvbuf = NULL; /* clear the pointer */
135 for(; http->push_headers_used > 0; --http->push_headers_used) {
136 free(http->push_headers[http->push_headers_used - 1]);
138 free(http->push_headers);
139 http->push_headers = NULL;
143 static CURLcode http2_disconnect(struct connectdata *conn,
144 bool dead_connection)
146 struct http_conn *c = &conn->proto.httpc;
147 (void)dead_connection;
149 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
151 nghttp2_session_del(c->h2);
152 Curl_safefree(c->inbuf);
153 http2_stream_free(conn->data->req.protop);
155 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
161 * The server may send us data at any point (e.g. PING frames). Therefore,
162 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
164 * Instead, if it is readable, run Curl_connalive() to peek at the socket
165 * and distinguish between closed and data.
167 static bool http2_connisdead(struct connectdata *check)
172 sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
177 else if(sval & CURL_CSELECT_ERR) {
178 /* socket is in an error state */
181 else if(sval & CURL_CSELECT_IN) {
182 /* readable with no error. could still be closed */
183 ret_val = !Curl_connalive(check);
190 static unsigned int http2_conncheck(struct connectdata *check,
191 unsigned int checks_to_perform)
193 unsigned int ret_val = CONNRESULT_NONE;
195 if(checks_to_perform & CONNCHECK_ISDEAD) {
196 if(http2_connisdead(check))
197 ret_val |= CONNRESULT_DEAD;
203 /* called from Curl_http_setup_conn */
204 void Curl_http2_setup_req(struct Curl_easy *data)
206 struct HTTP *http = data->req.protop;
208 http->nread_header_recvbuf = 0;
209 http->bodystarted = FALSE;
210 http->status_code = -1;
211 http->pausedata = NULL;
213 http->closed = FALSE;
214 http->close_handled = FALSE;
215 http->mem = data->state.buffer;
216 http->len = data->set.buffer_size;
220 /* called from Curl_http_setup_conn */
221 void Curl_http2_setup_conn(struct connectdata *conn)
223 conn->proto.httpc.settings.max_concurrent_streams =
224 DEFAULT_MAX_CONCURRENT_STREAMS;
225 conn->proto.httpc.error_code = NGHTTP2_NO_ERROR;
229 * HTTP2 handler interface. This isn't added to the general list of protocols
230 * but will be used at run-time when the protocol is dynamically switched from
233 static const struct Curl_handler Curl_handler_http2 = {
235 ZERO_NULL, /* setup_connection */
236 Curl_http, /* do_it */
237 Curl_http_done, /* done */
238 ZERO_NULL, /* do_more */
239 ZERO_NULL, /* connect_it */
240 ZERO_NULL, /* connecting */
241 ZERO_NULL, /* doing */
242 http2_getsock, /* proto_getsock */
243 http2_getsock, /* doing_getsock */
244 ZERO_NULL, /* domore_getsock */
245 http2_perform_getsock, /* perform_getsock */
246 http2_disconnect, /* disconnect */
247 ZERO_NULL, /* readwrite */
248 http2_conncheck, /* connection_check */
249 PORT_HTTP, /* defport */
250 CURLPROTO_HTTP, /* protocol */
251 PROTOPT_STREAM /* flags */
254 static const struct Curl_handler Curl_handler_http2_ssl = {
255 "HTTPS", /* scheme */
256 ZERO_NULL, /* setup_connection */
257 Curl_http, /* do_it */
258 Curl_http_done, /* done */
259 ZERO_NULL, /* do_more */
260 ZERO_NULL, /* connect_it */
261 ZERO_NULL, /* connecting */
262 ZERO_NULL, /* doing */
263 http2_getsock, /* proto_getsock */
264 http2_getsock, /* doing_getsock */
265 ZERO_NULL, /* domore_getsock */
266 http2_perform_getsock, /* perform_getsock */
267 http2_disconnect, /* disconnect */
268 ZERO_NULL, /* readwrite */
269 http2_conncheck, /* connection_check */
270 PORT_HTTP, /* defport */
271 CURLPROTO_HTTPS, /* protocol */
272 PROTOPT_SSL | PROTOPT_STREAM /* flags */
276 * Store nghttp2 version info in this buffer, Prefix with a space. Return
277 * total length written.
279 int Curl_http2_ver(char *p, size_t len)
281 nghttp2_info *h2 = nghttp2_version(0);
282 return snprintf(p, len, " nghttp2/%s", h2->version_str);
285 /* HTTP/2 error code to name based on the Error Code Registry.
286 https://tools.ietf.org/html/rfc7540#page-77
287 nghttp2_error_code enums are identical.
289 const char *Curl_http2_strerror(uint32_t err)
291 #ifndef NGHTTP2_HAS_HTTP2_STRERROR
292 const char *str[] = {
293 "NO_ERROR", /* 0x0 */
294 "PROTOCOL_ERROR", /* 0x1 */
295 "INTERNAL_ERROR", /* 0x2 */
296 "FLOW_CONTROL_ERROR", /* 0x3 */
297 "SETTINGS_TIMEOUT", /* 0x4 */
298 "STREAM_CLOSED", /* 0x5 */
299 "FRAME_SIZE_ERROR", /* 0x6 */
300 "REFUSED_STREAM", /* 0x7 */
302 "COMPRESSION_ERROR", /* 0x9 */
303 "CONNECT_ERROR", /* 0xA */
304 "ENHANCE_YOUR_CALM", /* 0xB */
305 "INADEQUATE_SECURITY", /* 0xC */
306 "HTTP_1_1_REQUIRED" /* 0xD */
308 return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown";
310 return nghttp2_http2_strerror(err);
315 * The implementation of nghttp2_send_callback type. Here we write |data| with
316 * size |length| to the network and return the number of bytes actually
317 * written. See the documentation of nghttp2_send_callback for the details.
319 static ssize_t send_callback(nghttp2_session *h2,
320 const uint8_t *data, size_t length, int flags,
323 struct connectdata *conn = (struct connectdata *)userp;
324 struct http_conn *c = &conn->proto.httpc;
326 CURLcode result = CURLE_OK;
331 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
332 data, length, &result);
334 if(result == CURLE_AGAIN) {
335 return NGHTTP2_ERR_WOULDBLOCK;
339 failf(conn->data, "Failed sending HTTP2 data");
340 return NGHTTP2_ERR_CALLBACK_FAILURE;
344 return NGHTTP2_ERR_WOULDBLOCK;
350 /* We pass a pointer to this struct in the push callback, but the contents of
351 the struct are hidden from the user. */
352 struct curl_pushheaders {
353 struct Curl_easy *data;
354 const nghttp2_push_promise *frame;
358 * push header access function. Only to be used from within the push callback
360 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
362 /* Verify that we got a good easy handle in the push header struct, mostly to
363 detect rubbish input fast(er). */
364 if(!h || !GOOD_EASY_HANDLE(h->data))
367 struct HTTP *stream = h->data->req.protop;
368 if(num < stream->push_headers_used)
369 return stream->push_headers[num];
375 * push header access function. Only to be used from within the push callback
377 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
379 /* Verify that we got a good easy handle in the push header struct,
380 mostly to detect rubbish input fast(er). Also empty header name
381 is just a rubbish too. We have to allow ":" at the beginning of
382 the header, but header == ":" must be rejected. If we have ':' in
383 the middle of header, it could be matched in middle of the value,
384 this is because we do prefix match.*/
385 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
386 !strcmp(header, ":") || strchr(header + 1, ':'))
389 struct HTTP *stream = h->data->req.protop;
390 size_t len = strlen(header);
392 for(i = 0; i<stream->push_headers_used; i++) {
393 if(!strncmp(header, stream->push_headers[i], len)) {
394 /* sub-match, make sure that it is followed by a colon */
395 if(stream->push_headers[i][len] != ':')
397 return &stream->push_headers[i][len + 1];
404 static struct Curl_easy *duphandle(struct Curl_easy *data)
406 struct Curl_easy *second = curl_easy_duphandle(data);
408 /* setup the request struct */
409 struct HTTP *http = calloc(1, sizeof(struct HTTP));
411 (void)Curl_close(second);
415 second->req.protop = http;
416 http->header_recvbuf = Curl_add_buffer_init();
417 if(!http->header_recvbuf) {
419 (void)Curl_close(second);
423 Curl_http2_setup_req(second);
424 second->state.stream_weight = data->state.stream_weight;
432 static int push_promise(struct Curl_easy *data,
433 struct connectdata *conn,
434 const nghttp2_push_promise *frame)
437 H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
438 frame->promised_stream_id));
439 if(data->multi->push_cb) {
441 struct HTTP *newstream;
442 struct curl_pushheaders heads;
444 struct http_conn *httpc;
446 /* clone the parent */
447 struct Curl_easy *newhandle = duphandle(data);
449 infof(data, "failed to duplicate handle\n");
450 rv = 1; /* FAIL HARD */
456 /* ask the application */
457 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
459 stream = data->req.protop;
461 failf(data, "Internal NULL stream!\n");
462 (void)Curl_close(newhandle);
467 Curl_set_in_callback(data, true);
468 rv = data->multi->push_cb(data, newhandle,
469 stream->push_headers_used, &heads,
470 data->multi->push_userp);
471 Curl_set_in_callback(data, false);
473 /* free the headers again */
474 for(i = 0; i<stream->push_headers_used; i++)
475 free(stream->push_headers[i]);
476 free(stream->push_headers);
477 stream->push_headers = NULL;
478 stream->push_headers_used = 0;
481 /* denied, kill off the new handle again */
482 http2_stream_free(newhandle->req.protop);
483 (void)Curl_close(newhandle);
487 newstream = newhandle->req.protop;
488 newstream->stream_id = frame->promised_stream_id;
489 newhandle->req.maxdownload = -1;
490 newhandle->req.size = -1;
492 /* approved, add to the multi handle and immediately switch to PERFORM
493 state with the given connection !*/
494 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
496 infof(data, "failed to add handle to multi\n");
497 http2_stream_free(newhandle->req.protop);
498 Curl_close(newhandle);
503 httpc = &conn->proto.httpc;
504 nghttp2_session_set_stream_user_data(httpc->h2,
505 frame->promised_stream_id, newhandle);
508 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
515 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
518 struct connectdata *conn = (struct connectdata *)userp;
519 struct http_conn *httpc = &conn->proto.httpc;
520 struct Curl_easy *data_s = NULL;
521 struct HTTP *stream = NULL;
522 static int lastStream = -1;
525 int32_t stream_id = frame->hd.stream_id;
528 /* stream ID zero is for connection-oriented stuff */
529 if(frame->hd.type == NGHTTP2_SETTINGS) {
530 uint32_t max_conn = httpc->settings.max_concurrent_streams;
531 H2BUGF(infof(conn->data, "Got SETTINGS\n"));
532 httpc->settings.max_concurrent_streams =
533 nghttp2_session_get_remote_settings(
534 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
535 httpc->settings.enable_push =
536 nghttp2_session_get_remote_settings(
537 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
538 H2BUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
539 httpc->settings.max_concurrent_streams));
540 H2BUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
541 httpc->settings.enable_push?"TRUE":"false"));
542 if(max_conn != httpc->settings.max_concurrent_streams) {
543 /* only signal change if the value actually changed */
545 "Connection state changed (MAX_CONCURRENT_STREAMS == %d)!\n",
546 httpc->settings.max_concurrent_streams);
547 Curl_multi_connchanged(conn->data->multi);
552 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
553 if(lastStream != stream_id) {
554 lastStream = stream_id;
557 H2BUGF(infof(conn->data,
558 "No Curl_easy associated with stream: %x\n",
563 stream = data_s->req.protop;
565 H2BUGF(infof(conn->data, "No proto pointer for stream: %x\n",
567 return NGHTTP2_ERR_CALLBACK_FAILURE;
570 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
571 frame->hd.type, stream_id));
573 switch(frame->hd.type) {
575 /* If body started on this stream, then receiving DATA is illegal. */
576 if(!stream->bodystarted) {
577 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
578 stream_id, NGHTTP2_PROTOCOL_ERROR);
580 if(nghttp2_is_fatal(rv)) {
581 return NGHTTP2_ERR_CALLBACK_FAILURE;
585 case NGHTTP2_HEADERS:
586 if(stream->bodystarted) {
587 /* Only valid HEADERS after body started is trailer HEADERS. We
588 buffer them in on_header callback. */
592 /* nghttp2 guarantees that :status is received, and we store it to
593 stream->status_code */
594 DEBUGASSERT(stream->status_code != -1);
596 /* Only final status code signals the end of header */
597 if(stream->status_code / 100 != 1) {
598 stream->bodystarted = TRUE;
599 stream->status_code = -1;
602 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
604 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
605 ncopy = MIN(stream->len, left);
607 memcpy(&stream->mem[stream->memlen],
608 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
610 stream->nread_header_recvbuf += ncopy;
612 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
613 ncopy, stream_id, stream->mem));
615 stream->len -= ncopy;
616 stream->memlen += ncopy;
618 data_s->state.drain++;
619 httpc->drain_total++;
621 /* get the pointer from userp again since it was re-assigned above */
622 struct connectdata *conn_s = (struct connectdata *)userp;
624 /* if we receive data for another handle, wake that up */
625 if(conn_s->data != data_s)
626 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
629 case NGHTTP2_PUSH_PROMISE:
630 rv = push_promise(data_s, conn, &frame->push_promise);
632 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
633 frame->push_promise.promised_stream_id,
635 if(nghttp2_is_fatal(rv)) {
641 H2BUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
642 frame->hd.type, stream_id));
648 static int on_invalid_frame_recv(nghttp2_session *session,
649 const nghttp2_frame *frame,
650 int lib_error_code, void *userp)
652 struct Curl_easy *data_s = NULL;
654 #if !defined(DEBUG_HTTP2) || defined(CURL_DISABLE_VERBOSE_STRINGS)
655 (void)lib_error_code;
658 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
661 "on_invalid_frame_recv() was called, error=%d:%s\n",
662 lib_error_code, nghttp2_strerror(lib_error_code)));
667 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
669 const uint8_t *data, size_t len, void *userp)
672 struct Curl_easy *data_s;
674 struct connectdata *conn = (struct connectdata *)userp;
679 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
681 /* get the stream from the hash based on Stream ID */
682 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
684 /* Receiving a Stream ID not in the hash should not happen, this is an
685 internal error more than anything else! */
686 return NGHTTP2_ERR_CALLBACK_FAILURE;
688 stream = data_s->req.protop;
690 return NGHTTP2_ERR_CALLBACK_FAILURE;
692 nread = MIN(stream->len, len);
693 memcpy(&stream->mem[stream->memlen], data, nread);
695 stream->len -= nread;
696 stream->memlen += nread;
698 data_s->state.drain++;
699 conn->proto.httpc.drain_total++;
701 /* if we receive data for another handle, wake that up */
702 if(conn->data != data_s)
703 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
705 H2BUGF(infof(data_s, "%zu data received for stream %u "
706 "(%zu left in buffer %p, total %zu)\n",
708 stream->len, stream->mem,
712 stream->pausedata = data + nread;
713 stream->pauselen = len - nread;
714 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
716 len - nread, stream_id));
717 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
719 return NGHTTP2_ERR_PAUSE;
722 /* pause execution of nghttp2 if we received data for another handle
723 in order to process them first. */
724 if(conn->data != data_s) {
725 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
727 return NGHTTP2_ERR_PAUSE;
733 static int before_frame_send(nghttp2_session *session,
734 const nghttp2_frame *frame,
737 struct Curl_easy *data_s;
740 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
742 H2BUGF(infof(data_s, "before_frame_send() was called\n"));
747 static int on_frame_send(nghttp2_session *session,
748 const nghttp2_frame *frame,
751 struct Curl_easy *data_s;
754 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
756 H2BUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
761 static int on_frame_not_send(nghttp2_session *session,
762 const nghttp2_frame *frame,
763 int lib_error_code, void *userp)
765 struct Curl_easy *data_s;
767 #if !defined(DEBUG_HTTP2) || defined(CURL_DISABLE_VERBOSE_STRINGS)
768 (void)lib_error_code;
771 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
774 "on_frame_not_send() was called, lib_error_code = %d\n",
779 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
780 uint32_t error_code, void *userp)
782 struct Curl_easy *data_s;
784 struct connectdata *conn = (struct connectdata *)userp;
789 struct http_conn *httpc;
790 /* get the stream from the hash based on Stream ID, stream ID zero is for
791 connection-oriented stuff */
792 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
794 /* We could get stream ID not in the hash. For example, if we
795 decided to reject stream (e.g., PUSH_PROMISE). */
798 H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
799 Curl_http2_strerror(error_code), error_code, stream_id));
800 stream = data_s->req.protop;
802 return NGHTTP2_ERR_CALLBACK_FAILURE;
804 stream->closed = TRUE;
805 data_s->state.drain++;
806 httpc = &conn->proto.httpc;
807 httpc->drain_total++;
808 httpc->error_code = error_code;
810 /* remove the entry from the hash as the stream is now gone */
811 nghttp2_session_set_stream_user_data(session, stream_id, 0);
812 H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
817 static int on_begin_headers(nghttp2_session *session,
818 const nghttp2_frame *frame, void *userp)
821 struct Curl_easy *data_s = NULL;
824 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
829 H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
831 if(frame->hd.type != NGHTTP2_HEADERS) {
835 stream = data_s->req.protop;
836 if(!stream || !stream->bodystarted) {
840 /* This is trailer HEADERS started. Allocate buffer for them. */
841 H2BUGF(infof(data_s, "trailer field started\n"));
843 DEBUGASSERT(stream->trailer_recvbuf == NULL);
845 stream->trailer_recvbuf = Curl_add_buffer_init();
846 if(!stream->trailer_recvbuf) {
847 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
853 /* Decode HTTP status code. Returns -1 if no valid status code was
855 static int decode_status_code(const uint8_t *value, size_t len)
866 for(i = 0; i < 3; ++i) {
869 if(c < '0' || c > '9') {
880 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
881 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
882 const uint8_t *name, size_t namelen,
883 const uint8_t *value, size_t valuelen,
888 struct Curl_easy *data_s;
889 int32_t stream_id = frame->hd.stream_id;
890 struct connectdata *conn = (struct connectdata *)userp;
893 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
895 /* get the stream from the hash based on Stream ID */
896 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
898 /* Receiving a Stream ID not in the hash should not happen, this is an
899 internal error more than anything else! */
900 return NGHTTP2_ERR_CALLBACK_FAILURE;
902 stream = data_s->req.protop;
904 failf(data_s, "Internal NULL stream! 5\n");
905 return NGHTTP2_ERR_CALLBACK_FAILURE;
908 /* Store received PUSH_PROMISE headers to be used when the subsequent
909 PUSH_PROMISE callback comes */
910 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
913 if(!stream->push_headers) {
914 stream->push_headers_alloc = 10;
915 stream->push_headers = malloc(stream->push_headers_alloc *
917 stream->push_headers_used = 0;
919 else if(stream->push_headers_used ==
920 stream->push_headers_alloc) {
922 stream->push_headers_alloc *= 2;
923 headp = Curl_saferealloc(stream->push_headers,
924 stream->push_headers_alloc * sizeof(char *));
926 stream->push_headers = NULL;
927 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
929 stream->push_headers = headp;
931 h = aprintf("%s:%s", name, value);
933 stream->push_headers[stream->push_headers_used++] = h;
937 if(stream->bodystarted) {
938 /* This is trailer fields. */
939 /* 4 is for ": " and "\r\n". */
940 uint32_t n = (uint32_t)(namelen + valuelen + 4);
942 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
945 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
946 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
947 Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
948 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
949 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
954 if(namelen == sizeof(":status") - 1 &&
955 memcmp(":status", name, namelen) == 0) {
956 /* nghttp2 guarantees :status is received first and only once, and
957 value is 3 digits status code, and decode_status_code always
959 stream->status_code = decode_status_code(value, valuelen);
960 DEBUGASSERT(stream->status_code != -1);
962 Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
963 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
964 /* the space character after the status code is mandatory */
965 Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
966 /* if we receive data for another handle, wake that up */
967 if(conn->data != data_s)
968 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
970 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
971 stream->status_code, data_s));
975 /* nghttp2 guarantees that namelen > 0, and :status was already
976 received, and this is not pseudo-header field . */
977 /* convert to a HTTP1-style header */
978 Curl_add_buffer(stream->header_recvbuf, name, namelen);
979 Curl_add_buffer(stream->header_recvbuf, ": ", 2);
980 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
981 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
982 /* if we receive data for another handle, wake that up */
983 if(conn->data != data_s)
984 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
986 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
989 return 0; /* 0 is successful */
992 static ssize_t data_source_read_callback(nghttp2_session *session,
994 uint8_t *buf, size_t length,
995 uint32_t *data_flags,
996 nghttp2_data_source *source,
999 struct Curl_easy *data_s;
1000 struct HTTP *stream = NULL;
1006 /* get the stream from the hash based on Stream ID, stream ID zero is for
1007 connection-oriented stuff */
1008 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1010 /* Receiving a Stream ID not in the hash should not happen, this is an
1011 internal error more than anything else! */
1012 return NGHTTP2_ERR_CALLBACK_FAILURE;
1014 stream = data_s->req.protop;
1016 return NGHTTP2_ERR_CALLBACK_FAILURE;
1019 return NGHTTP2_ERR_INVALID_ARGUMENT;
1021 nread = MIN(stream->upload_len, length);
1023 memcpy(buf, stream->upload_mem, nread);
1024 stream->upload_mem += nread;
1025 stream->upload_len -= nread;
1026 if(data_s->state.infilesize != -1)
1027 stream->upload_left -= nread;
1030 if(stream->upload_left == 0)
1031 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1033 return NGHTTP2_ERR_DEFERRED;
1035 H2BUGF(infof(data_s, "data_source_read_callback: "
1036 "returns %zu bytes stream %u\n",
1042 #define H2_BUFSIZE 32768
1044 #ifdef NGHTTP2_HAS_ERROR_CALLBACK
1045 static int error_callback(nghttp2_session *session,
1050 struct connectdata *conn = (struct connectdata *)userp;
1052 infof(conn->data, "http2 error: %.*s\n", len, msg);
1057 static void populate_settings(struct connectdata *conn,
1058 struct http_conn *httpc)
1060 nghttp2_settings_entry *iv = httpc->local_settings;
1062 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1065 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1066 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1068 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1069 iv[2].value = conn->data->multi->push_cb != NULL;
1071 httpc->local_settings_num = 3;
1074 void Curl_http2_done(struct connectdata *conn, bool premature)
1076 struct Curl_easy *data = conn->data;
1077 struct HTTP *http = data->req.protop;
1078 struct http_conn *httpc = &conn->proto.httpc;
1080 if(http->header_recvbuf) {
1081 H2BUGF(infof(data, "free header_recvbuf!!\n"));
1082 Curl_add_buffer_free(http->header_recvbuf);
1083 http->header_recvbuf = NULL; /* clear the pointer */
1084 Curl_add_buffer_free(http->trailer_recvbuf);
1085 http->trailer_recvbuf = NULL; /* clear the pointer */
1086 if(http->push_headers) {
1087 /* if they weren't used and then freed before */
1088 for(; http->push_headers_used > 0; --http->push_headers_used) {
1089 free(http->push_headers[http->push_headers_used - 1]);
1091 free(http->push_headers);
1092 http->push_headers = NULL;
1098 nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
1099 NGHTTP2_STREAM_CLOSED);
1100 if(http->stream_id == httpc->pause_stream_id) {
1101 infof(data, "stopped the pause stream!\n");
1102 httpc->pause_stream_id = 0;
1105 if(http->stream_id) {
1106 nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
1107 http->stream_id = 0;
1112 * Initialize nghttp2 for a Curl connection
1114 CURLcode Curl_http2_init(struct connectdata *conn)
1116 if(!conn->proto.httpc.h2) {
1118 nghttp2_session_callbacks *callbacks;
1120 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1121 if(conn->proto.httpc.inbuf == NULL)
1122 return CURLE_OUT_OF_MEMORY;
1124 rc = nghttp2_session_callbacks_new(&callbacks);
1127 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1128 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1131 /* nghttp2_send_callback */
1132 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1133 /* nghttp2_on_frame_recv_callback */
1134 nghttp2_session_callbacks_set_on_frame_recv_callback
1135 (callbacks, on_frame_recv);
1136 /* nghttp2_on_invalid_frame_recv_callback */
1137 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
1138 (callbacks, on_invalid_frame_recv);
1139 /* nghttp2_on_data_chunk_recv_callback */
1140 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1141 (callbacks, on_data_chunk_recv);
1142 /* nghttp2_before_frame_send_callback */
1143 nghttp2_session_callbacks_set_before_frame_send_callback
1144 (callbacks, before_frame_send);
1145 /* nghttp2_on_frame_send_callback */
1146 nghttp2_session_callbacks_set_on_frame_send_callback
1147 (callbacks, on_frame_send);
1148 /* nghttp2_on_frame_not_send_callback */
1149 nghttp2_session_callbacks_set_on_frame_not_send_callback
1150 (callbacks, on_frame_not_send);
1151 /* nghttp2_on_stream_close_callback */
1152 nghttp2_session_callbacks_set_on_stream_close_callback
1153 (callbacks, on_stream_close);
1154 /* nghttp2_on_begin_headers_callback */
1155 nghttp2_session_callbacks_set_on_begin_headers_callback
1156 (callbacks, on_begin_headers);
1157 /* nghttp2_on_header_callback */
1158 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1160 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1162 /* The nghttp2 session is not yet setup, do it */
1163 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1165 nghttp2_session_callbacks_del(callbacks);
1168 failf(conn->data, "Couldn't initialize nghttp2!");
1169 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1176 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1178 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1179 struct connectdata *conn)
1185 struct SingleRequest *k = &conn->data->req;
1186 uint8_t *binsettings = conn->proto.httpc.binsettings;
1187 struct http_conn *httpc = &conn->proto.httpc;
1189 populate_settings(conn, httpc);
1191 /* this returns number of bytes it wrote */
1192 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1193 httpc->local_settings,
1194 httpc->local_settings_num);
1196 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1197 Curl_add_buffer_free(req);
1198 return CURLE_FAILED_INIT;
1200 conn->proto.httpc.binlen = binlen;
1202 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1205 Curl_add_buffer_free(req);
1209 result = Curl_add_bufferf(req,
1210 "Connection: Upgrade, HTTP2-Settings\r\n"
1212 "HTTP2-Settings: %s\r\n",
1213 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1216 k->upgr101 = UPGR101_REQUESTED;
1222 * Returns nonzero if current HTTP/2 session should be closed.
1224 static int should_close_session(struct http_conn *httpc)
1226 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1227 !nghttp2_session_want_write(httpc->h2);
1230 static int h2_session_send(struct Curl_easy *data,
1231 nghttp2_session *h2);
1234 * h2_process_pending_input() processes pending input left in
1235 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1236 * This function returns 0 if it succeeds, or -1 and error code will
1237 * be assigned to *err.
1239 static int h2_process_pending_input(struct connectdata *conn,
1240 struct http_conn *httpc,
1246 struct Curl_easy *data = conn->data;
1248 nread = httpc->inbuflen - httpc->nread_inbuf;
1249 inbuf = httpc->inbuf + httpc->nread_inbuf;
1251 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1254 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1255 "%d:%s\n", rv, nghttp2_strerror((int)rv));
1256 *err = CURLE_RECV_ERROR;
1262 "h2_process_pending_input: All data in connection buffer "
1264 httpc->inbuflen = 0;
1265 httpc->nread_inbuf = 0;
1268 httpc->nread_inbuf += rv;
1270 "h2_process_pending_input: %zu bytes left in connection "
1272 httpc->inbuflen - httpc->nread_inbuf));
1275 rv = h2_session_send(data, httpc->h2);
1277 *err = CURLE_SEND_ERROR;
1281 if(should_close_session(httpc)) {
1283 "h2_process_pending_input: nothing to do in this session\n"));
1284 if(httpc->error_code)
1287 /* not an error per se, but should still close the connection */
1288 connclose(conn, "GOAWAY received");
1298 * Called from transfer.c:done_sending when we stop uploading.
1300 CURLcode Curl_http2_done_sending(struct connectdata *conn)
1302 CURLcode result = CURLE_OK;
1304 if((conn->handler == &Curl_handler_http2_ssl) ||
1305 (conn->handler == &Curl_handler_http2)) {
1306 /* make sure this is only attempted for HTTP/2 transfers */
1308 struct HTTP *stream = conn->data->req.protop;
1310 if(stream->upload_left) {
1311 /* If the stream still thinks there's data left to upload. */
1312 struct http_conn *httpc = &conn->proto.httpc;
1313 nghttp2_session *h2 = httpc->h2;
1315 stream->upload_left = 0; /* DONE! */
1317 /* resume sending here to trigger the callback to get called again so
1318 that it can signal EOF to nghttp2 */
1319 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1321 (void)h2_process_pending_input(conn, httpc, &result);
1328 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1329 struct Curl_easy *data,
1330 struct HTTP *stream, CURLcode *err)
1332 char *trailer_pos, *trailer_end;
1334 struct http_conn *httpc = &conn->proto.httpc;
1336 if(httpc->pause_stream_id == stream->stream_id) {
1337 httpc->pause_stream_id = 0;
1340 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1341 httpc->drain_total -= data->state.drain;
1342 data->state.drain = 0;
1344 if(httpc->pause_stream_id == 0) {
1345 if(h2_process_pending_input(conn, httpc, err) != 0) {
1350 DEBUGASSERT(data->state.drain == 0);
1352 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1353 stream->closed = FALSE;
1354 if(httpc->error_code != NGHTTP2_NO_ERROR) {
1355 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
1356 stream->stream_id, Curl_http2_strerror(httpc->error_code),
1358 *err = CURLE_HTTP2_STREAM;
1362 if(!stream->bodystarted) {
1363 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1364 " all response header fields, teated as error",
1366 *err = CURLE_HTTP2_STREAM;
1370 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1371 trailer_pos = stream->trailer_recvbuf->buffer;
1372 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1374 for(; trailer_pos < trailer_end;) {
1376 memcpy(&n, trailer_pos, sizeof(n));
1377 trailer_pos += sizeof(n);
1379 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1385 trailer_pos += n + 1;
1389 stream->close_handled = TRUE;
1391 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1396 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1397 * and dependency to the peer. It also stores the updated values in the state
1401 static void h2_pri_spec(struct Curl_easy *data,
1402 nghttp2_priority_spec *pri_spec)
1404 struct HTTP *depstream = (data->set.stream_depends_on?
1405 data->set.stream_depends_on->req.protop:NULL);
1406 int32_t depstream_id = depstream? depstream->stream_id:0;
1407 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1408 data->set.stream_depends_e);
1409 data->state.stream_weight = data->set.stream_weight;
1410 data->state.stream_depends_e = data->set.stream_depends_e;
1411 data->state.stream_depends_on = data->set.stream_depends_on;
1415 * h2_session_send() checks if there's been an update in the priority /
1416 * dependency settings and if so it submits a PRIORITY frame with the updated
1419 static int h2_session_send(struct Curl_easy *data,
1420 nghttp2_session *h2)
1422 struct HTTP *stream = data->req.protop;
1423 if((data->set.stream_weight != data->state.stream_weight) ||
1424 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1425 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1426 /* send new weight and/or dependency */
1427 nghttp2_priority_spec pri_spec;
1430 h2_pri_spec(data, &pri_spec);
1432 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1433 stream->stream_id, data));
1434 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1440 return nghttp2_session_send(h2);
1443 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1444 char *mem, size_t len, CURLcode *err)
1446 CURLcode result = CURLE_OK;
1449 struct http_conn *httpc = &conn->proto.httpc;
1450 struct Curl_easy *data = conn->data;
1451 struct HTTP *stream = data->req.protop;
1453 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1455 if(should_close_session(httpc)) {
1457 "http2_recv: nothing to do in this session\n"));
1462 /* Nullify here because we call nghttp2_session_send() and they
1463 might refer to the old buffer. */
1464 stream->upload_mem = NULL;
1465 stream->upload_len = 0;
1468 * At this point 'stream' is just in the Curl_easy the connection
1469 * identifies as its owner at this time.
1472 if(stream->bodystarted &&
1473 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1474 /* If there is body data pending for this stream to return, do that */
1476 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1477 size_t ncopy = MIN(len, left);
1478 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1480 stream->nread_header_recvbuf += ncopy;
1482 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1487 H2BUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1488 data, stream->stream_id));
1490 if((data->state.drain) && stream->memlen) {
1491 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1492 stream->memlen, stream->stream_id,
1494 if(mem != stream->mem) {
1495 /* if we didn't get the same buffer this time, we must move the data to
1497 memmove(mem, stream->mem, stream->memlen);
1498 stream->len = len - stream->memlen;
1501 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1502 /* We have paused nghttp2, but we have no pause data (see
1503 on_data_chunk_recv). */
1504 httpc->pause_stream_id = 0;
1505 if(h2_process_pending_input(conn, httpc, &result) != 0) {
1511 else if(stream->pausedata) {
1512 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1513 nread = MIN(len, stream->pauselen);
1514 memcpy(mem, stream->pausedata, nread);
1516 stream->pausedata += nread;
1517 stream->pauselen -= nread;
1519 infof(data, "%zu data bytes written\n", nread);
1520 if(stream->pauselen == 0) {
1521 H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1522 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1523 httpc->pause_stream_id = 0;
1525 stream->pausedata = NULL;
1526 stream->pauselen = 0;
1528 /* When NGHTTP2_ERR_PAUSE is returned from
1529 data_source_read_callback, we might not process DATA frame
1530 fully. Calling nghttp2_session_mem_recv() again will
1531 continue to process DATA frame, but if there is no incoming
1532 frames, then we have to call it again with 0-length data.
1533 Without this, on_stream_close callback will not be called,
1534 and stream could be hanged. */
1535 if(h2_process_pending_input(conn, httpc, &result) != 0) {
1540 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1541 nread, stream->stream_id));
1544 else if(httpc->pause_stream_id) {
1545 /* If a stream paused nghttp2_session_mem_recv previously, and has
1546 not processed all data, it still refers to the buffer in
1547 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1548 overwrite that buffer. To avoid that situation, just return
1549 here with CURLE_AGAIN. This could be busy loop since data in
1550 socket is not read. But it seems that usually streams are
1551 notified with its drain property, and socket is read again
1553 H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
1554 stream->stream_id, httpc->pause_stream_id));
1560 /* remember where to store incoming data for this stream and how big the
1566 if(httpc->inbuflen == 0) {
1567 nread = ((Curl_recv *)httpc->recv_underlying)(
1568 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1571 if(result != CURLE_AGAIN)
1572 failf(data, "Failed receiving HTTP2 data");
1573 else if(stream->closed)
1574 /* received when the stream was already closed! */
1575 return http2_handle_stream_close(conn, data, stream, err);
1582 failf(data, "Unexpected EOF");
1583 *err = CURLE_RECV_ERROR;
1587 H2BUGF(infof(data, "nread=%zd\n", nread));
1589 httpc->inbuflen = nread;
1590 inbuf = httpc->inbuf;
1593 nread = httpc->inbuflen - httpc->nread_inbuf;
1594 inbuf = httpc->inbuf + httpc->nread_inbuf;
1596 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1599 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1601 if(nghttp2_is_fatal((int)rv)) {
1602 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1603 rv, nghttp2_strerror((int)rv));
1604 *err = CURLE_RECV_ERROR;
1607 H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1609 H2BUGF(infof(data, "All data in connection buffer processed\n"));
1610 httpc->inbuflen = 0;
1611 httpc->nread_inbuf = 0;
1614 httpc->nread_inbuf += rv;
1615 H2BUGF(infof(data, "%zu bytes left in connection buffer\n",
1616 httpc->inbuflen - httpc->nread_inbuf));
1618 /* Always send pending frames in nghttp2 session, because
1619 nghttp2_session_mem_recv() may queue new frame */
1620 rv = h2_session_send(data, httpc->h2);
1622 *err = CURLE_SEND_ERROR;
1626 if(should_close_session(httpc)) {
1627 H2BUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1632 if(stream->memlen) {
1633 ssize_t retlen = stream->memlen;
1634 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1635 retlen, stream->stream_id));
1638 if(httpc->pause_stream_id == stream->stream_id) {
1639 /* data for this stream is returned now, but this stream caused a pause
1640 already so we need it called again asap */
1641 H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
1642 stream->stream_id));
1644 else if(!stream->closed) {
1645 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1646 httpc->drain_total -= data->state.drain;
1647 data->state.drain = 0; /* this stream is hereby drained */
1652 /* If stream is closed, return 0 to signal the http routine to close
1654 if(stream->closed) {
1655 return http2_handle_stream_close(conn, data, stream, err);
1658 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1659 stream->stream_id));
1663 /* Index where :authority header field will appear in request header
1665 #define AUTHORITY_DST_IDX 3
1667 #define HEADER_OVERFLOW(x) \
1668 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1671 * Check header memory for the token "trailers".
1672 * Parse the tokens as separated by comma and surrounded by whitespace.
1673 * Returns TRUE if found or FALSE if not.
1675 static bool contains_trailers(const char *p, size_t len)
1677 const char *end = p + len;
1679 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1681 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1683 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1684 p += sizeof("trailers") - 1;
1685 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1687 if(p == end || *p == ',')
1690 /* skip to next token */
1691 for(; p != end && *p != ','; ++p)
1700 /* Send header to server */
1702 /* Don't send header to server */
1704 /* Discard header, and replace it with "te: trailers" */
1705 HEADERINST_TE_TRAILERS
1706 } header_instruction;
1708 /* Decides how to treat given header field. */
1709 static header_instruction inspect_header(const char *name, size_t namelen,
1710 const char *value, size_t valuelen) {
1713 if(!strncasecompare("te", name, namelen))
1714 return HEADERINST_FORWARD;
1716 return contains_trailers(value, valuelen) ?
1717 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1719 return strncasecompare("upgrade", name, namelen) ?
1720 HEADERINST_IGNORE : HEADERINST_FORWARD;
1722 return (strncasecompare("connection", name, namelen) ||
1723 strncasecompare("keep-alive", name, namelen)) ?
1724 HEADERINST_IGNORE : HEADERINST_FORWARD;
1726 return strncasecompare("proxy-connection", name, namelen) ?
1727 HEADERINST_IGNORE : HEADERINST_FORWARD;
1729 return strncasecompare("transfer-encoding", name, namelen) ?
1730 HEADERINST_IGNORE : HEADERINST_FORWARD;
1732 return HEADERINST_FORWARD;
1736 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1737 const void *mem, size_t len, CURLcode *err)
1740 * BIG TODO: Currently, we send request in this function, but this
1741 * function is also used to send request body. It would be nice to
1742 * add dedicated function for request.
1745 struct http_conn *httpc = &conn->proto.httpc;
1746 struct HTTP *stream = conn->data->req.protop;
1747 nghttp2_nv *nva = NULL;
1750 size_t authority_idx;
1751 char *hdbuf = (char *)mem;
1752 char *end, *line_end;
1753 nghttp2_data_provider data_prd;
1755 nghttp2_session *h2 = httpc->h2;
1756 nghttp2_priority_spec pri_spec;
1760 H2BUGF(infof(conn->data, "http2_send len=%zu\n", len));
1762 if(stream->stream_id != -1) {
1763 if(stream->close_handled) {
1764 infof(conn->data, "stream %d closed\n", stream->stream_id);
1765 *err = CURLE_HTTP2_STREAM;
1768 else if(stream->closed) {
1769 return http2_handle_stream_close(conn, conn->data, stream, err);
1771 /* If stream_id != -1, we have dispatched request HEADERS, and now
1772 are going to send or sending request body in DATA frame */
1773 stream->upload_mem = mem;
1774 stream->upload_len = len;
1775 nghttp2_session_resume_data(h2, stream->stream_id);
1776 rv = h2_session_send(conn->data, h2);
1777 if(nghttp2_is_fatal(rv)) {
1778 *err = CURLE_SEND_ERROR;
1781 len -= stream->upload_len;
1783 /* Nullify here because we call nghttp2_session_send() and they
1784 might refer to the old buffer. */
1785 stream->upload_mem = NULL;
1786 stream->upload_len = 0;
1788 if(should_close_session(httpc)) {
1789 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1794 if(stream->upload_left) {
1795 /* we are sure that we have more data to send here. Calling the
1796 following API will make nghttp2_session_want_write() return
1797 nonzero if remote window allows it, which then libcurl checks
1798 socket is writable or not. See http2_perform_getsock(). */
1799 nghttp2_session_resume_data(h2, stream->stream_id);
1802 H2BUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1803 stream->stream_id));
1807 /* Calculate number of headers contained in [mem, mem + len) */
1808 /* Here, we assume the curl http code generate *correct* HTTP header
1811 for(i = 1; i < len; ++i) {
1812 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1820 /* We counted additional 2 \r\n in the first and last line. We need 3
1821 new headers: :method, :path and :scheme. Therefore we need one
1824 nva = malloc(sizeof(nghttp2_nv) * nheader);
1826 *err = CURLE_OUT_OF_MEMORY;
1830 /* Extract :method, :path from request line */
1831 line_end = strstr(hdbuf, "\r\n");
1833 /* Method does not contain spaces */
1834 end = memchr(hdbuf, ' ', line_end - hdbuf);
1835 if(!end || end == hdbuf)
1837 nva[0].name = (unsigned char *)":method";
1838 nva[0].namelen = strlen((char *)nva[0].name);
1839 nva[0].value = (unsigned char *)hdbuf;
1840 nva[0].valuelen = (size_t)(end - hdbuf);
1841 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1842 if(HEADER_OVERFLOW(nva[0])) {
1843 failf(conn->data, "Failed sending HTTP request: Header overflow");
1849 /* Path may contain spaces so scan backwards */
1851 for(i = (size_t)(line_end - hdbuf); i; --i) {
1852 if(hdbuf[i - 1] == ' ') {
1853 end = &hdbuf[i - 1];
1857 if(!end || end == hdbuf)
1859 nva[1].name = (unsigned char *)":path";
1860 nva[1].namelen = strlen((char *)nva[1].name);
1861 nva[1].value = (unsigned char *)hdbuf;
1862 nva[1].valuelen = (size_t)(end - hdbuf);
1863 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1864 if(HEADER_OVERFLOW(nva[1])) {
1865 failf(conn->data, "Failed sending HTTP request: Header overflow");
1869 nva[2].name = (unsigned char *)":scheme";
1870 nva[2].namelen = strlen((char *)nva[2].name);
1871 if(conn->handler->flags & PROTOPT_SSL)
1872 nva[2].value = (unsigned char *)"https";
1874 nva[2].value = (unsigned char *)"http";
1875 nva[2].valuelen = strlen((char *)nva[2].value);
1876 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1877 if(HEADER_OVERFLOW(nva[2])) {
1878 failf(conn->data, "Failed sending HTTP request: Header overflow");
1884 while(i < nheader) {
1887 hdbuf = line_end + 2;
1889 line_end = strstr(hdbuf, "\r\n");
1890 if(line_end == hdbuf)
1893 /* header continuation lines are not supported */
1894 if(*hdbuf == ' ' || *hdbuf == '\t')
1897 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1899 if(end == hdbuf || end == line_end)
1903 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1905 nva[i].name = (unsigned char *)":authority";
1906 nva[i].namelen = strlen((char *)nva[i].name);
1909 nva[i].name = (unsigned char *)hdbuf;
1910 nva[i].namelen = (size_t)(end - hdbuf);
1913 while(*hdbuf == ' ' || *hdbuf == '\t')
1917 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1919 case HEADERINST_IGNORE:
1920 /* skip header fields prohibited by HTTP/2 specification. */
1923 case HEADERINST_TE_TRAILERS:
1924 nva[i].value = (uint8_t*)"trailers";
1925 nva[i].valuelen = sizeof("trailers") - 1;
1928 nva[i].value = (unsigned char *)hdbuf;
1929 nva[i].valuelen = (size_t)(end - hdbuf);
1932 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1933 if(HEADER_OVERFLOW(nva[i])) {
1934 failf(conn->data, "Failed sending HTTP request: Header overflow");
1940 /* :authority must come before non-pseudo header fields */
1941 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1942 nghttp2_nv authority = nva[authority_idx];
1943 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1944 nva[i] = nva[i - 1];
1949 /* Warn stream may be rejected if cumulative length of headers is too large.
1950 It appears nghttp2 will not send a header frame larger than 64KB. */
1951 #define MAX_ACC 60000 /* <64KB to account for some overhead */
1955 for(i = 0; i < nheader; ++i) {
1956 acc += nva[i].namelen + nva[i].valuelen;
1958 H2BUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
1959 nva[i].namelen, nva[i].name,
1960 nva[i].valuelen, nva[i].value));
1964 infof(conn->data, "http2_send: Warning: The cumulative length of all "
1965 "headers exceeds %zu bytes and that could cause the "
1966 "stream to be rejected.\n", MAX_ACC);
1970 h2_pri_spec(conn->data, &pri_spec);
1972 switch(conn->data->set.httpreq) {
1974 case HTTPREQ_POST_FORM:
1975 case HTTPREQ_POST_MIME:
1977 if(conn->data->state.infilesize != -1)
1978 stream->upload_left = conn->data->state.infilesize;
1980 /* data sending without specifying the data amount up front */
1981 stream->upload_left = -1; /* unknown, but not zero */
1983 data_prd.read_callback = data_source_read_callback;
1984 data_prd.source.ptr = NULL;
1985 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1986 &data_prd, conn->data);
1989 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1996 H2BUGF(infof(conn->data, "http2_send() send error\n"));
1997 *err = CURLE_SEND_ERROR;
2001 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
2002 stream_id, conn->data);
2003 stream->stream_id = stream_id;
2005 /* this does not call h2_session_send() since there can not have been any
2006 * priority upodate since the nghttp2_submit_request() call above */
2007 rv = nghttp2_session_send(h2);
2010 *err = CURLE_SEND_ERROR;
2014 if(should_close_session(httpc)) {
2015 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
2020 if(stream->stream_id != -1) {
2021 /* If whole HEADERS frame was sent off to the underlying socket,
2022 the nghttp2 library calls data_source_read_callback. But only
2023 it found that no data available, so it deferred the DATA
2024 transmission. Which means that nghttp2_session_want_write()
2025 returns 0 on http2_perform_getsock(), which results that no
2026 writable socket check is performed. To workaround this, we
2027 issue nghttp2_session_resume_data() here to bring back DATA
2028 transmission from deferred state. */
2029 nghttp2_session_resume_data(h2, stream->stream_id);
2036 *err = CURLE_SEND_ERROR;
2040 CURLcode Curl_http2_setup(struct connectdata *conn)
2043 struct http_conn *httpc = &conn->proto.httpc;
2044 struct HTTP *stream = conn->data->req.protop;
2046 stream->stream_id = -1;
2048 if(!stream->header_recvbuf)
2049 stream->header_recvbuf = Curl_add_buffer_init();
2051 if((conn->handler == &Curl_handler_http2_ssl) ||
2052 (conn->handler == &Curl_handler_http2))
2053 return CURLE_OK; /* already done */
2055 if(conn->handler->flags & PROTOPT_SSL)
2056 conn->handler = &Curl_handler_http2_ssl;
2058 conn->handler = &Curl_handler_http2;
2060 result = Curl_http2_init(conn);
2064 infof(conn->data, "Using HTTP2, server supports multi-use\n");
2065 stream->upload_left = 0;
2066 stream->upload_mem = NULL;
2067 stream->upload_len = 0;
2069 httpc->inbuflen = 0;
2070 httpc->nread_inbuf = 0;
2072 httpc->pause_stream_id = 0;
2073 httpc->drain_total = 0;
2075 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2076 conn->httpversion = 20;
2077 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2079 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2080 Curl_multi_connchanged(conn->data->multi);
2085 CURLcode Curl_http2_switched(struct connectdata *conn,
2086 const char *mem, size_t nread)
2089 struct http_conn *httpc = &conn->proto.httpc;
2092 struct Curl_easy *data = conn->data;
2093 struct HTTP *stream = conn->data->req.protop;
2095 result = Curl_http2_setup(conn);
2099 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
2100 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
2101 conn->recv[FIRSTSOCKET] = http2_recv;
2102 conn->send[FIRSTSOCKET] = http2_send;
2104 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2105 /* stream 1 is opened implicitly on upgrade */
2106 stream->stream_id = 1;
2107 /* queue SETTINGS frame (again) */
2108 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2109 httpc->binlen, NULL);
2111 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2112 nghttp2_strerror(rv), rv);
2116 nghttp2_session_set_stream_user_data(httpc->h2,
2121 populate_settings(conn, httpc);
2123 /* stream ID is unknown at this point */
2124 stream->stream_id = -1;
2125 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2126 httpc->local_settings,
2127 httpc->local_settings_num);
2129 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2130 nghttp2_strerror(rv), rv);
2135 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2136 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2137 HTTP2_HUGE_WINDOW_SIZE);
2139 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2140 nghttp2_strerror(rv), rv);
2145 /* we are going to copy mem to httpc->inbuf. This is required since
2146 mem is part of buffer pointed by stream->mem, and callbacks
2147 called by nghttp2_session_mem_recv() will write stream specific
2148 data into stream->mem, overwriting data already there. */
2149 if(H2_BUFSIZE < nread) {
2150 failf(data, "connection buffer size is too small to store data following "
2151 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2156 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2157 " after upgrade: len=%zu\n",
2161 memcpy(httpc->inbuf, mem, nread);
2162 httpc->inbuflen = nread;
2164 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
2167 if(nghttp2_is_fatal((int)nproc)) {
2168 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
2169 nghttp2_strerror((int)nproc), (int)nproc);
2173 H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
2175 if((ssize_t)nread == nproc) {
2176 httpc->inbuflen = 0;
2177 httpc->nread_inbuf = 0;
2180 httpc->nread_inbuf += nproc;
2183 /* Try to send some frames since we may read SETTINGS already. */
2184 rv = h2_session_send(data, httpc->h2);
2187 failf(data, "nghttp2_session_send() failed: %s(%d)",
2188 nghttp2_strerror(rv), rv);
2192 if(should_close_session(httpc)) {
2194 "nghttp2_session_send(): nothing to do in this session\n"));
2201 CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2202 struct Curl_easy *child,
2206 struct Curl_http2_dep **tail;
2207 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2209 return CURLE_OUT_OF_MEMORY;
2212 if(parent->set.stream_dependents && exclusive) {
2213 struct Curl_http2_dep *node = parent->set.stream_dependents;
2215 node->data->set.stream_depends_on = child;
2219 tail = &child->set.stream_dependents;
2221 tail = &(*tail)->next;
2223 DEBUGASSERT(!*tail);
2224 *tail = parent->set.stream_dependents;
2225 parent->set.stream_dependents = 0;
2228 tail = &parent->set.stream_dependents;
2230 (*tail)->data->set.stream_depends_e = FALSE;
2231 tail = &(*tail)->next;
2234 DEBUGASSERT(!*tail);
2238 child->set.stream_depends_on = parent;
2239 child->set.stream_depends_e = exclusive;
2243 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2245 struct Curl_http2_dep *last = 0;
2246 struct Curl_http2_dep *data = parent->set.stream_dependents;
2247 DEBUGASSERT(child->set.stream_depends_on == parent);
2249 while(data && data->data != child) {
2258 last->next = data->next;
2261 parent->set.stream_dependents = data->next;
2266 child->set.stream_depends_on = 0;
2267 child->set.stream_depends_e = FALSE;
2270 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2272 while(data->set.stream_dependents) {
2273 struct Curl_easy *tmp = data->set.stream_dependents->data;
2274 Curl_http2_remove_child(data, tmp);
2275 if(data->set.stream_depends_on)
2276 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2279 if(data->set.stream_depends_on)
2280 Curl_http2_remove_child(data->set.stream_depends_on, data);
2283 #else /* !USE_NGHTTP2 */
2285 /* Satisfy external references even if http2 is not compiled in. */
2287 #define CURL_DISABLE_TYPECHECK
2288 #include <curl/curl.h>
2290 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2297 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2304 #endif /* USE_NGHTTP2 */