26 #include <nghttp2/nghttp2.h> 45 #define MIN(x,y) ((x)<(y)?(x):(y)) 47 #if (NGHTTP2_VERSION_NUM < 0x010000) 48 #error too old nghttp2 version, upgrade! 51 #if (NGHTTP2_VERSION_NUM > 0x010800) 52 #define NGHTTP2_HAS_HTTP2_STRERROR 1 55 #if (NGHTTP2_VERSION_NUM >= 0x010900) 58 #define NGHTTP2_HAS_ERROR_CALLBACK 1 60 #define nghttp2_session_callbacks_set_error_callback(x,y) 63 #if (NGHTTP2_VERSION_NUM >= 0x010c00) 64 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 67 #define HTTP2_HUGE_WINDOW_SIZE (1 << 30) 84 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
87 static int http2_perform_getsock(
const struct connectdata *conn,
106 if(nghttp2_session_want_write(c->h2))
117 return http2_perform_getsock(conn, sock, numsocks);
123 static void http2_stream_free(
struct HTTP *http)
127 http->header_recvbuf = NULL;
129 http->trailer_recvbuf = NULL;
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;
139 bool dead_connection)
142 (void)dead_connection;
146 nghttp2_session_del(c->h2);
162 static bool http2_connisdead(
struct connectdata *check)
185 static unsigned int http2_conncheck(
struct connectdata *check,
186 unsigned int checks_to_perform)
191 if(http2_connisdead(check))
203 http->nread_header_recvbuf = 0;
204 http->bodystarted =
FALSE;
205 http->status_code = -1;
206 http->pausedata = NULL;
208 http->error_code = NGHTTP2_NO_ERROR;
209 http->closed =
FALSE;
210 http->close_handled =
FALSE;
219 conn->
proto.
httpc.settings.max_concurrent_streams =
220 DEFAULT_MAX_CONCURRENT_STREAMS;
240 http2_perform_getsock,
249 static const struct Curl_handler Curl_handler_http2_ssl = {
261 http2_perform_getsock,
274 int Curl_http2_ver(
char *
p,
size_t len)
276 nghttp2_info *h2 = nghttp2_version(0);
277 return snprintf(p, len,
" nghttp2/%s", h2->version_str);
284 const char *Curl_http2_strerror(uint32_t err)
286 #ifndef NGHTTP2_HAS_HTTP2_STRERROR 287 const char *
str[] = {
291 "FLOW_CONTROL_ERROR",
300 "INADEQUATE_SECURITY",
303 return (err <
sizeof str /
sizeof str[0]) ? str[err] :
"unknown";
305 return nghttp2_http2_strerror(err);
314 static ssize_t send_callback(nghttp2_session *h2,
315 const uint8_t *
data,
size_t length,
int flags,
330 return NGHTTP2_ERR_WOULDBLOCK;
334 failf(conn->
data,
"Failed sending HTTP2 data");
335 return NGHTTP2_ERR_CALLBACK_FAILURE;
339 return NGHTTP2_ERR_WOULDBLOCK;
347 struct curl_pushheaders {
349 const nghttp2_push_promise *frame;
362 struct HTTP *stream = h->data->req.protop;
363 if(num < stream->push_headers_used)
364 return stream->push_headers[num];
381 !strcmp(header,
":") || strchr(header + 1,
':'))
384 struct HTTP *stream = h->data->req.protop;
385 size_t len = strlen(header);
387 for(i = 0; i<stream->push_headers_used; i++) {
388 if(!strncmp(header, stream->push_headers[i], len)) {
390 if(stream->push_headers[i][len] !=
':')
392 return &stream->push_headers[
i][len + 1];
412 if(!http->header_recvbuf) {
427 static int push_promise(
struct Curl_easy *data,
429 const nghttp2_push_promise *frame)
432 DEBUGF(
infof(data,
"PUSH_PROMISE received, stream %u!\n",
433 frame->promised_stream_id));
436 struct HTTP *newstream;
437 struct curl_pushheaders heads;
442 struct Curl_easy *newhandle = duphandle(data);
444 infof(data,
"failed to duplicate handle\n");
452 DEBUGF(
infof(data,
"Got PUSH_PROMISE, ask application!\n"));
456 failf(data,
"Internal NULL stream!\n");
463 stream->push_headers_used, &heads,
467 for(i = 0; i<stream->push_headers_used; i++)
468 free(stream->push_headers[i]);
469 free(stream->push_headers);
470 stream->push_headers = NULL;
471 stream->push_headers_used = 0;
475 http2_stream_free(newhandle->
req.
protop);
481 newstream->stream_id = frame->promised_stream_id;
489 infof(data,
"failed to add handle to multi\n");
490 http2_stream_free(newhandle->
req.
protop);
497 nghttp2_session_set_stream_user_data(httpc->h2,
498 frame->promised_stream_id, newhandle);
501 DEBUGF(
infof(data,
"Got PUSH_PROMISE, ignore it!\n"));
508 static int on_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame,
514 struct HTTP *stream = NULL;
515 static int lastStream = -1;
518 int32_t stream_id = frame->hd.stream_id;
522 if(frame->hd.type == NGHTTP2_SETTINGS) {
523 uint32_t max_conn = httpc->settings.max_concurrent_streams;
525 httpc->settings.max_concurrent_streams =
526 nghttp2_session_get_remote_settings(
527 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
528 httpc->settings.enable_push =
529 nghttp2_session_get_remote_settings(
530 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
532 httpc->settings.max_concurrent_streams));
534 httpc->settings.enable_push?
"TRUE":
"false"));
535 if(max_conn != httpc->settings.max_concurrent_streams) {
538 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
544 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
545 if(lastStream != stream_id) {
546 lastStream = stream_id;
550 "No Curl_easy associated with stream: %x\n",
559 return NGHTTP2_ERR_CALLBACK_FAILURE;
562 DEBUGF(
infof(data_s,
"on_frame_recv() header %x stream %x\n",
563 frame->hd.type, stream_id));
565 switch(frame->hd.type) {
568 if(!stream->bodystarted) {
569 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
570 stream_id, NGHTTP2_PROTOCOL_ERROR);
572 if(nghttp2_is_fatal(rv)) {
573 return NGHTTP2_ERR_CALLBACK_FAILURE;
577 case NGHTTP2_HEADERS:
578 if(stream->bodystarted) {
589 if(stream->status_code / 100 != 1) {
590 stream->bodystarted =
TRUE;
591 stream->status_code = -1;
596 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
597 ncopy =
MIN(stream->len, left);
599 memcpy(&stream->mem[stream->memlen],
600 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
602 stream->nread_header_recvbuf += ncopy;
604 DEBUGF(
infof(data_s,
"Store %zu bytes headers from stream %u at %p\n",
605 ncopy, stream_id, stream->mem));
607 stream->len -= ncopy;
608 stream->memlen += ncopy;
611 httpc->drain_total++;
617 if(conn_s->
data != data_s)
621 case NGHTTP2_PUSH_PROMISE:
622 rv = push_promise(data_s, conn, &frame->push_promise);
624 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
625 frame->push_promise.promised_stream_id,
627 if(nghttp2_is_fatal(rv)) {
634 frame->hd.type, stream_id));
640 static int on_invalid_frame_recv(nghttp2_session *session,
641 const nghttp2_frame *frame,
642 int lib_error_code,
void *userp)
646 #if !defined(DEBUGBUILD) || defined(CURL_DISABLE_VERBOSE_STRINGS) 647 (void)lib_error_code;
650 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
653 "on_invalid_frame_recv() was called, error=%d:%s\n",
654 lib_error_code, nghttp2_strerror(lib_error_code)));
659 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
661 const uint8_t *data,
size_t len,
void *userp)
674 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
678 return NGHTTP2_ERR_CALLBACK_FAILURE;
682 return NGHTTP2_ERR_CALLBACK_FAILURE;
684 nread =
MIN(stream->len, len);
685 memcpy(&stream->mem[stream->memlen], data, nread);
687 stream->len -= nread;
688 stream->memlen += nread;
694 if(conn->
data != data_s)
697 DEBUGF(
infof(data_s,
"%zu data received for stream %u " 698 "(%zu left in buffer %p, total %zu)\n",
700 stream->len, stream->mem,
704 stream->pausedata = data + nread;
705 stream->pauselen = len - nread;
706 DEBUGF(
infof(data_s,
"NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" 708 len - nread, stream_id));
711 return NGHTTP2_ERR_PAUSE;
716 if(conn->
data != data_s) {
719 return NGHTTP2_ERR_PAUSE;
725 static int before_frame_send(nghttp2_session *session,
726 const nghttp2_frame *frame,
732 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
734 DEBUGF(
infof(data_s,
"before_frame_send() was called\n"));
739 static int on_frame_send(nghttp2_session *session,
740 const nghttp2_frame *frame,
746 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
748 DEBUGF(
infof(data_s,
"on_frame_send() was called, length = %zd\n",
753 static int on_frame_not_send(nghttp2_session *session,
754 const nghttp2_frame *frame,
755 int lib_error_code,
void *userp)
759 #if !defined(DEBUGBUILD) || defined(CURL_DISABLE_VERBOSE_STRINGS) 760 (void)lib_error_code;
763 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
766 "on_frame_not_send() was called, lib_error_code = %d\n",
771 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
772 uint32_t error_code,
void *userp)
783 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
789 DEBUGF(
infof(data_s,
"on_stream_close(), %s (err %d), stream %u\n",
790 Curl_http2_strerror(error_code), error_code, stream_id));
793 return NGHTTP2_ERR_CALLBACK_FAILURE;
795 stream->error_code = error_code;
796 stream->closed =
TRUE;
801 nghttp2_session_set_stream_user_data(session, stream_id, 0);
802 DEBUGF(
infof(data_s,
"Removed stream %u hash!\n", stream_id));
807 static int on_begin_headers(nghttp2_session *session,
808 const nghttp2_frame *frame,
void *userp)
814 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
819 DEBUGF(
infof(data_s,
"on_begin_headers() was called\n"));
821 if(frame->hd.type != NGHTTP2_HEADERS) {
826 if(!stream || !stream->bodystarted) {
836 if(!stream->trailer_recvbuf) {
837 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
845 static int decode_status_code(
const uint8_t *
value,
size_t len)
856 for(i = 0; i < 3; ++
i) {
859 if(c < '0' || c >
'9') {
871 static int on_header(nghttp2_session *session,
const nghttp2_frame *frame,
872 const uint8_t *
name,
size_t namelen,
873 const uint8_t *value,
size_t valuelen,
879 int32_t stream_id = frame->hd.stream_id;
886 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
890 return NGHTTP2_ERR_CALLBACK_FAILURE;
894 failf(data_s,
"Internal NULL stream! 5\n");
895 return NGHTTP2_ERR_CALLBACK_FAILURE;
900 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
903 if(!stream->push_headers) {
904 stream->push_headers_alloc = 10;
905 stream->push_headers =
malloc(stream->push_headers_alloc *
907 stream->push_headers_used = 0;
909 else if(stream->push_headers_used ==
910 stream->push_headers_alloc) {
912 stream->push_headers_alloc *= 2;
914 stream->push_headers_alloc *
sizeof(
char *));
916 stream->push_headers = NULL;
917 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
919 stream->push_headers = headp;
921 h =
aprintf(
"%s:%s", name, value);
923 stream->push_headers[stream->push_headers_used++] = h;
927 if(stream->bodystarted) {
930 uint32_t n = (uint32_t)(namelen + valuelen + 3);
932 DEBUGF(
infof(data_s,
"h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
944 if(namelen ==
sizeof(
":status") - 1 &&
945 memcmp(
":status", name, namelen) == 0) {
949 stream->status_code = decode_status_code(value, valuelen);
957 if(conn->
data != data_s)
960 DEBUGF(
infof(data_s,
"h2 status: HTTP/2 %03d (easy %p)\n",
961 stream->status_code, data_s));
973 if(conn->
data != data_s)
976 DEBUGF(
infof(data_s,
"h2 header: %.*s: %.*s\n", namelen, name, valuelen,
982 static ssize_t data_source_read_callback(nghttp2_session *session,
984 uint8_t *
buf,
size_t length,
985 uint32_t *data_flags,
986 nghttp2_data_source *source,
990 struct HTTP *stream = NULL;
998 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1002 return NGHTTP2_ERR_CALLBACK_FAILURE;
1006 return NGHTTP2_ERR_CALLBACK_FAILURE;
1009 return NGHTTP2_ERR_INVALID_ARGUMENT;
1011 nread =
MIN(stream->upload_len, length);
1013 memcpy(buf, stream->upload_mem, nread);
1014 stream->upload_mem += nread;
1015 stream->upload_len -= nread;
1017 stream->upload_left -= nread;
1020 if(stream->upload_left == 0)
1021 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1023 return NGHTTP2_ERR_DEFERRED;
1025 DEBUGF(
infof(data_s,
"data_source_read_callback: " 1026 "returns %zu bytes stream %u\n",
1032 #define H2_BUFSIZE 32768 1034 #ifdef NGHTTP2_HAS_ERROR_CALLBACK 1035 static int error_callback(nghttp2_session *session,
1042 infof(conn->
data,
"http2 error: %.*s\n", len, msg);
1047 static void populate_settings(
struct connectdata *conn,
1050 nghttp2_settings_entry *iv = httpc->local_settings;
1052 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1055 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1056 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1058 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1061 httpc->local_settings_num = 3;
1070 if(http->header_recvbuf) {
1073 http->header_recvbuf = NULL;
1075 http->trailer_recvbuf = NULL;
1076 if(http->push_headers) {
1078 for(; http->push_headers_used > 0; --http->push_headers_used) {
1079 free(http->push_headers[http->push_headers_used - 1]);
1081 free(http->push_headers);
1082 http->push_headers = NULL;
1088 nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
1089 NGHTTP2_STREAM_CLOSED);
1090 if(http->stream_id == httpc->pause_stream_id) {
1091 infof(data,
"stopped the pause stream!\n");
1092 httpc->pause_stream_id = 0;
1095 if(http->stream_id) {
1096 nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
1097 http->stream_id = 0;
1108 nghttp2_session_callbacks *callbacks;
1114 rc = nghttp2_session_callbacks_new(&callbacks);
1117 failf(conn->
data,
"Couldn't initialize nghttp2 callbacks!");
1122 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1124 nghttp2_session_callbacks_set_on_frame_recv_callback
1125 (callbacks, on_frame_recv);
1127 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
1128 (callbacks, on_invalid_frame_recv);
1130 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1131 (callbacks, on_data_chunk_recv);
1133 nghttp2_session_callbacks_set_before_frame_send_callback
1134 (callbacks, before_frame_send);
1136 nghttp2_session_callbacks_set_on_frame_send_callback
1137 (callbacks, on_frame_send);
1139 nghttp2_session_callbacks_set_on_frame_not_send_callback
1140 (callbacks, on_frame_not_send);
1142 nghttp2_session_callbacks_set_on_stream_close_callback
1143 (callbacks, on_stream_close);
1145 nghttp2_session_callbacks_set_on_begin_headers_callback
1146 (callbacks, on_begin_headers);
1148 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1150 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1153 rc = nghttp2_session_client_new(&conn->
proto.
httpc.h2, callbacks, conn);
1155 nghttp2_session_callbacks_del(callbacks);
1158 failf(conn->
data,
"Couldn't initialize nghttp2!");
1176 uint8_t *binsettings = conn->
proto.
httpc.binsettings;
1179 populate_settings(conn, httpc);
1182 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1183 httpc->local_settings,
1184 httpc->local_settings_num);
1186 failf(conn->
data,
"nghttp2 unexpectedly failed on pack_settings_payload");
1197 "Connection: Upgrade, HTTP2-Settings\r\n" 1199 "HTTP2-Settings: %s\r\n",
1200 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1211 static int should_close_session(
struct http_conn *httpc)
1213 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1214 !nghttp2_session_want_write(httpc->h2);
1217 static int h2_session_send(
struct Curl_easy *data,
1218 nghttp2_session *h2);
1226 static int h2_process_pending_input(
struct Curl_easy *data,
1234 nread = httpc->inbuflen - httpc->nread_inbuf;
1235 inbuf = httpc->inbuf + httpc->nread_inbuf;
1237 rv = nghttp2_session_mem_recv(httpc->h2, (
const uint8_t *)inbuf, nread);
1240 "h2_process_pending_input: nghttp2_session_mem_recv() returned " 1241 "%d:%s\n", rv, nghttp2_strerror((
int)rv));
1248 "h2_process_pending_input: All data in connection buffer " 1250 httpc->inbuflen = 0;
1251 httpc->nread_inbuf = 0;
1254 httpc->nread_inbuf += rv;
1256 "h2_process_pending_input: %zu bytes left in connection " 1258 httpc->inbuflen - httpc->nread_inbuf));
1261 rv = h2_session_send(data, httpc->h2);
1267 if(should_close_session(httpc)) {
1269 "h2_process_pending_input: nothing to do in this session\n"));
1284 if((conn->
handler == &Curl_handler_http2_ssl) ||
1285 (conn->
handler == &Curl_handler_http2)) {
1290 if(stream->upload_left) {
1293 nghttp2_session *h2 = httpc->h2;
1295 stream->upload_left = 0;
1299 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1301 (void)h2_process_pending_input(conn->
data, httpc, &result);
1312 char *trailer_pos, *trailer_end;
1316 if(httpc->pause_stream_id == stream->stream_id) {
1317 httpc->pause_stream_id = 0;
1324 if(httpc->pause_stream_id == 0) {
1325 if(h2_process_pending_input(data, httpc, err) != 0) {
1333 stream->closed =
FALSE;
1334 if(stream->error_code != NGHTTP2_NO_ERROR) {
1335 failf(data,
"HTTP/2 stream %u was not closed cleanly: %s (err %d)",
1336 stream->stream_id, Curl_http2_strerror(stream->error_code),
1337 stream->error_code);
1342 if(!stream->bodystarted) {
1343 failf(data,
"HTTP/2 stream %u was closed cleanly, but before getting " 1344 " all response header fields, teated as error",
1350 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1351 trailer_pos = stream->trailer_recvbuf->buffer;
1352 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1354 for(; trailer_pos < trailer_end;) {
1356 memcpy(&n, trailer_pos,
sizeof(n));
1357 trailer_pos +=
sizeof(n);
1365 trailer_pos += n + 1;
1369 stream->close_handled =
TRUE;
1371 DEBUGF(
infof(data,
"http2_recv returns 0, http2_handle_stream_close\n"));
1381 static void h2_pri_spec(
struct Curl_easy *data,
1382 nghttp2_priority_spec *pri_spec)
1386 int32_t depstream_id = depstream? depstream->stream_id:0;
1387 nghttp2_priority_spec_init(pri_spec, depstream_id, data->
set.
stream_weight,
1399 static int h2_session_send(
struct Curl_easy *data,
1400 nghttp2_session *h2)
1407 nghttp2_priority_spec pri_spec;
1410 h2_pri_spec(data, &pri_spec);
1412 DEBUGF(
infof(data,
"Queuing PRIORITY on stream %u (easy %p)\n",
1413 stream->stream_id, data));
1414 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1420 return nghttp2_session_send(h2);
1424 char *mem,
size_t len,
CURLcode *err)
1435 if(should_close_session(httpc)) {
1437 "http2_recv: nothing to do in this session\n"));
1444 stream->upload_mem = NULL;
1445 stream->upload_len = 0;
1452 if(stream->bodystarted &&
1453 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1456 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1457 size_t ncopy =
MIN(len, left);
1458 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1460 stream->nread_header_recvbuf += ncopy;
1462 DEBUGF(
infof(data,
"http2_recv: Got %d bytes from header_recvbuf\n",
1467 DEBUGF(
infof(data,
"http2_recv: easy %p (stream %u)\n",
1468 data, stream->stream_id));
1471 DEBUGF(
infof(data,
"http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1472 stream->memlen, stream->stream_id,
1474 if(mem != stream->mem) {
1477 memmove(mem, stream->mem, stream->memlen);
1478 stream->len = len - stream->memlen;
1481 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1484 httpc->pause_stream_id = 0;
1485 if(h2_process_pending_input(data, httpc, &result) != 0) {
1491 else if(stream->pausedata) {
1492 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1493 nread =
MIN(len, stream->pauselen);
1494 memcpy(mem, stream->pausedata, nread);
1496 stream->pausedata += nread;
1497 stream->pauselen -= nread;
1499 infof(data,
"%zu data bytes written\n", nread);
1500 if(stream->pauselen == 0) {
1501 DEBUGF(
infof(data,
"Unpaused by stream %u\n", stream->stream_id));
1502 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1503 httpc->pause_stream_id = 0;
1505 stream->pausedata = NULL;
1506 stream->pauselen = 0;
1515 if(h2_process_pending_input(data, httpc, &result) != 0) {
1520 DEBUGF(
infof(data,
"http2_recv: returns unpaused %zd bytes on stream %u\n",
1521 nread, stream->stream_id));
1524 else if(httpc->pause_stream_id) {
1533 DEBUGF(
infof(data,
"stream %x is paused, pause id: %x\n",
1534 stream->stream_id, httpc->pause_stream_id));
1546 if(httpc->inbuflen == 0) {
1547 nread = ((
Curl_recv *)httpc->recv_underlying)(
1552 failf(data,
"Failed receiving HTTP2 data");
1553 else if(stream->closed)
1555 return http2_handle_stream_close(conn, data, stream, err);
1562 failf(data,
"Unexpected EOF");
1569 httpc->inbuflen = nread;
1570 inbuf = httpc->inbuf;
1573 nread = httpc->inbuflen - httpc->nread_inbuf;
1574 inbuf = httpc->inbuf + httpc->nread_inbuf;
1576 DEBUGF(
infof(data,
"Use data left in connection buffer, nread=%zd\n",
1579 rv = nghttp2_session_mem_recv(httpc->h2, (
const uint8_t *)inbuf, nread);
1581 if(nghttp2_is_fatal((
int)rv)) {
1582 failf(data,
"nghttp2_session_mem_recv() returned %d:%s\n",
1583 rv, nghttp2_strerror((
int)rv));
1587 DEBUGF(
infof(data,
"nghttp2_session_mem_recv() returns %zd\n", rv));
1589 DEBUGF(
infof(data,
"All data in connection buffer processed\n"));
1590 httpc->inbuflen = 0;
1591 httpc->nread_inbuf = 0;
1594 httpc->nread_inbuf += rv;
1595 DEBUGF(
infof(data,
"%zu bytes left in connection buffer\n",
1596 httpc->inbuflen - httpc->nread_inbuf));
1600 rv = h2_session_send(data, httpc->h2);
1606 if(should_close_session(httpc)) {
1607 DEBUGF(
infof(data,
"http2_recv: nothing to do in this session\n"));
1612 if(stream->memlen) {
1613 ssize_t retlen = stream->memlen;
1614 DEBUGF(
infof(data,
"http2_recv: returns %zd for stream %u\n",
1615 retlen, stream->stream_id));
1618 if(httpc->pause_stream_id == stream->stream_id) {
1621 DEBUGF(
infof(data,
"Data returned for PAUSED stream %u\n",
1622 stream->stream_id));
1624 else if(!stream->closed) {
1634 if(stream->closed) {
1635 return http2_handle_stream_close(conn, data, stream, err);
1638 DEBUGF(
infof(data,
"http2_recv returns AGAIN for stream %u\n",
1639 stream->stream_id));
1645 #define AUTHORITY_DST_IDX 3 1647 #define HEADER_OVERFLOW(x) \ 1648 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen) 1655 static bool contains_trailers(
const char *
p,
size_t len)
1657 const char *end = p +
len;
1659 for(; p != end && (*p ==
' ' || *p ==
'\t'); ++
p)
1661 if(p == end || (
size_t)(end -
p) <
sizeof(
"trailers") - 1)
1664 p +=
sizeof(
"trailers") - 1;
1665 for(; p != end && (*p ==
' ' || *p ==
'\t'); ++
p)
1667 if(p == end || *p ==
',')
1671 for(; p != end && *p !=
','; ++
p)
1685 HEADERINST_TE_TRAILERS
1686 } header_instruction;
1689 static header_instruction inspect_header(
const char *name,
size_t namelen,
1690 const char *value,
size_t valuelen) {
1694 return HEADERINST_FORWARD;
1696 return contains_trailers(value, valuelen) ?
1697 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1700 HEADERINST_IGNORE : HEADERINST_FORWARD;
1704 HEADERINST_IGNORE : HEADERINST_FORWARD;
1707 HEADERINST_IGNORE : HEADERINST_FORWARD;
1710 HEADERINST_IGNORE : HEADERINST_FORWARD;
1712 return HEADERINST_FORWARD;
1717 const void *mem,
size_t len,
CURLcode *err)
1727 nghttp2_nv *nva = NULL;
1730 size_t authority_idx;
1731 char *hdbuf = (
char *)mem;
1732 char *end, *line_end;
1733 nghttp2_data_provider data_prd;
1735 nghttp2_session *h2 = httpc->h2;
1736 nghttp2_priority_spec pri_spec;
1742 if(stream->stream_id != -1) {
1743 if(stream->close_handled) {
1744 infof(conn->
data,
"stream %d closed\n", stream->stream_id);
1748 else if(stream->closed) {
1749 return http2_handle_stream_close(conn, conn->
data, stream, err);
1753 stream->upload_mem = mem;
1754 stream->upload_len =
len;
1755 nghttp2_session_resume_data(h2, stream->stream_id);
1756 rv = h2_session_send(conn->
data, h2);
1757 if(nghttp2_is_fatal(rv)) {
1761 len -= stream->upload_len;
1765 stream->upload_mem = NULL;
1766 stream->upload_len = 0;
1768 if(should_close_session(httpc)) {
1769 DEBUGF(
infof(conn->
data,
"http2_send: nothing to do in this session\n"));
1774 if(stream->upload_left) {
1779 nghttp2_session_resume_data(h2, stream->stream_id);
1782 DEBUGF(
infof(conn->
data,
"http2_send returns %zu for stream %u\n", len,
1783 stream->stream_id));
1791 for(i = 1; i <
len; ++
i) {
1792 if(hdbuf[i] ==
'\n' && hdbuf[i - 1] ==
'\r') {
1804 nva =
malloc(
sizeof(nghttp2_nv) * nheader);
1811 line_end = strstr(hdbuf,
"\r\n");
1814 end = memchr(hdbuf,
' ', line_end - hdbuf);
1815 if(!end || end == hdbuf)
1817 nva[0].name = (
unsigned char *)
":method";
1818 nva[0].namelen = strlen((
char *)nva[0].name);
1819 nva[0].value = (
unsigned char *)hdbuf;
1820 nva[0].valuelen = (
size_t)(end - hdbuf);
1821 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1822 if(HEADER_OVERFLOW(nva[0])) {
1823 failf(conn->
data,
"Failed sending HTTP request: Header overflow");
1831 for(i = (
size_t)(line_end - hdbuf);
i; --
i) {
1832 if(hdbuf[i - 1] ==
' ') {
1833 end = &hdbuf[i - 1];
1837 if(!end || end == hdbuf)
1839 nva[1].name = (
unsigned char *)
":path";
1840 nva[1].namelen = strlen((
char *)nva[1].name);
1841 nva[1].value = (
unsigned char *)hdbuf;
1842 nva[1].valuelen = (
size_t)(end - hdbuf);
1843 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1844 if(HEADER_OVERFLOW(nva[1])) {
1845 failf(conn->
data,
"Failed sending HTTP request: Header overflow");
1852 nva[2].name = (
unsigned char *)
":scheme";
1853 nva[2].namelen = strlen((
char *)nva[2].name);
1855 nva[2].value = (
unsigned char *)
"https";
1857 nva[2].value = (
unsigned char *)
"http";
1858 nva[2].valuelen = strlen((
char *)nva[2].value);
1859 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1860 if(HEADER_OVERFLOW(nva[2])) {
1861 failf(conn->
data,
"Failed sending HTTP request: Header overflow");
1867 while(i < nheader) {
1870 hdbuf = line_end + 2;
1872 line_end = strstr(hdbuf,
"\r\n");
1873 if(line_end == hdbuf)
1877 if(*hdbuf ==
' ' || *hdbuf ==
'\t')
1880 for(end = hdbuf; end < line_end && *end !=
':'; ++end)
1882 if(end == hdbuf || end == line_end)
1888 nva[
i].name = (
unsigned char *)
":authority";
1889 nva[
i].namelen = strlen((
char *)nva[i].name);
1892 nva[
i].name = (
unsigned char *)hdbuf;
1893 nva[
i].namelen = (
size_t)(end - hdbuf);
1896 while(*hdbuf ==
' ' || *hdbuf ==
'\t')
1900 switch(inspect_header((
const char *)nva[
i].name, nva[
i].namelen, hdbuf,
1902 case HEADERINST_IGNORE:
1906 case HEADERINST_TE_TRAILERS:
1907 nva[
i].value = (uint8_t*)
"trailers";
1908 nva[
i].valuelen =
sizeof(
"trailers") - 1;
1911 nva[
i].value = (
unsigned char *)hdbuf;
1912 nva[
i].valuelen = (
size_t)(end - hdbuf);
1915 nva[
i].flags = NGHTTP2_NV_FLAG_NONE;
1916 if(HEADER_OVERFLOW(nva[i])) {
1917 failf(conn->
data,
"Failed sending HTTP request: Header overflow");
1924 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1925 nghttp2_nv authority = nva[authority_idx];
1926 for(i = authority_idx; i > AUTHORITY_DST_IDX; --
i) {
1927 nva[
i] = nva[i - 1];
1934 #define MAX_ACC 60000 1938 for(i = 0; i < nheader; ++
i) {
1939 acc += nva[
i].namelen + nva[
i].valuelen;
1942 nva[i].namelen, nva[i].name,
1943 nva[i].valuelen, nva[i].value));
1947 infof(conn->
data,
"http2_send: Warning: The cumulative length of all " 1948 "headers exceeds %zu bytes and that could cause the " 1949 "stream to be rejected.\n", MAX_ACC);
1953 h2_pri_spec(conn->
data, &pri_spec);
1964 stream->upload_left = -1;
1966 data_prd.read_callback = data_source_read_callback;
1967 data_prd.source.ptr = NULL;
1968 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1969 &data_prd, conn->
data);
1972 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1984 infof(conn->
data,
"Using Stream ID: %x (easy handle %p)\n",
1985 stream_id, conn->
data);
1986 stream->stream_id = stream_id;
1990 rv = nghttp2_session_send(h2);
1997 if(should_close_session(httpc)) {
1998 DEBUGF(
infof(conn->
data,
"http2_send: nothing to do in this session\n"));
2003 if(stream->stream_id != -1) {
2012 nghttp2_session_resume_data(h2, stream->stream_id);
2029 stream->stream_id = -1;
2031 if(!stream->header_recvbuf)
2034 if((conn->
handler == &Curl_handler_http2_ssl) ||
2035 (conn->
handler == &Curl_handler_http2))
2039 conn->
handler = &Curl_handler_http2_ssl;
2041 conn->
handler = &Curl_handler_http2;
2047 infof(conn->
data,
"Using HTTP2, server supports multi-use\n");
2048 stream->upload_left = 0;
2049 stream->upload_mem = NULL;
2050 stream->upload_len = 0;
2052 httpc->inbuflen = 0;
2053 httpc->nread_inbuf = 0;
2055 httpc->pause_stream_id = 0;
2056 httpc->drain_total = 0;
2062 infof(conn->
data,
"Connection state changed (HTTP/2 confirmed)\n");
2069 const char *mem,
size_t nread)
2082 httpc->recv_underlying = (
recving)conn->
recv[FIRSTSOCKET];
2089 stream->stream_id = 1;
2091 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2092 httpc->binlen, NULL);
2094 failf(data,
"nghttp2_session_upgrade() failed: %s(%d)",
2095 nghttp2_strerror(rv), rv);
2099 nghttp2_session_set_stream_user_data(httpc->h2,
2104 populate_settings(conn, httpc);
2107 stream->stream_id = -1;
2108 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2109 httpc->local_settings,
2110 httpc->local_settings_num);
2112 failf(data,
"nghttp2_submit_settings() failed: %s(%d)",
2113 nghttp2_strerror(rv), rv);
2118 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 2119 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2120 HTTP2_HUGE_WINDOW_SIZE);
2122 failf(data,
"nghttp2_session_set_local_window_size() failed: %s(%d)",
2123 nghttp2_strerror(rv), rv);
2132 if(H2_BUFSIZE < nread) {
2133 failf(data,
"connection buffer size is too small to store data following " 2134 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2139 infof(conn->
data,
"Copying HTTP/2 data in stream buffer to connection buffer" 2140 " after upgrade: len=%zu\n",
2144 memcpy(httpc->inbuf, mem, nread);
2145 httpc->inbuflen = nread;
2147 nproc = nghttp2_session_mem_recv(httpc->h2, (
const uint8_t *)httpc->inbuf,
2150 if(nghttp2_is_fatal((
int)nproc)) {
2151 failf(data,
"nghttp2_session_mem_recv() failed: %s(%d)",
2152 nghttp2_strerror((
int)nproc), (
int)nproc);
2156 DEBUGF(
infof(data,
"nghttp2_session_mem_recv() returns %zd\n", nproc));
2159 httpc->inbuflen = 0;
2160 httpc->nread_inbuf = 0;
2163 httpc->nread_inbuf += nproc;
2167 rv = h2_session_send(data, httpc->h2);
2170 failf(data,
"nghttp2_session_send() failed: %s(%d)",
2171 nghttp2_strerror(rv), rv);
2175 if(should_close_session(httpc)) {
2177 "nghttp2_session_send(): nothing to do in this session\n"));
2204 tail = &(*tail)->
next;
2214 tail = &(*tail)->
next;
2232 while(data && data->
data != child) {
2270 #define CURL_DISABLE_TYPECHECK
ssize_t( Curl_recv)(struct connectdata *conn, int sockindex, char *buf, size_t len, CURLcode *err)
struct Curl_easy * stream_depends_on
#define Curl_http2_init_userset(x)
struct Curl_easy * stream_depends_on
CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn)
void Curl_add_buffer_free(Curl_send_buffer *buff)
CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
char * curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
struct Curl_http2_dep * stream_dependents
const struct Curl_handler * handler
struct Curl_multi * multi
#define GETSOCK_READSOCK(x)
#define Curl_http2_request_upgrade(x, y)
#define Curl_http2_done(x, y)
UNITTEST_START int result
CURL_EXTERN CURL * curl_easy_duphandle(CURL *curl)
char * curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
#define Curl_http2_cleanup_dependencies(x)
CURLcode Curl_base64url_encode(struct Curl_easy *data, const char *inputbuff, size_t insize, char **outptr, size_t *outlen)
struct connectdata * easy_conn
memcpy(filename, filename1, strlen(filename1))
#define Curl_http2_switched(x, y, z)
struct curl_httppost * last
curl_easy_setopt expects a curl_off_t argument for this option curl_easy_setopt expects a curl_write_callback argument for this option curl_easy_setopt expects a curl_ioctl_callback argument for this option curl_easy_setopt expects a curl_opensocket_callback argument for this option curl_easy_setopt expects a curl_debug_callback argument for this option curl_easy_setopt expects a curl_conv_callback argument for this option curl_easy_setopt expects a private data pointer as argument for this option curl_easy_setopt expects a FILE *argument for this option curl_easy_setopt expects a struct curl_httppost *argument for this option curl_easy_setopt expects a struct curl_slist *argument for this option curl_easy_getinfo expects a pointer to char *for this info curl_easy_getinfo expects a pointer to double for this info curl_easy_getinfo expects a pointer to struct curl_tlssessioninfo *for this info curl_easy_getinfo expects a pointer to curl_socket_t for this info size_t
#define SOCKET_READABLE(x, z)
UNITTEST_START struct Curl_easy data
#define strncasecompare(a, b, c)
void * Curl_saferealloc(void *ptr, size_t size)
void Curl_multi_connchanged(struct Curl_multi *multi)
#define CLIENTWRITE_HEADER
#define GETSOCK_WRITESOCK(x)
ssize_t( Curl_send)(struct connectdata *conn, int sockindex, const void *buf, size_t len, CURLcode *err)
curl_push_callback push_cb
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len)
#define Curl_http2_init(x)
#define Curl_safefree(ptr)
bool Curl_connalive(struct connectdata *conn)
#define Curl_http2_add_child(x, y, z)
#define Curl_http2_init_state(x)
#define Curl_http2_setup_req(x)
#define Curl_http2_done_sending(x)
struct connectbundle * bundle
struct Curl_http2_dep * next
CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt,...)
CURLcode Curl_http_done(struct connectdata *conn, CURLcode status, bool premature)
#define Curl_http2_setup_conn(x)
UNITTEST_START int * value
CURLcode Curl_http(struct connectdata *conn, bool *done)
Curl_send_buffer * Curl_add_buffer_init(void)
union connectdata::@34 proto
void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id)
CURLcode Curl_close(struct Curl_easy *data)
#define GOOD_EASY_HANDLE(x)
#define Curl_http2_setup(x)
#define Curl_http2_remove_child(x, y)
#define calloc(nbelem, size)
static const char base64[]