1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, 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 http://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 "curl_printf.h"
27 #include <nghttp2/nghttp2.h>
32 #include "curl_base64.h"
35 #include "conncache.h"
38 /* The last #include files should be: */
39 #include "curl_memory.h"
42 #define MIN(x,y) ((x)<(y)?(x):(y))
44 #if (NGHTTP2_VERSION_NUM < 0x000600)
45 #error too old nghttp2 version, upgrade!
48 static int http2_perform_getsock(const struct connectdata *conn,
49 curl_socket_t *sock, /* points to
55 const struct http_conn *c = &conn->proto.httpc;
56 int bitmap = GETSOCK_BLANK;
59 /* TODO We should check underlying socket state if it is SSL socket
60 because of renegotiation. */
61 sock[0] = conn->sock[FIRSTSOCKET];
63 if(nghttp2_session_want_read(c->h2))
64 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
66 if(nghttp2_session_want_write(c->h2))
67 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
72 static int http2_getsock(struct connectdata *conn,
73 curl_socket_t *sock, /* points to numsocks
77 return http2_perform_getsock(conn, sock, numsocks);
80 static CURLcode http2_disconnect(struct connectdata *conn,
83 struct HTTP *http = conn->data->req.protop;
84 struct http_conn *c = &conn->proto.httpc;
85 (void)dead_connection;
87 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
89 nghttp2_session_del(c->h2);
90 Curl_safefree(c->inbuf);
93 Curl_add_buffer_free(http->header_recvbuf);
94 http->header_recvbuf = NULL; /* clear the pointer */
95 for(; http->push_headers_used > 0; --http->push_headers_used) {
96 free(http->push_headers[http->push_headers_used - 1]);
98 free(http->push_headers);
99 http->push_headers = NULL;
102 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
107 /* called from Curl_http_setup_conn */
108 void Curl_http2_setup_req(struct SessionHandle *data)
110 struct HTTP *http = data->req.protop;
112 http->nread_header_recvbuf = 0;
113 http->bodystarted = FALSE;
114 http->status_code = -1;
115 http->pausedata = NULL;
117 http->error_code = NGHTTP2_NO_ERROR;
118 http->closed = FALSE;
119 http->mem = data->state.buffer;
124 /* called from Curl_http_setup_conn */
125 void Curl_http2_setup_conn(struct connectdata *conn)
127 conn->proto.httpc.settings.max_concurrent_streams =
128 DEFAULT_MAX_CONCURRENT_STREAMS;
132 * HTTP2 handler interface. This isn't added to the general list of protocols
133 * but will be used at run-time when the protocol is dynamically switched from
136 const struct Curl_handler Curl_handler_http2 = {
137 "HTTP2", /* scheme */
138 ZERO_NULL, /* setup_connection */
139 Curl_http, /* do_it */
140 Curl_http_done, /* done */
141 ZERO_NULL, /* do_more */
142 ZERO_NULL, /* connect_it */
143 ZERO_NULL, /* connecting */
144 ZERO_NULL, /* doing */
145 http2_getsock, /* proto_getsock */
146 http2_getsock, /* doing_getsock */
147 ZERO_NULL, /* domore_getsock */
148 http2_perform_getsock, /* perform_getsock */
149 http2_disconnect, /* disconnect */
150 ZERO_NULL, /* readwrite */
151 PORT_HTTP, /* defport */
152 CURLPROTO_HTTP, /* protocol */
153 PROTOPT_NONE /* flags */
156 const struct Curl_handler Curl_handler_http2_ssl = {
157 "HTTP2", /* scheme */
158 ZERO_NULL, /* setup_connection */
159 Curl_http, /* do_it */
160 Curl_http_done, /* done */
161 ZERO_NULL, /* do_more */
162 ZERO_NULL, /* connect_it */
163 ZERO_NULL, /* connecting */
164 ZERO_NULL, /* doing */
165 http2_getsock, /* proto_getsock */
166 http2_getsock, /* doing_getsock */
167 ZERO_NULL, /* domore_getsock */
168 http2_perform_getsock, /* perform_getsock */
169 http2_disconnect, /* disconnect */
170 ZERO_NULL, /* readwrite */
171 PORT_HTTP, /* defport */
172 CURLPROTO_HTTPS, /* protocol */
173 PROTOPT_SSL /* flags */
177 * Store nghttp2 version info in this buffer, Prefix with a space. Return
178 * total length written.
180 int Curl_http2_ver(char *p, size_t len)
182 nghttp2_info *h2 = nghttp2_version(0);
183 return snprintf(p, len, " nghttp2/%s", h2->version_str);
187 * The implementation of nghttp2_send_callback type. Here we write |data| with
188 * size |length| to the network and return the number of bytes actually
189 * written. See the documentation of nghttp2_send_callback for the details.
191 static ssize_t send_callback(nghttp2_session *h2,
192 const uint8_t *data, size_t length, int flags,
195 struct connectdata *conn = (struct connectdata *)userp;
196 struct http_conn *c = &conn->proto.httpc;
198 CURLcode result = CURLE_OK;
203 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
204 data, length, &result);
206 if(result == CURLE_AGAIN) {
207 return NGHTTP2_ERR_WOULDBLOCK;
211 failf(conn->data, "Failed sending HTTP2 data");
212 return NGHTTP2_ERR_CALLBACK_FAILURE;
216 return NGHTTP2_ERR_WOULDBLOCK;
222 /* We pass a pointer to this struct in the push callback, but the contents of
223 the struct are hidden from the user. */
224 struct curl_pushheaders {
225 struct SessionHandle *data;
226 const nghttp2_push_promise *frame;
230 * push header access function. Only to be used from within the push callback
232 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
234 /* Verify that we got a good easy handle in the push header struct, mostly to
235 detect rubbish input fast(er). */
236 if(!h || !GOOD_EASY_HANDLE(h->data))
239 struct HTTP *stream = h->data->req.protop;
240 if(num < stream->push_headers_used)
241 return stream->push_headers[num];
247 * push header access function. Only to be used from within the push callback
249 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
251 /* Verify that we got a good easy handle in the push header struct,
252 mostly to detect rubbish input fast(er). Also empty header name
253 is just a rubbish too. We have to allow ":" at the beginning of
254 the header, but header == ":" must be rejected. If we have ':' in
255 the middle of header, it could be matched in middle of the value,
256 this is because we do prefix match.*/
257 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
258 Curl_raw_equal(header, ":") || strchr(header + 1, ':'))
261 struct HTTP *stream = h->data->req.protop;
262 size_t len = strlen(header);
264 for(i=0; i<stream->push_headers_used; i++) {
265 if(!strncmp(header, stream->push_headers[i], len)) {
266 /* sub-match, make sure that it us followed by a colon */
267 if(stream->push_headers[i][len] != ':')
269 return &stream->push_headers[i][len+1];
276 static CURL *duphandle(struct SessionHandle *data)
278 struct SessionHandle *second = curl_easy_duphandle(data);
280 /* setup the request struct */
281 struct HTTP *http = calloc(1, sizeof(struct HTTP));
283 (void)Curl_close(second);
287 second->req.protop = http;
288 http->header_recvbuf = Curl_add_buffer_init();
289 if(!http->header_recvbuf) {
291 (void)Curl_close(second);
295 Curl_http2_setup_req(second);
302 static int push_promise(struct SessionHandle *data,
303 struct connectdata *conn,
304 const nghttp2_push_promise *frame)
307 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
308 frame->promised_stream_id));
309 if(data->multi->push_cb) {
311 struct curl_pushheaders heads;
313 struct http_conn *httpc;
315 /* clone the parent */
316 CURL *newhandle = duphandle(data);
318 infof(data, "failed to duplicate handle\n");
319 rv = 1; /* FAIL HARD */
325 /* ask the application */
326 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
328 stream = data->req.protop;
330 failf(data, "Internal NULL stream!\n");
335 rv = data->multi->push_cb(data, newhandle,
336 stream->push_headers_used, &heads,
337 data->multi->push_userp);
339 /* free the headers again */
340 for(i=0; i<stream->push_headers_used; i++)
341 free(stream->push_headers[i]);
342 free(stream->push_headers);
343 stream->push_headers = NULL;
346 /* denied, kill off the new handle again */
347 (void)Curl_close(newhandle);
351 /* approved, add to the multi handle and immediately switch to PERFORM
352 state with the given connection !*/
353 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
355 infof(data, "failed to add handle to multi\n");
356 Curl_close(newhandle);
361 httpc = &conn->proto.httpc;
362 nghttp2_session_set_stream_user_data(httpc->h2,
363 frame->promised_stream_id, newhandle);
366 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
373 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
376 struct connectdata *conn = NULL;
377 struct http_conn *httpc = NULL;
378 struct SessionHandle *data_s = NULL;
379 struct HTTP *stream = NULL;
380 static int lastStream = -1;
383 int32_t stream_id = frame->hd.stream_id;
388 /* stream ID zero is for connection-oriented stuff */
391 data_s = nghttp2_session_get_stream_user_data(session,
392 frame->hd.stream_id);
393 if(lastStream != frame->hd.stream_id) {
394 lastStream = frame->hd.stream_id;
397 DEBUGF(infof(conn->data,
398 "No SessionHandle associated with stream: %x\n",
403 stream = data_s->req.protop;
405 return NGHTTP2_ERR_CALLBACK_FAILURE;
407 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
408 frame->hd.type, stream_id));
410 conn = data_s->easy_conn;
412 assert(conn->data == data_s);
413 httpc = &conn->proto.httpc;
414 switch(frame->hd.type) {
416 /* If body started on this stream, then receiving DATA is illegal. */
417 if(!stream->bodystarted) {
418 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
419 stream_id, NGHTTP2_PROTOCOL_ERROR);
421 if(nghttp2_is_fatal(rv)) {
422 return NGHTTP2_ERR_CALLBACK_FAILURE;
426 case NGHTTP2_HEADERS:
427 if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
430 if(stream->bodystarted) {
431 /* Only valid HEADERS after body started is trailer HEADERS. We
432 ignores trailer HEADERS for now. nghttp2 guarantees that it
433 has END_STREAM flag set. */
437 /* nghttp2 guarantees that :status is received, and we store it to
438 stream->status_code */
439 DEBUGASSERT(stream->status_code != -1);
441 /* Only final status code signals the end of header */
442 if(stream->status_code / 100 != 1) {
443 stream->bodystarted = TRUE;
444 stream->status_code = -1;
447 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
449 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
450 ncopy = MIN(stream->len, left);
452 memcpy(&stream->mem[stream->memlen],
453 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
455 stream->nread_header_recvbuf += ncopy;
457 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
458 ncopy, stream_id, stream->mem));
460 stream->len -= ncopy;
461 stream->memlen += ncopy;
463 data_s->state.drain++;
464 Curl_expire(data_s, 1);
466 case NGHTTP2_PUSH_PROMISE:
467 rv = push_promise(data_s, conn, &frame->push_promise);
469 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
470 frame->push_promise.promised_stream_id,
472 if(nghttp2_is_fatal(rv)) {
477 case NGHTTP2_SETTINGS:
479 uint32_t max_conn = httpc->settings.max_concurrent_streams;
480 DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id));
481 httpc->settings.max_concurrent_streams =
482 nghttp2_session_get_remote_settings(
483 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
484 httpc->settings.enable_push =
485 nghttp2_session_get_remote_settings(
486 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
487 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
488 httpc->settings.max_concurrent_streams));
489 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
490 httpc->settings.enable_push?"TRUE":"false"));
491 if(max_conn != httpc->settings.max_concurrent_streams) {
492 /* only signal change if the value actually changed */
494 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
495 Curl_multi_connchanged(conn->data->multi);
500 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
501 frame->hd.type, stream_id));
507 static int on_invalid_frame_recv(nghttp2_session *session,
508 const nghttp2_frame *frame,
509 int lib_error_code, void *userp)
511 struct SessionHandle *data_s = NULL;
514 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
517 "on_invalid_frame_recv() was called, error=%d:%s\n",
518 lib_error_code, nghttp2_strerror(lib_error_code)));
523 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
525 const uint8_t *data, size_t len, void *userp)
528 struct SessionHandle *data_s;
535 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
537 /* get the stream from the hash based on Stream ID */
538 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
540 /* Receiving a Stream ID not in the hash should not happen, this is an
541 internal error more than anything else! */
542 return NGHTTP2_ERR_CALLBACK_FAILURE;
544 stream = data_s->req.protop;
546 return NGHTTP2_ERR_CALLBACK_FAILURE;
548 nread = MIN(stream->len, len);
549 memcpy(&stream->mem[stream->memlen], data, nread);
551 stream->len -= nread;
552 stream->memlen += nread;
554 data_s->state.drain++;
555 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
558 DEBUGF(infof(data_s, "%zu data received for stream %u "
559 "(%zu left in buffer %p, total %zu)\n",
561 stream->len, stream->mem,
565 stream->pausedata = data + nread;
566 stream->pauselen = len - nread;
567 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
569 len - nread, stream_id));
570 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
571 return NGHTTP2_ERR_PAUSE;
576 static int before_frame_send(nghttp2_session *session,
577 const nghttp2_frame *frame,
580 struct SessionHandle *data_s;
583 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
585 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
590 static int on_frame_send(nghttp2_session *session,
591 const nghttp2_frame *frame,
594 struct SessionHandle *data_s;
597 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
599 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
604 static int on_frame_not_send(nghttp2_session *session,
605 const nghttp2_frame *frame,
606 int lib_error_code, void *userp)
608 struct SessionHandle *data_s;
611 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
614 "on_frame_not_send() was called, lib_error_code = %d\n",
619 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
620 uint32_t error_code, void *userp)
622 struct SessionHandle *data_s;
629 /* get the stream from the hash based on Stream ID, stream ID zero is for
630 connection-oriented stuff */
631 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
633 /* We could get stream ID not in the hash. For example, if we
634 decided to reject stream (e.g., PUSH_PROMISE). */
637 DEBUGF(infof(data_s, "on_stream_close(), error_code = %d, stream %u\n",
638 error_code, stream_id));
639 stream = data_s->req.protop;
641 return NGHTTP2_ERR_CALLBACK_FAILURE;
643 stream->error_code = error_code;
644 stream->closed = TRUE;
646 /* remove the entry from the hash as the stream is now gone */
647 nghttp2_session_set_stream_user_data(session, stream_id, 0);
648 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
653 static int on_begin_headers(nghttp2_session *session,
654 const nghttp2_frame *frame, void *userp)
656 struct SessionHandle *data_s = NULL;
659 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
661 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
666 /* Decode HTTP status code. Returns -1 if no valid status code was
668 static int decode_status_code(const uint8_t *value, size_t len)
679 for(i = 0; i < 3; ++i) {
682 if(c < '0' || c > '9') {
693 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
694 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
695 const uint8_t *name, size_t namelen,
696 const uint8_t *value, size_t valuelen,
701 struct SessionHandle *data_s;
702 int32_t stream_id = frame->hd.stream_id;
707 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
709 /* get the stream from the hash based on Stream ID */
710 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
712 /* Receiving a Stream ID not in the hash should not happen, this is an
713 internal error more than anything else! */
714 return NGHTTP2_ERR_CALLBACK_FAILURE;
716 stream = data_s->req.protop;
718 failf(data_s, "Internal NULL stream! 5\n");
719 return NGHTTP2_ERR_CALLBACK_FAILURE;
722 if(stream->bodystarted)
723 /* Ignore trailer or HEADERS not mapped to HTTP semantics. The
724 consequence is handled in on_frame_recv(). */
727 /* Store received PUSH_PROMISE headers to be used when the subsequent
728 PUSH_PROMISE callback comes */
729 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
732 if(!stream->push_headers) {
733 stream->push_headers_alloc = 10;
734 stream->push_headers = malloc(stream->push_headers_alloc *
736 stream->push_headers_used = 0;
738 else if(stream->push_headers_used ==
739 stream->push_headers_alloc) {
741 stream->push_headers_alloc *= 2;
742 headp = realloc(stream->push_headers,
743 stream->push_headers_alloc * sizeof(char *));
745 free(stream->push_headers);
746 stream->push_headers = NULL;
747 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
749 stream->push_headers = headp;
751 h = aprintf("%s:%s", name, value);
753 stream->push_headers[stream->push_headers_used++] = h;
757 if(namelen == sizeof(":status") - 1 &&
758 memcmp(":status", name, namelen) == 0) {
759 /* nghttp2 guarantees :status is received first and only once, and
760 value is 3 digits status code, and decode_status_code always
762 stream->status_code = decode_status_code(value, valuelen);
763 DEBUGASSERT(stream->status_code != -1);
765 Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
766 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
767 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
768 data_s->state.drain++;
769 Curl_expire(data_s, 1);
771 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n",
772 stream->status_code));
776 /* nghttp2 guarantees that namelen > 0, and :status was already
777 received, and this is not pseudo-header field . */
778 /* convert to a HTTP1-style header */
779 Curl_add_buffer(stream->header_recvbuf, name, namelen);
780 Curl_add_buffer(stream->header_recvbuf, ":", 1);
781 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
782 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
783 data_s->state.drain++;
784 Curl_expire(data_s, 1);
786 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
789 return 0; /* 0 is successful */
792 static ssize_t data_source_read_callback(nghttp2_session *session,
794 uint8_t *buf, size_t length,
795 uint32_t *data_flags,
796 nghttp2_data_source *source,
799 struct SessionHandle *data_s;
800 struct HTTP *stream = NULL;
806 /* get the stream from the hash based on Stream ID, stream ID zero is for
807 connection-oriented stuff */
808 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
810 /* Receiving a Stream ID not in the hash should not happen, this is an
811 internal error more than anything else! */
812 return NGHTTP2_ERR_CALLBACK_FAILURE;
814 stream = data_s->req.protop;
816 return NGHTTP2_ERR_CALLBACK_FAILURE;
819 return NGHTTP2_ERR_INVALID_ARGUMENT;
821 nread = MIN(stream->upload_len, length);
823 memcpy(buf, stream->upload_mem, nread);
824 stream->upload_mem += nread;
825 stream->upload_len -= nread;
826 stream->upload_left -= nread;
829 if(stream->upload_left == 0)
832 return NGHTTP2_ERR_DEFERRED;
834 DEBUGF(infof(data_s, "data_source_read_callback: "
835 "returns %zu bytes stream %u\n",
842 * The HTTP2 settings we send in the Upgrade request
844 static nghttp2_settings_entry settings[] = {
845 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
846 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
849 #define H2_BUFSIZE 32768
852 * Initialize nghttp2 for a Curl connection
854 CURLcode Curl_http2_init(struct connectdata *conn)
856 if(!conn->proto.httpc.h2) {
858 nghttp2_session_callbacks *callbacks;
860 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
861 if(conn->proto.httpc.inbuf == NULL)
862 return CURLE_OUT_OF_MEMORY;
864 rc = nghttp2_session_callbacks_new(&callbacks);
867 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
868 return CURLE_OUT_OF_MEMORY; /* most likely at least */
871 /* nghttp2_send_callback */
872 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
873 /* nghttp2_on_frame_recv_callback */
874 nghttp2_session_callbacks_set_on_frame_recv_callback
875 (callbacks, on_frame_recv);
876 /* nghttp2_on_invalid_frame_recv_callback */
877 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
878 (callbacks, on_invalid_frame_recv);
879 /* nghttp2_on_data_chunk_recv_callback */
880 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
881 (callbacks, on_data_chunk_recv);
882 /* nghttp2_before_frame_send_callback */
883 nghttp2_session_callbacks_set_before_frame_send_callback
884 (callbacks, before_frame_send);
885 /* nghttp2_on_frame_send_callback */
886 nghttp2_session_callbacks_set_on_frame_send_callback
887 (callbacks, on_frame_send);
888 /* nghttp2_on_frame_not_send_callback */
889 nghttp2_session_callbacks_set_on_frame_not_send_callback
890 (callbacks, on_frame_not_send);
891 /* nghttp2_on_stream_close_callback */
892 nghttp2_session_callbacks_set_on_stream_close_callback
893 (callbacks, on_stream_close);
894 /* nghttp2_on_begin_headers_callback */
895 nghttp2_session_callbacks_set_on_begin_headers_callback
896 (callbacks, on_begin_headers);
897 /* nghttp2_on_header_callback */
898 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
900 /* The nghttp2 session is not yet setup, do it */
901 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
903 nghttp2_session_callbacks_del(callbacks);
906 failf(conn->data, "Couldn't initialize nghttp2!");
907 return CURLE_OUT_OF_MEMORY; /* most likely at least */
911 failf(conn->data, "Couldn't init stream hash!");
912 return CURLE_OUT_OF_MEMORY; /* most likely at least */
919 * Send a request using http2
921 CURLcode Curl_http2_send_request(struct connectdata *conn)
928 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
930 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
931 struct connectdata *conn)
937 struct SingleRequest *k = &conn->data->req;
938 uint8_t *binsettings = conn->proto.httpc.binsettings;
940 /* As long as we have a fixed set of settings, we don't have to dynamically
941 * figure out the base64 strings since it'll always be the same. However,
942 * the settings will likely not be fixed every time in the future.
945 /* this returns number of bytes it wrote */
946 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
948 sizeof(settings)/sizeof(settings[0]));
950 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
951 return CURLE_FAILED_INIT;
953 conn->proto.httpc.binlen = binlen;
955 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
960 result = Curl_add_bufferf(req,
961 "Connection: Upgrade, HTTP2-Settings\r\n"
963 "HTTP2-Settings: %s\r\n",
964 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
967 k->upgr101 = UPGR101_REQUESTED;
972 static ssize_t http2_handle_stream_close(struct http_conn *httpc,
973 struct SessionHandle *data,
974 struct HTTP *stream, CURLcode *err) {
975 if(httpc->pause_stream_id == stream->stream_id) {
976 httpc->pause_stream_id = 0;
978 /* Reset to FALSE to prevent infinite loop in readwrite_data
980 stream->closed = FALSE;
981 if(stream->error_code != NGHTTP2_NO_ERROR) {
982 failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
983 stream->stream_id, stream->error_code);
987 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
992 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
993 * a regular CURLcode value.
995 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
996 char *mem, size_t len, CURLcode *err)
998 CURLcode result = CURLE_OK;
1001 struct http_conn *httpc = &conn->proto.httpc;
1002 struct SessionHandle *data = conn->data;
1003 struct HTTP *stream = data->req.protop;
1005 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1007 /* If stream is closed, return 0 to signal the http routine to close
1008 the connection. We need to handle stream closure here,
1009 otherwise, we may be going to read from underlying connection,
1010 and gets EAGAIN, and we will get stuck there. */
1011 if(stream->memlen == 0 && stream->closed) {
1012 return http2_handle_stream_close(httpc, data, stream, err);
1015 /* Nullify here because we call nghttp2_session_send() and they
1016 might refer to the old buffer. */
1017 stream->upload_mem = NULL;
1018 stream->upload_len = 0;
1021 * At this point 'stream' is just in the SessionHandle the connection
1022 * identifies as its owner at this time.
1025 if(stream->bodystarted &&
1026 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1027 /* If there is body data pending for this stream to return, do that */
1029 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1030 size_t ncopy = MIN(len, left);
1031 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1033 stream->nread_header_recvbuf += ncopy;
1035 infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1040 infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n",
1041 len, mem, stream->stream_id);
1043 if((data->state.drain) && stream->memlen) {
1044 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1045 stream->memlen, stream->stream_id,
1047 if(mem != stream->mem) {
1048 /* if we didn't get the same buffer this time, we must move the data to
1050 memmove(mem, stream->mem, stream->memlen);
1051 stream->len = len - stream->memlen;
1055 else if(stream->pausedata) {
1056 nread = MIN(len, stream->pauselen);
1057 memcpy(mem, stream->pausedata, nread);
1059 stream->pausedata += nread;
1060 stream->pauselen -= nread;
1062 infof(data, "%zu data bytes written\n", nread);
1063 if(stream->pauselen == 0) {
1064 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1065 assert(httpc->pause_stream_id == stream->stream_id);
1066 httpc->pause_stream_id = 0;
1068 stream->pausedata = NULL;
1069 stream->pauselen = 0;
1071 infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1072 nread, stream->stream_id);
1075 else if(httpc->pause_stream_id) {
1076 /* If a stream paused nghttp2_session_mem_recv previously, and has
1077 not processed all data, it still refers to the buffer in
1078 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1079 overwrite that buffer. To avoid that situation, just return
1080 here with CURLE_AGAIN. This could be busy loop since data in
1081 socket is not read. But it seems that usually streams are
1082 notified with its drain property, and socket is read again
1089 /* remember where to store incoming data for this stream and how big the
1095 if(httpc->inbuflen == 0) {
1096 nread = ((Curl_recv *)httpc->recv_underlying)(
1097 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1099 if(result == CURLE_AGAIN) {
1105 failf(data, "Failed receiving HTTP2 data");
1111 failf(data, "Unexpected EOF");
1112 *err = CURLE_RECV_ERROR;
1116 DEBUGF(infof(data, "nread=%zd\n", nread));
1118 httpc->inbuflen = nread;
1119 inbuf = httpc->inbuf;
1122 nread = httpc->inbuflen - httpc->nread_inbuf;
1123 inbuf = httpc->inbuf + httpc->nread_inbuf;
1125 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1128 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1130 if(nghttp2_is_fatal((int)rv)) {
1131 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1132 rv, nghttp2_strerror((int)rv));
1133 *err = CURLE_RECV_ERROR;
1136 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1138 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1139 httpc->inbuflen = 0;
1140 httpc->nread_inbuf = 0;
1143 httpc->nread_inbuf += rv;
1144 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1145 httpc->inbuflen - httpc->nread_inbuf));
1147 /* Always send pending frames in nghttp2 session, because
1148 nghttp2_session_mem_recv() may queue new frame */
1149 rv = nghttp2_session_send(httpc->h2);
1151 *err = CURLE_SEND_ERROR;
1155 if(stream->memlen) {
1156 ssize_t retlen = stream->memlen;
1157 infof(data, "http2_recv: returns %zd for stream %u\n",
1158 retlen, stream->stream_id);
1161 if(httpc->pause_stream_id == stream->stream_id) {
1162 /* data for this stream is returned now, but this stream caused a pause
1163 already so we need it called again asap */
1164 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1165 stream->stream_id));
1168 data->state.drain = 0; /* this stream is hereby drained */
1172 /* If stream is closed, return 0 to signal the http routine to close
1174 if(stream->closed) {
1175 return http2_handle_stream_close(httpc, data, stream, err);
1178 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1179 stream->stream_id));
1183 /* Index where :authority header field will appear in request header
1185 #define AUTHORITY_DST_IDX 3
1187 /* return number of received (decrypted) bytes */
1188 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1189 const void *mem, size_t len, CURLcode *err)
1192 * BIG TODO: Currently, we send request in this function, but this
1193 * function is also used to send request body. It would be nice to
1194 * add dedicated function for request.
1197 struct http_conn *httpc = &conn->proto.httpc;
1198 struct HTTP *stream = conn->data->req.protop;
1202 size_t authority_idx;
1203 char *hdbuf = (char*)mem;
1205 nghttp2_data_provider data_prd;
1207 nghttp2_session *h2 = httpc->h2;
1211 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1213 if(stream->stream_id != -1) {
1214 /* If stream_id != -1, we have dispatched request HEADERS, and now
1215 are going to send or sending request body in DATA frame */
1216 stream->upload_mem = mem;
1217 stream->upload_len = len;
1218 nghttp2_session_resume_data(h2, stream->stream_id);
1219 rv = nghttp2_session_send(h2);
1220 if(nghttp2_is_fatal(rv)) {
1221 *err = CURLE_SEND_ERROR;
1224 len -= stream->upload_len;
1226 /* Nullify here because we call nghttp2_session_send() and they
1227 might refer to the old buffer. */
1228 stream->upload_mem = NULL;
1229 stream->upload_len = 0;
1231 if(stream->upload_left) {
1232 /* we are sure that we have more data to send here. Calling the
1233 following API will make nghttp2_session_want_write() return
1234 nonzero if remote window allows it, which then libcurl checks
1235 socket is writable or not. See http2_perform_getsock(). */
1236 nghttp2_session_resume_data(h2, stream->stream_id);
1239 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1240 stream->stream_id));
1244 /* Calculate number of headers contained in [mem, mem + len) */
1245 /* Here, we assume the curl http code generate *correct* HTTP header
1248 for(i = 0; i < len; ++i) {
1249 if(hdbuf[i] == 0x0a) {
1253 /* We counted additional 2 \n in the first and last line. We need 3
1254 new headers: :method, :path and :scheme. Therefore we need one
1257 nva = malloc(sizeof(nghttp2_nv) * nheader);
1259 *err = CURLE_OUT_OF_MEMORY;
1262 /* Extract :method, :path from request line */
1263 end = strchr(hdbuf, ' ');
1266 nva[0].name = (unsigned char *)":method";
1267 nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
1268 nva[0].value = (unsigned char *)hdbuf;
1269 nva[0].valuelen = (uint16_t)(end - hdbuf);
1270 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1274 end = strchr(hdbuf, ' ');
1277 nva[1].name = (unsigned char *)":path";
1278 nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
1279 nva[1].value = (unsigned char *)hdbuf;
1280 nva[1].valuelen = (uint16_t)(end - hdbuf);
1281 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1283 nva[2].name = (unsigned char *)":scheme";
1284 nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
1285 if(conn->handler->flags & PROTOPT_SSL)
1286 nva[2].value = (unsigned char *)"https";
1288 nva[2].value = (unsigned char *)"http";
1289 nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
1290 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1292 hdbuf = strchr(hdbuf, 0x0a);
1299 for(i = 3; i < nheader; ++i) {
1300 end = strchr(hdbuf, ':');
1303 if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1305 nva[i].name = (unsigned char *)":authority";
1306 nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
1309 nva[i].name = (unsigned char *)hdbuf;
1310 nva[i].namelen = (uint16_t)(end - hdbuf);
1313 for(; *hdbuf == ' '; ++hdbuf);
1314 end = strchr(hdbuf, 0x0d);
1317 nva[i].value = (unsigned char *)hdbuf;
1318 nva[i].valuelen = (uint16_t)(end - hdbuf);
1319 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1322 /* Inspect Content-Length header field and retrieve the request
1323 entity length so that we can set END_STREAM to the last DATA
1325 if(nva[i].namelen == 14 &&
1326 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
1328 stream->upload_left = 0;
1329 for(j = 0; j < nva[i].valuelen; ++j) {
1330 stream->upload_left *= 10;
1331 stream->upload_left += nva[i].value[j] - '0';
1333 DEBUGF(infof(conn->data,
1334 "request content-length=%"
1335 CURL_FORMAT_CURL_OFF_T
1336 "\n", stream->upload_left));
1340 /* :authority must come before non-pseudo header fields */
1341 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1342 nghttp2_nv authority = nva[authority_idx];
1343 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1344 nva[i] = nva[i - 1];
1349 switch(conn->data->set.httpreq) {
1351 case HTTPREQ_POST_FORM:
1353 data_prd.read_callback = data_source_read_callback;
1354 data_prd.source.ptr = NULL;
1355 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1356 &data_prd, conn->data);
1359 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1366 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1367 *err = CURLE_SEND_ERROR;
1371 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1372 stream_id, conn->data);
1373 stream->stream_id = stream_id;
1375 rv = nghttp2_session_send(h2);
1378 *err = CURLE_SEND_ERROR;
1382 if(stream->stream_id != -1) {
1383 /* If whole HEADERS frame was sent off to the underlying socket,
1384 the nghttp2 library calls data_source_read_callback. But only
1385 it found that no data available, so it deferred the DATA
1386 transmission. Which means that nghttp2_session_want_write()
1387 returns 0 on http2_perform_getsock(), which results that no
1388 writable socket check is performed. To workaround this, we
1389 issue nghttp2_session_resume_data() here to bring back DATA
1390 transmission from deferred state. */
1391 nghttp2_session_resume_data(h2, stream->stream_id);
1398 *err = CURLE_SEND_ERROR;
1402 CURLcode Curl_http2_setup(struct connectdata *conn)
1405 struct http_conn *httpc = &conn->proto.httpc;
1406 struct HTTP *stream = conn->data->req.protop;
1408 stream->stream_id = -1;
1410 if(!stream->header_recvbuf)
1411 stream->header_recvbuf = Curl_add_buffer_init();
1413 if((conn->handler == &Curl_handler_http2_ssl) ||
1414 (conn->handler == &Curl_handler_http2))
1415 return CURLE_OK; /* already done */
1417 if(conn->handler->flags & PROTOPT_SSL)
1418 conn->handler = &Curl_handler_http2_ssl;
1420 conn->handler = &Curl_handler_http2;
1422 result = Curl_http2_init(conn);
1426 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1427 stream->upload_left = 0;
1428 stream->upload_mem = NULL;
1429 stream->upload_len = 0;
1431 httpc->inbuflen = 0;
1432 httpc->nread_inbuf = 0;
1434 httpc->pause_stream_id = 0;
1436 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1437 conn->httpversion = 20;
1438 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1440 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1441 Curl_multi_connchanged(conn->data->multi);
1446 CURLcode Curl_http2_switched(struct connectdata *conn,
1447 const char *mem, size_t nread)
1450 struct http_conn *httpc = &conn->proto.httpc;
1453 struct SessionHandle *data = conn->data;
1454 struct HTTP *stream = conn->data->req.protop;
1456 result = Curl_http2_setup(conn);
1460 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1461 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1462 conn->recv[FIRSTSOCKET] = http2_recv;
1463 conn->send[FIRSTSOCKET] = http2_send;
1465 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1466 /* stream 1 is opened implicitly on upgrade */
1467 stream->stream_id = 1;
1468 /* queue SETTINGS frame (again) */
1469 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1470 httpc->binlen, NULL);
1472 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1473 nghttp2_strerror(rv), rv);
1477 nghttp2_session_set_stream_user_data(httpc->h2,
1482 /* stream ID is unknown at this point */
1483 stream->stream_id = -1;
1484 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1486 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1487 nghttp2_strerror(rv), rv);
1492 /* we are going to copy mem to httpc->inbuf. This is required since
1493 mem is part of buffer pointed by stream->mem, and callbacks
1494 called by nghttp2_session_mem_recv() will write stream specific
1495 data into stream->mem, overwriting data already there. */
1496 if(H2_BUFSIZE < nread) {
1497 failf(data, "connection buffer size is too small to store data following "
1498 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1503 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1504 " after upgrade: len=%zu\n",
1507 memcpy(httpc->inbuf, mem, nread);
1508 httpc->inbuflen = nread;
1510 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1513 if(nghttp2_is_fatal((int)nproc)) {
1514 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1515 nghttp2_strerror((int)nproc), (int)nproc);
1519 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1521 if((ssize_t)nread == nproc) {
1522 httpc->inbuflen = 0;
1523 httpc->nread_inbuf = 0;
1526 httpc->nread_inbuf += nproc;
1529 /* Try to send some frames since we may read SETTINGS already. */
1530 rv = nghttp2_session_send(httpc->h2);
1533 failf(data, "nghttp2_session_send() failed: %s(%d)",
1534 nghttp2_strerror(rv), rv);
1541 #else /* !USE_NGHTTP2 */
1543 /* Satisfy external references even if http2 is not compiled in. */
1545 #define CURL_DISABLE_TYPECHECK
1546 #include <curl/curl.h>
1548 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
1555 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
1562 #endif /* USE_NGHTTP2 */