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 "curl_printf.h"
27 #include <nghttp2/nghttp2.h>
32 #include "curl_base64.h"
35 #include "conncache.h"
39 /* The last #include files should be: */
40 #include "curl_memory.h"
43 #define MIN(x,y) ((x)<(y)?(x):(y))
45 #if (NGHTTP2_VERSION_NUM < 0x010000)
46 #error too old nghttp2 version, upgrade!
50 * Curl_http2_init_state() is called when the easy handle is created and
51 * allows for HTTP/2 specific init of state.
53 void Curl_http2_init_state(struct UrlState *state)
55 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
59 * Curl_http2_init_userset() is called when the easy handle is created and
60 * allows for HTTP/2 specific user-set fields.
62 void Curl_http2_init_userset(struct UserDefined *set)
64 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
67 static int http2_perform_getsock(const struct connectdata *conn,
68 curl_socket_t *sock, /* points to
74 const struct http_conn *c = &conn->proto.httpc;
75 int bitmap = GETSOCK_BLANK;
78 /* TODO We should check underlying socket state if it is SSL socket
79 because of renegotiation. */
80 sock[0] = conn->sock[FIRSTSOCKET];
82 if(nghttp2_session_want_read(c->h2))
83 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
85 if(nghttp2_session_want_write(c->h2))
86 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
91 static int http2_getsock(struct connectdata *conn,
92 curl_socket_t *sock, /* points to numsocks
96 return http2_perform_getsock(conn, sock, numsocks);
99 static CURLcode http2_disconnect(struct connectdata *conn,
100 bool dead_connection)
102 struct HTTP *http = conn->data->req.protop;
103 struct http_conn *c = &conn->proto.httpc;
104 (void)dead_connection;
106 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
108 nghttp2_session_del(c->h2);
109 Curl_safefree(c->inbuf);
112 Curl_add_buffer_free(http->header_recvbuf);
113 http->header_recvbuf = NULL; /* clear the pointer */
114 Curl_add_buffer_free(http->trailer_recvbuf);
115 http->trailer_recvbuf = NULL; /* clear the pointer */
116 for(; http->push_headers_used > 0; --http->push_headers_used) {
117 free(http->push_headers[http->push_headers_used - 1]);
119 free(http->push_headers);
120 http->push_headers = NULL;
123 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
128 /* called from Curl_http_setup_conn */
129 void Curl_http2_setup_req(struct SessionHandle *data)
131 struct HTTP *http = data->req.protop;
133 http->nread_header_recvbuf = 0;
134 http->bodystarted = FALSE;
135 http->status_code = -1;
136 http->pausedata = NULL;
138 http->error_code = NGHTTP2_NO_ERROR;
139 http->closed = FALSE;
140 http->mem = data->state.buffer;
145 /* called from Curl_http_setup_conn */
146 void Curl_http2_setup_conn(struct connectdata *conn)
148 conn->proto.httpc.settings.max_concurrent_streams =
149 DEFAULT_MAX_CONCURRENT_STREAMS;
153 * HTTP2 handler interface. This isn't added to the general list of protocols
154 * but will be used at run-time when the protocol is dynamically switched from
157 const struct Curl_handler Curl_handler_http2 = {
158 "HTTP2", /* scheme */
159 ZERO_NULL, /* setup_connection */
160 Curl_http, /* do_it */
161 Curl_http_done, /* done */
162 ZERO_NULL, /* do_more */
163 ZERO_NULL, /* connect_it */
164 ZERO_NULL, /* connecting */
165 ZERO_NULL, /* doing */
166 http2_getsock, /* proto_getsock */
167 http2_getsock, /* doing_getsock */
168 ZERO_NULL, /* domore_getsock */
169 http2_perform_getsock, /* perform_getsock */
170 http2_disconnect, /* disconnect */
171 ZERO_NULL, /* readwrite */
172 PORT_HTTP, /* defport */
173 CURLPROTO_HTTP, /* protocol */
174 PROTOPT_NONE /* flags */
177 const struct Curl_handler Curl_handler_http2_ssl = {
178 "HTTP2", /* scheme */
179 ZERO_NULL, /* setup_connection */
180 Curl_http, /* do_it */
181 Curl_http_done, /* done */
182 ZERO_NULL, /* do_more */
183 ZERO_NULL, /* connect_it */
184 ZERO_NULL, /* connecting */
185 ZERO_NULL, /* doing */
186 http2_getsock, /* proto_getsock */
187 http2_getsock, /* doing_getsock */
188 ZERO_NULL, /* domore_getsock */
189 http2_perform_getsock, /* perform_getsock */
190 http2_disconnect, /* disconnect */
191 ZERO_NULL, /* readwrite */
192 PORT_HTTP, /* defport */
193 CURLPROTO_HTTPS, /* protocol */
194 PROTOPT_SSL /* flags */
198 * Store nghttp2 version info in this buffer, Prefix with a space. Return
199 * total length written.
201 int Curl_http2_ver(char *p, size_t len)
203 nghttp2_info *h2 = nghttp2_version(0);
204 return snprintf(p, len, " nghttp2/%s", h2->version_str);
208 * The implementation of nghttp2_send_callback type. Here we write |data| with
209 * size |length| to the network and return the number of bytes actually
210 * written. See the documentation of nghttp2_send_callback for the details.
212 static ssize_t send_callback(nghttp2_session *h2,
213 const uint8_t *data, size_t length, int flags,
216 struct connectdata *conn = (struct connectdata *)userp;
217 struct http_conn *c = &conn->proto.httpc;
219 CURLcode result = CURLE_OK;
224 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
225 data, length, &result);
227 if(result == CURLE_AGAIN) {
228 return NGHTTP2_ERR_WOULDBLOCK;
232 failf(conn->data, "Failed sending HTTP2 data");
233 return NGHTTP2_ERR_CALLBACK_FAILURE;
237 return NGHTTP2_ERR_WOULDBLOCK;
243 /* We pass a pointer to this struct in the push callback, but the contents of
244 the struct are hidden from the user. */
245 struct curl_pushheaders {
246 struct SessionHandle *data;
247 const nghttp2_push_promise *frame;
251 * push header access function. Only to be used from within the push callback
253 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
255 /* Verify that we got a good easy handle in the push header struct, mostly to
256 detect rubbish input fast(er). */
257 if(!h || !GOOD_EASY_HANDLE(h->data))
260 struct HTTP *stream = h->data->req.protop;
261 if(num < stream->push_headers_used)
262 return stream->push_headers[num];
268 * push header access function. Only to be used from within the push callback
270 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
272 /* Verify that we got a good easy handle in the push header struct,
273 mostly to detect rubbish input fast(er). Also empty header name
274 is just a rubbish too. We have to allow ":" at the beginning of
275 the header, but header == ":" must be rejected. If we have ':' in
276 the middle of header, it could be matched in middle of the value,
277 this is because we do prefix match.*/
278 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
279 Curl_raw_equal(header, ":") || strchr(header + 1, ':'))
282 struct HTTP *stream = h->data->req.protop;
283 size_t len = strlen(header);
285 for(i=0; i<stream->push_headers_used; i++) {
286 if(!strncmp(header, stream->push_headers[i], len)) {
287 /* sub-match, make sure that it is followed by a colon */
288 if(stream->push_headers[i][len] != ':')
290 return &stream->push_headers[i][len+1];
297 static CURL *duphandle(struct SessionHandle *data)
299 struct SessionHandle *second = curl_easy_duphandle(data);
301 /* setup the request struct */
302 struct HTTP *http = calloc(1, sizeof(struct HTTP));
304 (void)Curl_close(second);
308 second->req.protop = http;
309 http->header_recvbuf = Curl_add_buffer_init();
310 if(!http->header_recvbuf) {
312 (void)Curl_close(second);
316 Curl_http2_setup_req(second);
317 second->state.stream_weight = data->state.stream_weight;
325 static int push_promise(struct SessionHandle *data,
326 struct connectdata *conn,
327 const nghttp2_push_promise *frame)
330 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
331 frame->promised_stream_id));
332 if(data->multi->push_cb) {
334 struct HTTP *newstream;
335 struct curl_pushheaders heads;
337 struct http_conn *httpc;
339 /* clone the parent */
340 struct SessionHandle *newhandle = duphandle(data);
342 infof(data, "failed to duplicate handle\n");
343 rv = 1; /* FAIL HARD */
349 /* ask the application */
350 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
352 stream = data->req.protop;
354 failf(data, "Internal NULL stream!\n");
359 rv = data->multi->push_cb(data, newhandle,
360 stream->push_headers_used, &heads,
361 data->multi->push_userp);
363 /* free the headers again */
364 for(i=0; i<stream->push_headers_used; i++)
365 free(stream->push_headers[i]);
366 free(stream->push_headers);
367 stream->push_headers = NULL;
370 /* denied, kill off the new handle again */
371 (void)Curl_close(newhandle);
375 newstream = newhandle->req.protop;
376 newstream->stream_id = frame->promised_stream_id;
377 newhandle->req.maxdownload = -1;
378 newhandle->req.size = -1;
380 /* approved, add to the multi handle and immediately switch to PERFORM
381 state with the given connection !*/
382 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
384 infof(data, "failed to add handle to multi\n");
385 Curl_close(newhandle);
390 httpc = &conn->proto.httpc;
391 nghttp2_session_set_stream_user_data(httpc->h2,
392 frame->promised_stream_id, newhandle);
395 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
402 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
405 struct connectdata *conn = (struct connectdata *)userp;
406 struct http_conn *httpc = &conn->proto.httpc;
407 struct SessionHandle *data_s = NULL;
408 struct HTTP *stream = NULL;
409 static int lastStream = -1;
412 int32_t stream_id = frame->hd.stream_id;
415 /* stream ID zero is for connection-oriented stuff */
416 if(frame->hd.type == NGHTTP2_SETTINGS) {
417 uint32_t max_conn = httpc->settings.max_concurrent_streams;
418 DEBUGF(infof(conn->data, "Got SETTINGS\n"));
419 httpc->settings.max_concurrent_streams =
420 nghttp2_session_get_remote_settings(
421 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
422 httpc->settings.enable_push =
423 nghttp2_session_get_remote_settings(
424 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
425 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
426 httpc->settings.max_concurrent_streams));
427 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
428 httpc->settings.enable_push?"TRUE":"false"));
429 if(max_conn != httpc->settings.max_concurrent_streams) {
430 /* only signal change if the value actually changed */
432 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
433 Curl_multi_connchanged(conn->data->multi);
438 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
439 if(lastStream != stream_id) {
440 lastStream = stream_id;
443 DEBUGF(infof(conn->data,
444 "No SessionHandle associated with stream: %x\n",
449 stream = data_s->req.protop;
451 return NGHTTP2_ERR_CALLBACK_FAILURE;
453 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
454 frame->hd.type, stream_id));
456 switch(frame->hd.type) {
458 /* If body started on this stream, then receiving DATA is illegal. */
459 if(!stream->bodystarted) {
460 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
461 stream_id, NGHTTP2_PROTOCOL_ERROR);
463 if(nghttp2_is_fatal(rv)) {
464 return NGHTTP2_ERR_CALLBACK_FAILURE;
468 case NGHTTP2_HEADERS:
469 if(stream->bodystarted) {
470 /* Only valid HEADERS after body started is trailer HEADERS. We
471 buffer them in on_header callback. */
475 /* nghttp2 guarantees that :status is received, and we store it to
476 stream->status_code */
477 DEBUGASSERT(stream->status_code != -1);
479 /* Only final status code signals the end of header */
480 if(stream->status_code / 100 != 1) {
481 stream->bodystarted = TRUE;
482 stream->status_code = -1;
485 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
487 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
488 ncopy = MIN(stream->len, left);
490 memcpy(&stream->mem[stream->memlen],
491 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
493 stream->nread_header_recvbuf += ncopy;
495 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
496 ncopy, stream_id, stream->mem));
498 stream->len -= ncopy;
499 stream->memlen += ncopy;
501 data_s->state.drain++;
503 /* get the pointer from userp again since it was re-assigned above */
504 struct connectdata *conn_s = (struct connectdata *)userp;
506 /* if we receive data for another handle, wake that up */
507 if(conn_s->data != data_s)
508 Curl_expire(data_s, 1);
511 case NGHTTP2_PUSH_PROMISE:
512 rv = push_promise(data_s, conn, &frame->push_promise);
514 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
515 frame->push_promise.promised_stream_id,
517 if(nghttp2_is_fatal(rv)) {
523 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
524 frame->hd.type, stream_id));
530 static int on_invalid_frame_recv(nghttp2_session *session,
531 const nghttp2_frame *frame,
532 int lib_error_code, void *userp)
534 struct SessionHandle *data_s = NULL;
537 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
540 "on_invalid_frame_recv() was called, error=%d:%s\n",
541 lib_error_code, nghttp2_strerror(lib_error_code)));
546 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
548 const uint8_t *data, size_t len, void *userp)
551 struct SessionHandle *data_s;
553 struct connectdata *conn = (struct connectdata *)userp;
558 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
560 /* get the stream from the hash based on Stream ID */
561 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
563 /* Receiving a Stream ID not in the hash should not happen, this is an
564 internal error more than anything else! */
565 return NGHTTP2_ERR_CALLBACK_FAILURE;
567 stream = data_s->req.protop;
569 return NGHTTP2_ERR_CALLBACK_FAILURE;
571 nread = MIN(stream->len, len);
572 memcpy(&stream->mem[stream->memlen], data, nread);
574 stream->len -= nread;
575 stream->memlen += nread;
577 data_s->state.drain++;
579 /* if we receive data for another handle, wake that up */
580 if(conn->data != data_s)
581 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
584 DEBUGF(infof(data_s, "%zu data received for stream %u "
585 "(%zu left in buffer %p, total %zu)\n",
587 stream->len, stream->mem,
591 stream->pausedata = data + nread;
592 stream->pauselen = len - nread;
593 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
595 len - nread, stream_id));
596 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
597 return NGHTTP2_ERR_PAUSE;
602 static int before_frame_send(nghttp2_session *session,
603 const nghttp2_frame *frame,
606 struct SessionHandle *data_s;
609 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
611 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
616 static int on_frame_send(nghttp2_session *session,
617 const nghttp2_frame *frame,
620 struct SessionHandle *data_s;
623 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
625 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
630 static int on_frame_not_send(nghttp2_session *session,
631 const nghttp2_frame *frame,
632 int lib_error_code, void *userp)
634 struct SessionHandle *data_s;
637 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
640 "on_frame_not_send() was called, lib_error_code = %d\n",
645 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
646 uint32_t error_code, void *userp)
648 struct SessionHandle *data_s;
655 /* get the stream from the hash based on Stream ID, stream ID zero is for
656 connection-oriented stuff */
657 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
659 /* We could get stream ID not in the hash. For example, if we
660 decided to reject stream (e.g., PUSH_PROMISE). */
663 DEBUGF(infof(data_s, "on_stream_close(), error_code = %d, stream %u\n",
664 error_code, stream_id));
665 stream = data_s->req.protop;
667 return NGHTTP2_ERR_CALLBACK_FAILURE;
669 stream->error_code = error_code;
670 stream->closed = TRUE;
672 /* remove the entry from the hash as the stream is now gone */
673 nghttp2_session_set_stream_user_data(session, stream_id, 0);
674 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
679 static int on_begin_headers(nghttp2_session *session,
680 const nghttp2_frame *frame, void *userp)
683 struct SessionHandle *data_s = NULL;
686 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
691 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
693 if(frame->hd.type != NGHTTP2_HEADERS) {
697 stream = data_s->req.protop;
698 if(!stream || !stream->bodystarted) {
702 /* This is trailer HEADERS started. Allocate buffer for them. */
703 DEBUGF(infof(data_s, "trailer field started\n"));
705 assert(stream->trailer_recvbuf == NULL);
707 stream->trailer_recvbuf = Curl_add_buffer_init();
708 if(!stream->trailer_recvbuf) {
709 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
715 /* Decode HTTP status code. Returns -1 if no valid status code was
717 static int decode_status_code(const uint8_t *value, size_t len)
728 for(i = 0; i < 3; ++i) {
731 if(c < '0' || c > '9') {
742 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
743 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
744 const uint8_t *name, size_t namelen,
745 const uint8_t *value, size_t valuelen,
750 struct SessionHandle *data_s;
751 int32_t stream_id = frame->hd.stream_id;
752 struct connectdata *conn = (struct connectdata *)userp;
755 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
757 /* get the stream from the hash based on Stream ID */
758 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
760 /* Receiving a Stream ID not in the hash should not happen, this is an
761 internal error more than anything else! */
762 return NGHTTP2_ERR_CALLBACK_FAILURE;
764 stream = data_s->req.protop;
766 failf(data_s, "Internal NULL stream! 5\n");
767 return NGHTTP2_ERR_CALLBACK_FAILURE;
770 /* Store received PUSH_PROMISE headers to be used when the subsequent
771 PUSH_PROMISE callback comes */
772 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
775 if(!stream->push_headers) {
776 stream->push_headers_alloc = 10;
777 stream->push_headers = malloc(stream->push_headers_alloc *
779 stream->push_headers_used = 0;
781 else if(stream->push_headers_used ==
782 stream->push_headers_alloc) {
784 stream->push_headers_alloc *= 2;
785 headp = realloc(stream->push_headers,
786 stream->push_headers_alloc * sizeof(char *));
788 free(stream->push_headers);
789 stream->push_headers = NULL;
790 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
792 stream->push_headers = headp;
794 h = aprintf("%s:%s", name, value);
796 stream->push_headers[stream->push_headers_used++] = h;
800 if(stream->bodystarted) {
801 /* This is trailer fields. */
802 /* 3 is for ":" and "\r\n". */
803 uint32_t n = (uint32_t)(namelen + valuelen + 3);
805 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
808 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
809 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
810 Curl_add_buffer(stream->trailer_recvbuf, ":", 1);
811 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
812 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
817 if(namelen == sizeof(":status") - 1 &&
818 memcmp(":status", name, namelen) == 0) {
819 /* nghttp2 guarantees :status is received first and only once, and
820 value is 3 digits status code, and decode_status_code always
822 stream->status_code = decode_status_code(value, valuelen);
823 DEBUGASSERT(stream->status_code != -1);
825 Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
826 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
827 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
828 data_s->state.drain++;
829 /* if we receive data for another handle, wake that up */
830 if(conn->data != data_s)
831 Curl_expire(data_s, 1);
833 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
834 stream->status_code, data_s));
838 /* nghttp2 guarantees that namelen > 0, and :status was already
839 received, and this is not pseudo-header field . */
840 /* convert to a HTTP1-style header */
841 Curl_add_buffer(stream->header_recvbuf, name, namelen);
842 Curl_add_buffer(stream->header_recvbuf, ":", 1);
843 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
844 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
845 data_s->state.drain++;
846 /* if we receive data for another handle, wake that up */
847 if(conn->data != data_s)
848 Curl_expire(data_s, 1);
850 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
853 return 0; /* 0 is successful */
856 static ssize_t data_source_read_callback(nghttp2_session *session,
858 uint8_t *buf, size_t length,
859 uint32_t *data_flags,
860 nghttp2_data_source *source,
863 struct SessionHandle *data_s;
864 struct HTTP *stream = NULL;
870 /* get the stream from the hash based on Stream ID, stream ID zero is for
871 connection-oriented stuff */
872 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
874 /* Receiving a Stream ID not in the hash should not happen, this is an
875 internal error more than anything else! */
876 return NGHTTP2_ERR_CALLBACK_FAILURE;
878 stream = data_s->req.protop;
880 return NGHTTP2_ERR_CALLBACK_FAILURE;
883 return NGHTTP2_ERR_INVALID_ARGUMENT;
885 nread = MIN(stream->upload_len, length);
887 memcpy(buf, stream->upload_mem, nread);
888 stream->upload_mem += nread;
889 stream->upload_len -= nread;
890 stream->upload_left -= nread;
893 if(stream->upload_left == 0)
896 return NGHTTP2_ERR_DEFERRED;
898 DEBUGF(infof(data_s, "data_source_read_callback: "
899 "returns %zu bytes stream %u\n",
906 * The HTTP2 settings we send in the Upgrade request
908 static nghttp2_settings_entry settings[] = {
909 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
910 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
913 #define H2_BUFSIZE 32768
916 * Initialize nghttp2 for a Curl connection
918 CURLcode Curl_http2_init(struct connectdata *conn)
920 if(!conn->proto.httpc.h2) {
922 nghttp2_session_callbacks *callbacks;
924 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
925 if(conn->proto.httpc.inbuf == NULL)
926 return CURLE_OUT_OF_MEMORY;
928 rc = nghttp2_session_callbacks_new(&callbacks);
931 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
932 return CURLE_OUT_OF_MEMORY; /* most likely at least */
935 /* nghttp2_send_callback */
936 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
937 /* nghttp2_on_frame_recv_callback */
938 nghttp2_session_callbacks_set_on_frame_recv_callback
939 (callbacks, on_frame_recv);
940 /* nghttp2_on_invalid_frame_recv_callback */
941 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
942 (callbacks, on_invalid_frame_recv);
943 /* nghttp2_on_data_chunk_recv_callback */
944 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
945 (callbacks, on_data_chunk_recv);
946 /* nghttp2_before_frame_send_callback */
947 nghttp2_session_callbacks_set_before_frame_send_callback
948 (callbacks, before_frame_send);
949 /* nghttp2_on_frame_send_callback */
950 nghttp2_session_callbacks_set_on_frame_send_callback
951 (callbacks, on_frame_send);
952 /* nghttp2_on_frame_not_send_callback */
953 nghttp2_session_callbacks_set_on_frame_not_send_callback
954 (callbacks, on_frame_not_send);
955 /* nghttp2_on_stream_close_callback */
956 nghttp2_session_callbacks_set_on_stream_close_callback
957 (callbacks, on_stream_close);
958 /* nghttp2_on_begin_headers_callback */
959 nghttp2_session_callbacks_set_on_begin_headers_callback
960 (callbacks, on_begin_headers);
961 /* nghttp2_on_header_callback */
962 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
964 /* The nghttp2 session is not yet setup, do it */
965 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
967 nghttp2_session_callbacks_del(callbacks);
970 failf(conn->data, "Couldn't initialize nghttp2!");
971 return CURLE_OUT_OF_MEMORY; /* most likely at least */
978 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
980 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
981 struct connectdata *conn)
987 struct SingleRequest *k = &conn->data->req;
988 uint8_t *binsettings = conn->proto.httpc.binsettings;
990 /* As long as we have a fixed set of settings, we don't have to dynamically
991 * figure out the base64 strings since it'll always be the same. However,
992 * the settings will likely not be fixed every time in the future.
995 /* this returns number of bytes it wrote */
996 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
998 sizeof(settings)/sizeof(settings[0]));
1000 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1001 return CURLE_FAILED_INIT;
1003 conn->proto.httpc.binlen = binlen;
1005 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1010 result = Curl_add_bufferf(req,
1011 "Connection: Upgrade, HTTP2-Settings\r\n"
1013 "HTTP2-Settings: %s\r\n",
1014 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1017 k->upgr101 = UPGR101_REQUESTED;
1022 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1023 struct SessionHandle *data,
1024 struct HTTP *stream, CURLcode *err) {
1025 char *trailer_pos, *trailer_end;
1027 struct http_conn *httpc = &conn->proto.httpc;
1029 if(httpc->pause_stream_id == stream->stream_id) {
1030 httpc->pause_stream_id = 0;
1032 /* Reset to FALSE to prevent infinite loop in readwrite_data
1034 stream->closed = FALSE;
1035 if(stream->error_code != NGHTTP2_NO_ERROR) {
1036 failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
1037 stream->stream_id, stream->error_code);
1042 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1043 trailer_pos = stream->trailer_recvbuf->buffer;
1044 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1046 for(; trailer_pos < trailer_end;) {
1048 memcpy(&n, trailer_pos, sizeof(n));
1049 trailer_pos += sizeof(n);
1051 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1057 trailer_pos += n + 1;
1061 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1066 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1067 * and dependency to the peer. It also stores the updated values in the state
1071 static void h2_pri_spec(struct SessionHandle *data,
1072 nghttp2_priority_spec *pri_spec)
1074 struct HTTP *depstream = (data->set.stream_depends_on?
1075 data->set.stream_depends_on->req.protop:NULL);
1076 int32_t depstream_id = depstream? depstream->stream_id:0;
1077 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1078 data->set.stream_depends_e);
1079 data->state.stream_weight = data->set.stream_weight;
1080 data->state.stream_depends_e = data->set.stream_depends_e;
1081 data->state.stream_depends_on = data->set.stream_depends_on;
1085 * h2_session_send() checks if there's been an update in the priority /
1086 * dependency settings and if so it submits a PRIORITY frame with the updated
1089 static int h2_session_send(struct SessionHandle *data,
1090 nghttp2_session *h2)
1092 struct HTTP *stream = data->req.protop;
1093 if((data->set.stream_weight != data->state.stream_weight) ||
1094 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1095 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1096 /* send new weight and/or dependency */
1097 nghttp2_priority_spec pri_spec;
1100 h2_pri_spec(data, &pri_spec);
1102 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1103 stream->stream_id, data));
1104 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1110 return nghttp2_session_send(h2);
1114 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
1115 * a regular CURLcode value.
1117 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1118 char *mem, size_t len, CURLcode *err)
1120 CURLcode result = CURLE_OK;
1123 struct http_conn *httpc = &conn->proto.httpc;
1124 struct SessionHandle *data = conn->data;
1125 struct HTTP *stream = data->req.protop;
1127 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1129 /* If stream is closed, return 0 to signal the http routine to close
1130 the connection. We need to handle stream closure here,
1131 otherwise, we may be going to read from underlying connection,
1132 and gets EAGAIN, and we will get stuck there. */
1133 if(stream->memlen == 0 && stream->closed) {
1134 return http2_handle_stream_close(conn, data, stream, err);
1137 /* Nullify here because we call nghttp2_session_send() and they
1138 might refer to the old buffer. */
1139 stream->upload_mem = NULL;
1140 stream->upload_len = 0;
1143 * At this point 'stream' is just in the SessionHandle the connection
1144 * identifies as its owner at this time.
1147 if(stream->bodystarted &&
1148 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1149 /* If there is body data pending for this stream to return, do that */
1151 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1152 size_t ncopy = MIN(len, left);
1153 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1155 stream->nread_header_recvbuf += ncopy;
1157 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1162 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1163 data, stream->stream_id));
1165 if((data->state.drain) && stream->memlen) {
1166 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1167 stream->memlen, stream->stream_id,
1169 if(mem != stream->mem) {
1170 /* if we didn't get the same buffer this time, we must move the data to
1172 memmove(mem, stream->mem, stream->memlen);
1173 stream->len = len - stream->memlen;
1177 else if(stream->pausedata) {
1178 nread = MIN(len, stream->pauselen);
1179 memcpy(mem, stream->pausedata, nread);
1181 stream->pausedata += nread;
1182 stream->pauselen -= nread;
1184 infof(data, "%zu data bytes written\n", nread);
1185 if(stream->pauselen == 0) {
1186 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1187 assert(httpc->pause_stream_id == stream->stream_id);
1188 httpc->pause_stream_id = 0;
1190 stream->pausedata = NULL;
1191 stream->pauselen = 0;
1193 /* When NGHTTP2_ERR_PAUSE is returned from
1194 data_source_read_callback, we might not process DATA frame
1195 fully. Calling nghttp2_session_mem_recv() again will
1196 continue to process DATA frame, but if there is no incoming
1197 frames, then we have to call it again with 0-length data.
1198 Without this, on_stream_close callback will not be called,
1199 and stream could be hanged. */
1200 nghttp2_session_mem_recv(httpc->h2, NULL, 0);
1202 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1203 nread, stream->stream_id));
1206 else if(httpc->pause_stream_id) {
1207 /* If a stream paused nghttp2_session_mem_recv previously, and has
1208 not processed all data, it still refers to the buffer in
1209 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1210 overwrite that buffer. To avoid that situation, just return
1211 here with CURLE_AGAIN. This could be busy loop since data in
1212 socket is not read. But it seems that usually streams are
1213 notified with its drain property, and socket is read again
1220 /* remember where to store incoming data for this stream and how big the
1226 if(httpc->inbuflen == 0) {
1227 nread = ((Curl_recv *)httpc->recv_underlying)(
1228 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1231 if(result != CURLE_AGAIN)
1232 failf(data, "Failed receiving HTTP2 data");
1238 failf(data, "Unexpected EOF");
1239 *err = CURLE_RECV_ERROR;
1243 DEBUGF(infof(data, "nread=%zd\n", nread));
1245 httpc->inbuflen = nread;
1246 inbuf = httpc->inbuf;
1249 nread = httpc->inbuflen - httpc->nread_inbuf;
1250 inbuf = httpc->inbuf + httpc->nread_inbuf;
1252 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1255 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1257 if(nghttp2_is_fatal((int)rv)) {
1258 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1259 rv, nghttp2_strerror((int)rv));
1260 *err = CURLE_RECV_ERROR;
1263 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1265 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1266 httpc->inbuflen = 0;
1267 httpc->nread_inbuf = 0;
1270 httpc->nread_inbuf += rv;
1271 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1272 httpc->inbuflen - httpc->nread_inbuf));
1274 /* Always send pending frames in nghttp2 session, because
1275 nghttp2_session_mem_recv() may queue new frame */
1276 rv = h2_session_send(data, httpc->h2);
1278 *err = CURLE_SEND_ERROR;
1282 if(stream->memlen) {
1283 ssize_t retlen = stream->memlen;
1284 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1285 retlen, stream->stream_id));
1288 if(httpc->pause_stream_id == stream->stream_id) {
1289 /* data for this stream is returned now, but this stream caused a pause
1290 already so we need it called again asap */
1291 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1292 stream->stream_id));
1295 data->state.drain = 0; /* this stream is hereby drained */
1299 /* If stream is closed, return 0 to signal the http routine to close
1301 if(stream->closed) {
1302 return http2_handle_stream_close(conn, data, stream, err);
1305 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1306 stream->stream_id));
1310 /* Index where :authority header field will appear in request header
1312 #define AUTHORITY_DST_IDX 3
1314 /* return number of received (decrypted) bytes */
1315 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1316 const void *mem, size_t len, CURLcode *err)
1319 * BIG TODO: Currently, we send request in this function, but this
1320 * function is also used to send request body. It would be nice to
1321 * add dedicated function for request.
1324 struct http_conn *httpc = &conn->proto.httpc;
1325 struct HTTP *stream = conn->data->req.protop;
1329 size_t authority_idx;
1330 char *hdbuf = (char*)mem;
1332 nghttp2_data_provider data_prd;
1334 nghttp2_session *h2 = httpc->h2;
1335 nghttp2_priority_spec pri_spec;
1339 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1341 if(stream->stream_id != -1) {
1342 /* If stream_id != -1, we have dispatched request HEADERS, and now
1343 are going to send or sending request body in DATA frame */
1344 stream->upload_mem = mem;
1345 stream->upload_len = len;
1346 nghttp2_session_resume_data(h2, stream->stream_id);
1347 rv = h2_session_send(conn->data, h2);
1348 if(nghttp2_is_fatal(rv)) {
1349 *err = CURLE_SEND_ERROR;
1352 len -= stream->upload_len;
1354 /* Nullify here because we call nghttp2_session_send() and they
1355 might refer to the old buffer. */
1356 stream->upload_mem = NULL;
1357 stream->upload_len = 0;
1359 if(stream->upload_left) {
1360 /* we are sure that we have more data to send here. Calling the
1361 following API will make nghttp2_session_want_write() return
1362 nonzero if remote window allows it, which then libcurl checks
1363 socket is writable or not. See http2_perform_getsock(). */
1364 nghttp2_session_resume_data(h2, stream->stream_id);
1367 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1368 stream->stream_id));
1372 /* Calculate number of headers contained in [mem, mem + len) */
1373 /* Here, we assume the curl http code generate *correct* HTTP header
1376 for(i = 0; i < len; ++i) {
1377 if(hdbuf[i] == 0x0a) {
1381 /* We counted additional 2 \n in the first and last line. We need 3
1382 new headers: :method, :path and :scheme. Therefore we need one
1385 nva = malloc(sizeof(nghttp2_nv) * nheader);
1387 *err = CURLE_OUT_OF_MEMORY;
1390 /* Extract :method, :path from request line */
1391 end = strchr(hdbuf, ' ');
1394 nva[0].name = (unsigned char *)":method";
1395 nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
1396 nva[0].value = (unsigned char *)hdbuf;
1397 nva[0].valuelen = (uint16_t)(end - hdbuf);
1398 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1402 end = strchr(hdbuf, ' ');
1405 nva[1].name = (unsigned char *)":path";
1406 nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
1407 nva[1].value = (unsigned char *)hdbuf;
1408 nva[1].valuelen = (uint16_t)(end - hdbuf);
1409 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1411 nva[2].name = (unsigned char *)":scheme";
1412 nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
1413 if(conn->handler->flags & PROTOPT_SSL)
1414 nva[2].value = (unsigned char *)"https";
1416 nva[2].value = (unsigned char *)"http";
1417 nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
1418 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1420 hdbuf = strchr(hdbuf, 0x0a);
1428 while(i < nheader) {
1431 end = strchr(hdbuf, ':');
1435 if(hlen == 10 && Curl_raw_nequal("connection", hdbuf, 10)) {
1436 /* skip Connection: headers! */
1440 else if(hlen == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1442 nva[i].name = (unsigned char *)":authority";
1443 nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
1446 nva[i].name = (unsigned char *)hdbuf;
1447 nva[i].namelen = (uint16_t)(end - hdbuf);
1450 for(; *hdbuf == ' '; ++hdbuf);
1451 end = strchr(hdbuf, 0x0d);
1455 nva[i].value = (unsigned char *)hdbuf;
1456 nva[i].valuelen = (uint16_t)(end - hdbuf);
1457 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1458 /* Inspect Content-Length header field and retrieve the request
1459 entity length so that we can set END_STREAM to the last DATA
1461 if(nva[i].namelen == 14 &&
1462 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
1464 stream->upload_left = 0;
1465 for(j = 0; j < nva[i].valuelen; ++j) {
1466 stream->upload_left *= 10;
1467 stream->upload_left += nva[i].value[j] - '0';
1469 DEBUGF(infof(conn->data,
1470 "request content-length=%"
1471 CURL_FORMAT_CURL_OFF_T
1472 "\n", stream->upload_left));
1479 /* :authority must come before non-pseudo header fields */
1480 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1481 nghttp2_nv authority = nva[authority_idx];
1482 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1483 nva[i] = nva[i - 1];
1488 h2_pri_spec(conn->data, &pri_spec);
1490 switch(conn->data->set.httpreq) {
1492 case HTTPREQ_POST_FORM:
1494 data_prd.read_callback = data_source_read_callback;
1495 data_prd.source.ptr = NULL;
1496 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1497 &data_prd, conn->data);
1500 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1507 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1508 *err = CURLE_SEND_ERROR;
1512 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1513 stream_id, conn->data);
1514 stream->stream_id = stream_id;
1516 /* this does not call h2_session_send() since there can not have been any
1517 * priority upodate since the nghttp2_submit_request() call above */
1518 rv = nghttp2_session_send(h2);
1521 *err = CURLE_SEND_ERROR;
1525 if(stream->stream_id != -1) {
1526 /* If whole HEADERS frame was sent off to the underlying socket,
1527 the nghttp2 library calls data_source_read_callback. But only
1528 it found that no data available, so it deferred the DATA
1529 transmission. Which means that nghttp2_session_want_write()
1530 returns 0 on http2_perform_getsock(), which results that no
1531 writable socket check is performed. To workaround this, we
1532 issue nghttp2_session_resume_data() here to bring back DATA
1533 transmission from deferred state. */
1534 nghttp2_session_resume_data(h2, stream->stream_id);
1541 *err = CURLE_SEND_ERROR;
1545 CURLcode Curl_http2_setup(struct connectdata *conn)
1548 struct http_conn *httpc = &conn->proto.httpc;
1549 struct HTTP *stream = conn->data->req.protop;
1551 stream->stream_id = -1;
1553 if(!stream->header_recvbuf)
1554 stream->header_recvbuf = Curl_add_buffer_init();
1556 if((conn->handler == &Curl_handler_http2_ssl) ||
1557 (conn->handler == &Curl_handler_http2))
1558 return CURLE_OK; /* already done */
1560 if(conn->handler->flags & PROTOPT_SSL)
1561 conn->handler = &Curl_handler_http2_ssl;
1563 conn->handler = &Curl_handler_http2;
1565 result = Curl_http2_init(conn);
1569 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1570 stream->upload_left = 0;
1571 stream->upload_mem = NULL;
1572 stream->upload_len = 0;
1574 httpc->inbuflen = 0;
1575 httpc->nread_inbuf = 0;
1577 httpc->pause_stream_id = 0;
1579 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1580 conn->httpversion = 20;
1581 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1583 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1584 Curl_multi_connchanged(conn->data->multi);
1586 /* switch on TCP_NODELAY as we need to send off packets without delay for
1587 maximum throughput */
1588 Curl_tcpnodelay(conn, conn->sock[FIRSTSOCKET]);
1593 CURLcode Curl_http2_switched(struct connectdata *conn,
1594 const char *mem, size_t nread)
1597 struct http_conn *httpc = &conn->proto.httpc;
1600 struct SessionHandle *data = conn->data;
1601 struct HTTP *stream = conn->data->req.protop;
1603 result = Curl_http2_setup(conn);
1607 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1608 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1609 conn->recv[FIRSTSOCKET] = http2_recv;
1610 conn->send[FIRSTSOCKET] = http2_send;
1612 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1613 /* stream 1 is opened implicitly on upgrade */
1614 stream->stream_id = 1;
1615 /* queue SETTINGS frame (again) */
1616 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1617 httpc->binlen, NULL);
1619 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1620 nghttp2_strerror(rv), rv);
1624 nghttp2_session_set_stream_user_data(httpc->h2,
1629 /* stream ID is unknown at this point */
1630 stream->stream_id = -1;
1631 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1633 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1634 nghttp2_strerror(rv), rv);
1639 /* we are going to copy mem to httpc->inbuf. This is required since
1640 mem is part of buffer pointed by stream->mem, and callbacks
1641 called by nghttp2_session_mem_recv() will write stream specific
1642 data into stream->mem, overwriting data already there. */
1643 if(H2_BUFSIZE < nread) {
1644 failf(data, "connection buffer size is too small to store data following "
1645 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1650 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1651 " after upgrade: len=%zu\n",
1654 memcpy(httpc->inbuf, mem, nread);
1655 httpc->inbuflen = nread;
1657 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1660 if(nghttp2_is_fatal((int)nproc)) {
1661 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1662 nghttp2_strerror((int)nproc), (int)nproc);
1666 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1668 if((ssize_t)nread == nproc) {
1669 httpc->inbuflen = 0;
1670 httpc->nread_inbuf = 0;
1673 httpc->nread_inbuf += nproc;
1676 /* Try to send some frames since we may read SETTINGS already. */
1677 rv = h2_session_send(data, httpc->h2);
1680 failf(data, "nghttp2_session_send() failed: %s(%d)",
1681 nghttp2_strerror(rv), rv);
1688 #else /* !USE_NGHTTP2 */
1690 /* Satisfy external references even if http2 is not compiled in. */
1692 #define CURL_DISABLE_TYPECHECK
1693 #include <curl/curl.h>
1695 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
1702 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
1709 #endif /* USE_NGHTTP2 */