1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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>
31 #include "curl_base64.h"
34 #include "conncache.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)
69 * Curl_http2_init_state() is called when the easy handle is created and
70 * allows for HTTP/2 specific init of state.
72 void Curl_http2_init_state(struct UrlState *state)
74 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
78 * Curl_http2_init_userset() is called when the easy handle is created and
79 * allows for HTTP/2 specific user-set fields.
81 void Curl_http2_init_userset(struct UserDefined *set)
83 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
86 static int http2_perform_getsock(const struct connectdata *conn,
87 curl_socket_t *sock, /* points to
93 const struct http_conn *c = &conn->proto.httpc;
94 int bitmap = GETSOCK_BLANK;
97 /* TODO We should check underlying socket state if it is SSL socket
98 because of renegotiation. */
99 sock[0] = conn->sock[FIRSTSOCKET];
101 /* in a HTTP/2 connection we can basically always get a frame so we should
102 always be ready for one */
103 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
105 if(nghttp2_session_want_write(c->h2))
106 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
111 static int http2_getsock(struct connectdata *conn,
112 curl_socket_t *sock, /* points to numsocks
116 return http2_perform_getsock(conn, sock, numsocks);
120 * http2_stream_free() free HTTP2 stream related data
122 static void http2_stream_free(struct HTTP *http)
125 Curl_add_buffer_free(http->header_recvbuf);
126 http->header_recvbuf = NULL; /* clear the pointer */
127 Curl_add_buffer_free(http->trailer_recvbuf);
128 http->trailer_recvbuf = NULL; /* clear the pointer */
129 for(; http->push_headers_used > 0; --http->push_headers_used) {
130 free(http->push_headers[http->push_headers_used - 1]);
132 free(http->push_headers);
133 http->push_headers = NULL;
137 static CURLcode http2_disconnect(struct connectdata *conn,
138 bool dead_connection)
140 struct http_conn *c = &conn->proto.httpc;
141 (void)dead_connection;
143 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
145 nghttp2_session_del(c->h2);
146 Curl_safefree(c->inbuf);
147 http2_stream_free(conn->data->req.protop);
149 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
154 /* called from Curl_http_setup_conn */
155 void Curl_http2_setup_req(struct Curl_easy *data)
157 struct HTTP *http = data->req.protop;
159 http->nread_header_recvbuf = 0;
160 http->bodystarted = FALSE;
161 http->status_code = -1;
162 http->pausedata = NULL;
164 http->error_code = NGHTTP2_NO_ERROR;
165 http->closed = FALSE;
166 http->close_handled = FALSE;
167 http->mem = data->state.buffer;
172 /* called from Curl_http_setup_conn */
173 void Curl_http2_setup_conn(struct connectdata *conn)
175 conn->proto.httpc.settings.max_concurrent_streams =
176 DEFAULT_MAX_CONCURRENT_STREAMS;
180 * HTTP2 handler interface. This isn't added to the general list of protocols
181 * but will be used at run-time when the protocol is dynamically switched from
184 const struct Curl_handler Curl_handler_http2 = {
186 ZERO_NULL, /* setup_connection */
187 Curl_http, /* do_it */
188 Curl_http_done, /* done */
189 ZERO_NULL, /* do_more */
190 ZERO_NULL, /* connect_it */
191 ZERO_NULL, /* connecting */
192 ZERO_NULL, /* doing */
193 http2_getsock, /* proto_getsock */
194 http2_getsock, /* doing_getsock */
195 ZERO_NULL, /* domore_getsock */
196 http2_perform_getsock, /* perform_getsock */
197 http2_disconnect, /* disconnect */
198 ZERO_NULL, /* readwrite */
199 PORT_HTTP, /* defport */
200 CURLPROTO_HTTP, /* protocol */
201 PROTOPT_STREAM /* flags */
204 const struct Curl_handler Curl_handler_http2_ssl = {
205 "HTTPS", /* scheme */
206 ZERO_NULL, /* setup_connection */
207 Curl_http, /* do_it */
208 Curl_http_done, /* done */
209 ZERO_NULL, /* do_more */
210 ZERO_NULL, /* connect_it */
211 ZERO_NULL, /* connecting */
212 ZERO_NULL, /* doing */
213 http2_getsock, /* proto_getsock */
214 http2_getsock, /* doing_getsock */
215 ZERO_NULL, /* domore_getsock */
216 http2_perform_getsock, /* perform_getsock */
217 http2_disconnect, /* disconnect */
218 ZERO_NULL, /* readwrite */
219 PORT_HTTP, /* defport */
220 CURLPROTO_HTTPS, /* protocol */
221 PROTOPT_SSL | PROTOPT_STREAM /* flags */
225 * Store nghttp2 version info in this buffer, Prefix with a space. Return
226 * total length written.
228 int Curl_http2_ver(char *p, size_t len)
230 nghttp2_info *h2 = nghttp2_version(0);
231 return snprintf(p, len, " nghttp2/%s", h2->version_str);
234 /* HTTP/2 error code to name based on the Error Code Registry.
235 https://tools.ietf.org/html/rfc7540#page-77
236 nghttp2_error_code enums are identical.
238 const char *Curl_http2_strerror(uint32_t err)
240 #ifndef NGHTTP2_HAS_HTTP2_STRERROR
241 const char *str[] = {
242 "NO_ERROR", /* 0x0 */
243 "PROTOCOL_ERROR", /* 0x1 */
244 "INTERNAL_ERROR", /* 0x2 */
245 "FLOW_CONTROL_ERROR", /* 0x3 */
246 "SETTINGS_TIMEOUT", /* 0x4 */
247 "STREAM_CLOSED", /* 0x5 */
248 "FRAME_SIZE_ERROR", /* 0x6 */
249 "REFUSED_STREAM", /* 0x7 */
251 "COMPRESSION_ERROR", /* 0x9 */
252 "CONNECT_ERROR", /* 0xA */
253 "ENHANCE_YOUR_CALM", /* 0xB */
254 "INADEQUATE_SECURITY", /* 0xC */
255 "HTTP_1_1_REQUIRED" /* 0xD */
257 return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown";
259 return nghttp2_http2_strerror(err);
264 * The implementation of nghttp2_send_callback type. Here we write |data| with
265 * size |length| to the network and return the number of bytes actually
266 * written. See the documentation of nghttp2_send_callback for the details.
268 static ssize_t send_callback(nghttp2_session *h2,
269 const uint8_t *data, size_t length, int flags,
272 struct connectdata *conn = (struct connectdata *)userp;
273 struct http_conn *c = &conn->proto.httpc;
275 CURLcode result = CURLE_OK;
280 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
281 data, length, &result);
283 if(result == CURLE_AGAIN) {
284 return NGHTTP2_ERR_WOULDBLOCK;
288 failf(conn->data, "Failed sending HTTP2 data");
289 return NGHTTP2_ERR_CALLBACK_FAILURE;
293 return NGHTTP2_ERR_WOULDBLOCK;
299 /* We pass a pointer to this struct in the push callback, but the contents of
300 the struct are hidden from the user. */
301 struct curl_pushheaders {
302 struct Curl_easy *data;
303 const nghttp2_push_promise *frame;
307 * push header access function. Only to be used from within the push callback
309 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
311 /* Verify that we got a good easy handle in the push header struct, mostly to
312 detect rubbish input fast(er). */
313 if(!h || !GOOD_EASY_HANDLE(h->data))
316 struct HTTP *stream = h->data->req.protop;
317 if(num < stream->push_headers_used)
318 return stream->push_headers[num];
324 * push header access function. Only to be used from within the push callback
326 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
328 /* Verify that we got a good easy handle in the push header struct,
329 mostly to detect rubbish input fast(er). Also empty header name
330 is just a rubbish too. We have to allow ":" at the beginning of
331 the header, but header == ":" must be rejected. If we have ':' in
332 the middle of header, it could be matched in middle of the value,
333 this is because we do prefix match.*/
334 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
335 !strcmp(header, ":") || strchr(header + 1, ':'))
338 struct HTTP *stream = h->data->req.protop;
339 size_t len = strlen(header);
341 for(i=0; i<stream->push_headers_used; i++) {
342 if(!strncmp(header, stream->push_headers[i], len)) {
343 /* sub-match, make sure that it is followed by a colon */
344 if(stream->push_headers[i][len] != ':')
346 return &stream->push_headers[i][len+1];
353 static struct Curl_easy *duphandle(struct Curl_easy *data)
355 struct Curl_easy *second = curl_easy_duphandle(data);
357 /* setup the request struct */
358 struct HTTP *http = calloc(1, sizeof(struct HTTP));
360 (void)Curl_close(second);
364 second->req.protop = http;
365 http->header_recvbuf = Curl_add_buffer_init();
366 if(!http->header_recvbuf) {
368 (void)Curl_close(second);
372 Curl_http2_setup_req(second);
373 second->state.stream_weight = data->state.stream_weight;
381 static int push_promise(struct Curl_easy *data,
382 struct connectdata *conn,
383 const nghttp2_push_promise *frame)
386 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
387 frame->promised_stream_id));
388 if(data->multi->push_cb) {
390 struct HTTP *newstream;
391 struct curl_pushheaders heads;
393 struct http_conn *httpc;
395 /* clone the parent */
396 struct Curl_easy *newhandle = duphandle(data);
398 infof(data, "failed to duplicate handle\n");
399 rv = 1; /* FAIL HARD */
405 /* ask the application */
406 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
408 stream = data->req.protop;
410 failf(data, "Internal NULL stream!\n");
415 rv = data->multi->push_cb(data, newhandle,
416 stream->push_headers_used, &heads,
417 data->multi->push_userp);
419 /* free the headers again */
420 for(i=0; i<stream->push_headers_used; i++)
421 free(stream->push_headers[i]);
422 free(stream->push_headers);
423 stream->push_headers = NULL;
424 stream->push_headers_used = 0;
427 /* denied, kill off the new handle again */
428 http2_stream_free(newhandle->req.protop);
429 (void)Curl_close(newhandle);
433 newstream = newhandle->req.protop;
434 newstream->stream_id = frame->promised_stream_id;
435 newhandle->req.maxdownload = -1;
436 newhandle->req.size = -1;
438 /* approved, add to the multi handle and immediately switch to PERFORM
439 state with the given connection !*/
440 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
442 infof(data, "failed to add handle to multi\n");
443 http2_stream_free(newhandle->req.protop);
444 Curl_close(newhandle);
449 httpc = &conn->proto.httpc;
450 nghttp2_session_set_stream_user_data(httpc->h2,
451 frame->promised_stream_id, newhandle);
454 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
461 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
464 struct connectdata *conn = (struct connectdata *)userp;
465 struct http_conn *httpc = &conn->proto.httpc;
466 struct Curl_easy *data_s = NULL;
467 struct HTTP *stream = NULL;
468 static int lastStream = -1;
471 int32_t stream_id = frame->hd.stream_id;
474 /* stream ID zero is for connection-oriented stuff */
475 if(frame->hd.type == NGHTTP2_SETTINGS) {
476 uint32_t max_conn = httpc->settings.max_concurrent_streams;
477 DEBUGF(infof(conn->data, "Got SETTINGS\n"));
478 httpc->settings.max_concurrent_streams =
479 nghttp2_session_get_remote_settings(
480 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
481 httpc->settings.enable_push =
482 nghttp2_session_get_remote_settings(
483 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
484 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
485 httpc->settings.max_concurrent_streams));
486 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
487 httpc->settings.enable_push?"TRUE":"false"));
488 if(max_conn != httpc->settings.max_concurrent_streams) {
489 /* only signal change if the value actually changed */
491 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
492 Curl_multi_connchanged(conn->data->multi);
497 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
498 if(lastStream != stream_id) {
499 lastStream = stream_id;
502 DEBUGF(infof(conn->data,
503 "No Curl_easy associated with stream: %x\n",
508 stream = data_s->req.protop;
510 DEBUGF(infof(conn->data, "No proto pointer for stream: %x\n",
512 return NGHTTP2_ERR_CALLBACK_FAILURE;
515 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
516 frame->hd.type, stream_id));
518 switch(frame->hd.type) {
520 /* If body started on this stream, then receiving DATA is illegal. */
521 if(!stream->bodystarted) {
522 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
523 stream_id, NGHTTP2_PROTOCOL_ERROR);
525 if(nghttp2_is_fatal(rv)) {
526 return NGHTTP2_ERR_CALLBACK_FAILURE;
530 case NGHTTP2_HEADERS:
531 if(stream->bodystarted) {
532 /* Only valid HEADERS after body started is trailer HEADERS. We
533 buffer them in on_header callback. */
537 /* nghttp2 guarantees that :status is received, and we store it to
538 stream->status_code */
539 DEBUGASSERT(stream->status_code != -1);
541 /* Only final status code signals the end of header */
542 if(stream->status_code / 100 != 1) {
543 stream->bodystarted = TRUE;
544 stream->status_code = -1;
547 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
549 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
550 ncopy = MIN(stream->len, left);
552 memcpy(&stream->mem[stream->memlen],
553 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
555 stream->nread_header_recvbuf += ncopy;
557 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
558 ncopy, stream_id, stream->mem));
560 stream->len -= ncopy;
561 stream->memlen += ncopy;
563 data_s->state.drain++;
564 httpc->drain_total++;
566 /* get the pointer from userp again since it was re-assigned above */
567 struct connectdata *conn_s = (struct connectdata *)userp;
569 /* if we receive data for another handle, wake that up */
570 if(conn_s->data != data_s)
571 Curl_expire(data_s, 0);
574 case NGHTTP2_PUSH_PROMISE:
575 rv = push_promise(data_s, conn, &frame->push_promise);
577 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
578 frame->push_promise.promised_stream_id,
580 if(nghttp2_is_fatal(rv)) {
586 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
587 frame->hd.type, stream_id));
593 static int on_invalid_frame_recv(nghttp2_session *session,
594 const nghttp2_frame *frame,
595 int lib_error_code, void *userp)
597 struct Curl_easy *data_s = NULL;
600 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
603 "on_invalid_frame_recv() was called, error=%d:%s\n",
604 lib_error_code, nghttp2_strerror(lib_error_code)));
609 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
611 const uint8_t *data, size_t len, void *userp)
614 struct Curl_easy *data_s;
616 struct connectdata *conn = (struct connectdata *)userp;
621 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
623 /* get the stream from the hash based on Stream ID */
624 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
626 /* Receiving a Stream ID not in the hash should not happen, this is an
627 internal error more than anything else! */
628 return NGHTTP2_ERR_CALLBACK_FAILURE;
630 stream = data_s->req.protop;
632 return NGHTTP2_ERR_CALLBACK_FAILURE;
634 nread = MIN(stream->len, len);
635 memcpy(&stream->mem[stream->memlen], data, nread);
637 stream->len -= nread;
638 stream->memlen += nread;
640 data_s->state.drain++;
641 conn->proto.httpc.drain_total++;
643 /* if we receive data for another handle, wake that up */
644 if(conn->data != data_s)
645 Curl_expire(data_s, 0);
647 DEBUGF(infof(data_s, "%zu data received for stream %u "
648 "(%zu left in buffer %p, total %zu)\n",
650 stream->len, stream->mem,
654 stream->pausedata = data + nread;
655 stream->pauselen = len - nread;
656 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
658 len - nread, stream_id));
659 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
661 return NGHTTP2_ERR_PAUSE;
664 /* pause execution of nghttp2 if we received data for another handle
665 in order to process them first. */
666 if(conn->data != data_s) {
667 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
669 return NGHTTP2_ERR_PAUSE;
675 static int before_frame_send(nghttp2_session *session,
676 const nghttp2_frame *frame,
679 struct Curl_easy *data_s;
682 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
684 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
689 static int on_frame_send(nghttp2_session *session,
690 const nghttp2_frame *frame,
693 struct Curl_easy *data_s;
696 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
698 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
703 static int on_frame_not_send(nghttp2_session *session,
704 const nghttp2_frame *frame,
705 int lib_error_code, void *userp)
707 struct Curl_easy *data_s;
710 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
713 "on_frame_not_send() was called, lib_error_code = %d\n",
718 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
719 uint32_t error_code, void *userp)
721 struct Curl_easy *data_s;
723 struct connectdata *conn = (struct connectdata *)userp;
728 /* get the stream from the hash based on Stream ID, stream ID zero is for
729 connection-oriented stuff */
730 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
732 /* We could get stream ID not in the hash. For example, if we
733 decided to reject stream (e.g., PUSH_PROMISE). */
736 DEBUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
737 Curl_http2_strerror(error_code), error_code, stream_id));
738 stream = data_s->req.protop;
740 return NGHTTP2_ERR_CALLBACK_FAILURE;
742 stream->error_code = error_code;
743 stream->closed = TRUE;
744 data_s->state.drain++;
745 conn->proto.httpc.drain_total++;
747 /* remove the entry from the hash as the stream is now gone */
748 nghttp2_session_set_stream_user_data(session, stream_id, 0);
749 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
754 static int on_begin_headers(nghttp2_session *session,
755 const nghttp2_frame *frame, void *userp)
758 struct Curl_easy *data_s = NULL;
761 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
766 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
768 if(frame->hd.type != NGHTTP2_HEADERS) {
772 stream = data_s->req.protop;
773 if(!stream || !stream->bodystarted) {
777 /* This is trailer HEADERS started. Allocate buffer for them. */
778 DEBUGF(infof(data_s, "trailer field started\n"));
780 assert(stream->trailer_recvbuf == NULL);
782 stream->trailer_recvbuf = Curl_add_buffer_init();
783 if(!stream->trailer_recvbuf) {
784 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
790 /* Decode HTTP status code. Returns -1 if no valid status code was
792 static int decode_status_code(const uint8_t *value, size_t len)
803 for(i = 0; i < 3; ++i) {
806 if(c < '0' || c > '9') {
817 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
818 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
819 const uint8_t *name, size_t namelen,
820 const uint8_t *value, size_t valuelen,
825 struct Curl_easy *data_s;
826 int32_t stream_id = frame->hd.stream_id;
827 struct connectdata *conn = (struct connectdata *)userp;
830 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
832 /* get the stream from the hash based on Stream ID */
833 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
835 /* Receiving a Stream ID not in the hash should not happen, this is an
836 internal error more than anything else! */
837 return NGHTTP2_ERR_CALLBACK_FAILURE;
839 stream = data_s->req.protop;
841 failf(data_s, "Internal NULL stream! 5\n");
842 return NGHTTP2_ERR_CALLBACK_FAILURE;
845 /* Store received PUSH_PROMISE headers to be used when the subsequent
846 PUSH_PROMISE callback comes */
847 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
850 if(!stream->push_headers) {
851 stream->push_headers_alloc = 10;
852 stream->push_headers = malloc(stream->push_headers_alloc *
854 stream->push_headers_used = 0;
856 else if(stream->push_headers_used ==
857 stream->push_headers_alloc) {
859 stream->push_headers_alloc *= 2;
860 headp = Curl_saferealloc(stream->push_headers,
861 stream->push_headers_alloc * sizeof(char *));
863 stream->push_headers = NULL;
864 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
866 stream->push_headers = headp;
868 h = aprintf("%s:%s", name, value);
870 stream->push_headers[stream->push_headers_used++] = h;
874 if(stream->bodystarted) {
875 /* This is trailer fields. */
876 /* 3 is for ":" and "\r\n". */
877 uint32_t n = (uint32_t)(namelen + valuelen + 3);
879 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
882 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
883 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
884 Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
885 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
886 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
891 if(namelen == sizeof(":status") - 1 &&
892 memcmp(":status", name, namelen) == 0) {
893 /* nghttp2 guarantees :status is received first and only once, and
894 value is 3 digits status code, and decode_status_code always
896 stream->status_code = decode_status_code(value, valuelen);
897 DEBUGASSERT(stream->status_code != -1);
899 Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
900 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
901 /* the space character after the status code is mandatory */
902 Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
903 /* if we receive data for another handle, wake that up */
904 if(conn->data != data_s)
905 Curl_expire(data_s, 0);
907 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
908 stream->status_code, data_s));
912 /* nghttp2 guarantees that namelen > 0, and :status was already
913 received, and this is not pseudo-header field . */
914 /* convert to a HTTP1-style header */
915 Curl_add_buffer(stream->header_recvbuf, name, namelen);
916 Curl_add_buffer(stream->header_recvbuf, ": ", 2);
917 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
918 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
919 /* if we receive data for another handle, wake that up */
920 if(conn->data != data_s)
921 Curl_expire(data_s, 0);
923 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
926 return 0; /* 0 is successful */
929 static ssize_t data_source_read_callback(nghttp2_session *session,
931 uint8_t *buf, size_t length,
932 uint32_t *data_flags,
933 nghttp2_data_source *source,
936 struct Curl_easy *data_s;
937 struct HTTP *stream = NULL;
943 /* get the stream from the hash based on Stream ID, stream ID zero is for
944 connection-oriented stuff */
945 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
947 /* Receiving a Stream ID not in the hash should not happen, this is an
948 internal error more than anything else! */
949 return NGHTTP2_ERR_CALLBACK_FAILURE;
951 stream = data_s->req.protop;
953 return NGHTTP2_ERR_CALLBACK_FAILURE;
956 return NGHTTP2_ERR_INVALID_ARGUMENT;
958 nread = MIN(stream->upload_len, length);
960 memcpy(buf, stream->upload_mem, nread);
961 stream->upload_mem += nread;
962 stream->upload_len -= nread;
963 if(data_s->state.infilesize != -1)
964 stream->upload_left -= nread;
967 if(stream->upload_left == 0)
968 *data_flags = NGHTTP2_DATA_FLAG_EOF;
970 return NGHTTP2_ERR_DEFERRED;
972 DEBUGF(infof(data_s, "data_source_read_callback: "
973 "returns %zu bytes stream %u\n",
979 #define H2_BUFSIZE 32768
981 #ifdef NGHTTP2_HAS_ERROR_CALLBACK
982 static int error_callback(nghttp2_session *session,
987 struct connectdata *conn = (struct connectdata *)userp;
989 infof(conn->data, "http2 error: %.*s\n", len, msg);
994 static void populate_settings(struct connectdata *conn,
995 struct http_conn *httpc)
997 nghttp2_settings_entry *iv = httpc->local_settings;
999 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1002 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1003 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1005 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1006 iv[2].value = conn->data->multi->push_cb != NULL;
1008 httpc->local_settings_num = 3;
1011 void Curl_http2_done(struct connectdata *conn, bool premature)
1013 struct Curl_easy *data = conn->data;
1014 struct HTTP *http = data->req.protop;
1015 struct http_conn *httpc = &conn->proto.httpc;
1017 if(http->header_recvbuf) {
1018 DEBUGF(infof(data, "free header_recvbuf!!\n"));
1019 Curl_add_buffer_free(http->header_recvbuf);
1020 http->header_recvbuf = NULL; /* clear the pointer */
1021 Curl_add_buffer_free(http->trailer_recvbuf);
1022 http->trailer_recvbuf = NULL; /* clear the pointer */
1023 if(http->push_headers) {
1024 /* if they weren't used and then freed before */
1025 for(; http->push_headers_used > 0; --http->push_headers_used) {
1026 free(http->push_headers[http->push_headers_used - 1]);
1028 free(http->push_headers);
1029 http->push_headers = NULL;
1035 nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
1036 NGHTTP2_STREAM_CLOSED);
1037 if(http->stream_id == httpc->pause_stream_id) {
1038 infof(data, "stopped the pause stream!\n");
1039 httpc->pause_stream_id = 0;
1042 if(http->stream_id) {
1043 nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
1044 http->stream_id = 0;
1049 * Initialize nghttp2 for a Curl connection
1051 CURLcode Curl_http2_init(struct connectdata *conn)
1053 if(!conn->proto.httpc.h2) {
1055 nghttp2_session_callbacks *callbacks;
1057 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1058 if(conn->proto.httpc.inbuf == NULL)
1059 return CURLE_OUT_OF_MEMORY;
1061 rc = nghttp2_session_callbacks_new(&callbacks);
1064 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1065 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1068 /* nghttp2_send_callback */
1069 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1070 /* nghttp2_on_frame_recv_callback */
1071 nghttp2_session_callbacks_set_on_frame_recv_callback
1072 (callbacks, on_frame_recv);
1073 /* nghttp2_on_invalid_frame_recv_callback */
1074 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
1075 (callbacks, on_invalid_frame_recv);
1076 /* nghttp2_on_data_chunk_recv_callback */
1077 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1078 (callbacks, on_data_chunk_recv);
1079 /* nghttp2_before_frame_send_callback */
1080 nghttp2_session_callbacks_set_before_frame_send_callback
1081 (callbacks, before_frame_send);
1082 /* nghttp2_on_frame_send_callback */
1083 nghttp2_session_callbacks_set_on_frame_send_callback
1084 (callbacks, on_frame_send);
1085 /* nghttp2_on_frame_not_send_callback */
1086 nghttp2_session_callbacks_set_on_frame_not_send_callback
1087 (callbacks, on_frame_not_send);
1088 /* nghttp2_on_stream_close_callback */
1089 nghttp2_session_callbacks_set_on_stream_close_callback
1090 (callbacks, on_stream_close);
1091 /* nghttp2_on_begin_headers_callback */
1092 nghttp2_session_callbacks_set_on_begin_headers_callback
1093 (callbacks, on_begin_headers);
1094 /* nghttp2_on_header_callback */
1095 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1097 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1099 /* The nghttp2 session is not yet setup, do it */
1100 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1102 nghttp2_session_callbacks_del(callbacks);
1105 failf(conn->data, "Couldn't initialize nghttp2!");
1106 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1113 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1115 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1116 struct connectdata *conn)
1122 struct SingleRequest *k = &conn->data->req;
1123 uint8_t *binsettings = conn->proto.httpc.binsettings;
1124 struct http_conn *httpc = &conn->proto.httpc;
1126 populate_settings(conn, httpc);
1128 /* this returns number of bytes it wrote */
1129 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1130 httpc->local_settings,
1131 httpc->local_settings_num);
1133 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1134 return CURLE_FAILED_INIT;
1136 conn->proto.httpc.binlen = binlen;
1138 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1143 result = Curl_add_bufferf(req,
1144 "Connection: Upgrade, HTTP2-Settings\r\n"
1146 "HTTP2-Settings: %s\r\n",
1147 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1150 k->upgr101 = UPGR101_REQUESTED;
1156 * Returns nonzero if current HTTP/2 session should be closed.
1158 static int should_close_session(struct http_conn *httpc)
1160 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1161 !nghttp2_session_want_write(httpc->h2);
1164 static int h2_session_send(struct Curl_easy *data,
1165 nghttp2_session *h2);
1168 * h2_process_pending_input() processes pending input left in
1169 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1170 * This function returns 0 if it succeeds, or -1 and error code will
1171 * be assigned to *err.
1173 static int h2_process_pending_input(struct Curl_easy *data,
1174 struct http_conn *httpc,
1181 nread = httpc->inbuflen - httpc->nread_inbuf;
1182 inbuf = httpc->inbuf + httpc->nread_inbuf;
1184 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1187 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1188 "%d:%s\n", rv, nghttp2_strerror((int)rv));
1189 *err = CURLE_RECV_ERROR;
1195 "h2_process_pending_input: All data in connection buffer "
1197 httpc->inbuflen = 0;
1198 httpc->nread_inbuf = 0;
1201 httpc->nread_inbuf += rv;
1203 "h2_process_pending_input: %zu bytes left in connection "
1205 httpc->inbuflen - httpc->nread_inbuf));
1208 rv = h2_session_send(data, httpc->h2);
1210 *err = CURLE_SEND_ERROR;
1214 if(should_close_session(httpc)) {
1216 "h2_process_pending_input: nothing to do in this session\n"));
1225 * Called from transfer.c:done_sending when we stop uploading.
1227 CURLcode Curl_http2_done_sending(struct connectdata *conn)
1229 CURLcode result = CURLE_OK;
1231 if((conn->handler == &Curl_handler_http2_ssl) ||
1232 (conn->handler == &Curl_handler_http2)) {
1233 /* make sure this is only attempted for HTTP/2 transfers */
1235 struct HTTP *stream = conn->data->req.protop;
1237 if(stream->upload_left) {
1238 /* If the stream still thinks there's data left to upload. */
1239 struct http_conn *httpc = &conn->proto.httpc;
1240 nghttp2_session *h2 = httpc->h2;
1242 stream->upload_left = 0; /* DONE! */
1244 /* resume sending here to trigger the callback to get called again so
1245 that it can signal EOF to nghttp2 */
1246 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1248 (void)h2_process_pending_input(conn->data, httpc, &result);
1255 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1256 struct Curl_easy *data,
1257 struct HTTP *stream, CURLcode *err)
1259 char *trailer_pos, *trailer_end;
1261 struct http_conn *httpc = &conn->proto.httpc;
1263 if(httpc->pause_stream_id == stream->stream_id) {
1264 httpc->pause_stream_id = 0;
1267 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1268 httpc->drain_total -= data->state.drain;
1269 data->state.drain = 0;
1271 if(httpc->pause_stream_id == 0) {
1272 if(h2_process_pending_input(data, httpc, err) != 0) {
1277 DEBUGASSERT(data->state.drain == 0);
1279 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1280 stream->closed = FALSE;
1281 if(stream->error_code != NGHTTP2_NO_ERROR) {
1282 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
1283 stream->stream_id, Curl_http2_strerror(stream->error_code),
1284 stream->error_code);
1285 *err = CURLE_HTTP2_STREAM;
1289 if(!stream->bodystarted) {
1290 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1291 " all response header fields, teated as error",
1293 *err = CURLE_HTTP2_STREAM;
1297 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1298 trailer_pos = stream->trailer_recvbuf->buffer;
1299 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1301 for(; trailer_pos < trailer_end;) {
1303 memcpy(&n, trailer_pos, sizeof(n));
1304 trailer_pos += sizeof(n);
1306 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1312 trailer_pos += n + 1;
1316 stream->close_handled = TRUE;
1318 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1323 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1324 * and dependency to the peer. It also stores the updated values in the state
1328 static void h2_pri_spec(struct Curl_easy *data,
1329 nghttp2_priority_spec *pri_spec)
1331 struct HTTP *depstream = (data->set.stream_depends_on?
1332 data->set.stream_depends_on->req.protop:NULL);
1333 int32_t depstream_id = depstream? depstream->stream_id:0;
1334 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1335 data->set.stream_depends_e);
1336 data->state.stream_weight = data->set.stream_weight;
1337 data->state.stream_depends_e = data->set.stream_depends_e;
1338 data->state.stream_depends_on = data->set.stream_depends_on;
1342 * h2_session_send() checks if there's been an update in the priority /
1343 * dependency settings and if so it submits a PRIORITY frame with the updated
1346 static int h2_session_send(struct Curl_easy *data,
1347 nghttp2_session *h2)
1349 struct HTTP *stream = data->req.protop;
1350 if((data->set.stream_weight != data->state.stream_weight) ||
1351 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1352 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1353 /* send new weight and/or dependency */
1354 nghttp2_priority_spec pri_spec;
1357 h2_pri_spec(data, &pri_spec);
1359 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1360 stream->stream_id, data));
1361 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1367 return nghttp2_session_send(h2);
1370 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1371 char *mem, size_t len, CURLcode *err)
1373 CURLcode result = CURLE_OK;
1376 struct http_conn *httpc = &conn->proto.httpc;
1377 struct Curl_easy *data = conn->data;
1378 struct HTTP *stream = data->req.protop;
1380 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1382 if(should_close_session(httpc)) {
1384 "http2_recv: nothing to do in this session\n"));
1389 /* Nullify here because we call nghttp2_session_send() and they
1390 might refer to the old buffer. */
1391 stream->upload_mem = NULL;
1392 stream->upload_len = 0;
1395 * At this point 'stream' is just in the Curl_easy the connection
1396 * identifies as its owner at this time.
1399 if(stream->bodystarted &&
1400 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1401 /* If there is body data pending for this stream to return, do that */
1403 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1404 size_t ncopy = MIN(len, left);
1405 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1407 stream->nread_header_recvbuf += ncopy;
1409 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1414 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1415 data, stream->stream_id));
1417 if((data->state.drain) && stream->memlen) {
1418 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1419 stream->memlen, stream->stream_id,
1421 if(mem != stream->mem) {
1422 /* if we didn't get the same buffer this time, we must move the data to
1424 memmove(mem, stream->mem, stream->memlen);
1425 stream->len = len - stream->memlen;
1428 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1429 /* We have paused nghttp2, but we have no pause data (see
1430 on_data_chunk_recv). */
1431 httpc->pause_stream_id = 0;
1432 if(h2_process_pending_input(data, httpc, &result) != 0) {
1438 else if(stream->pausedata) {
1439 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1440 nread = MIN(len, stream->pauselen);
1441 memcpy(mem, stream->pausedata, nread);
1443 stream->pausedata += nread;
1444 stream->pauselen -= nread;
1446 infof(data, "%zu data bytes written\n", nread);
1447 if(stream->pauselen == 0) {
1448 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1449 assert(httpc->pause_stream_id == stream->stream_id);
1450 httpc->pause_stream_id = 0;
1452 stream->pausedata = NULL;
1453 stream->pauselen = 0;
1455 /* When NGHTTP2_ERR_PAUSE is returned from
1456 data_source_read_callback, we might not process DATA frame
1457 fully. Calling nghttp2_session_mem_recv() again will
1458 continue to process DATA frame, but if there is no incoming
1459 frames, then we have to call it again with 0-length data.
1460 Without this, on_stream_close callback will not be called,
1461 and stream could be hanged. */
1462 if(h2_process_pending_input(data, httpc, &result) != 0) {
1467 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1468 nread, stream->stream_id));
1471 else if(httpc->pause_stream_id) {
1472 /* If a stream paused nghttp2_session_mem_recv previously, and has
1473 not processed all data, it still refers to the buffer in
1474 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1475 overwrite that buffer. To avoid that situation, just return
1476 here with CURLE_AGAIN. This could be busy loop since data in
1477 socket is not read. But it seems that usually streams are
1478 notified with its drain property, and socket is read again
1480 DEBUGF(infof(data, "stream %x is paused, pause id: %x\n",
1481 stream->stream_id, httpc->pause_stream_id));
1487 /* remember where to store incoming data for this stream and how big the
1493 if(httpc->inbuflen == 0) {
1494 nread = ((Curl_recv *)httpc->recv_underlying)(
1495 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1498 if(result != CURLE_AGAIN)
1499 failf(data, "Failed receiving HTTP2 data");
1500 else if(stream->closed)
1501 /* received when the stream was already closed! */
1502 return http2_handle_stream_close(conn, data, stream, err);
1509 failf(data, "Unexpected EOF");
1510 *err = CURLE_RECV_ERROR;
1514 DEBUGF(infof(data, "nread=%zd\n", nread));
1516 httpc->inbuflen = nread;
1517 inbuf = httpc->inbuf;
1520 nread = httpc->inbuflen - httpc->nread_inbuf;
1521 inbuf = httpc->inbuf + httpc->nread_inbuf;
1523 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1526 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1528 if(nghttp2_is_fatal((int)rv)) {
1529 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1530 rv, nghttp2_strerror((int)rv));
1531 *err = CURLE_RECV_ERROR;
1534 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1536 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1537 httpc->inbuflen = 0;
1538 httpc->nread_inbuf = 0;
1541 httpc->nread_inbuf += rv;
1542 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1543 httpc->inbuflen - httpc->nread_inbuf));
1545 /* Always send pending frames in nghttp2 session, because
1546 nghttp2_session_mem_recv() may queue new frame */
1547 rv = h2_session_send(data, httpc->h2);
1549 *err = CURLE_SEND_ERROR;
1553 if(should_close_session(httpc)) {
1554 DEBUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1559 if(stream->memlen) {
1560 ssize_t retlen = stream->memlen;
1561 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1562 retlen, stream->stream_id));
1565 if(httpc->pause_stream_id == stream->stream_id) {
1566 /* data for this stream is returned now, but this stream caused a pause
1567 already so we need it called again asap */
1568 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1569 stream->stream_id));
1571 else if(!stream->closed) {
1572 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1573 httpc->drain_total -= data->state.drain;
1574 data->state.drain = 0; /* this stream is hereby drained */
1579 /* If stream is closed, return 0 to signal the http routine to close
1581 if(stream->closed) {
1582 return http2_handle_stream_close(conn, data, stream, err);
1585 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1586 stream->stream_id));
1590 /* Index where :authority header field will appear in request header
1592 #define AUTHORITY_DST_IDX 3
1594 #define HEADER_OVERFLOW(x) \
1595 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1598 * Check header memory for the token "trailers".
1599 * Parse the tokens as separated by comma and surrounded by whitespace.
1600 * Returns TRUE if found or FALSE if not.
1602 static bool contains_trailers(const char *p, size_t len)
1604 const char *end = p + len;
1606 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1608 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1610 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1611 p += sizeof("trailers") - 1;
1612 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1614 if(p == end || *p == ',')
1617 /* skip to next token */
1618 for(; p != end && *p != ','; ++p)
1627 /* Send header to server */
1629 /* Don't send header to server */
1631 /* Discard header, and replace it with "te: trailers" */
1632 HEADERINST_TE_TRAILERS
1633 } header_instruction;
1635 /* Decides how to treat given header field. */
1636 static header_instruction inspect_header(const char *name, size_t namelen,
1637 const char *value, size_t valuelen) {
1640 if(!strncasecompare("te", name, namelen))
1641 return HEADERINST_FORWARD;
1643 return contains_trailers(value, valuelen) ?
1644 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1646 return strncasecompare("upgrade", name, namelen) ?
1647 HEADERINST_IGNORE : HEADERINST_FORWARD;
1649 return (strncasecompare("connection", name, namelen) ||
1650 strncasecompare("keep-alive", name, namelen)) ?
1651 HEADERINST_IGNORE : HEADERINST_FORWARD;
1653 return strncasecompare("proxy-connection", name, namelen) ?
1654 HEADERINST_IGNORE : HEADERINST_FORWARD;
1656 return strncasecompare("transfer-encoding", name, namelen) ?
1657 HEADERINST_IGNORE : HEADERINST_FORWARD;
1659 return HEADERINST_FORWARD;
1663 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1664 const void *mem, size_t len, CURLcode *err)
1667 * BIG TODO: Currently, we send request in this function, but this
1668 * function is also used to send request body. It would be nice to
1669 * add dedicated function for request.
1672 struct http_conn *httpc = &conn->proto.httpc;
1673 struct HTTP *stream = conn->data->req.protop;
1674 nghttp2_nv *nva = NULL;
1677 size_t authority_idx;
1678 char *hdbuf = (char *)mem;
1679 char *end, *line_end;
1680 nghttp2_data_provider data_prd;
1682 nghttp2_session *h2 = httpc->h2;
1683 nghttp2_priority_spec pri_spec;
1687 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1689 if(stream->stream_id != -1) {
1690 if(stream->close_handled) {
1691 infof(conn->data, "stream %d closed\n", stream->stream_id);
1692 *err = CURLE_HTTP2_STREAM;
1695 else if(stream->closed) {
1696 return http2_handle_stream_close(conn, conn->data, stream, err);
1698 /* If stream_id != -1, we have dispatched request HEADERS, and now
1699 are going to send or sending request body in DATA frame */
1700 stream->upload_mem = mem;
1701 stream->upload_len = len;
1702 nghttp2_session_resume_data(h2, stream->stream_id);
1703 rv = h2_session_send(conn->data, h2);
1704 if(nghttp2_is_fatal(rv)) {
1705 *err = CURLE_SEND_ERROR;
1708 len -= stream->upload_len;
1710 /* Nullify here because we call nghttp2_session_send() and they
1711 might refer to the old buffer. */
1712 stream->upload_mem = NULL;
1713 stream->upload_len = 0;
1715 if(should_close_session(httpc)) {
1716 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1721 if(stream->upload_left) {
1722 /* we are sure that we have more data to send here. Calling the
1723 following API will make nghttp2_session_want_write() return
1724 nonzero if remote window allows it, which then libcurl checks
1725 socket is writable or not. See http2_perform_getsock(). */
1726 nghttp2_session_resume_data(h2, stream->stream_id);
1729 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1730 stream->stream_id));
1734 /* Calculate number of headers contained in [mem, mem + len) */
1735 /* Here, we assume the curl http code generate *correct* HTTP header
1738 for(i = 1; i < len; ++i) {
1739 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1747 /* We counted additional 2 \r\n in the first and last line. We need 3
1748 new headers: :method, :path and :scheme. Therefore we need one
1751 nva = malloc(sizeof(nghttp2_nv) * nheader);
1753 *err = CURLE_OUT_OF_MEMORY;
1757 /* Extract :method, :path from request line */
1758 line_end = strstr(hdbuf, "\r\n");
1760 /* Method does not contain spaces */
1761 end = memchr(hdbuf, ' ', line_end - hdbuf);
1762 if(!end || end == hdbuf)
1764 nva[0].name = (unsigned char *)":method";
1765 nva[0].namelen = strlen((char *)nva[0].name);
1766 nva[0].value = (unsigned char *)hdbuf;
1767 nva[0].valuelen = (size_t)(end - hdbuf);
1768 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1769 if(HEADER_OVERFLOW(nva[0])) {
1770 failf(conn->data, "Failed sending HTTP request: Header overflow");
1776 /* Path may contain spaces so scan backwards */
1778 for(i = (size_t)(line_end - hdbuf); i; --i) {
1779 if(hdbuf[i - 1] == ' ') {
1780 end = &hdbuf[i - 1];
1784 if(!end || end == hdbuf)
1786 nva[1].name = (unsigned char *)":path";
1787 nva[1].namelen = strlen((char *)nva[1].name);
1788 nva[1].value = (unsigned char *)hdbuf;
1789 nva[1].valuelen = (size_t)(end - hdbuf);
1790 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1791 if(HEADER_OVERFLOW(nva[1])) {
1792 failf(conn->data, "Failed sending HTTP request: Header overflow");
1799 nva[2].name = (unsigned char *)":scheme";
1800 nva[2].namelen = strlen((char *)nva[2].name);
1801 if(conn->handler->flags & PROTOPT_SSL)
1802 nva[2].value = (unsigned char *)"https";
1804 nva[2].value = (unsigned char *)"http";
1805 nva[2].valuelen = strlen((char *)nva[2].value);
1806 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1807 if(HEADER_OVERFLOW(nva[2])) {
1808 failf(conn->data, "Failed sending HTTP request: Header overflow");
1814 while(i < nheader) {
1817 hdbuf = line_end + 2;
1819 line_end = strstr(hdbuf, "\r\n");
1820 if(line_end == hdbuf)
1823 /* header continuation lines are not supported */
1824 if(*hdbuf == ' ' || *hdbuf == '\t')
1827 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1829 if(end == hdbuf || end == line_end)
1833 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1835 nva[i].name = (unsigned char *)":authority";
1836 nva[i].namelen = strlen((char *)nva[i].name);
1839 nva[i].name = (unsigned char *)hdbuf;
1840 nva[i].namelen = (size_t)(end - hdbuf);
1843 while(*hdbuf == ' ' || *hdbuf == '\t')
1847 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1849 case HEADERINST_IGNORE:
1850 /* skip header fields prohibited by HTTP/2 specification. */
1853 case HEADERINST_TE_TRAILERS:
1854 nva[i].value = (uint8_t*)"trailers";
1855 nva[i].valuelen = sizeof("trailers") - 1;
1858 nva[i].value = (unsigned char *)hdbuf;
1859 nva[i].valuelen = (size_t)(end - hdbuf);
1862 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1863 if(HEADER_OVERFLOW(nva[i])) {
1864 failf(conn->data, "Failed sending HTTP request: Header overflow");
1870 /* :authority must come before non-pseudo header fields */
1871 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1872 nghttp2_nv authority = nva[authority_idx];
1873 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1874 nva[i] = nva[i - 1];
1879 /* Warn stream may be rejected if cumulative length of headers is too large.
1880 It appears nghttp2 will not send a header frame larger than 64KB. */
1881 #define MAX_ACC 60000 /* <64KB to account for some overhead */
1885 for(i = 0; i < nheader; ++i) {
1886 acc += nva[i].namelen + nva[i].valuelen;
1888 DEBUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
1889 nva[i].namelen, nva[i].name,
1890 nva[i].valuelen, nva[i].value));
1894 infof(conn->data, "http2_send: Warning: The cumulative length of all "
1895 "headers exceeds %zu bytes and that could cause the "
1896 "stream to be rejected.\n", MAX_ACC);
1900 h2_pri_spec(conn->data, &pri_spec);
1902 switch(conn->data->set.httpreq) {
1904 case HTTPREQ_POST_FORM:
1906 if(conn->data->state.infilesize != -1)
1907 stream->upload_left = conn->data->state.infilesize;
1909 /* data sending without specifying the data amount up front */
1910 stream->upload_left = -1; /* unknown, but not zero */
1912 data_prd.read_callback = data_source_read_callback;
1913 data_prd.source.ptr = NULL;
1914 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1915 &data_prd, conn->data);
1918 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1925 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1926 *err = CURLE_SEND_ERROR;
1930 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1931 stream_id, conn->data);
1932 stream->stream_id = stream_id;
1934 /* this does not call h2_session_send() since there can not have been any
1935 * priority upodate since the nghttp2_submit_request() call above */
1936 rv = nghttp2_session_send(h2);
1939 *err = CURLE_SEND_ERROR;
1943 if(should_close_session(httpc)) {
1944 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1949 if(stream->stream_id != -1) {
1950 /* If whole HEADERS frame was sent off to the underlying socket,
1951 the nghttp2 library calls data_source_read_callback. But only
1952 it found that no data available, so it deferred the DATA
1953 transmission. Which means that nghttp2_session_want_write()
1954 returns 0 on http2_perform_getsock(), which results that no
1955 writable socket check is performed. To workaround this, we
1956 issue nghttp2_session_resume_data() here to bring back DATA
1957 transmission from deferred state. */
1958 nghttp2_session_resume_data(h2, stream->stream_id);
1965 *err = CURLE_SEND_ERROR;
1969 CURLcode Curl_http2_setup(struct connectdata *conn)
1972 struct http_conn *httpc = &conn->proto.httpc;
1973 struct HTTP *stream = conn->data->req.protop;
1975 stream->stream_id = -1;
1977 if(!stream->header_recvbuf)
1978 stream->header_recvbuf = Curl_add_buffer_init();
1980 if((conn->handler == &Curl_handler_http2_ssl) ||
1981 (conn->handler == &Curl_handler_http2))
1982 return CURLE_OK; /* already done */
1984 if(conn->handler->flags & PROTOPT_SSL)
1985 conn->handler = &Curl_handler_http2_ssl;
1987 conn->handler = &Curl_handler_http2;
1989 result = Curl_http2_init(conn);
1993 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1994 stream->upload_left = 0;
1995 stream->upload_mem = NULL;
1996 stream->upload_len = 0;
1998 httpc->inbuflen = 0;
1999 httpc->nread_inbuf = 0;
2001 httpc->pause_stream_id = 0;
2002 httpc->drain_total = 0;
2004 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2005 conn->httpversion = 20;
2006 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2008 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2009 Curl_multi_connchanged(conn->data->multi);
2014 CURLcode Curl_http2_switched(struct connectdata *conn,
2015 const char *mem, size_t nread)
2018 struct http_conn *httpc = &conn->proto.httpc;
2021 struct Curl_easy *data = conn->data;
2022 struct HTTP *stream = conn->data->req.protop;
2024 result = Curl_http2_setup(conn);
2028 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
2029 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
2030 conn->recv[FIRSTSOCKET] = http2_recv;
2031 conn->send[FIRSTSOCKET] = http2_send;
2033 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2034 /* stream 1 is opened implicitly on upgrade */
2035 stream->stream_id = 1;
2036 /* queue SETTINGS frame (again) */
2037 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2038 httpc->binlen, NULL);
2040 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2041 nghttp2_strerror(rv), rv);
2045 nghttp2_session_set_stream_user_data(httpc->h2,
2050 populate_settings(conn, httpc);
2052 /* stream ID is unknown at this point */
2053 stream->stream_id = -1;
2054 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2055 httpc->local_settings,
2056 httpc->local_settings_num);
2058 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2059 nghttp2_strerror(rv), rv);
2064 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2065 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2066 HTTP2_HUGE_WINDOW_SIZE);
2068 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2069 nghttp2_strerror(rv), rv);
2074 /* we are going to copy mem to httpc->inbuf. This is required since
2075 mem is part of buffer pointed by stream->mem, and callbacks
2076 called by nghttp2_session_mem_recv() will write stream specific
2077 data into stream->mem, overwriting data already there. */
2078 if(H2_BUFSIZE < nread) {
2079 failf(data, "connection buffer size is too small to store data following "
2080 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2085 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2086 " after upgrade: len=%zu\n",
2090 memcpy(httpc->inbuf, mem, nread);
2091 httpc->inbuflen = nread;
2093 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
2096 if(nghttp2_is_fatal((int)nproc)) {
2097 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
2098 nghttp2_strerror((int)nproc), (int)nproc);
2102 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
2104 if((ssize_t)nread == nproc) {
2105 httpc->inbuflen = 0;
2106 httpc->nread_inbuf = 0;
2109 httpc->nread_inbuf += nproc;
2112 /* Try to send some frames since we may read SETTINGS already. */
2113 rv = h2_session_send(data, httpc->h2);
2116 failf(data, "nghttp2_session_send() failed: %s(%d)",
2117 nghttp2_strerror(rv), rv);
2121 if(should_close_session(httpc)) {
2123 "nghttp2_session_send(): nothing to do in this session\n"));
2130 void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child,
2133 struct Curl_http2_dep **tail;
2134 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2137 if(parent->set.stream_dependents && exclusive) {
2138 struct Curl_http2_dep *node = parent->set.stream_dependents;
2140 node->data->set.stream_depends_on = child;
2144 tail = &child->set.stream_dependents;
2146 tail = &(*tail)->next;
2148 DEBUGASSERT(!*tail);
2149 *tail = parent->set.stream_dependents;
2150 parent->set.stream_dependents = 0;
2153 tail = &parent->set.stream_dependents;
2155 (*tail)->data->set.stream_depends_e = FALSE;
2156 tail = &(*tail)->next;
2159 DEBUGASSERT(!*tail);
2162 child->set.stream_depends_on = parent;
2163 child->set.stream_depends_e = exclusive;
2166 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2168 struct Curl_http2_dep *last = 0;
2169 struct Curl_http2_dep *data = parent->set.stream_dependents;
2170 DEBUGASSERT(child->set.stream_depends_on == parent);
2172 while(data && data->data != child) {
2181 last->next = data->next;
2184 parent->set.stream_dependents = data->next;
2189 child->set.stream_depends_on = 0;
2190 child->set.stream_depends_e = FALSE;
2193 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2195 while(data->set.stream_dependents) {
2196 struct Curl_easy *tmp = data->set.stream_dependents->data;
2197 Curl_http2_remove_child(data, tmp);
2198 if(data->set.stream_depends_on)
2199 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2202 if(data->set.stream_depends_on)
2203 Curl_http2_remove_child(data->set.stream_depends_on, data);
2206 #else /* !USE_NGHTTP2 */
2208 /* Satisfy external references even if http2 is not compiled in. */
2210 #define CURL_DISABLE_TYPECHECK
2211 #include <curl/curl.h>
2213 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2220 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2227 #endif /* USE_NGHTTP2 */