1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 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. 13 * 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. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23 #include "curl_setup.h" 24 25 #ifdef USE_NGHTTP2 26 #include <nghttp2/nghttp2.h> 27 #include "urldata.h" 28 #include "http2.h" 29 #include "http.h" 30 #include "sendf.h" 31 #include "curl_base64.h" 32 #include "rawstr.h" 33 #include "multiif.h" 34 #include "conncache.h" 35 #include "url.h" 36 #include "connect.h" 37 #include "strtoofft.h" 38 39 /* The last 3 #include files should be in this order */ 40 #include "curl_printf.h" 41 #include "curl_memory.h" 42 #include "memdebug.h" 43 44 #define MIN(x,y) ((x)<(y)?(x):(y)) 45 46 #if (NGHTTP2_VERSION_NUM < 0x010000) 47 #error too old nghttp2 version, upgrade! 48 #endif 49 50 #if (NGHTTP2_VERSION_NUM > 0x010800) 51 #define NGHTTP2_HAS_HTTP2_STRERROR 1 52 #endif 53 54 #if (NGHTTP2_VERSION_NUM >= 0x010900) 55 /* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or 56 later */ 57 #define NGHTTP2_HAS_ERROR_CALLBACK 1 58 #else 59 #define nghttp2_session_callbacks_set_error_callback(x,y) 60 #endif 61 62 /* 63 * Curl_http2_init_state() is called when the easy handle is created and 64 * allows for HTTP/2 specific init of state. 65 */ 66 void Curl_http2_init_state(struct UrlState *state) 67 { 68 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT; 69 } 70 71 /* 72 * Curl_http2_init_userset() is called when the easy handle is created and 73 * allows for HTTP/2 specific user-set fields. 74 */ 75 void Curl_http2_init_userset(struct UserDefined *set) 76 { 77 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT; 78 } 79 80 static int http2_perform_getsock(const struct connectdata *conn, 81 curl_socket_t *sock, /* points to 82 numsocks 83 number of 84 sockets */ 85 int numsocks) 86 { 87 const struct http_conn *c = &conn->proto.httpc; 88 int bitmap = GETSOCK_BLANK; 89 (void)numsocks; 90 91 /* TODO We should check underlying socket state if it is SSL socket 92 because of renegotiation. */ 93 sock[0] = conn->sock[FIRSTSOCKET]; 94 95 if(nghttp2_session_want_read(c->h2)) 96 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); 97 98 if(nghttp2_session_want_write(c->h2)) 99 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); 100 101 return bitmap; 102 } 103 104 static int http2_getsock(struct connectdata *conn, 105 curl_socket_t *sock, /* points to numsocks 106 number of sockets */ 107 int numsocks) 108 { 109 return http2_perform_getsock(conn, sock, numsocks); 110 } 111 112 static CURLcode http2_disconnect(struct connectdata *conn, 113 bool dead_connection) 114 { 115 struct HTTP *http = conn->data->req.protop; 116 struct http_conn *c = &conn->proto.httpc; 117 (void)dead_connection; 118 119 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n")); 120 121 nghttp2_session_del(c->h2); 122 Curl_safefree(c->inbuf); 123 124 if(http) { 125 Curl_add_buffer_free(http->header_recvbuf); 126 http->header_recvbuf = NULL; /* clear the pointer */ 127 Curl_add_buffer_free(http->trailer_recvbuf); 128 http->trailer_recvbuf = NULL; /* clear the pointer */ 129 for(; http->push_headers_used > 0; --http->push_headers_used) { 130 free(http->push_headers[http->push_headers_used - 1]); 131 } 132 free(http->push_headers); 133 http->push_headers = NULL; 134 } 135 136 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n")); 137 138 return CURLE_OK; 139 } 140 141 /* called from Curl_http_setup_conn */ 142 void Curl_http2_setup_req(struct Curl_easy *data) 143 { 144 struct HTTP *http = data->req.protop; 145 146 http->nread_header_recvbuf = 0; 147 http->bodystarted = FALSE; 148 http->status_code = -1; 149 http->pausedata = NULL; 150 http->pauselen = 0; 151 http->error_code = NGHTTP2_NO_ERROR; 152 http->closed = FALSE; 153 http->mem = data->state.buffer; 154 http->len = BUFSIZE; 155 http->memlen = 0; 156 } 157 158 /* called from Curl_http_setup_conn */ 159 void Curl_http2_setup_conn(struct connectdata *conn) 160 { 161 conn->proto.httpc.settings.max_concurrent_streams = 162 DEFAULT_MAX_CONCURRENT_STREAMS; 163 } 164 165 /* 166 * HTTP2 handler interface. This isn't added to the general list of protocols 167 * but will be used at run-time when the protocol is dynamically switched from 168 * HTTP to HTTP2. 169 */ 170 const struct Curl_handler Curl_handler_http2 = { 171 "HTTP", /* scheme */ 172 ZERO_NULL, /* setup_connection */ 173 Curl_http, /* do_it */ 174 Curl_http_done, /* done */ 175 ZERO_NULL, /* do_more */ 176 ZERO_NULL, /* connect_it */ 177 ZERO_NULL, /* connecting */ 178 ZERO_NULL, /* doing */ 179 http2_getsock, /* proto_getsock */ 180 http2_getsock, /* doing_getsock */ 181 ZERO_NULL, /* domore_getsock */ 182 http2_perform_getsock, /* perform_getsock */ 183 http2_disconnect, /* disconnect */ 184 ZERO_NULL, /* readwrite */ 185 PORT_HTTP, /* defport */ 186 CURLPROTO_HTTP, /* protocol */ 187 PROTOPT_NONE /* flags */ 188 }; 189 190 const struct Curl_handler Curl_handler_http2_ssl = { 191 "HTTPS", /* scheme */ 192 ZERO_NULL, /* setup_connection */ 193 Curl_http, /* do_it */ 194 Curl_http_done, /* done */ 195 ZERO_NULL, /* do_more */ 196 ZERO_NULL, /* connect_it */ 197 ZERO_NULL, /* connecting */ 198 ZERO_NULL, /* doing */ 199 http2_getsock, /* proto_getsock */ 200 http2_getsock, /* doing_getsock */ 201 ZERO_NULL, /* domore_getsock */ 202 http2_perform_getsock, /* perform_getsock */ 203 http2_disconnect, /* disconnect */ 204 ZERO_NULL, /* readwrite */ 205 PORT_HTTP, /* defport */ 206 CURLPROTO_HTTPS, /* protocol */ 207 PROTOPT_SSL /* flags */ 208 }; 209 210 /* 211 * Store nghttp2 version info in this buffer, Prefix with a space. Return 212 * total length written. 213 */ 214 int Curl_http2_ver(char *p, size_t len) 215 { 216 nghttp2_info *h2 = nghttp2_version(0); 217 return snprintf(p, len, " nghttp2/%s", h2->version_str); 218 } 219 220 /* HTTP/2 error code to name based on the Error Code Registry. 221 https://tools.ietf.org/html/rfc7540#page-77 222 nghttp2_error_code enums are identical. 223 */ 224 const char *Curl_http2_strerror(uint32_t err) { 225 #ifndef NGHTTP2_HAS_HTTP2_STRERROR 226 const char *str[] = { 227 "NO_ERROR", /* 0x0 */ 228 "PROTOCOL_ERROR", /* 0x1 */ 229 "INTERNAL_ERROR", /* 0x2 */ 230 "FLOW_CONTROL_ERROR", /* 0x3 */ 231 "SETTINGS_TIMEOUT", /* 0x4 */ 232 "STREAM_CLOSED", /* 0x5 */ 233 "FRAME_SIZE_ERROR", /* 0x6 */ 234 "REFUSED_STREAM", /* 0x7 */ 235 "CANCEL", /* 0x8 */ 236 "COMPRESSION_ERROR", /* 0x9 */ 237 "CONNECT_ERROR", /* 0xA */ 238 "ENHANCE_YOUR_CALM", /* 0xB */ 239 "INADEQUATE_SECURITY", /* 0xC */ 240 "HTTP_1_1_REQUIRED" /* 0xD */ 241 }; 242 return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown"; 243 #else 244 return nghttp2_http2_strerror(err); 245 #endif 246 } 247 248 /* 249 * The implementation of nghttp2_send_callback type. Here we write |data| with 250 * size |length| to the network and return the number of bytes actually 251 * written. See the documentation of nghttp2_send_callback for the details. 252 */ 253 static ssize_t send_callback(nghttp2_session *h2, 254 const uint8_t *data, size_t length, int flags, 255 void *userp) 256 { 257 struct connectdata *conn = (struct connectdata *)userp; 258 struct http_conn *c = &conn->proto.httpc; 259 ssize_t written; 260 CURLcode result = CURLE_OK; 261 262 (void)h2; 263 (void)flags; 264 265 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET, 266 data, length, &result); 267 268 if(result == CURLE_AGAIN) { 269 return NGHTTP2_ERR_WOULDBLOCK; 270 } 271 272 if(written == -1) { 273 failf(conn->data, "Failed sending HTTP2 data"); 274 return NGHTTP2_ERR_CALLBACK_FAILURE; 275 } 276 277 if(!written) 278 return NGHTTP2_ERR_WOULDBLOCK; 279 280 return written; 281 } 282 283 284 /* We pass a pointer to this struct in the push callback, but the contents of 285 the struct are hidden from the user. */ 286 struct curl_pushheaders { 287 struct Curl_easy *data; 288 const nghttp2_push_promise *frame; 289 }; 290 291 /* 292 * push header access function. Only to be used from within the push callback 293 */ 294 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) 295 { 296 /* Verify that we got a good easy handle in the push header struct, mostly to 297 detect rubbish input fast(er). */ 298 if(!h || !GOOD_EASY_HANDLE(h->data)) 299 return NULL; 300 else { 301 struct HTTP *stream = h->data->req.protop; 302 if(num < stream->push_headers_used) 303 return stream->push_headers[num]; 304 } 305 return NULL; 306 } 307 308 /* 309 * push header access function. Only to be used from within the push callback 310 */ 311 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) 312 { 313 /* Verify that we got a good easy handle in the push header struct, 314 mostly to detect rubbish input fast(er). Also empty header name 315 is just a rubbish too. We have to allow ":" at the beginning of 316 the header, but header == ":" must be rejected. If we have ':' in 317 the middle of header, it could be matched in middle of the value, 318 this is because we do prefix match.*/ 319 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || 320 Curl_raw_equal(header, ":") || strchr(header + 1, ':')) 321 return NULL; 322 else { 323 struct HTTP *stream = h->data->req.protop; 324 size_t len = strlen(header); 325 size_t i; 326 for(i=0; i<stream->push_headers_used; i++) { 327 if(!strncmp(header, stream->push_headers[i], len)) { 328 /* sub-match, make sure that it is followed by a colon */ 329 if(stream->push_headers[i][len] != ':') 330 continue; 331 return &stream->push_headers[i][len+1]; 332 } 333 } 334 } 335 return NULL; 336 } 337 338 static struct Curl_easy *duphandle(struct Curl_easy *data) 339 { 340 struct Curl_easy *second = curl_easy_duphandle(data); 341 if(second) { 342 /* setup the request struct */ 343 struct HTTP *http = calloc(1, sizeof(struct HTTP)); 344 if(!http) { 345 (void)Curl_close(second); 346 second = NULL; 347 } 348 else { 349 second->req.protop = http; 350 http->header_recvbuf = Curl_add_buffer_init(); 351 if(!http->header_recvbuf) { 352 free(http); 353 (void)Curl_close(second); 354 second = NULL; 355 } 356 else { 357 Curl_http2_setup_req(second); 358 second->state.stream_weight = data->state.stream_weight; 359 } 360 } 361 } 362 return second; 363 } 364 365 366 static int push_promise(struct Curl_easy *data, 367 struct connectdata *conn, 368 const nghttp2_push_promise *frame) 369 { 370 int rv; 371 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n", 372 frame->promised_stream_id)); 373 if(data->multi->push_cb) { 374 struct HTTP *stream; 375 struct HTTP *newstream; 376 struct curl_pushheaders heads; 377 CURLMcode rc; 378 struct http_conn *httpc; 379 size_t i; 380 /* clone the parent */ 381 struct Curl_easy *newhandle = duphandle(data); 382 if(!newhandle) { 383 infof(data, "failed to duplicate handle\n"); 384 rv = 1; /* FAIL HARD */ 385 goto fail; 386 } 387 388 heads.data = data; 389 heads.frame = frame; 390 /* ask the application */ 391 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n")); 392 393 stream = data->req.protop; 394 if(!stream) { 395 failf(data, "Internal NULL stream!\n"); 396 rv = 1; 397 goto fail; 398 } 399 400 rv = data->multi->push_cb(data, newhandle, 401 stream->push_headers_used, &heads, 402 data->multi->push_userp); 403 404 /* free the headers again */ 405 for(i=0; i<stream->push_headers_used; i++) 406 free(stream->push_headers[i]); 407 free(stream->push_headers); 408 stream->push_headers = NULL; 409 410 if(rv) { 411 /* denied, kill off the new handle again */ 412 (void)Curl_close(newhandle); 413 goto fail; 414 } 415 416 newstream = newhandle->req.protop; 417 newstream->stream_id = frame->promised_stream_id; 418 newhandle->req.maxdownload = -1; 419 newhandle->req.size = -1; 420 421 /* approved, add to the multi handle and immediately switch to PERFORM 422 state with the given connection !*/ 423 rc = Curl_multi_add_perform(data->multi, newhandle, conn); 424 if(rc) { 425 infof(data, "failed to add handle to multi\n"); 426 Curl_close(newhandle); 427 rv = 1; 428 goto fail; 429 } 430 431 httpc = &conn->proto.httpc; 432 nghttp2_session_set_stream_user_data(httpc->h2, 433 frame->promised_stream_id, newhandle); 434 } 435 else { 436 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n")); 437 rv = 1; 438 } 439 fail: 440 return rv; 441 } 442 443 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 444 void *userp) 445 { 446 struct connectdata *conn = (struct connectdata *)userp; 447 struct http_conn *httpc = &conn->proto.httpc; 448 struct Curl_easy *data_s = NULL; 449 struct HTTP *stream = NULL; 450 static int lastStream = -1; 451 int rv; 452 size_t left, ncopy; 453 int32_t stream_id = frame->hd.stream_id; 454 455 if(!stream_id) { 456 /* stream ID zero is for connection-oriented stuff */ 457 if(frame->hd.type == NGHTTP2_SETTINGS) { 458 uint32_t max_conn = httpc->settings.max_concurrent_streams; 459 DEBUGF(infof(conn->data, "Got SETTINGS\n")); 460 httpc->settings.max_concurrent_streams = 461 nghttp2_session_get_remote_settings( 462 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); 463 httpc->settings.enable_push = 464 nghttp2_session_get_remote_settings( 465 session, NGHTTP2_SETTINGS_ENABLE_PUSH); 466 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n", 467 httpc->settings.max_concurrent_streams)); 468 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n", 469 httpc->settings.enable_push?"TRUE":"false")); 470 if(max_conn != httpc->settings.max_concurrent_streams) { 471 /* only signal change if the value actually changed */ 472 infof(conn->data, 473 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n"); 474 Curl_multi_connchanged(conn->data->multi); 475 } 476 } 477 return 0; 478 } 479 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 480 if(lastStream != stream_id) { 481 lastStream = stream_id; 482 } 483 if(!data_s) { 484 DEBUGF(infof(conn->data, 485 "No Curl_easy associated with stream: %x\n", 486 stream_id)); 487 return 0; 488 } 489 490 stream = data_s->req.protop; 491 if(!stream) 492 return NGHTTP2_ERR_CALLBACK_FAILURE; 493 494 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n", 495 frame->hd.type, stream_id)); 496 497 switch(frame->hd.type) { 498 case NGHTTP2_DATA: 499 /* If body started on this stream, then receiving DATA is illegal. */ 500 if(!stream->bodystarted) { 501 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 502 stream_id, NGHTTP2_PROTOCOL_ERROR); 503 504 if(nghttp2_is_fatal(rv)) { 505 return NGHTTP2_ERR_CALLBACK_FAILURE; 506 } 507 } 508 break; 509 case NGHTTP2_HEADERS: 510 if(stream->bodystarted) { 511 /* Only valid HEADERS after body started is trailer HEADERS. We 512 buffer them in on_header callback. */ 513 break; 514 } 515 516 /* nghttp2 guarantees that :status is received, and we store it to 517 stream->status_code */ 518 DEBUGASSERT(stream->status_code != -1); 519 520 /* Only final status code signals the end of header */ 521 if(stream->status_code / 100 != 1) { 522 stream->bodystarted = TRUE; 523 stream->status_code = -1; 524 } 525 526 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); 527 528 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; 529 ncopy = MIN(stream->len, left); 530 531 memcpy(&stream->mem[stream->memlen], 532 stream->header_recvbuf->buffer + stream->nread_header_recvbuf, 533 ncopy); 534 stream->nread_header_recvbuf += ncopy; 535 536 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n", 537 ncopy, stream_id, stream->mem)); 538 539 stream->len -= ncopy; 540 stream->memlen += ncopy; 541 542 data_s->state.drain++; 543 httpc->drain_total++; 544 { 545 /* get the pointer from userp again since it was re-assigned above */ 546 struct connectdata *conn_s = (struct connectdata *)userp; 547 548 /* if we receive data for another handle, wake that up */ 549 if(conn_s->data != data_s) 550 Curl_expire(data_s, 1); 551 } 552 break; 553 case NGHTTP2_PUSH_PROMISE: 554 rv = push_promise(data_s, conn, &frame->push_promise); 555 if(rv) { /* deny! */ 556 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 557 frame->push_promise.promised_stream_id, 558 NGHTTP2_CANCEL); 559 if(nghttp2_is_fatal(rv)) { 560 return rv; 561 } 562 } 563 break; 564 default: 565 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n", 566 frame->hd.type, stream_id)); 567 break; 568 } 569 return 0; 570 } 571 572 static int on_invalid_frame_recv(nghttp2_session *session, 573 const nghttp2_frame *frame, 574 int lib_error_code, void *userp) 575 { 576 struct Curl_easy *data_s = NULL; 577 (void)userp; 578 579 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 580 if(data_s) { 581 DEBUGF(infof(data_s, 582 "on_invalid_frame_recv() was called, error=%d:%s\n", 583 lib_error_code, nghttp2_strerror(lib_error_code))); 584 } 585 return 0; 586 } 587 588 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, 589 int32_t stream_id, 590 const uint8_t *data, size_t len, void *userp) 591 { 592 struct HTTP *stream; 593 struct Curl_easy *data_s; 594 size_t nread; 595 struct connectdata *conn = (struct connectdata *)userp; 596 (void)session; 597 (void)flags; 598 (void)data; 599 600 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 601 602 /* get the stream from the hash based on Stream ID */ 603 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 604 if(!data_s) 605 /* Receiving a Stream ID not in the hash should not happen, this is an 606 internal error more than anything else! */ 607 return NGHTTP2_ERR_CALLBACK_FAILURE; 608 609 stream = data_s->req.protop; 610 if(!stream) 611 return NGHTTP2_ERR_CALLBACK_FAILURE; 612 613 nread = MIN(stream->len, len); 614 memcpy(&stream->mem[stream->memlen], data, nread); 615 616 stream->len -= nread; 617 stream->memlen += nread; 618 619 data_s->state.drain++; 620 conn->proto.httpc.drain_total++; 621 622 /* if we receive data for another handle, wake that up */ 623 if(conn->data != data_s) 624 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for 625 immediately? */ 626 627 DEBUGF(infof(data_s, "%zu data received for stream %u " 628 "(%zu left in buffer %p, total %zu)\n", 629 nread, stream_id, 630 stream->len, stream->mem, 631 stream->memlen)); 632 633 if(nread < len) { 634 stream->pausedata = data + nread; 635 stream->pauselen = len - nread; 636 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" 637 ", stream %u\n", 638 len - nread, stream_id)); 639 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id; 640 641 return NGHTTP2_ERR_PAUSE; 642 } 643 644 /* pause execution of nghttp2 if we received data for another handle 645 in order to process them first. */ 646 if(conn->data != data_s) { 647 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id; 648 649 return NGHTTP2_ERR_PAUSE; 650 } 651 652 return 0; 653 } 654 655 static int before_frame_send(nghttp2_session *session, 656 const nghttp2_frame *frame, 657 void *userp) 658 { 659 struct Curl_easy *data_s; 660 (void)userp; 661 662 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 663 if(data_s) { 664 DEBUGF(infof(data_s, "before_frame_send() was called\n")); 665 } 666 667 return 0; 668 } 669 static int on_frame_send(nghttp2_session *session, 670 const nghttp2_frame *frame, 671 void *userp) 672 { 673 struct Curl_easy *data_s; 674 (void)userp; 675 676 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 677 if(data_s) { 678 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n", 679 frame->hd.length)); 680 } 681 return 0; 682 } 683 static int on_frame_not_send(nghttp2_session *session, 684 const nghttp2_frame *frame, 685 int lib_error_code, void *userp) 686 { 687 struct Curl_easy *data_s; 688 (void)userp; 689 690 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 691 if(data_s) { 692 DEBUGF(infof(data_s, 693 "on_frame_not_send() was called, lib_error_code = %d\n", 694 lib_error_code)); 695 } 696 return 0; 697 } 698 static int on_stream_close(nghttp2_session *session, int32_t stream_id, 699 uint32_t error_code, void *userp) 700 { 701 struct Curl_easy *data_s; 702 struct HTTP *stream; 703 struct connectdata *conn = (struct connectdata *)userp; 704 (void)session; 705 (void)stream_id; 706 707 if(stream_id) { 708 /* get the stream from the hash based on Stream ID, stream ID zero is for 709 connection-oriented stuff */ 710 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 711 if(!data_s) { 712 /* We could get stream ID not in the hash. For example, if we 713 decided to reject stream (e.g., PUSH_PROMISE). */ 714 return 0; 715 } 716 DEBUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n", 717 Curl_http2_strerror(error_code), error_code, stream_id)); 718 stream = data_s->req.protop; 719 if(!stream) 720 return NGHTTP2_ERR_CALLBACK_FAILURE; 721 722 stream->error_code = error_code; 723 stream->closed = TRUE; 724 data_s->state.drain++; 725 conn->proto.httpc.drain_total++; 726 727 /* remove the entry from the hash as the stream is now gone */ 728 nghttp2_session_set_stream_user_data(session, stream_id, 0); 729 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id)); 730 } 731 return 0; 732 } 733 734 static int on_begin_headers(nghttp2_session *session, 735 const nghttp2_frame *frame, void *userp) 736 { 737 struct HTTP *stream; 738 struct Curl_easy *data_s = NULL; 739 (void)userp; 740 741 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 742 if(!data_s) { 743 return 0; 744 } 745 746 DEBUGF(infof(data_s, "on_begin_headers() was called\n")); 747 748 if(frame->hd.type != NGHTTP2_HEADERS) { 749 return 0; 750 } 751 752 stream = data_s->req.protop; 753 if(!stream || !stream->bodystarted) { 754 return 0; 755 } 756 757 /* This is trailer HEADERS started. Allocate buffer for them. */ 758 DEBUGF(infof(data_s, "trailer field started\n")); 759 760 assert(stream->trailer_recvbuf == NULL); 761 762 stream->trailer_recvbuf = Curl_add_buffer_init(); 763 if(!stream->trailer_recvbuf) { 764 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 765 } 766 767 return 0; 768 } 769 770 /* Decode HTTP status code. Returns -1 if no valid status code was 771 decoded. */ 772 static int decode_status_code(const uint8_t *value, size_t len) 773 { 774 int i; 775 int res; 776 777 if(len != 3) { 778 return -1; 779 } 780 781 res = 0; 782 783 for(i = 0; i < 3; ++i) { 784 char c = value[i]; 785 786 if(c < '0' || c > '9') { 787 return -1; 788 } 789 790 res *= 10; 791 res += c - '0'; 792 } 793 794 return res; 795 } 796 797 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ 798 static int on_header(nghttp2_session *session, const nghttp2_frame *frame, 799 const uint8_t *name, size_t namelen, 800 const uint8_t *value, size_t valuelen, 801 uint8_t flags, 802 void *userp) 803 { 804 struct HTTP *stream; 805 struct Curl_easy *data_s; 806 int32_t stream_id = frame->hd.stream_id; 807 struct connectdata *conn = (struct connectdata *)userp; 808 (void)flags; 809 810 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 811 812 /* get the stream from the hash based on Stream ID */ 813 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 814 if(!data_s) 815 /* Receiving a Stream ID not in the hash should not happen, this is an 816 internal error more than anything else! */ 817 return NGHTTP2_ERR_CALLBACK_FAILURE; 818 819 stream = data_s->req.protop; 820 if(!stream) { 821 failf(data_s, "Internal NULL stream! 5\n"); 822 return NGHTTP2_ERR_CALLBACK_FAILURE; 823 } 824 825 /* Store received PUSH_PROMISE headers to be used when the subsequent 826 PUSH_PROMISE callback comes */ 827 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { 828 char *h; 829 830 if(!stream->push_headers) { 831 stream->push_headers_alloc = 10; 832 stream->push_headers = malloc(stream->push_headers_alloc * 833 sizeof(char *)); 834 stream->push_headers_used = 0; 835 } 836 else if(stream->push_headers_used == 837 stream->push_headers_alloc) { 838 char **headp; 839 stream->push_headers_alloc *= 2; 840 headp = realloc(stream->push_headers, 841 stream->push_headers_alloc * sizeof(char *)); 842 if(!headp) { 843 free(stream->push_headers); 844 stream->push_headers = NULL; 845 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 846 } 847 stream->push_headers = headp; 848 } 849 h = aprintf("%s:%s", name, value); 850 if(h) 851 stream->push_headers[stream->push_headers_used++] = h; 852 return 0; 853 } 854 855 if(stream->bodystarted) { 856 /* This is trailer fields. */ 857 /* 3 is for ":" and "\r\n". */ 858 uint32_t n = (uint32_t)(namelen + valuelen + 3); 859 860 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen, 861 value)); 862 863 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n)); 864 Curl_add_buffer(stream->trailer_recvbuf, name, namelen); 865 Curl_add_buffer(stream->trailer_recvbuf, ": ", 2); 866 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen); 867 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3); 868 869 return 0; 870 } 871 872 if(namelen == sizeof(":status") - 1 && 873 memcmp(":status", name, namelen) == 0) { 874 /* nghttp2 guarantees :status is received first and only once, and 875 value is 3 digits status code, and decode_status_code always 876 succeeds. */ 877 stream->status_code = decode_status_code(value, valuelen); 878 DEBUGASSERT(stream->status_code != -1); 879 880 Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7); 881 Curl_add_buffer(stream->header_recvbuf, value, valuelen); 882 /* the space character after the status code is mandatory */ 883 Curl_add_buffer(stream->header_recvbuf, " \r\n", 3); 884 /* if we receive data for another handle, wake that up */ 885 if(conn->data != data_s) 886 Curl_expire(data_s, 1); 887 888 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n", 889 stream->status_code, data_s)); 890 return 0; 891 } 892 893 /* nghttp2 guarantees that namelen > 0, and :status was already 894 received, and this is not pseudo-header field . */ 895 /* convert to a HTTP1-style header */ 896 Curl_add_buffer(stream->header_recvbuf, name, namelen); 897 Curl_add_buffer(stream->header_recvbuf, ": ", 2); 898 Curl_add_buffer(stream->header_recvbuf, value, valuelen); 899 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); 900 /* if we receive data for another handle, wake that up */ 901 if(conn->data != data_s) 902 Curl_expire(data_s, 1); 903 904 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen, 905 value)); 906 907 return 0; /* 0 is successful */ 908 } 909 910 static ssize_t data_source_read_callback(nghttp2_session *session, 911 int32_t stream_id, 912 uint8_t *buf, size_t length, 913 uint32_t *data_flags, 914 nghttp2_data_source *source, 915 void *userp) 916 { 917 struct Curl_easy *data_s; 918 struct HTTP *stream = NULL; 919 size_t nread; 920 (void)source; 921 (void)userp; 922 923 if(stream_id) { 924 /* get the stream from the hash based on Stream ID, stream ID zero is for 925 connection-oriented stuff */ 926 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 927 if(!data_s) 928 /* Receiving a Stream ID not in the hash should not happen, this is an 929 internal error more than anything else! */ 930 return NGHTTP2_ERR_CALLBACK_FAILURE; 931 932 stream = data_s->req.protop; 933 if(!stream) 934 return NGHTTP2_ERR_CALLBACK_FAILURE; 935 } 936 else 937 return NGHTTP2_ERR_INVALID_ARGUMENT; 938 939 nread = MIN(stream->upload_len, length); 940 if(nread > 0) { 941 memcpy(buf, stream->upload_mem, nread); 942 stream->upload_mem += nread; 943 stream->upload_len -= nread; 944 stream->upload_left -= nread; 945 } 946 947 if(stream->upload_left == 0) 948 *data_flags = 1; 949 else if(nread == 0) 950 return NGHTTP2_ERR_DEFERRED; 951 952 DEBUGF(infof(data_s, "data_source_read_callback: " 953 "returns %zu bytes stream %u\n", 954 nread, stream_id)); 955 956 return nread; 957 } 958 959 /* 960 * The HTTP2 settings we send in the Upgrade request 961 */ 962 static nghttp2_settings_entry settings[] = { 963 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }, 964 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE }, 965 }; 966 967 #define H2_BUFSIZE 32768 968 969 #ifdef NGHTTP2_HAS_ERROR_CALLBACK 970 static int error_callback(nghttp2_session *session, 971 const char *msg, 972 size_t len, 973 void *userp) 974 { 975 struct connectdata *conn = (struct connectdata *)userp; 976 (void)session; 977 infof(conn->data, "http2 error: %.*s\n", len, msg); 978 return 0; 979 } 980 #endif 981 982 /* 983 * Initialize nghttp2 for a Curl connection 984 */ 985 CURLcode Curl_http2_init(struct connectdata *conn) 986 { 987 if(!conn->proto.httpc.h2) { 988 int rc; 989 nghttp2_session_callbacks *callbacks; 990 991 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE); 992 if(conn->proto.httpc.inbuf == NULL) 993 return CURLE_OUT_OF_MEMORY; 994 995 rc = nghttp2_session_callbacks_new(&callbacks); 996 997 if(rc) { 998 failf(conn->data, "Couldn't initialize nghttp2 callbacks!"); 999 return CURLE_OUT_OF_MEMORY; /* most likely at least */ 1000 } 1001 1002 /* nghttp2_send_callback */ 1003 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 1004 /* nghttp2_on_frame_recv_callback */ 1005 nghttp2_session_callbacks_set_on_frame_recv_callback 1006 (callbacks, on_frame_recv); 1007 /* nghttp2_on_invalid_frame_recv_callback */ 1008 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback 1009 (callbacks, on_invalid_frame_recv); 1010 /* nghttp2_on_data_chunk_recv_callback */ 1011 nghttp2_session_callbacks_set_on_data_chunk_recv_callback 1012 (callbacks, on_data_chunk_recv); 1013 /* nghttp2_before_frame_send_callback */ 1014 nghttp2_session_callbacks_set_before_frame_send_callback 1015 (callbacks, before_frame_send); 1016 /* nghttp2_on_frame_send_callback */ 1017 nghttp2_session_callbacks_set_on_frame_send_callback 1018 (callbacks, on_frame_send); 1019 /* nghttp2_on_frame_not_send_callback */ 1020 nghttp2_session_callbacks_set_on_frame_not_send_callback 1021 (callbacks, on_frame_not_send); 1022 /* nghttp2_on_stream_close_callback */ 1023 nghttp2_session_callbacks_set_on_stream_close_callback 1024 (callbacks, on_stream_close); 1025 /* nghttp2_on_begin_headers_callback */ 1026 nghttp2_session_callbacks_set_on_begin_headers_callback 1027 (callbacks, on_begin_headers); 1028 /* nghttp2_on_header_callback */ 1029 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); 1030 1031 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback); 1032 1033 /* The nghttp2 session is not yet setup, do it */ 1034 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); 1035 1036 nghttp2_session_callbacks_del(callbacks); 1037 1038 if(rc) { 1039 failf(conn->data, "Couldn't initialize nghttp2!"); 1040 return CURLE_OUT_OF_MEMORY; /* most likely at least */ 1041 } 1042 } 1043 return CURLE_OK; 1044 } 1045 1046 /* 1047 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. 1048 */ 1049 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, 1050 struct connectdata *conn) 1051 { 1052 CURLcode result; 1053 ssize_t binlen; 1054 char *base64; 1055 size_t blen; 1056 struct SingleRequest *k = &conn->data->req; 1057 uint8_t *binsettings = conn->proto.httpc.binsettings; 1058 1059 /* As long as we have a fixed set of settings, we don't have to dynamically 1060 * figure out the base64 strings since it'll always be the same. However, 1061 * the settings will likely not be fixed every time in the future. 1062 */ 1063 1064 /* this returns number of bytes it wrote */ 1065 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, 1066 settings, 1067 sizeof(settings)/sizeof(settings[0])); 1068 if(!binlen) { 1069 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); 1070 return CURLE_FAILED_INIT; 1071 } 1072 conn->proto.httpc.binlen = binlen; 1073 1074 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen, 1075 &base64, &blen); 1076 if(result) 1077 return result; 1078 1079 result = Curl_add_bufferf(req, 1080 "Connection: Upgrade, HTTP2-Settings\r\n" 1081 "Upgrade: %s\r\n" 1082 "HTTP2-Settings: %s\r\n", 1083 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); 1084 free(base64); 1085 1086 k->upgr101 = UPGR101_REQUESTED; 1087 1088 return result; 1089 } 1090 1091 /* 1092 * Returns nonzero if current HTTP/2 session should be closed. 1093 */ 1094 static int should_close_session(struct http_conn *httpc) { 1095 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) && 1096 !nghttp2_session_want_write(httpc->h2); 1097 } 1098 1099 static int h2_session_send(struct Curl_easy *data, 1100 nghttp2_session *h2); 1101 1102 /* 1103 * h2_process_pending_input() processes pending input left in 1104 * httpc->inbuf. Then, call h2_session_send() to send pending data. 1105 * This function returns 0 if it succeeds, or -1 and error code will 1106 * be assigned to *err. 1107 */ 1108 static int h2_process_pending_input(struct Curl_easy *data, 1109 struct http_conn *httpc, 1110 CURLcode *err) { 1111 ssize_t nread; 1112 char *inbuf; 1113 ssize_t rv; 1114 1115 nread = httpc->inbuflen - httpc->nread_inbuf; 1116 inbuf = httpc->inbuf + httpc->nread_inbuf; 1117 1118 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); 1119 if(rv < 0) { 1120 failf(data, 1121 "h2_process_pending_input: nghttp2_session_mem_recv() returned " 1122 "%d:%s\n", rv, nghttp2_strerror((int)rv)); 1123 *err = CURLE_RECV_ERROR; 1124 return -1; 1125 } 1126 1127 if(nread == rv) { 1128 DEBUGF(infof(data, 1129 "h2_process_pending_input: All data in connection buffer " 1130 "processed\n")); 1131 httpc->inbuflen = 0; 1132 httpc->nread_inbuf = 0; 1133 } 1134 else { 1135 httpc->nread_inbuf += rv; 1136 DEBUGF(infof(data, 1137 "h2_process_pending_input: %zu bytes left in connection " 1138 "buffer\n", 1139 httpc->inbuflen - httpc->nread_inbuf)); 1140 } 1141 1142 rv = h2_session_send(data, httpc->h2); 1143 if(rv != 0) { 1144 *err = CURLE_SEND_ERROR; 1145 return -1; 1146 } 1147 1148 if(should_close_session(httpc)) { 1149 DEBUGF(infof(data, 1150 "h2_process_pending_input: nothing to do in this session\n")); 1151 *err = CURLE_HTTP2; 1152 return -1; 1153 } 1154 1155 return 0; 1156 } 1157 1158 static ssize_t http2_handle_stream_close(struct connectdata *conn, 1159 struct Curl_easy *data, 1160 struct HTTP *stream, CURLcode *err) { 1161 char *trailer_pos, *trailer_end; 1162 CURLcode result; 1163 struct http_conn *httpc = &conn->proto.httpc; 1164 1165 if(httpc->pause_stream_id == stream->stream_id) { 1166 httpc->pause_stream_id = 0; 1167 } 1168 1169 DEBUGASSERT(httpc->drain_total >= data->state.drain); 1170 httpc->drain_total -= data->state.drain; 1171 data->state.drain = 0; 1172 1173 if(httpc->pause_stream_id == 0) { 1174 if(h2_process_pending_input(data, httpc, err) != 0) { 1175 return -1; 1176 } 1177 } 1178 1179 DEBUGASSERT(data->state.drain == 0); 1180 1181 /* Reset to FALSE to prevent infinite loop in readwrite_data 1182 function. */ 1183 stream->closed = FALSE; 1184 if(stream->error_code != NGHTTP2_NO_ERROR) { 1185 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)", 1186 stream->stream_id, Curl_http2_strerror(stream->error_code), 1187 stream->error_code); 1188 *err = CURLE_HTTP2_STREAM; 1189 return -1; 1190 } 1191 1192 if(!stream->bodystarted) { 1193 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " 1194 " all response header fields, teated as error", 1195 stream->stream_id); 1196 *err = CURLE_HTTP2_STREAM; 1197 return -1; 1198 } 1199 1200 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) { 1201 trailer_pos = stream->trailer_recvbuf->buffer; 1202 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used; 1203 1204 for(; trailer_pos < trailer_end;) { 1205 uint32_t n; 1206 memcpy(&n, trailer_pos, sizeof(n)); 1207 trailer_pos += sizeof(n); 1208 1209 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n); 1210 if(result) { 1211 *err = result; 1212 return -1; 1213 } 1214 1215 trailer_pos += n + 1; 1216 } 1217 } 1218 1219 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n")); 1220 return 0; 1221 } 1222 1223 /* 1224 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight 1225 * and dependency to the peer. It also stores the updated values in the state 1226 * struct. 1227 */ 1228 1229 static void h2_pri_spec(struct Curl_easy *data, 1230 nghttp2_priority_spec *pri_spec) 1231 { 1232 struct HTTP *depstream = (data->set.stream_depends_on? 1233 data->set.stream_depends_on->req.protop:NULL); 1234 int32_t depstream_id = depstream? depstream->stream_id:0; 1235 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight, 1236 data->set.stream_depends_e); 1237 data->state.stream_weight = data->set.stream_weight; 1238 data->state.stream_depends_e = data->set.stream_depends_e; 1239 data->state.stream_depends_on = data->set.stream_depends_on; 1240 } 1241 1242 /* 1243 * h2_session_send() checks if there's been an update in the priority / 1244 * dependency settings and if so it submits a PRIORITY frame with the updated 1245 * info. 1246 */ 1247 static int h2_session_send(struct Curl_easy *data, 1248 nghttp2_session *h2) 1249 { 1250 struct HTTP *stream = data->req.protop; 1251 if((data->set.stream_weight != data->state.stream_weight) || 1252 (data->set.stream_depends_e != data->state.stream_depends_e) || 1253 (data->set.stream_depends_on != data->state.stream_depends_on) ) { 1254 /* send new weight and/or dependency */ 1255 nghttp2_priority_spec pri_spec; 1256 int rv; 1257 1258 h2_pri_spec(data, &pri_spec); 1259 1260 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n", 1261 stream->stream_id, data)); 1262 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id, 1263 &pri_spec); 1264 if(rv) 1265 return rv; 1266 } 1267 1268 return nghttp2_session_send(h2); 1269 } 1270 1271 /* 1272 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return 1273 * a regular CURLcode value. 1274 */ 1275 static ssize_t http2_recv(struct connectdata *conn, int sockindex, 1276 char *mem, size_t len, CURLcode *err) 1277 { 1278 CURLcode result = CURLE_OK; 1279 ssize_t rv; 1280 ssize_t nread; 1281 struct http_conn *httpc = &conn->proto.httpc; 1282 struct Curl_easy *data = conn->data; 1283 struct HTTP *stream = data->req.protop; 1284 1285 (void)sockindex; /* we always do HTTP2 on sockindex 0 */ 1286 1287 if(should_close_session(httpc)) { 1288 DEBUGF(infof(data, 1289 "http2_recv: nothing to do in this session\n")); 1290 *err = CURLE_HTTP2; 1291 return -1; 1292 } 1293 1294 /* Nullify here because we call nghttp2_session_send() and they 1295 might refer to the old buffer. */ 1296 stream->upload_mem = NULL; 1297 stream->upload_len = 0; 1298 1299 /* 1300 * At this point 'stream' is just in the Curl_easy the connection 1301 * identifies as its owner at this time. 1302 */ 1303 1304 if(stream->bodystarted && 1305 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) { 1306 /* If there is body data pending for this stream to return, do that */ 1307 size_t left = 1308 stream->header_recvbuf->size_used - stream->nread_header_recvbuf; 1309 size_t ncopy = MIN(len, left); 1310 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf, 1311 ncopy); 1312 stream->nread_header_recvbuf += ncopy; 1313 1314 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n", 1315 (int)ncopy)); 1316 return ncopy; 1317 } 1318 1319 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n", 1320 data, stream->stream_id)); 1321 1322 if((data->state.drain) && stream->memlen) { 1323 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n", 1324 stream->memlen, stream->stream_id, 1325 stream->mem, mem)); 1326 if(mem != stream->mem) { 1327 /* if we didn't get the same buffer this time, we must move the data to 1328 the beginning */ 1329 memmove(mem, stream->mem, stream->memlen); 1330 stream->len = len - stream->memlen; 1331 stream->mem = mem; 1332 } 1333 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) { 1334 /* We have paused nghttp2, but we have no pause data (see 1335 on_data_chunk_recv). */ 1336 httpc->pause_stream_id = 0; 1337 if(h2_process_pending_input(data, httpc, &result) != 0) { 1338 *err = result; 1339 return -1; 1340 } 1341 } 1342 } 1343 else if(stream->pausedata) { 1344 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id); 1345 nread = MIN(len, stream->pauselen); 1346 memcpy(mem, stream->pausedata, nread); 1347 1348 stream->pausedata += nread; 1349 stream->pauselen -= nread; 1350 1351 infof(data, "%zu data bytes written\n", nread); 1352 if(stream->pauselen == 0) { 1353 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id)); 1354 assert(httpc->pause_stream_id == stream->stream_id); 1355 httpc->pause_stream_id = 0; 1356 1357 stream->pausedata = NULL; 1358 stream->pauselen = 0; 1359 1360 /* When NGHTTP2_ERR_PAUSE is returned from 1361 data_source_read_callback, we might not process DATA frame 1362 fully. Calling nghttp2_session_mem_recv() again will 1363 continue to process DATA frame, but if there is no incoming 1364 frames, then we have to call it again with 0-length data. 1365 Without this, on_stream_close callback will not be called, 1366 and stream could be hanged. */ 1367 if(h2_process_pending_input(data, httpc, &result) != 0) { 1368 *err = result; 1369 return -1; 1370 } 1371 } 1372 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n", 1373 nread, stream->stream_id)); 1374 return nread; 1375 } 1376 else if(httpc->pause_stream_id) { 1377 /* If a stream paused nghttp2_session_mem_recv previously, and has 1378 not processed all data, it still refers to the buffer in 1379 nghttp2_session. If we call nghttp2_session_mem_recv(), we may 1380 overwrite that buffer. To avoid that situation, just return 1381 here with CURLE_AGAIN. This could be busy loop since data in 1382 socket is not read. But it seems that usually streams are 1383 notified with its drain property, and socket is read again 1384 quickly. */ 1385 *err = CURLE_AGAIN; 1386 return -1; 1387 } 1388 else { 1389 char *inbuf; 1390 /* remember where to store incoming data for this stream and how big the 1391 buffer is */ 1392 stream->mem = mem; 1393 stream->len = len; 1394 stream->memlen = 0; 1395 1396 if(httpc->inbuflen == 0) { 1397 nread = ((Curl_recv *)httpc->recv_underlying)( 1398 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); 1399 1400 if(nread == -1) { 1401 if(result != CURLE_AGAIN) 1402 failf(data, "Failed receiving HTTP2 data"); 1403 else if(stream->closed) 1404 /* received when the stream was already closed! */ 1405 return http2_handle_stream_close(conn, data, stream, err); 1406 1407 *err = result; 1408 return -1; 1409 } 1410 1411 if(nread == 0) { 1412 failf(data, "Unexpected EOF"); 1413 *err = CURLE_RECV_ERROR; 1414 return -1; 1415 } 1416 1417 DEBUGF(infof(data, "nread=%zd\n", nread)); 1418 1419 httpc->inbuflen = nread; 1420 inbuf = httpc->inbuf; 1421 } 1422 else { 1423 nread = httpc->inbuflen - httpc->nread_inbuf; 1424 inbuf = httpc->inbuf + httpc->nread_inbuf; 1425 1426 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n", 1427 nread)); 1428 } 1429 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); 1430 1431 if(nghttp2_is_fatal((int)rv)) { 1432 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n", 1433 rv, nghttp2_strerror((int)rv)); 1434 *err = CURLE_RECV_ERROR; 1435 return 0; 1436 } 1437 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv)); 1438 if(nread == rv) { 1439 DEBUGF(infof(data, "All data in connection buffer processed\n")); 1440 httpc->inbuflen = 0; 1441 httpc->nread_inbuf = 0; 1442 } 1443 else { 1444 httpc->nread_inbuf += rv; 1445 DEBUGF(infof(data, "%zu bytes left in connection buffer\n", 1446 httpc->inbuflen - httpc->nread_inbuf)); 1447 } 1448 /* Always send pending frames in nghttp2 session, because 1449 nghttp2_session_mem_recv() may queue new frame */ 1450 rv = h2_session_send(data, httpc->h2); 1451 if(rv != 0) { 1452 *err = CURLE_SEND_ERROR; 1453 return 0; 1454 } 1455 1456 if(should_close_session(httpc)) { 1457 DEBUGF(infof(data, "http2_recv: nothing to do in this session\n")); 1458 *err = CURLE_HTTP2; 1459 return -1; 1460 } 1461 } 1462 if(stream->memlen) { 1463 ssize_t retlen = stream->memlen; 1464 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n", 1465 retlen, stream->stream_id)); 1466 stream->memlen = 0; 1467 1468 if(httpc->pause_stream_id == stream->stream_id) { 1469 /* data for this stream is returned now, but this stream caused a pause 1470 already so we need it called again asap */ 1471 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n", 1472 stream->stream_id)); 1473 } 1474 else if(!stream->closed) { 1475 DEBUGASSERT(httpc->drain_total >= data->state.drain); 1476 httpc->drain_total -= data->state.drain; 1477 data->state.drain = 0; /* this stream is hereby drained */ 1478 } 1479 1480 return retlen; 1481 } 1482 /* If stream is closed, return 0 to signal the http routine to close 1483 the connection */ 1484 if(stream->closed) { 1485 return http2_handle_stream_close(conn, data, stream, err); 1486 } 1487 *err = CURLE_AGAIN; 1488 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n", 1489 stream->stream_id)); 1490 return -1; 1491 } 1492 1493 /* Index where :authority header field will appear in request header 1494 field list. */ 1495 #define AUTHORITY_DST_IDX 3 1496 1497 #define HEADER_OVERFLOW(x) \ 1498 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen) 1499 1500 /* return number of received (decrypted) bytes */ 1501 static ssize_t http2_send(struct connectdata *conn, int sockindex, 1502 const void *mem, size_t len, CURLcode *err) 1503 { 1504 /* 1505 * BIG TODO: Currently, we send request in this function, but this 1506 * function is also used to send request body. It would be nice to 1507 * add dedicated function for request. 1508 */ 1509 int rv; 1510 struct http_conn *httpc = &conn->proto.httpc; 1511 struct HTTP *stream = conn->data->req.protop; 1512 nghttp2_nv *nva = NULL; 1513 size_t nheader; 1514 size_t i; 1515 size_t authority_idx; 1516 char *hdbuf = (char*)mem; 1517 char *end, *line_end; 1518 nghttp2_data_provider data_prd; 1519 int32_t stream_id; 1520 nghttp2_session *h2 = httpc->h2; 1521 nghttp2_priority_spec pri_spec; 1522 1523 (void)sockindex; 1524 1525 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len)); 1526 1527 if(stream->stream_id != -1) { 1528 /* If stream_id != -1, we have dispatched request HEADERS, and now 1529 are going to send or sending request body in DATA frame */ 1530 stream->upload_mem = mem; 1531 stream->upload_len = len; 1532 nghttp2_session_resume_data(h2, stream->stream_id); 1533 rv = h2_session_send(conn->data, h2); 1534 if(nghttp2_is_fatal(rv)) { 1535 *err = CURLE_SEND_ERROR; 1536 return -1; 1537 } 1538 len -= stream->upload_len; 1539 1540 /* Nullify here because we call nghttp2_session_send() and they 1541 might refer to the old buffer. */ 1542 stream->upload_mem = NULL; 1543 stream->upload_len = 0; 1544 1545 if(should_close_session(httpc)) { 1546 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n")); 1547 *err = CURLE_HTTP2; 1548 return -1; 1549 } 1550 1551 if(stream->upload_left) { 1552 /* we are sure that we have more data to send here. Calling the 1553 following API will make nghttp2_session_want_write() return 1554 nonzero if remote window allows it, which then libcurl checks 1555 socket is writable or not. See http2_perform_getsock(). */ 1556 nghttp2_session_resume_data(h2, stream->stream_id); 1557 } 1558 1559 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len, 1560 stream->stream_id)); 1561 return len; 1562 } 1563 1564 /* Calculate number of headers contained in [mem, mem + len) */ 1565 /* Here, we assume the curl http code generate *correct* HTTP header 1566 field block */ 1567 nheader = 0; 1568 for(i = 1; i < len; ++i) { 1569 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { 1570 ++nheader; 1571 ++i; 1572 } 1573 } 1574 if(nheader < 2) 1575 goto fail; 1576 1577 /* We counted additional 2 \r\n in the first and last line. We need 3 1578 new headers: :method, :path and :scheme. Therefore we need one 1579 more space. */ 1580 nheader += 1; 1581 nva = malloc(sizeof(nghttp2_nv) * nheader); 1582 if(nva == NULL) { 1583 *err = CURLE_OUT_OF_MEMORY; 1584 return -1; 1585 } 1586 1587 /* Extract :method, :path from request line */ 1588 line_end = strstr(hdbuf, "\r\n"); 1589 1590 /* Method does not contain spaces */ 1591 end = memchr(hdbuf, ' ', line_end - hdbuf); 1592 if(!end || end == hdbuf) 1593 goto fail; 1594 nva[0].name = (unsigned char *)":method"; 1595 nva[0].namelen = strlen((char *)nva[0].name); 1596 nva[0].value = (unsigned char *)hdbuf; 1597 nva[0].valuelen = (size_t)(end - hdbuf); 1598 nva[0].flags = NGHTTP2_NV_FLAG_NONE; 1599 if(HEADER_OVERFLOW(nva[0])) { 1600 failf(conn->data, "Failed sending HTTP request: Header overflow"); 1601 goto fail; 1602 } 1603 1604 hdbuf = end + 1; 1605 1606 /* Path may contain spaces so scan backwards */ 1607 end = NULL; 1608 for(i = (size_t)(line_end - hdbuf); i; --i) { 1609 if(hdbuf[i - 1] == ' ') { 1610 end = &hdbuf[i - 1]; 1611 break; 1612 } 1613 } 1614 if(!end || end == hdbuf) 1615 goto fail; 1616 nva[1].name = (unsigned char *)":path"; 1617 nva[1].namelen = strlen((char *)nva[1].name); 1618 nva[1].value = (unsigned char *)hdbuf; 1619 nva[1].valuelen = (size_t)(end - hdbuf); 1620 nva[1].flags = NGHTTP2_NV_FLAG_NONE; 1621 if(HEADER_OVERFLOW(nva[1])) { 1622 failf(conn->data, "Failed sending HTTP request: Header overflow"); 1623 goto fail; 1624 } 1625 1626 hdbuf = end + 1; 1627 1628 end = line_end; 1629 nva[2].name = (unsigned char *)":scheme"; 1630 nva[2].namelen = strlen((char *)nva[2].name); 1631 if(conn->handler->flags & PROTOPT_SSL) 1632 nva[2].value = (unsigned char *)"https"; 1633 else 1634 nva[2].value = (unsigned char *)"http"; 1635 nva[2].valuelen = strlen((char *)nva[2].value); 1636 nva[2].flags = NGHTTP2_NV_FLAG_NONE; 1637 if(HEADER_OVERFLOW(nva[2])) { 1638 failf(conn->data, "Failed sending HTTP request: Header overflow"); 1639 goto fail; 1640 } 1641 1642 authority_idx = 0; 1643 i = 3; 1644 while(i < nheader) { 1645 size_t hlen; 1646 int skip = 0; 1647 1648 hdbuf = line_end + 2; 1649 1650 line_end = strstr(hdbuf, "\r\n"); 1651 if(line_end == hdbuf) 1652 goto fail; 1653 1654 /* header continuation lines are not supported */ 1655 if(*hdbuf == ' ' || *hdbuf == '\t') 1656 goto fail; 1657 1658 for(end = hdbuf; end < line_end && *end != ':'; ++end) 1659 ; 1660 if(end == hdbuf || end == line_end) 1661 goto fail; 1662 hlen = end - hdbuf; 1663 1664 if(hlen == 10 && Curl_raw_nequal("connection", hdbuf, 10)) { 1665 /* skip Connection: headers! */ 1666 skip = 1; 1667 --nheader; 1668 } 1669 else if(hlen == 4 && Curl_raw_nequal("host", hdbuf, 4)) { 1670 authority_idx = i; 1671 nva[i].name = (unsigned char *)":authority"; 1672 nva[i].namelen = strlen((char *)nva[i].name); 1673 } 1674 else { 1675 nva[i].name = (unsigned char *)hdbuf; 1676 nva[i].namelen = (size_t)(end - hdbuf); 1677 } 1678 hdbuf = end + 1; 1679 while(*hdbuf == ' ' || *hdbuf == '\t') 1680 ++hdbuf; 1681 end = line_end; 1682 if(!skip) { 1683 nva[i].value = (unsigned char *)hdbuf; 1684 nva[i].valuelen = (size_t)(end - hdbuf); 1685 nva[i].flags = NGHTTP2_NV_FLAG_NONE; 1686 if(HEADER_OVERFLOW(nva[i])) { 1687 failf(conn->data, "Failed sending HTTP request: Header overflow"); 1688 goto fail; 1689 } 1690 /* Inspect Content-Length header field and retrieve the request 1691 entity length so that we can set END_STREAM to the last DATA 1692 frame. */ 1693 if(nva[i].namelen == 14 && 1694 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) { 1695 size_t j; 1696 stream->upload_left = 0; 1697 if(!nva[i].valuelen) 1698 goto fail; 1699 for(j = 0; j < nva[i].valuelen; ++j) { 1700 if(nva[i].value[j] < '0' || nva[i].value[j] > '9') 1701 goto fail; 1702 if(stream->upload_left >= CURL_OFF_T_MAX / 10) 1703 goto fail; 1704 stream->upload_left *= 10; 1705 stream->upload_left += nva[i].value[j] - '0'; 1706 } 1707 DEBUGF(infof(conn->data, 1708 "request content-length=%" 1709 CURL_FORMAT_CURL_OFF_T 1710 "\n", stream->upload_left)); 1711 } 1712 ++i; 1713 } 1714 } 1715 1716 /* :authority must come before non-pseudo header fields */ 1717 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { 1718 nghttp2_nv authority = nva[authority_idx]; 1719 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { 1720 nva[i] = nva[i - 1]; 1721 } 1722 nva[i] = authority; 1723 } 1724 1725 /* Warn stream may be rejected if cumulative length of headers is too large. 1726 It appears nghttp2 will not send a header frame larger than 64KB. */ 1727 { 1728 size_t acc = 0; 1729 const size_t max_acc = 60000; /* <64KB to account for some overhead */ 1730 1731 for(i = 0; i < nheader; ++i) { 1732 if(nva[i].namelen > max_acc - acc) 1733 break; 1734 acc += nva[i].namelen; 1735 1736 if(nva[i].valuelen > max_acc - acc) 1737 break; 1738 acc += nva[i].valuelen; 1739 } 1740 1741 if(i != nheader) { 1742 infof(conn->data, "http2_send: Warning: The cumulative length of all " 1743 "headers exceeds %zu bytes and that could cause the " 1744 "stream to be rejected.\n", max_acc); 1745 } 1746 } 1747 1748 h2_pri_spec(conn->data, &pri_spec); 1749 1750 switch(conn->data->set.httpreq) { 1751 case HTTPREQ_POST: 1752 case HTTPREQ_POST_FORM: 1753 case HTTPREQ_PUT: 1754 data_prd.read_callback = data_source_read_callback; 1755 data_prd.source.ptr = NULL; 1756 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, 1757 &data_prd, conn->data); 1758 break; 1759 default: 1760 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader, 1761 NULL, conn->data); 1762 } 1763 1764 Curl_safefree(nva); 1765 1766 if(stream_id < 0) { 1767 DEBUGF(infof(conn->data, "http2_send() send error\n")); 1768 *err = CURLE_SEND_ERROR; 1769 return -1; 1770 } 1771 1772 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n", 1773 stream_id, conn->data); 1774 stream->stream_id = stream_id; 1775 1776 /* this does not call h2_session_send() since there can not have been any 1777 * priority upodate since the nghttp2_submit_request() call above */ 1778 rv = nghttp2_session_send(h2); 1779 1780 if(rv != 0) { 1781 *err = CURLE_SEND_ERROR; 1782 return -1; 1783 } 1784 1785 if(should_close_session(httpc)) { 1786 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n")); 1787 *err = CURLE_HTTP2; 1788 return -1; 1789 } 1790 1791 if(stream->stream_id != -1) { 1792 /* If whole HEADERS frame was sent off to the underlying socket, 1793 the nghttp2 library calls data_source_read_callback. But only 1794 it found that no data available, so it deferred the DATA 1795 transmission. Which means that nghttp2_session_want_write() 1796 returns 0 on http2_perform_getsock(), which results that no 1797 writable socket check is performed. To workaround this, we 1798 issue nghttp2_session_resume_data() here to bring back DATA 1799 transmission from deferred state. */ 1800 nghttp2_session_resume_data(h2, stream->stream_id); 1801 } 1802 1803 return len; 1804 1805 fail: 1806 free(nva); 1807 *err = CURLE_SEND_ERROR; 1808 return -1; 1809 } 1810 1811 CURLcode Curl_http2_setup(struct connectdata *conn) 1812 { 1813 CURLcode result; 1814 struct http_conn *httpc = &conn->proto.httpc; 1815 struct HTTP *stream = conn->data->req.protop; 1816 1817 stream->stream_id = -1; 1818 1819 if(!stream->header_recvbuf) 1820 stream->header_recvbuf = Curl_add_buffer_init(); 1821 1822 if((conn->handler == &Curl_handler_http2_ssl) || 1823 (conn->handler == &Curl_handler_http2)) 1824 return CURLE_OK; /* already done */ 1825 1826 if(conn->handler->flags & PROTOPT_SSL) 1827 conn->handler = &Curl_handler_http2_ssl; 1828 else 1829 conn->handler = &Curl_handler_http2; 1830 1831 result = Curl_http2_init(conn); 1832 if(result) 1833 return result; 1834 1835 infof(conn->data, "Using HTTP2, server supports multi-use\n"); 1836 stream->upload_left = 0; 1837 stream->upload_mem = NULL; 1838 stream->upload_len = 0; 1839 1840 httpc->inbuflen = 0; 1841 httpc->nread_inbuf = 0; 1842 1843 httpc->pause_stream_id = 0; 1844 httpc->drain_total = 0; 1845 1846 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 1847 conn->httpversion = 20; 1848 conn->bundle->multiuse = BUNDLE_MULTIPLEX; 1849 1850 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n"); 1851 Curl_multi_connchanged(conn->data->multi); 1852 1853 /* switch on TCP_NODELAY as we need to send off packets without delay for 1854 maximum throughput */ 1855 Curl_tcpnodelay(conn, conn->sock[FIRSTSOCKET]); 1856 1857 return CURLE_OK; 1858 } 1859 1860 CURLcode Curl_http2_switched(struct connectdata *conn, 1861 const char *mem, size_t nread) 1862 { 1863 CURLcode result; 1864 struct http_conn *httpc = &conn->proto.httpc; 1865 int rv; 1866 ssize_t nproc; 1867 struct Curl_easy *data = conn->data; 1868 struct HTTP *stream = conn->data->req.protop; 1869 1870 result = Curl_http2_setup(conn); 1871 if(result) 1872 return result; 1873 1874 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; 1875 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; 1876 conn->recv[FIRSTSOCKET] = http2_recv; 1877 conn->send[FIRSTSOCKET] = http2_send; 1878 1879 if(conn->data->req.upgr101 == UPGR101_RECEIVED) { 1880 /* stream 1 is opened implicitly on upgrade */ 1881 stream->stream_id = 1; 1882 /* queue SETTINGS frame (again) */ 1883 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, 1884 httpc->binlen, NULL); 1885 if(rv != 0) { 1886 failf(data, "nghttp2_session_upgrade() failed: %s(%d)", 1887 nghttp2_strerror(rv), rv); 1888 return CURLE_HTTP2; 1889 } 1890 1891 nghttp2_session_set_stream_user_data(httpc->h2, 1892 stream->stream_id, 1893 conn->data); 1894 } 1895 else { 1896 /* stream ID is unknown at this point */ 1897 stream->stream_id = -1; 1898 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); 1899 if(rv != 0) { 1900 failf(data, "nghttp2_submit_settings() failed: %s(%d)", 1901 nghttp2_strerror(rv), rv); 1902 return CURLE_HTTP2; 1903 } 1904 } 1905 1906 /* we are going to copy mem to httpc->inbuf. This is required since 1907 mem is part of buffer pointed by stream->mem, and callbacks 1908 called by nghttp2_session_mem_recv() will write stream specific 1909 data into stream->mem, overwriting data already there. */ 1910 if(H2_BUFSIZE < nread) { 1911 failf(data, "connection buffer size is too small to store data following " 1912 "HTTP Upgrade response header: buflen=%zu, datalen=%zu", 1913 H2_BUFSIZE, nread); 1914 return CURLE_HTTP2; 1915 } 1916 1917 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" 1918 " after upgrade: len=%zu\n", 1919 nread); 1920 1921 memcpy(httpc->inbuf, mem, nread); 1922 httpc->inbuflen = nread; 1923 1924 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, 1925 httpc->inbuflen); 1926 1927 if(nghttp2_is_fatal((int)nproc)) { 1928 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", 1929 nghttp2_strerror((int)nproc), (int)nproc); 1930 return CURLE_HTTP2; 1931 } 1932 1933 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); 1934 1935 if((ssize_t)nread == nproc) { 1936 httpc->inbuflen = 0; 1937 httpc->nread_inbuf = 0; 1938 } 1939 else { 1940 httpc->nread_inbuf += nproc; 1941 } 1942 1943 /* Try to send some frames since we may read SETTINGS already. */ 1944 rv = h2_session_send(data, httpc->h2); 1945 1946 if(rv != 0) { 1947 failf(data, "nghttp2_session_send() failed: %s(%d)", 1948 nghttp2_strerror(rv), rv); 1949 return CURLE_HTTP2; 1950 } 1951 1952 if(should_close_session(httpc)) { 1953 DEBUGF(infof(data, 1954 "nghttp2_session_send(): nothing to do in this session\n")); 1955 return CURLE_HTTP2; 1956 } 1957 1958 return CURLE_OK; 1959 } 1960 1961 #else /* !USE_NGHTTP2 */ 1962 1963 /* Satisfy external references even if http2 is not compiled in. */ 1964 1965 #define CURL_DISABLE_TYPECHECK 1966 #include <curl/curl.h> 1967 1968 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) 1969 { 1970 (void) h; 1971 (void) num; 1972 return NULL; 1973 } 1974 1975 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) 1976 { 1977 (void) h; 1978 (void) header; 1979 return NULL; 1980 } 1981 1982 #endif /* USE_NGHTTP2 */ 1983