1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, 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)
63 * Curl_http2_init_state() is called when the easy handle is created and
64 * allows for HTTP/2 specific init of state.
66 void Curl_http2_init_state(struct UrlState *state)
68 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
72 * Curl_http2_init_userset() is called when the easy handle is created and
73 * allows for HTTP/2 specific user-set fields.
75 void Curl_http2_init_userset(struct UserDefined *set)
77 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
80 static int http2_perform_getsock(const struct connectdata *conn,
81 curl_socket_t *sock, /* points to
87 const struct http_conn *c = &conn->proto.httpc;
88 int bitmap = GETSOCK_BLANK;
91 /* TODO We should check underlying socket state if it is SSL socket
92 because of renegotiation. */
93 sock[0] = conn->sock[FIRSTSOCKET];
95 /* in a HTTP/2 connection we can basically always get a frame so we should
96 always be ready for one */
97 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
99 if(nghttp2_session_want_write(c->h2))
100 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
105 static int http2_getsock(struct connectdata *conn,
106 curl_socket_t *sock, /* points to numsocks
110 return http2_perform_getsock(conn, sock, numsocks);
113 static CURLcode http2_disconnect(struct connectdata *conn,
114 bool dead_connection)
116 struct HTTP *http = conn->data->req.protop;
117 struct http_conn *c = &conn->proto.httpc;
118 (void)dead_connection;
120 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
122 nghttp2_session_del(c->h2);
123 Curl_safefree(c->inbuf);
126 Curl_add_buffer_free(http->header_recvbuf);
127 http->header_recvbuf = NULL; /* clear the pointer */
128 Curl_add_buffer_free(http->trailer_recvbuf);
129 http->trailer_recvbuf = NULL; /* clear the pointer */
130 for(; http->push_headers_used > 0; --http->push_headers_used) {
131 free(http->push_headers[http->push_headers_used - 1]);
133 free(http->push_headers);
134 http->push_headers = NULL;
137 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
142 /* called from Curl_http_setup_conn */
143 void Curl_http2_setup_req(struct Curl_easy *data)
145 struct HTTP *http = data->req.protop;
147 http->nread_header_recvbuf = 0;
148 http->bodystarted = FALSE;
149 http->status_code = -1;
150 http->pausedata = NULL;
152 http->error_code = NGHTTP2_NO_ERROR;
153 http->closed = FALSE;
154 http->close_handled = FALSE;
155 http->mem = data->state.buffer;
160 /* called from Curl_http_setup_conn */
161 void Curl_http2_setup_conn(struct connectdata *conn)
163 conn->proto.httpc.settings.max_concurrent_streams =
164 DEFAULT_MAX_CONCURRENT_STREAMS;
168 * HTTP2 handler interface. This isn't added to the general list of protocols
169 * but will be used at run-time when the protocol is dynamically switched from
172 const struct Curl_handler Curl_handler_http2 = {
174 ZERO_NULL, /* setup_connection */
175 Curl_http, /* do_it */
176 Curl_http_done, /* done */
177 ZERO_NULL, /* do_more */
178 ZERO_NULL, /* connect_it */
179 ZERO_NULL, /* connecting */
180 ZERO_NULL, /* doing */
181 http2_getsock, /* proto_getsock */
182 http2_getsock, /* doing_getsock */
183 ZERO_NULL, /* domore_getsock */
184 http2_perform_getsock, /* perform_getsock */
185 http2_disconnect, /* disconnect */
186 ZERO_NULL, /* readwrite */
187 PORT_HTTP, /* defport */
188 CURLPROTO_HTTP, /* protocol */
189 PROTOPT_STREAM /* flags */
192 const struct Curl_handler Curl_handler_http2_ssl = {
193 "HTTPS", /* scheme */
194 ZERO_NULL, /* setup_connection */
195 Curl_http, /* do_it */
196 Curl_http_done, /* done */
197 ZERO_NULL, /* do_more */
198 ZERO_NULL, /* connect_it */
199 ZERO_NULL, /* connecting */
200 ZERO_NULL, /* doing */
201 http2_getsock, /* proto_getsock */
202 http2_getsock, /* doing_getsock */
203 ZERO_NULL, /* domore_getsock */
204 http2_perform_getsock, /* perform_getsock */
205 http2_disconnect, /* disconnect */
206 ZERO_NULL, /* readwrite */
207 PORT_HTTP, /* defport */
208 CURLPROTO_HTTPS, /* protocol */
209 PROTOPT_SSL | PROTOPT_STREAM /* flags */
213 * Store nghttp2 version info in this buffer, Prefix with a space. Return
214 * total length written.
216 int Curl_http2_ver(char *p, size_t len)
218 nghttp2_info *h2 = nghttp2_version(0);
219 return snprintf(p, len, " nghttp2/%s", h2->version_str);
222 /* HTTP/2 error code to name based on the Error Code Registry.
223 https://tools.ietf.org/html/rfc7540#page-77
224 nghttp2_error_code enums are identical.
226 const char *Curl_http2_strerror(uint32_t err) {
227 #ifndef NGHTTP2_HAS_HTTP2_STRERROR
228 const char *str[] = {
229 "NO_ERROR", /* 0x0 */
230 "PROTOCOL_ERROR", /* 0x1 */
231 "INTERNAL_ERROR", /* 0x2 */
232 "FLOW_CONTROL_ERROR", /* 0x3 */
233 "SETTINGS_TIMEOUT", /* 0x4 */
234 "STREAM_CLOSED", /* 0x5 */
235 "FRAME_SIZE_ERROR", /* 0x6 */
236 "REFUSED_STREAM", /* 0x7 */
238 "COMPRESSION_ERROR", /* 0x9 */
239 "CONNECT_ERROR", /* 0xA */
240 "ENHANCE_YOUR_CALM", /* 0xB */
241 "INADEQUATE_SECURITY", /* 0xC */
242 "HTTP_1_1_REQUIRED" /* 0xD */
244 return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown";
246 return nghttp2_http2_strerror(err);
251 * The implementation of nghttp2_send_callback type. Here we write |data| with
252 * size |length| to the network and return the number of bytes actually
253 * written. See the documentation of nghttp2_send_callback for the details.
255 static ssize_t send_callback(nghttp2_session *h2,
256 const uint8_t *data, size_t length, int flags,
259 struct connectdata *conn = (struct connectdata *)userp;
260 struct http_conn *c = &conn->proto.httpc;
262 CURLcode result = CURLE_OK;
267 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
268 data, length, &result);
270 if(result == CURLE_AGAIN) {
271 return NGHTTP2_ERR_WOULDBLOCK;
275 failf(conn->data, "Failed sending HTTP2 data");
276 return NGHTTP2_ERR_CALLBACK_FAILURE;
280 return NGHTTP2_ERR_WOULDBLOCK;
286 /* We pass a pointer to this struct in the push callback, but the contents of
287 the struct are hidden from the user. */
288 struct curl_pushheaders {
289 struct Curl_easy *data;
290 const nghttp2_push_promise *frame;
294 * push header access function. Only to be used from within the push callback
296 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
298 /* Verify that we got a good easy handle in the push header struct, mostly to
299 detect rubbish input fast(er). */
300 if(!h || !GOOD_EASY_HANDLE(h->data))
303 struct HTTP *stream = h->data->req.protop;
304 if(num < stream->push_headers_used)
305 return stream->push_headers[num];
311 * push header access function. Only to be used from within the push callback
313 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
315 /* Verify that we got a good easy handle in the push header struct,
316 mostly to detect rubbish input fast(er). Also empty header name
317 is just a rubbish too. We have to allow ":" at the beginning of
318 the header, but header == ":" must be rejected. If we have ':' in
319 the middle of header, it could be matched in middle of the value,
320 this is because we do prefix match.*/
321 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
322 Curl_raw_equal(header, ":") || strchr(header + 1, ':'))
325 struct HTTP *stream = h->data->req.protop;
326 size_t len = strlen(header);
328 for(i=0; i<stream->push_headers_used; i++) {
329 if(!strncmp(header, stream->push_headers[i], len)) {
330 /* sub-match, make sure that it is followed by a colon */
331 if(stream->push_headers[i][len] != ':')
333 return &stream->push_headers[i][len+1];
340 static struct Curl_easy *duphandle(struct Curl_easy *data)
342 struct Curl_easy *second = curl_easy_duphandle(data);
344 /* setup the request struct */
345 struct HTTP *http = calloc(1, sizeof(struct HTTP));
347 (void)Curl_close(second);
351 second->req.protop = http;
352 http->header_recvbuf = Curl_add_buffer_init();
353 if(!http->header_recvbuf) {
355 (void)Curl_close(second);
359 Curl_http2_setup_req(second);
360 second->state.stream_weight = data->state.stream_weight;
368 static int push_promise(struct Curl_easy *data,
369 struct connectdata *conn,
370 const nghttp2_push_promise *frame)
373 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
374 frame->promised_stream_id));
375 if(data->multi->push_cb) {
377 struct HTTP *newstream;
378 struct curl_pushheaders heads;
380 struct http_conn *httpc;
382 /* clone the parent */
383 struct Curl_easy *newhandle = duphandle(data);
385 infof(data, "failed to duplicate handle\n");
386 rv = 1; /* FAIL HARD */
392 /* ask the application */
393 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
395 stream = data->req.protop;
397 failf(data, "Internal NULL stream!\n");
402 rv = data->multi->push_cb(data, newhandle,
403 stream->push_headers_used, &heads,
404 data->multi->push_userp);
406 /* free the headers again */
407 for(i=0; i<stream->push_headers_used; i++)
408 free(stream->push_headers[i]);
409 free(stream->push_headers);
410 stream->push_headers = NULL;
413 /* denied, kill off the new handle again */
414 (void)Curl_close(newhandle);
418 newstream = newhandle->req.protop;
419 newstream->stream_id = frame->promised_stream_id;
420 newhandle->req.maxdownload = -1;
421 newhandle->req.size = -1;
423 /* approved, add to the multi handle and immediately switch to PERFORM
424 state with the given connection !*/
425 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
427 infof(data, "failed to add handle to multi\n");
428 Curl_close(newhandle);
433 httpc = &conn->proto.httpc;
434 nghttp2_session_set_stream_user_data(httpc->h2,
435 frame->promised_stream_id, newhandle);
438 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
445 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
448 struct connectdata *conn = (struct connectdata *)userp;
449 struct http_conn *httpc = &conn->proto.httpc;
450 struct Curl_easy *data_s = NULL;
451 struct HTTP *stream = NULL;
452 static int lastStream = -1;
455 int32_t stream_id = frame->hd.stream_id;
458 /* stream ID zero is for connection-oriented stuff */
459 if(frame->hd.type == NGHTTP2_SETTINGS) {
460 uint32_t max_conn = httpc->settings.max_concurrent_streams;
461 DEBUGF(infof(conn->data, "Got SETTINGS\n"));
462 httpc->settings.max_concurrent_streams =
463 nghttp2_session_get_remote_settings(
464 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
465 httpc->settings.enable_push =
466 nghttp2_session_get_remote_settings(
467 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
468 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
469 httpc->settings.max_concurrent_streams));
470 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
471 httpc->settings.enable_push?"TRUE":"false"));
472 if(max_conn != httpc->settings.max_concurrent_streams) {
473 /* only signal change if the value actually changed */
475 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
476 Curl_multi_connchanged(conn->data->multi);
481 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
482 if(lastStream != stream_id) {
483 lastStream = stream_id;
486 DEBUGF(infof(conn->data,
487 "No Curl_easy associated with stream: %x\n",
492 stream = data_s->req.protop;
494 DEBUGF(infof(conn->data, "No proto pointer for stream: %x\n",
496 return NGHTTP2_ERR_CALLBACK_FAILURE;
499 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
500 frame->hd.type, stream_id));
502 switch(frame->hd.type) {
504 /* If body started on this stream, then receiving DATA is illegal. */
505 if(!stream->bodystarted) {
506 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
507 stream_id, NGHTTP2_PROTOCOL_ERROR);
509 if(nghttp2_is_fatal(rv)) {
510 return NGHTTP2_ERR_CALLBACK_FAILURE;
514 case NGHTTP2_HEADERS:
515 if(stream->bodystarted) {
516 /* Only valid HEADERS after body started is trailer HEADERS. We
517 buffer them in on_header callback. */
521 /* nghttp2 guarantees that :status is received, and we store it to
522 stream->status_code */
523 DEBUGASSERT(stream->status_code != -1);
525 /* Only final status code signals the end of header */
526 if(stream->status_code / 100 != 1) {
527 stream->bodystarted = TRUE;
528 stream->status_code = -1;
531 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
533 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
534 ncopy = MIN(stream->len, left);
536 memcpy(&stream->mem[stream->memlen],
537 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
539 stream->nread_header_recvbuf += ncopy;
541 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
542 ncopy, stream_id, stream->mem));
544 stream->len -= ncopy;
545 stream->memlen += ncopy;
547 data_s->state.drain++;
548 httpc->drain_total++;
550 /* get the pointer from userp again since it was re-assigned above */
551 struct connectdata *conn_s = (struct connectdata *)userp;
553 /* if we receive data for another handle, wake that up */
554 if(conn_s->data != data_s)
555 Curl_expire(data_s, 0);
558 case NGHTTP2_PUSH_PROMISE:
559 rv = push_promise(data_s, conn, &frame->push_promise);
561 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
562 frame->push_promise.promised_stream_id,
564 if(nghttp2_is_fatal(rv)) {
570 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
571 frame->hd.type, stream_id));
577 static int on_invalid_frame_recv(nghttp2_session *session,
578 const nghttp2_frame *frame,
579 int lib_error_code, void *userp)
581 struct Curl_easy *data_s = NULL;
584 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
587 "on_invalid_frame_recv() was called, error=%d:%s\n",
588 lib_error_code, nghttp2_strerror(lib_error_code)));
593 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
595 const uint8_t *data, size_t len, void *userp)
598 struct Curl_easy *data_s;
600 struct connectdata *conn = (struct connectdata *)userp;
605 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
607 /* get the stream from the hash based on Stream ID */
608 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
610 /* Receiving a Stream ID not in the hash should not happen, this is an
611 internal error more than anything else! */
612 return NGHTTP2_ERR_CALLBACK_FAILURE;
614 stream = data_s->req.protop;
616 return NGHTTP2_ERR_CALLBACK_FAILURE;
618 nread = MIN(stream->len, len);
619 memcpy(&stream->mem[stream->memlen], data, nread);
621 stream->len -= nread;
622 stream->memlen += nread;
624 data_s->state.drain++;
625 conn->proto.httpc.drain_total++;
627 /* if we receive data for another handle, wake that up */
628 if(conn->data != data_s)
629 Curl_expire(data_s, 0);
631 DEBUGF(infof(data_s, "%zu data received for stream %u "
632 "(%zu left in buffer %p, total %zu)\n",
634 stream->len, stream->mem,
638 stream->pausedata = data + nread;
639 stream->pauselen = len - nread;
640 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
642 len - nread, stream_id));
643 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
645 return NGHTTP2_ERR_PAUSE;
648 /* pause execution of nghttp2 if we received data for another handle
649 in order to process them first. */
650 if(conn->data != data_s) {
651 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
653 return NGHTTP2_ERR_PAUSE;
659 static int before_frame_send(nghttp2_session *session,
660 const nghttp2_frame *frame,
663 struct Curl_easy *data_s;
666 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
668 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
673 static int on_frame_send(nghttp2_session *session,
674 const nghttp2_frame *frame,
677 struct Curl_easy *data_s;
680 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
682 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
687 static int on_frame_not_send(nghttp2_session *session,
688 const nghttp2_frame *frame,
689 int lib_error_code, void *userp)
691 struct Curl_easy *data_s;
694 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
697 "on_frame_not_send() was called, lib_error_code = %d\n",
702 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
703 uint32_t error_code, void *userp)
705 struct Curl_easy *data_s;
707 struct connectdata *conn = (struct connectdata *)userp;
712 /* get the stream from the hash based on Stream ID, stream ID zero is for
713 connection-oriented stuff */
714 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
716 /* We could get stream ID not in the hash. For example, if we
717 decided to reject stream (e.g., PUSH_PROMISE). */
720 DEBUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
721 Curl_http2_strerror(error_code), error_code, stream_id));
722 stream = data_s->req.protop;
724 return NGHTTP2_ERR_CALLBACK_FAILURE;
726 stream->error_code = error_code;
727 stream->closed = TRUE;
728 data_s->state.drain++;
729 conn->proto.httpc.drain_total++;
731 /* remove the entry from the hash as the stream is now gone */
732 nghttp2_session_set_stream_user_data(session, stream_id, 0);
733 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
738 static int on_begin_headers(nghttp2_session *session,
739 const nghttp2_frame *frame, void *userp)
742 struct Curl_easy *data_s = NULL;
745 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
750 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
752 if(frame->hd.type != NGHTTP2_HEADERS) {
756 stream = data_s->req.protop;
757 if(!stream || !stream->bodystarted) {
761 /* This is trailer HEADERS started. Allocate buffer for them. */
762 DEBUGF(infof(data_s, "trailer field started\n"));
764 assert(stream->trailer_recvbuf == NULL);
766 stream->trailer_recvbuf = Curl_add_buffer_init();
767 if(!stream->trailer_recvbuf) {
768 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
774 /* Decode HTTP status code. Returns -1 if no valid status code was
776 static int decode_status_code(const uint8_t *value, size_t len)
787 for(i = 0; i < 3; ++i) {
790 if(c < '0' || c > '9') {
801 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
802 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
803 const uint8_t *name, size_t namelen,
804 const uint8_t *value, size_t valuelen,
809 struct Curl_easy *data_s;
810 int32_t stream_id = frame->hd.stream_id;
811 struct connectdata *conn = (struct connectdata *)userp;
814 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
816 /* get the stream from the hash based on Stream ID */
817 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
819 /* Receiving a Stream ID not in the hash should not happen, this is an
820 internal error more than anything else! */
821 return NGHTTP2_ERR_CALLBACK_FAILURE;
823 stream = data_s->req.protop;
825 failf(data_s, "Internal NULL stream! 5\n");
826 return NGHTTP2_ERR_CALLBACK_FAILURE;
829 /* Store received PUSH_PROMISE headers to be used when the subsequent
830 PUSH_PROMISE callback comes */
831 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
834 if(!stream->push_headers) {
835 stream->push_headers_alloc = 10;
836 stream->push_headers = malloc(stream->push_headers_alloc *
838 stream->push_headers_used = 0;
840 else if(stream->push_headers_used ==
841 stream->push_headers_alloc) {
843 stream->push_headers_alloc *= 2;
844 headp = realloc(stream->push_headers,
845 stream->push_headers_alloc * sizeof(char *));
847 free(stream->push_headers);
848 stream->push_headers = NULL;
849 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
851 stream->push_headers = headp;
853 h = aprintf("%s:%s", name, value);
855 stream->push_headers[stream->push_headers_used++] = h;
859 if(stream->bodystarted) {
860 /* This is trailer fields. */
861 /* 3 is for ":" and "\r\n". */
862 uint32_t n = (uint32_t)(namelen + valuelen + 3);
864 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
867 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
868 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
869 Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
870 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
871 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
876 if(namelen == sizeof(":status") - 1 &&
877 memcmp(":status", name, namelen) == 0) {
878 /* nghttp2 guarantees :status is received first and only once, and
879 value is 3 digits status code, and decode_status_code always
881 stream->status_code = decode_status_code(value, valuelen);
882 DEBUGASSERT(stream->status_code != -1);
884 Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
885 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
886 /* the space character after the status code is mandatory */
887 Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
888 /* if we receive data for another handle, wake that up */
889 if(conn->data != data_s)
890 Curl_expire(data_s, 0);
892 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
893 stream->status_code, data_s));
897 /* nghttp2 guarantees that namelen > 0, and :status was already
898 received, and this is not pseudo-header field . */
899 /* convert to a HTTP1-style header */
900 Curl_add_buffer(stream->header_recvbuf, name, namelen);
901 Curl_add_buffer(stream->header_recvbuf, ": ", 2);
902 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
903 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
904 /* if we receive data for another handle, wake that up */
905 if(conn->data != data_s)
906 Curl_expire(data_s, 0);
908 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
911 return 0; /* 0 is successful */
914 static ssize_t data_source_read_callback(nghttp2_session *session,
916 uint8_t *buf, size_t length,
917 uint32_t *data_flags,
918 nghttp2_data_source *source,
921 struct Curl_easy *data_s;
922 struct HTTP *stream = NULL;
928 /* get the stream from the hash based on Stream ID, stream ID zero is for
929 connection-oriented stuff */
930 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
932 /* Receiving a Stream ID not in the hash should not happen, this is an
933 internal error more than anything else! */
934 return NGHTTP2_ERR_CALLBACK_FAILURE;
936 stream = data_s->req.protop;
938 return NGHTTP2_ERR_CALLBACK_FAILURE;
941 return NGHTTP2_ERR_INVALID_ARGUMENT;
943 nread = MIN(stream->upload_len, length);
945 memcpy(buf, stream->upload_mem, nread);
946 stream->upload_mem += nread;
947 stream->upload_len -= nread;
948 stream->upload_left -= nread;
951 if(stream->upload_left == 0)
952 *data_flags = NGHTTP2_DATA_FLAG_EOF;
954 return NGHTTP2_ERR_DEFERRED;
956 DEBUGF(infof(data_s, "data_source_read_callback: "
957 "returns %zu bytes stream %u\n",
964 * The HTTP2 settings we send in the Upgrade request
966 static nghttp2_settings_entry settings[] = {
967 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
968 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
971 #define H2_BUFSIZE 32768
973 #ifdef NGHTTP2_HAS_ERROR_CALLBACK
974 static int error_callback(nghttp2_session *session,
979 struct connectdata *conn = (struct connectdata *)userp;
981 infof(conn->data, "http2 error: %.*s\n", len, msg);
986 void Curl_http2_done(struct connectdata *conn, bool premature)
988 struct Curl_easy *data = conn->data;
989 struct HTTP *http = data->req.protop;
990 struct http_conn *httpc = &conn->proto.httpc;
992 if(http->header_recvbuf) {
993 DEBUGF(infof(data, "free header_recvbuf!!\n"));
994 Curl_add_buffer_free(http->header_recvbuf);
995 http->header_recvbuf = NULL; /* clear the pointer */
996 Curl_add_buffer_free(http->trailer_recvbuf);
997 http->trailer_recvbuf = NULL; /* clear the pointer */
998 if(http->push_headers) {
999 /* if they weren't used and then freed before */
1000 for(; http->push_headers_used > 0; --http->push_headers_used) {
1001 free(http->push_headers[http->push_headers_used - 1]);
1003 free(http->push_headers);
1004 http->push_headers = NULL;
1010 nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
1011 NGHTTP2_STREAM_CLOSED);
1012 if(http->stream_id == httpc->pause_stream_id) {
1013 infof(data, "stopped the pause stream!\n");
1014 httpc->pause_stream_id = 0;
1017 if(http->stream_id) {
1018 nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
1019 http->stream_id = 0;
1024 * Initialize nghttp2 for a Curl connection
1026 CURLcode Curl_http2_init(struct connectdata *conn)
1028 if(!conn->proto.httpc.h2) {
1030 nghttp2_session_callbacks *callbacks;
1032 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1033 if(conn->proto.httpc.inbuf == NULL)
1034 return CURLE_OUT_OF_MEMORY;
1036 rc = nghttp2_session_callbacks_new(&callbacks);
1039 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1040 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1043 /* nghttp2_send_callback */
1044 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1045 /* nghttp2_on_frame_recv_callback */
1046 nghttp2_session_callbacks_set_on_frame_recv_callback
1047 (callbacks, on_frame_recv);
1048 /* nghttp2_on_invalid_frame_recv_callback */
1049 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
1050 (callbacks, on_invalid_frame_recv);
1051 /* nghttp2_on_data_chunk_recv_callback */
1052 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1053 (callbacks, on_data_chunk_recv);
1054 /* nghttp2_before_frame_send_callback */
1055 nghttp2_session_callbacks_set_before_frame_send_callback
1056 (callbacks, before_frame_send);
1057 /* nghttp2_on_frame_send_callback */
1058 nghttp2_session_callbacks_set_on_frame_send_callback
1059 (callbacks, on_frame_send);
1060 /* nghttp2_on_frame_not_send_callback */
1061 nghttp2_session_callbacks_set_on_frame_not_send_callback
1062 (callbacks, on_frame_not_send);
1063 /* nghttp2_on_stream_close_callback */
1064 nghttp2_session_callbacks_set_on_stream_close_callback
1065 (callbacks, on_stream_close);
1066 /* nghttp2_on_begin_headers_callback */
1067 nghttp2_session_callbacks_set_on_begin_headers_callback
1068 (callbacks, on_begin_headers);
1069 /* nghttp2_on_header_callback */
1070 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1072 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1074 /* The nghttp2 session is not yet setup, do it */
1075 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1077 nghttp2_session_callbacks_del(callbacks);
1080 failf(conn->data, "Couldn't initialize nghttp2!");
1081 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1088 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1090 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1091 struct connectdata *conn)
1097 struct SingleRequest *k = &conn->data->req;
1098 uint8_t *binsettings = conn->proto.httpc.binsettings;
1100 /* As long as we have a fixed set of settings, we don't have to dynamically
1101 * figure out the base64 strings since it'll always be the same. However,
1102 * the settings will likely not be fixed every time in the future.
1105 /* this returns number of bytes it wrote */
1106 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1108 sizeof(settings)/sizeof(settings[0]));
1110 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1111 return CURLE_FAILED_INIT;
1113 conn->proto.httpc.binlen = binlen;
1115 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1120 result = Curl_add_bufferf(req,
1121 "Connection: Upgrade, HTTP2-Settings\r\n"
1123 "HTTP2-Settings: %s\r\n",
1124 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1127 k->upgr101 = UPGR101_REQUESTED;
1133 * Returns nonzero if current HTTP/2 session should be closed.
1135 static int should_close_session(struct http_conn *httpc)
1137 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1138 !nghttp2_session_want_write(httpc->h2);
1141 static int h2_session_send(struct Curl_easy *data,
1142 nghttp2_session *h2);
1145 * h2_process_pending_input() processes pending input left in
1146 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1147 * This function returns 0 if it succeeds, or -1 and error code will
1148 * be assigned to *err.
1150 static int h2_process_pending_input(struct Curl_easy *data,
1151 struct http_conn *httpc,
1158 nread = httpc->inbuflen - httpc->nread_inbuf;
1159 inbuf = httpc->inbuf + httpc->nread_inbuf;
1161 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1164 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1165 "%d:%s\n", rv, nghttp2_strerror((int)rv));
1166 *err = CURLE_RECV_ERROR;
1172 "h2_process_pending_input: All data in connection buffer "
1174 httpc->inbuflen = 0;
1175 httpc->nread_inbuf = 0;
1178 httpc->nread_inbuf += rv;
1180 "h2_process_pending_input: %zu bytes left in connection "
1182 httpc->inbuflen - httpc->nread_inbuf));
1185 rv = h2_session_send(data, httpc->h2);
1187 *err = CURLE_SEND_ERROR;
1191 if(should_close_session(httpc)) {
1193 "h2_process_pending_input: nothing to do in this session\n"));
1202 * Called from transfer.c:done_sending when we stop uploading.
1204 CURLcode Curl_http2_done_sending(struct connectdata *conn)
1206 CURLcode result = CURLE_OK;
1208 if((conn->handler == &Curl_handler_http2_ssl) ||
1209 (conn->handler == &Curl_handler_http2)) {
1210 /* make sure this is only attempted for HTTP/2 transfers */
1212 struct HTTP *stream = conn->data->req.protop;
1214 if(stream->upload_left) {
1215 /* If the stream still thinks there's data left to upload. */
1216 struct http_conn *httpc = &conn->proto.httpc;
1217 nghttp2_session *h2 = httpc->h2;
1219 stream->upload_left = 0; /* DONE! */
1221 /* resume sending here to trigger the callback to get called again so
1222 that it can signal EOF to nghttp2 */
1223 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1225 (void)h2_process_pending_input(conn->data, httpc, &result);
1232 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1233 struct Curl_easy *data,
1234 struct HTTP *stream, CURLcode *err)
1236 char *trailer_pos, *trailer_end;
1238 struct http_conn *httpc = &conn->proto.httpc;
1240 if(httpc->pause_stream_id == stream->stream_id) {
1241 httpc->pause_stream_id = 0;
1244 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1245 httpc->drain_total -= data->state.drain;
1246 data->state.drain = 0;
1248 if(httpc->pause_stream_id == 0) {
1249 if(h2_process_pending_input(data, httpc, err) != 0) {
1254 DEBUGASSERT(data->state.drain == 0);
1256 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1257 stream->closed = FALSE;
1258 if(stream->error_code != NGHTTP2_NO_ERROR) {
1259 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
1260 stream->stream_id, Curl_http2_strerror(stream->error_code),
1261 stream->error_code);
1262 *err = CURLE_HTTP2_STREAM;
1266 if(!stream->bodystarted) {
1267 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1268 " all response header fields, teated as error",
1270 *err = CURLE_HTTP2_STREAM;
1274 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1275 trailer_pos = stream->trailer_recvbuf->buffer;
1276 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1278 for(; trailer_pos < trailer_end;) {
1280 memcpy(&n, trailer_pos, sizeof(n));
1281 trailer_pos += sizeof(n);
1283 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1289 trailer_pos += n + 1;
1293 stream->close_handled = TRUE;
1295 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1300 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1301 * and dependency to the peer. It also stores the updated values in the state
1305 static void h2_pri_spec(struct Curl_easy *data,
1306 nghttp2_priority_spec *pri_spec)
1308 struct HTTP *depstream = (data->set.stream_depends_on?
1309 data->set.stream_depends_on->req.protop:NULL);
1310 int32_t depstream_id = depstream? depstream->stream_id:0;
1311 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1312 data->set.stream_depends_e);
1313 data->state.stream_weight = data->set.stream_weight;
1314 data->state.stream_depends_e = data->set.stream_depends_e;
1315 data->state.stream_depends_on = data->set.stream_depends_on;
1319 * h2_session_send() checks if there's been an update in the priority /
1320 * dependency settings and if so it submits a PRIORITY frame with the updated
1323 static int h2_session_send(struct Curl_easy *data,
1324 nghttp2_session *h2)
1326 struct HTTP *stream = data->req.protop;
1327 if((data->set.stream_weight != data->state.stream_weight) ||
1328 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1329 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1330 /* send new weight and/or dependency */
1331 nghttp2_priority_spec pri_spec;
1334 h2_pri_spec(data, &pri_spec);
1336 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1337 stream->stream_id, data));
1338 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1344 return nghttp2_session_send(h2);
1347 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1348 char *mem, size_t len, CURLcode *err)
1350 CURLcode result = CURLE_OK;
1353 struct http_conn *httpc = &conn->proto.httpc;
1354 struct Curl_easy *data = conn->data;
1355 struct HTTP *stream = data->req.protop;
1357 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1359 if(should_close_session(httpc)) {
1361 "http2_recv: nothing to do in this session\n"));
1366 /* Nullify here because we call nghttp2_session_send() and they
1367 might refer to the old buffer. */
1368 stream->upload_mem = NULL;
1369 stream->upload_len = 0;
1372 * At this point 'stream' is just in the Curl_easy the connection
1373 * identifies as its owner at this time.
1376 if(stream->bodystarted &&
1377 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1378 /* If there is body data pending for this stream to return, do that */
1380 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1381 size_t ncopy = MIN(len, left);
1382 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1384 stream->nread_header_recvbuf += ncopy;
1386 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1391 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1392 data, stream->stream_id));
1394 if((data->state.drain) && stream->memlen) {
1395 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1396 stream->memlen, stream->stream_id,
1398 if(mem != stream->mem) {
1399 /* if we didn't get the same buffer this time, we must move the data to
1401 memmove(mem, stream->mem, stream->memlen);
1402 stream->len = len - stream->memlen;
1405 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1406 /* We have paused nghttp2, but we have no pause data (see
1407 on_data_chunk_recv). */
1408 httpc->pause_stream_id = 0;
1409 if(h2_process_pending_input(data, httpc, &result) != 0) {
1415 else if(stream->pausedata) {
1416 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1417 nread = MIN(len, stream->pauselen);
1418 memcpy(mem, stream->pausedata, nread);
1420 stream->pausedata += nread;
1421 stream->pauselen -= nread;
1423 infof(data, "%zu data bytes written\n", nread);
1424 if(stream->pauselen == 0) {
1425 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1426 assert(httpc->pause_stream_id == stream->stream_id);
1427 httpc->pause_stream_id = 0;
1429 stream->pausedata = NULL;
1430 stream->pauselen = 0;
1432 /* When NGHTTP2_ERR_PAUSE is returned from
1433 data_source_read_callback, we might not process DATA frame
1434 fully. Calling nghttp2_session_mem_recv() again will
1435 continue to process DATA frame, but if there is no incoming
1436 frames, then we have to call it again with 0-length data.
1437 Without this, on_stream_close callback will not be called,
1438 and stream could be hanged. */
1439 if(h2_process_pending_input(data, httpc, &result) != 0) {
1444 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1445 nread, stream->stream_id));
1448 else if(httpc->pause_stream_id) {
1449 /* If a stream paused nghttp2_session_mem_recv previously, and has
1450 not processed all data, it still refers to the buffer in
1451 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1452 overwrite that buffer. To avoid that situation, just return
1453 here with CURLE_AGAIN. This could be busy loop since data in
1454 socket is not read. But it seems that usually streams are
1455 notified with its drain property, and socket is read again
1457 DEBUGF(infof(data, "stream %x is paused, pause id: %x\n",
1458 stream->stream_id, httpc->pause_stream_id));
1464 /* remember where to store incoming data for this stream and how big the
1470 if(httpc->inbuflen == 0) {
1471 nread = ((Curl_recv *)httpc->recv_underlying)(
1472 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1475 if(result != CURLE_AGAIN)
1476 failf(data, "Failed receiving HTTP2 data");
1477 else if(stream->closed)
1478 /* received when the stream was already closed! */
1479 return http2_handle_stream_close(conn, data, stream, err);
1486 failf(data, "Unexpected EOF");
1487 *err = CURLE_RECV_ERROR;
1491 DEBUGF(infof(data, "nread=%zd\n", nread));
1493 httpc->inbuflen = nread;
1494 inbuf = httpc->inbuf;
1497 nread = httpc->inbuflen - httpc->nread_inbuf;
1498 inbuf = httpc->inbuf + httpc->nread_inbuf;
1500 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1503 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1505 if(nghttp2_is_fatal((int)rv)) {
1506 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1507 rv, nghttp2_strerror((int)rv));
1508 *err = CURLE_RECV_ERROR;
1511 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1513 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1514 httpc->inbuflen = 0;
1515 httpc->nread_inbuf = 0;
1518 httpc->nread_inbuf += rv;
1519 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1520 httpc->inbuflen - httpc->nread_inbuf));
1522 /* Always send pending frames in nghttp2 session, because
1523 nghttp2_session_mem_recv() may queue new frame */
1524 rv = h2_session_send(data, httpc->h2);
1526 *err = CURLE_SEND_ERROR;
1530 if(should_close_session(httpc)) {
1531 DEBUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1536 if(stream->memlen) {
1537 ssize_t retlen = stream->memlen;
1538 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1539 retlen, stream->stream_id));
1542 if(httpc->pause_stream_id == stream->stream_id) {
1543 /* data for this stream is returned now, but this stream caused a pause
1544 already so we need it called again asap */
1545 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1546 stream->stream_id));
1548 else if(!stream->closed) {
1549 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1550 httpc->drain_total -= data->state.drain;
1551 data->state.drain = 0; /* this stream is hereby drained */
1556 /* If stream is closed, return 0 to signal the http routine to close
1558 if(stream->closed) {
1559 return http2_handle_stream_close(conn, data, stream, err);
1562 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1563 stream->stream_id));
1567 /* Index where :authority header field will appear in request header
1569 #define AUTHORITY_DST_IDX 3
1571 #define HEADER_OVERFLOW(x) \
1572 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1574 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1575 const void *mem, size_t len, CURLcode *err)
1578 * BIG TODO: Currently, we send request in this function, but this
1579 * function is also used to send request body. It would be nice to
1580 * add dedicated function for request.
1583 struct http_conn *httpc = &conn->proto.httpc;
1584 struct HTTP *stream = conn->data->req.protop;
1585 nghttp2_nv *nva = NULL;
1588 size_t authority_idx;
1589 char *hdbuf = (char*)mem;
1590 char *end, *line_end;
1591 nghttp2_data_provider data_prd;
1593 nghttp2_session *h2 = httpc->h2;
1594 nghttp2_priority_spec pri_spec;
1598 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1600 if(stream->stream_id != -1) {
1601 if(stream->close_handled) {
1602 infof(conn->data, "stream %d closed\n", stream->stream_id);
1603 *err = CURLE_HTTP2_STREAM;
1606 else if(stream->closed) {
1607 return http2_handle_stream_close(conn, conn->data, stream, err);
1609 /* If stream_id != -1, we have dispatched request HEADERS, and now
1610 are going to send or sending request body in DATA frame */
1611 stream->upload_mem = mem;
1612 stream->upload_len = len;
1613 nghttp2_session_resume_data(h2, stream->stream_id);
1614 rv = h2_session_send(conn->data, h2);
1615 if(nghttp2_is_fatal(rv)) {
1616 *err = CURLE_SEND_ERROR;
1619 len -= stream->upload_len;
1621 /* Nullify here because we call nghttp2_session_send() and they
1622 might refer to the old buffer. */
1623 stream->upload_mem = NULL;
1624 stream->upload_len = 0;
1626 if(should_close_session(httpc)) {
1627 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1632 if(stream->upload_left) {
1633 /* we are sure that we have more data to send here. Calling the
1634 following API will make nghttp2_session_want_write() return
1635 nonzero if remote window allows it, which then libcurl checks
1636 socket is writable or not. See http2_perform_getsock(). */
1637 nghttp2_session_resume_data(h2, stream->stream_id);
1640 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1641 stream->stream_id));
1645 /* Calculate number of headers contained in [mem, mem + len) */
1646 /* Here, we assume the curl http code generate *correct* HTTP header
1649 for(i = 1; i < len; ++i) {
1650 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1658 /* We counted additional 2 \r\n in the first and last line. We need 3
1659 new headers: :method, :path and :scheme. Therefore we need one
1662 nva = malloc(sizeof(nghttp2_nv) * nheader);
1664 *err = CURLE_OUT_OF_MEMORY;
1668 /* Extract :method, :path from request line */
1669 line_end = strstr(hdbuf, "\r\n");
1671 /* Method does not contain spaces */
1672 end = memchr(hdbuf, ' ', line_end - hdbuf);
1673 if(!end || end == hdbuf)
1675 nva[0].name = (unsigned char *)":method";
1676 nva[0].namelen = strlen((char *)nva[0].name);
1677 nva[0].value = (unsigned char *)hdbuf;
1678 nva[0].valuelen = (size_t)(end - hdbuf);
1679 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1680 if(HEADER_OVERFLOW(nva[0])) {
1681 failf(conn->data, "Failed sending HTTP request: Header overflow");
1687 /* Path may contain spaces so scan backwards */
1689 for(i = (size_t)(line_end - hdbuf); i; --i) {
1690 if(hdbuf[i - 1] == ' ') {
1691 end = &hdbuf[i - 1];
1695 if(!end || end == hdbuf)
1697 nva[1].name = (unsigned char *)":path";
1698 nva[1].namelen = strlen((char *)nva[1].name);
1699 nva[1].value = (unsigned char *)hdbuf;
1700 nva[1].valuelen = (size_t)(end - hdbuf);
1701 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1702 if(HEADER_OVERFLOW(nva[1])) {
1703 failf(conn->data, "Failed sending HTTP request: Header overflow");
1710 nva[2].name = (unsigned char *)":scheme";
1711 nva[2].namelen = strlen((char *)nva[2].name);
1712 if(conn->handler->flags & PROTOPT_SSL)
1713 nva[2].value = (unsigned char *)"https";
1715 nva[2].value = (unsigned char *)"http";
1716 nva[2].valuelen = strlen((char *)nva[2].value);
1717 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1718 if(HEADER_OVERFLOW(nva[2])) {
1719 failf(conn->data, "Failed sending HTTP request: Header overflow");
1725 while(i < nheader) {
1729 hdbuf = line_end + 2;
1731 line_end = strstr(hdbuf, "\r\n");
1732 if(line_end == hdbuf)
1735 /* header continuation lines are not supported */
1736 if(*hdbuf == ' ' || *hdbuf == '\t')
1739 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1741 if(end == hdbuf || end == line_end)
1745 if(hlen == 10 && Curl_raw_nequal("connection", hdbuf, 10)) {
1746 /* skip Connection: headers! */
1750 else if(hlen == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1752 nva[i].name = (unsigned char *)":authority";
1753 nva[i].namelen = strlen((char *)nva[i].name);
1756 nva[i].name = (unsigned char *)hdbuf;
1757 nva[i].namelen = (size_t)(end - hdbuf);
1760 while(*hdbuf == ' ' || *hdbuf == '\t')
1764 nva[i].value = (unsigned char *)hdbuf;
1765 nva[i].valuelen = (size_t)(end - hdbuf);
1766 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1767 if(HEADER_OVERFLOW(nva[i])) {
1768 failf(conn->data, "Failed sending HTTP request: Header overflow");
1775 /* :authority must come before non-pseudo header fields */
1776 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1777 nghttp2_nv authority = nva[authority_idx];
1778 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1779 nva[i] = nva[i - 1];
1784 /* Warn stream may be rejected if cumulative length of headers is too large.
1785 It appears nghttp2 will not send a header frame larger than 64KB. */
1788 const size_t max_acc = 60000; /* <64KB to account for some overhead */
1790 for(i = 0; i < nheader; ++i) {
1791 if(nva[i].namelen > max_acc - acc)
1793 acc += nva[i].namelen;
1795 if(nva[i].valuelen > max_acc - acc)
1797 acc += nva[i].valuelen;
1801 infof(conn->data, "http2_send: Warning: The cumulative length of all "
1802 "headers exceeds %zu bytes and that could cause the "
1803 "stream to be rejected.\n", max_acc);
1807 h2_pri_spec(conn->data, &pri_spec);
1809 switch(conn->data->set.httpreq) {
1811 case HTTPREQ_POST_FORM:
1813 if(conn->data->state.infilesize != -1)
1814 stream->upload_left = conn->data->state.infilesize;
1816 /* data sending without specifying the data amount up front */
1817 stream->upload_left = -1; /* unknown, but not zero */
1819 data_prd.read_callback = data_source_read_callback;
1820 data_prd.source.ptr = NULL;
1821 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1822 &data_prd, conn->data);
1825 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1832 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1833 *err = CURLE_SEND_ERROR;
1837 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1838 stream_id, conn->data);
1839 stream->stream_id = stream_id;
1841 /* this does not call h2_session_send() since there can not have been any
1842 * priority upodate since the nghttp2_submit_request() call above */
1843 rv = nghttp2_session_send(h2);
1846 *err = CURLE_SEND_ERROR;
1850 if(should_close_session(httpc)) {
1851 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1856 if(stream->stream_id != -1) {
1857 /* If whole HEADERS frame was sent off to the underlying socket,
1858 the nghttp2 library calls data_source_read_callback. But only
1859 it found that no data available, so it deferred the DATA
1860 transmission. Which means that nghttp2_session_want_write()
1861 returns 0 on http2_perform_getsock(), which results that no
1862 writable socket check is performed. To workaround this, we
1863 issue nghttp2_session_resume_data() here to bring back DATA
1864 transmission from deferred state. */
1865 nghttp2_session_resume_data(h2, stream->stream_id);
1872 *err = CURLE_SEND_ERROR;
1876 CURLcode Curl_http2_setup(struct connectdata *conn)
1879 struct http_conn *httpc = &conn->proto.httpc;
1880 struct HTTP *stream = conn->data->req.protop;
1882 stream->stream_id = -1;
1884 if(!stream->header_recvbuf)
1885 stream->header_recvbuf = Curl_add_buffer_init();
1887 if((conn->handler == &Curl_handler_http2_ssl) ||
1888 (conn->handler == &Curl_handler_http2))
1889 return CURLE_OK; /* already done */
1891 if(conn->handler->flags & PROTOPT_SSL)
1892 conn->handler = &Curl_handler_http2_ssl;
1894 conn->handler = &Curl_handler_http2;
1896 result = Curl_http2_init(conn);
1900 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1901 stream->upload_left = 0;
1902 stream->upload_mem = NULL;
1903 stream->upload_len = 0;
1905 httpc->inbuflen = 0;
1906 httpc->nread_inbuf = 0;
1908 httpc->pause_stream_id = 0;
1909 httpc->drain_total = 0;
1911 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1912 conn->httpversion = 20;
1913 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1915 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1916 Curl_multi_connchanged(conn->data->multi);
1921 CURLcode Curl_http2_switched(struct connectdata *conn,
1922 const char *mem, size_t nread)
1925 struct http_conn *httpc = &conn->proto.httpc;
1928 struct Curl_easy *data = conn->data;
1929 struct HTTP *stream = conn->data->req.protop;
1931 result = Curl_http2_setup(conn);
1935 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1936 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1937 conn->recv[FIRSTSOCKET] = http2_recv;
1938 conn->send[FIRSTSOCKET] = http2_send;
1940 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1941 /* stream 1 is opened implicitly on upgrade */
1942 stream->stream_id = 1;
1943 /* queue SETTINGS frame (again) */
1944 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1945 httpc->binlen, NULL);
1947 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1948 nghttp2_strerror(rv), rv);
1952 nghttp2_session_set_stream_user_data(httpc->h2,
1957 /* stream ID is unknown at this point */
1958 stream->stream_id = -1;
1959 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1961 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1962 nghttp2_strerror(rv), rv);
1967 /* we are going to copy mem to httpc->inbuf. This is required since
1968 mem is part of buffer pointed by stream->mem, and callbacks
1969 called by nghttp2_session_mem_recv() will write stream specific
1970 data into stream->mem, overwriting data already there. */
1971 if(H2_BUFSIZE < nread) {
1972 failf(data, "connection buffer size is too small to store data following "
1973 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1978 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1979 " after upgrade: len=%zu\n",
1982 memcpy(httpc->inbuf, mem, nread);
1983 httpc->inbuflen = nread;
1985 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1988 if(nghttp2_is_fatal((int)nproc)) {
1989 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1990 nghttp2_strerror((int)nproc), (int)nproc);
1994 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1996 if((ssize_t)nread == nproc) {
1997 httpc->inbuflen = 0;
1998 httpc->nread_inbuf = 0;
2001 httpc->nread_inbuf += nproc;
2004 /* Try to send some frames since we may read SETTINGS already. */
2005 rv = h2_session_send(data, httpc->h2);
2008 failf(data, "nghttp2_session_send() failed: %s(%d)",
2009 nghttp2_strerror(rv), rv);
2013 if(should_close_session(httpc)) {
2015 "nghttp2_session_send(): nothing to do in this session\n"));
2022 #else /* !USE_NGHTTP2 */
2024 /* Satisfy external references even if http2 is not compiled in. */
2026 #define CURL_DISABLE_TYPECHECK
2027 #include <curl/curl.h>
2029 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2036 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2043 #endif /* USE_NGHTTP2 */