1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2015, 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 http://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 "curl_printf.h" 27 #include <nghttp2/nghttp2.h> 28 #include "urldata.h" 29 #include "http2.h" 30 #include "http.h" 31 #include "sendf.h" 32 #include "curl_base64.h" 33 #include "rawstr.h" 34 #include "multiif.h" 35 #include "conncache.h" 36 37 /* The last #include files should be: */ 38 #include "curl_memory.h" 39 #include "memdebug.h" 40 41 #define MIN(x,y) ((x)<(y)?(x):(y)) 42 43 #if (NGHTTP2_VERSION_NUM < 0x000600) 44 #error too old nghttp2 version, upgrade! 45 #endif 46 47 static int http2_perform_getsock(const struct connectdata *conn, 48 curl_socket_t *sock, /* points to 49 numsocks 50 number of 51 sockets */ 52 int numsocks) 53 { 54 const struct http_conn *c = &conn->proto.httpc; 55 int bitmap = GETSOCK_BLANK; 56 (void)numsocks; 57 58 /* TODO We should check underlying socket state if it is SSL socket 59 because of renegotiation. */ 60 sock[0] = conn->sock[FIRSTSOCKET]; 61 62 if(nghttp2_session_want_read(c->h2)) 63 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); 64 65 if(nghttp2_session_want_write(c->h2)) 66 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); 67 68 return bitmap; 69 } 70 71 static int http2_getsock(struct connectdata *conn, 72 curl_socket_t *sock, /* points to numsocks 73 number of sockets */ 74 int numsocks) 75 { 76 return http2_perform_getsock(conn, sock, numsocks); 77 } 78 79 static CURLcode http2_disconnect(struct connectdata *conn, 80 bool dead_connection) 81 { 82 struct http_conn *c = &conn->proto.httpc; 83 (void)dead_connection; 84 85 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n")); 86 87 nghttp2_session_del(c->h2); 88 Curl_safefree(c->inbuf); 89 Curl_hash_destroy(&c->streamsh); 90 91 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n")); 92 93 return CURLE_OK; 94 } 95 96 /* called from Curl_http_setup_conn */ 97 void Curl_http2_setup_conn(struct connectdata *conn) 98 { 99 struct HTTP *http = conn->data->req.protop; 100 101 conn->proto.httpc.settings.max_concurrent_streams = 102 DEFAULT_MAX_CONCURRENT_STREAMS; 103 104 http->nread_header_recvbuf = 0; 105 http->bodystarted = FALSE; 106 http->status_code = -1; 107 http->pausedata = NULL; 108 http->pauselen = 0; 109 http->error_code = NGHTTP2_NO_ERROR; 110 http->closed = FALSE; 111 112 /* where to store incoming data for this stream and how big the buffer is */ 113 http->mem = conn->data->state.buffer; 114 http->len = BUFSIZE; 115 http->memlen = 0; 116 } 117 118 /* 119 * HTTP2 handler interface. This isn't added to the general list of protocols 120 * but will be used at run-time when the protocol is dynamically switched from 121 * HTTP to HTTP2. 122 */ 123 const struct Curl_handler Curl_handler_http2 = { 124 "HTTP2", /* scheme */ 125 ZERO_NULL, /* setup_connection */ 126 Curl_http, /* do_it */ 127 Curl_http_done, /* done */ 128 ZERO_NULL, /* do_more */ 129 ZERO_NULL, /* connect_it */ 130 ZERO_NULL, /* connecting */ 131 ZERO_NULL, /* doing */ 132 http2_getsock, /* proto_getsock */ 133 http2_getsock, /* doing_getsock */ 134 ZERO_NULL, /* domore_getsock */ 135 http2_perform_getsock, /* perform_getsock */ 136 http2_disconnect, /* disconnect */ 137 ZERO_NULL, /* readwrite */ 138 PORT_HTTP, /* defport */ 139 CURLPROTO_HTTP, /* protocol */ 140 PROTOPT_NONE /* flags */ 141 }; 142 143 const struct Curl_handler Curl_handler_http2_ssl = { 144 "HTTP2", /* scheme */ 145 ZERO_NULL, /* setup_connection */ 146 Curl_http, /* do_it */ 147 Curl_http_done, /* done */ 148 ZERO_NULL, /* do_more */ 149 ZERO_NULL, /* connect_it */ 150 ZERO_NULL, /* connecting */ 151 ZERO_NULL, /* doing */ 152 http2_getsock, /* proto_getsock */ 153 http2_getsock, /* doing_getsock */ 154 ZERO_NULL, /* domore_getsock */ 155 http2_perform_getsock, /* perform_getsock */ 156 http2_disconnect, /* disconnect */ 157 ZERO_NULL, /* readwrite */ 158 PORT_HTTP, /* defport */ 159 CURLPROTO_HTTPS, /* protocol */ 160 PROTOPT_SSL /* flags */ 161 }; 162 163 /* 164 * Store nghttp2 version info in this buffer, Prefix with a space. Return 165 * total length written. 166 */ 167 int Curl_http2_ver(char *p, size_t len) 168 { 169 nghttp2_info *h2 = nghttp2_version(0); 170 return snprintf(p, len, " nghttp2/%s", h2->version_str); 171 } 172 173 /* 174 * The implementation of nghttp2_send_callback type. Here we write |data| with 175 * size |length| to the network and return the number of bytes actually 176 * written. See the documentation of nghttp2_send_callback for the details. 177 */ 178 static ssize_t send_callback(nghttp2_session *h2, 179 const uint8_t *data, size_t length, int flags, 180 void *userp) 181 { 182 struct connectdata *conn = (struct connectdata *)userp; 183 struct http_conn *c = &conn->proto.httpc; 184 ssize_t written; 185 CURLcode result = CURLE_OK; 186 187 (void)h2; 188 (void)flags; 189 190 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET, 191 data, length, &result); 192 193 if(result == CURLE_AGAIN) { 194 return NGHTTP2_ERR_WOULDBLOCK; 195 } 196 197 if(written == -1) { 198 failf(conn->data, "Failed sending HTTP2 data"); 199 return NGHTTP2_ERR_CALLBACK_FAILURE; 200 } 201 202 if(!written) 203 return NGHTTP2_ERR_WOULDBLOCK; 204 205 return written; 206 } 207 208 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 209 void *userp) 210 { 211 struct connectdata *conn = (struct connectdata *)userp; 212 struct http_conn *httpc = &conn->proto.httpc; 213 struct SessionHandle *data_s = NULL; 214 struct HTTP *stream = NULL; 215 int rv; 216 size_t left, ncopy; 217 int32_t stream_id = frame->hd.stream_id; 218 219 (void)session; 220 (void)frame; 221 DEBUGF(infof(conn->data, "on_frame_recv() header %x stream %x\n", 222 frame->hd.type, stream_id)); 223 224 if(stream_id) { 225 /* get the stream from the hash based on Stream ID, stream ID zero is for 226 connection-oriented stuff */ 227 data_s = Curl_hash_pick(&httpc->streamsh, &stream_id, 228 sizeof(stream_id)); 229 if(!data_s) { 230 /* Receiving a Stream ID not in the hash should not happen, this is an 231 internal error more than anything else! */ 232 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!", 233 stream_id); 234 return NGHTTP2_ERR_CALLBACK_FAILURE; 235 } 236 stream = data_s->req.protop; 237 } 238 else 239 /* we do nothing on stream zero */ 240 return 0; 241 242 switch(frame->hd.type) { 243 case NGHTTP2_DATA: 244 /* If body started on this stream, then receiving DATA is illegal. */ 245 if(!stream->bodystarted) { 246 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 247 stream_id, NGHTTP2_PROTOCOL_ERROR); 248 249 if(nghttp2_is_fatal(rv)) { 250 return NGHTTP2_ERR_CALLBACK_FAILURE; 251 } 252 } 253 break; 254 case NGHTTP2_HEADERS: 255 if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) 256 break; 257 258 if(stream->bodystarted) { 259 /* Only valid HEADERS after body started is trailer HEADERS. We 260 ignores trailer HEADERS for now. nghttp2 guarantees that it 261 has END_STREAM flag set. */ 262 break; 263 } 264 265 /* nghttp2 guarantees that :status is received, and we store it to 266 stream->status_code */ 267 DEBUGASSERT(stream->status_code != -1); 268 269 /* Only final status code signals the end of header */ 270 if(stream->status_code / 100 != 1) { 271 stream->bodystarted = TRUE; 272 stream->status_code = -1; 273 } 274 275 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); 276 277 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf; 278 ncopy = MIN(stream->len, left); 279 280 memcpy(&stream->mem[stream->memlen], 281 stream->header_recvbuf->buffer + stream->nread_header_recvbuf, 282 ncopy); 283 stream->nread_header_recvbuf += ncopy; 284 285 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n", 286 ncopy, stream_id, stream->mem)); 287 288 stream->len -= ncopy; 289 stream->memlen += ncopy; 290 291 data_s->state.drain++; 292 Curl_expire(data_s, 1); 293 break; 294 case NGHTTP2_PUSH_PROMISE: 295 DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n")); 296 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 297 frame->push_promise.promised_stream_id, 298 NGHTTP2_CANCEL); 299 if(nghttp2_is_fatal(rv)) { 300 return rv; 301 } 302 break; 303 case NGHTTP2_SETTINGS: 304 { 305 uint32_t max_conn = httpc->settings.max_concurrent_streams; 306 DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id)); 307 httpc->settings.max_concurrent_streams = 308 nghttp2_session_get_remote_settings( 309 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); 310 httpc->settings.enable_push = 311 nghttp2_session_get_remote_settings( 312 session, NGHTTP2_SETTINGS_ENABLE_PUSH); 313 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n", 314 httpc->settings.max_concurrent_streams)); 315 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n", 316 httpc->settings.enable_push?"TRUE":"false")); 317 if(max_conn != httpc->settings.max_concurrent_streams) { 318 /* only signal change if the value actually changed */ 319 infof(conn->data, 320 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n"); 321 Curl_multi_connchanged(conn->data->multi); 322 } 323 } 324 break; 325 default: 326 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n", 327 frame->hd.type, stream_id)); 328 break; 329 } 330 return 0; 331 } 332 333 static int on_invalid_frame_recv(nghttp2_session *session, 334 const nghttp2_frame *frame, 335 int lib_error_code, void *userp) 336 { 337 struct connectdata *conn = (struct connectdata *)userp; 338 (void)session; 339 (void)frame; 340 DEBUGF(infof(conn->data, 341 "on_invalid_frame_recv() was called, error=%d:%s\n", 342 lib_error_code, nghttp2_strerror(lib_error_code))); 343 return 0; 344 } 345 346 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, 347 int32_t stream_id, 348 const uint8_t *data, size_t len, void *userp) 349 { 350 struct connectdata *conn = (struct connectdata *)userp; 351 struct HTTP *stream; 352 struct SessionHandle *data_s; 353 size_t nread; 354 (void)session; 355 (void)flags; 356 (void)data; 357 DEBUGF(infof(conn->data, "on_data_chunk_recv() " 358 "len = %u, stream %u\n", len, stream_id)); 359 360 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 361 362 /* get the stream from the hash based on Stream ID */ 363 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id, 364 sizeof(stream_id)); 365 if(!data_s) { 366 /* Receiving a Stream ID not in the hash should not happen, this is an 367 internal error more than anything else! */ 368 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!", 369 stream_id); 370 return NGHTTP2_ERR_CALLBACK_FAILURE; 371 } 372 stream = data_s->req.protop; 373 374 nread = MIN(stream->len, len); 375 memcpy(&stream->mem[stream->memlen], data, nread); 376 377 stream->len -= nread; 378 stream->memlen += nread; 379 380 data_s->state.drain++; 381 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for 382 immediately? */ 383 384 DEBUGF(infof(data_s, "%zu data received for stream %u " 385 "(%zu left in buffer %p, total %zu)\n", 386 nread, stream_id, 387 stream->len, stream->mem, 388 stream->memlen)); 389 390 if(nread < len) { 391 stream->pausedata = data + nread; 392 stream->pauselen = len - nread; 393 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer" 394 ", stream %u\n", 395 len - nread, stream_id)); 396 conn->proto.httpc.pause_stream_id = stream_id; 397 return NGHTTP2_ERR_PAUSE; 398 } 399 return 0; 400 } 401 402 static int before_frame_send(nghttp2_session *session, 403 const nghttp2_frame *frame, 404 void *userp) 405 { 406 struct connectdata *conn = (struct connectdata *)userp; 407 (void)session; 408 (void)frame; 409 DEBUGF(infof(conn->data, "before_frame_send() was called\n")); 410 return 0; 411 } 412 static int on_frame_send(nghttp2_session *session, 413 const nghttp2_frame *frame, 414 void *userp) 415 { 416 struct connectdata *conn = (struct connectdata *)userp; 417 (void)session; 418 (void)frame; 419 DEBUGF(infof(conn->data, "on_frame_send() was called, length = %zd\n", 420 frame->hd.length)); 421 return 0; 422 } 423 static int on_frame_not_send(nghttp2_session *session, 424 const nghttp2_frame *frame, 425 int lib_error_code, void *userp) 426 { 427 struct connectdata *conn = (struct connectdata *)userp; 428 (void)session; 429 (void)frame; 430 DEBUGF(infof(conn->data, 431 "on_frame_not_send() was called, lib_error_code = %d\n", 432 lib_error_code)); 433 return 0; 434 } 435 static int on_stream_close(nghttp2_session *session, int32_t stream_id, 436 uint32_t error_code, void *userp) 437 { 438 struct connectdata *conn = (struct connectdata *)userp; 439 struct SessionHandle *data_s; 440 struct HTTP *stream; 441 (void)session; 442 (void)stream_id; 443 DEBUGF(infof(conn->data, "on_stream_close(), error_code = %d, stream %u\n", 444 error_code, stream_id)); 445 446 if(stream_id) { 447 /* get the stream from the hash based on Stream ID, stream ID zero is for 448 connection-oriented stuff */ 449 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id, 450 sizeof(stream_id)); 451 if(!data_s) { 452 /* We could get stream ID not in the hash. For example, if we 453 decided to reject stream (e.g., PUSH_PROMISE). We call infof 454 as a debugging purpose for now. */ 455 infof(conn->data, 456 "Received frame on Stream ID: %x not in stream hash!\n", 457 stream_id); 458 return 0; 459 } 460 stream = data_s->req.protop; 461 462 stream->error_code = error_code; 463 stream->closed = TRUE; 464 465 /* remove the entry from the hash as the stream is now gone */ 466 Curl_hash_delete(&conn->proto.httpc.streamsh, 467 &stream_id, sizeof(stream_id)); 468 DEBUGF(infof(conn->data, "Removed stream %u hash!\n", stream_id)); 469 } 470 return 0; 471 } 472 473 static int on_begin_headers(nghttp2_session *session, 474 const nghttp2_frame *frame, void *userp) 475 { 476 struct connectdata *conn = (struct connectdata *)userp; 477 (void)session; 478 (void)frame; 479 DEBUGF(infof(conn->data, "on_begin_headers() was called\n")); 480 return 0; 481 } 482 483 /* Decode HTTP status code. Returns -1 if no valid status code was 484 decoded. */ 485 static int decode_status_code(const uint8_t *value, size_t len) 486 { 487 int i; 488 int res; 489 490 if(len != 3) { 491 return -1; 492 } 493 494 res = 0; 495 496 for(i = 0; i < 3; ++i) { 497 char c = value[i]; 498 499 if(c < '0' || c > '9') { 500 return -1; 501 } 502 503 res *= 10; 504 res += c - '0'; 505 } 506 507 return res; 508 } 509 510 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ 511 static int on_header(nghttp2_session *session, const nghttp2_frame *frame, 512 const uint8_t *name, size_t namelen, 513 const uint8_t *value, size_t valuelen, 514 uint8_t flags, 515 void *userp) 516 { 517 struct connectdata *conn = (struct connectdata *)userp; 518 struct HTTP *stream; 519 struct SessionHandle *data_s; 520 int32_t stream_id = frame->hd.stream_id; 521 522 (void)session; 523 (void)frame; 524 (void)flags; 525 526 /* Ignore PUSH_PROMISE for now */ 527 if(frame->hd.type != NGHTTP2_HEADERS) { 528 return 0; 529 } 530 531 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 532 533 /* get the stream from the hash based on Stream ID */ 534 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id, 535 sizeof(stream_id)); 536 if(!data_s) { 537 /* Receiving a Stream ID not in the hash should not happen, this is an 538 internal error more than anything else! */ 539 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!", 540 stream_id); 541 return NGHTTP2_ERR_CALLBACK_FAILURE; 542 } 543 stream = data_s->req.protop; 544 545 if(stream->bodystarted) 546 /* Ignore trailer or HEADERS not mapped to HTTP semantics. The 547 consequence is handled in on_frame_recv(). */ 548 return 0; 549 550 if(namelen == sizeof(":status") - 1 && 551 memcmp(":status", name, namelen) == 0) { 552 /* nghttp2 guarantees :status is received first and only once, and 553 value is 3 digits status code, and decode_status_code always 554 succeeds. */ 555 stream->status_code = decode_status_code(value, valuelen); 556 DEBUGASSERT(stream->status_code != -1); 557 558 Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9); 559 Curl_add_buffer(stream->header_recvbuf, value, valuelen); 560 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); 561 data_s->state.drain++; 562 Curl_expire(data_s, 1); 563 564 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n", 565 stream->status_code)); 566 return 0; 567 } 568 569 /* nghttp2 guarantees that namelen > 0, and :status was already 570 received, and this is not pseudo-header field . */ 571 /* convert to a HTTP1-style header */ 572 Curl_add_buffer(stream->header_recvbuf, name, namelen); 573 Curl_add_buffer(stream->header_recvbuf, ":", 1); 574 Curl_add_buffer(stream->header_recvbuf, value, valuelen); 575 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2); 576 data_s->state.drain++; 577 Curl_expire(data_s, 1); 578 579 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen, 580 value)); 581 582 return 0; /* 0 is successful */ 583 } 584 585 static ssize_t data_source_read_callback(nghttp2_session *session, 586 int32_t stream_id, 587 uint8_t *buf, size_t length, 588 uint32_t *data_flags, 589 nghttp2_data_source *source, 590 void *userp) 591 { 592 struct connectdata *conn = (struct connectdata *)userp; 593 struct http_conn *c = &conn->proto.httpc; 594 struct SessionHandle *data_s; 595 struct HTTP *stream = NULL; 596 size_t nread; 597 (void)session; 598 (void)stream_id; 599 (void)source; 600 601 if(stream_id) { 602 /* get the stream from the hash based on Stream ID, stream ID zero is for 603 connection-oriented stuff */ 604 data_s = Curl_hash_pick(&c->streamsh, &stream_id, sizeof(stream_id)); 605 if(!data_s) { 606 /* Receiving a Stream ID not in the hash should not happen, this is an 607 internal error more than anything else! */ 608 failf(conn->data, "Asked for data to stream %u not in hash!", stream_id); 609 return NGHTTP2_ERR_CALLBACK_FAILURE; 610 } 611 stream = data_s->req.protop; 612 } 613 else { 614 failf(conn->data, "nghttp2 confusion"); 615 return NGHTTP2_ERR_INVALID_ARGUMENT; 616 } 617 618 nread = MIN(stream->upload_len, length); 619 if(nread > 0) { 620 memcpy(buf, stream->upload_mem, nread); 621 stream->upload_mem += nread; 622 stream->upload_len -= nread; 623 stream->upload_left -= nread; 624 } 625 626 if(stream->upload_left == 0) 627 *data_flags = 1; 628 else if(nread == 0) 629 return NGHTTP2_ERR_DEFERRED; 630 631 DEBUGF(infof(data_s, "data_source_read_callback: " 632 "returns %zu bytes stream %u\n", 633 nread, stream_id)); 634 635 return nread; 636 } 637 638 /* 639 * The HTTP2 settings we send in the Upgrade request 640 */ 641 static nghttp2_settings_entry settings[] = { 642 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }, 643 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE }, 644 }; 645 646 #define H2_BUFSIZE 32768 647 648 static void freestreamentry(void *freethis) 649 { 650 (void)freethis; 651 } 652 653 /* 654 * Initialize nghttp2 for a Curl connection 655 */ 656 CURLcode Curl_http2_init(struct connectdata *conn) 657 { 658 if(!conn->proto.httpc.h2) { 659 int rc; 660 nghttp2_session_callbacks *callbacks; 661 662 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE); 663 if(conn->proto.httpc.inbuf == NULL) 664 return CURLE_OUT_OF_MEMORY; 665 666 rc = nghttp2_session_callbacks_new(&callbacks); 667 668 if(rc) { 669 failf(conn->data, "Couldn't initialize nghttp2 callbacks!"); 670 return CURLE_OUT_OF_MEMORY; /* most likely at least */ 671 } 672 673 /* nghttp2_send_callback */ 674 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 675 /* nghttp2_on_frame_recv_callback */ 676 nghttp2_session_callbacks_set_on_frame_recv_callback 677 (callbacks, on_frame_recv); 678 /* nghttp2_on_invalid_frame_recv_callback */ 679 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback 680 (callbacks, on_invalid_frame_recv); 681 /* nghttp2_on_data_chunk_recv_callback */ 682 nghttp2_session_callbacks_set_on_data_chunk_recv_callback 683 (callbacks, on_data_chunk_recv); 684 /* nghttp2_before_frame_send_callback */ 685 nghttp2_session_callbacks_set_before_frame_send_callback 686 (callbacks, before_frame_send); 687 /* nghttp2_on_frame_send_callback */ 688 nghttp2_session_callbacks_set_on_frame_send_callback 689 (callbacks, on_frame_send); 690 /* nghttp2_on_frame_not_send_callback */ 691 nghttp2_session_callbacks_set_on_frame_not_send_callback 692 (callbacks, on_frame_not_send); 693 /* nghttp2_on_stream_close_callback */ 694 nghttp2_session_callbacks_set_on_stream_close_callback 695 (callbacks, on_stream_close); 696 /* nghttp2_on_begin_headers_callback */ 697 nghttp2_session_callbacks_set_on_begin_headers_callback 698 (callbacks, on_begin_headers); 699 /* nghttp2_on_header_callback */ 700 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header); 701 702 /* The nghttp2 session is not yet setup, do it */ 703 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn); 704 705 nghttp2_session_callbacks_del(callbacks); 706 707 if(rc) { 708 failf(conn->data, "Couldn't initialize nghttp2!"); 709 return CURLE_OUT_OF_MEMORY; /* most likely at least */ 710 } 711 712 rc = Curl_hash_init(&conn->proto.httpc.streamsh, 7, Curl_hash_str, 713 Curl_str_key_compare, freestreamentry); 714 if(rc) { 715 failf(conn->data, "Couldn't init stream hash!"); 716 return CURLE_OUT_OF_MEMORY; /* most likely at least */ 717 } 718 } 719 return CURLE_OK; 720 } 721 722 /* 723 * Send a request using http2 724 */ 725 CURLcode Curl_http2_send_request(struct connectdata *conn) 726 { 727 (void)conn; 728 return CURLE_OK; 729 } 730 731 /* 732 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. 733 */ 734 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, 735 struct connectdata *conn) 736 { 737 CURLcode result; 738 ssize_t binlen; 739 char *base64; 740 size_t blen; 741 struct SingleRequest *k = &conn->data->req; 742 uint8_t *binsettings = conn->proto.httpc.binsettings; 743 744 /* As long as we have a fixed set of settings, we don't have to dynamically 745 * figure out the base64 strings since it'll always be the same. However, 746 * the settings will likely not be fixed every time in the future. 747 */ 748 749 /* this returns number of bytes it wrote */ 750 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, 751 settings, 752 sizeof(settings)/sizeof(settings[0])); 753 if(!binlen) { 754 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); 755 return CURLE_FAILED_INIT; 756 } 757 conn->proto.httpc.binlen = binlen; 758 759 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen, 760 &base64, &blen); 761 if(result) 762 return result; 763 764 result = Curl_add_bufferf(req, 765 "Connection: Upgrade, HTTP2-Settings\r\n" 766 "Upgrade: %s\r\n" 767 "HTTP2-Settings: %s\r\n", 768 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); 769 free(base64); 770 771 k->upgr101 = UPGR101_REQUESTED; 772 773 return result; 774 } 775 776 static ssize_t http2_handle_stream_close(struct http_conn *httpc, 777 struct SessionHandle *data, 778 struct HTTP *stream, CURLcode *err) { 779 if(httpc->pause_stream_id == stream->stream_id) { 780 httpc->pause_stream_id = 0; 781 } 782 /* Reset to FALSE to prevent infinite loop in readwrite_data 783 function. */ 784 stream->closed = FALSE; 785 if(stream->error_code != NGHTTP2_NO_ERROR) { 786 failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d", 787 stream->stream_id, stream->error_code); 788 *err = CURLE_HTTP2; 789 return -1; 790 } 791 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n")); 792 return 0; 793 } 794 795 /* 796 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return 797 * a regular CURLcode value. 798 */ 799 static ssize_t http2_recv(struct connectdata *conn, int sockindex, 800 char *mem, size_t len, CURLcode *err) 801 { 802 CURLcode result = CURLE_OK; 803 ssize_t rv; 804 ssize_t nread; 805 struct http_conn *httpc = &conn->proto.httpc; 806 struct SessionHandle *data = conn->data; 807 struct HTTP *stream = data->req.protop; 808 809 (void)sockindex; /* we always do HTTP2 on sockindex 0 */ 810 811 /* If stream is closed, return 0 to signal the http routine to close 812 the connection. We need to handle stream closure here, 813 otherwise, we may be going to read from underlying connection, 814 and gets EAGAIN, and we will get stuck there. */ 815 if(stream->memlen == 0 && stream->closed) { 816 return http2_handle_stream_close(httpc, data, stream, err); 817 } 818 819 /* Nullify here because we call nghttp2_session_send() and they 820 might refer to the old buffer. */ 821 stream->upload_mem = NULL; 822 stream->upload_len = 0; 823 824 /* 825 * At this point 'stream' is just in the SessionHandle the connection 826 * identifies as its owner at this time. 827 */ 828 829 if(stream->bodystarted && 830 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) { 831 /* If there is body data pending for this stream to return, do that */ 832 size_t left = 833 stream->header_recvbuf->size_used - stream->nread_header_recvbuf; 834 size_t ncopy = MIN(len, left); 835 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf, 836 ncopy); 837 stream->nread_header_recvbuf += ncopy; 838 839 infof(data, "http2_recv: Got %d bytes from header_recvbuf\n", 840 (int)ncopy); 841 return ncopy; 842 } 843 844 infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n", 845 len, mem, stream->stream_id); 846 847 if((data->state.drain) && stream->memlen) { 848 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n", 849 stream->memlen, stream->stream_id, 850 stream->mem, mem)); 851 if(mem != stream->mem) { 852 /* if we didn't get the same buffer this time, we must move the data to 853 the beginning */ 854 memmove(mem, stream->mem, stream->memlen); 855 stream->len = len - stream->memlen; 856 stream->mem = mem; 857 } 858 } 859 else if(stream->pausedata) { 860 nread = MIN(len, stream->pauselen); 861 memcpy(mem, stream->pausedata, nread); 862 863 stream->pausedata += nread; 864 stream->pauselen -= nread; 865 866 infof(data, "%zu data bytes written\n", nread); 867 if(stream->pauselen == 0) { 868 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id)); 869 assert(httpc->pause_stream_id == stream->stream_id); 870 httpc->pause_stream_id = 0; 871 872 stream->pausedata = NULL; 873 stream->pauselen = 0; 874 } 875 infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n", 876 nread, stream->stream_id); 877 return nread; 878 } 879 else if(httpc->pause_stream_id) { 880 /* If a stream paused nghttp2_session_mem_recv previously, and has 881 not processed all data, it still refers to the buffer in 882 nghttp2_session. If we call nghttp2_session_mem_recv(), we may 883 overwrite that buffer. To avoid that situation, just return 884 here with CURLE_AGAIN. This could be busy loop since data in 885 socket is not read. But it seems that usually streams are 886 notified with its drain property, and socket is read again 887 quickly. */ 888 *err = CURLE_AGAIN; 889 return -1; 890 } 891 else { 892 char *inbuf; 893 /* remember where to store incoming data for this stream and how big the 894 buffer is */ 895 stream->mem = mem; 896 stream->len = len; 897 stream->memlen = 0; 898 899 if(httpc->inbuflen == 0) { 900 nread = ((Curl_recv *)httpc->recv_underlying)( 901 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result); 902 903 if(result == CURLE_AGAIN) { 904 *err = result; 905 return -1; 906 } 907 908 if(nread == -1) { 909 failf(data, "Failed receiving HTTP2 data"); 910 *err = result; 911 return 0; 912 } 913 914 if(nread == 0) { 915 failf(data, "Unexpected EOF"); 916 *err = CURLE_RECV_ERROR; 917 return -1; 918 } 919 920 DEBUGF(infof(data, "nread=%zd\n", nread)); 921 922 httpc->inbuflen = nread; 923 inbuf = httpc->inbuf; 924 } 925 else { 926 nread = httpc->inbuflen - httpc->nread_inbuf; 927 inbuf = httpc->inbuf + httpc->nread_inbuf; 928 929 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n", 930 nread)); 931 } 932 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread); 933 934 if(nghttp2_is_fatal((int)rv)) { 935 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n", 936 rv, nghttp2_strerror((int)rv)); 937 *err = CURLE_RECV_ERROR; 938 return 0; 939 } 940 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv)); 941 if(nread == rv) { 942 DEBUGF(infof(data, "All data in connection buffer processed\n")); 943 httpc->inbuflen = 0; 944 httpc->nread_inbuf = 0; 945 } 946 else { 947 httpc->nread_inbuf += rv; 948 DEBUGF(infof(data, "%zu bytes left in connection buffer\n", 949 httpc->inbuflen - httpc->nread_inbuf)); 950 } 951 /* Always send pending frames in nghttp2 session, because 952 nghttp2_session_mem_recv() may queue new frame */ 953 rv = nghttp2_session_send(httpc->h2); 954 if(rv != 0) { 955 *err = CURLE_SEND_ERROR; 956 return 0; 957 } 958 } 959 if(stream->memlen) { 960 ssize_t retlen = stream->memlen; 961 infof(data, "http2_recv: returns %zd for stream %u\n", 962 retlen, stream->stream_id); 963 stream->memlen = 0; 964 965 if(httpc->pause_stream_id == stream->stream_id) { 966 /* data for this stream is returned now, but this stream caused a pause 967 already so we need it called again asap */ 968 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n", 969 stream->stream_id)); 970 } 971 else 972 data->state.drain = 0; /* this stream is hereby drained */ 973 974 return retlen; 975 } 976 /* If stream is closed, return 0 to signal the http routine to close 977 the connection */ 978 if(stream->closed) { 979 return http2_handle_stream_close(httpc, data, stream, err); 980 } 981 *err = CURLE_AGAIN; 982 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n", 983 stream->stream_id)); 984 return -1; 985 } 986 987 /* Index where :authority header field will appear in request header 988 field list. */ 989 #define AUTHORITY_DST_IDX 3 990 991 /* return number of received (decrypted) bytes */ 992 static ssize_t http2_send(struct connectdata *conn, int sockindex, 993 const void *mem, size_t len, CURLcode *err) 994 { 995 /* 996 * BIG TODO: Currently, we send request in this function, but this 997 * function is also used to send request body. It would be nice to 998 * add dedicated function for request. 999 */ 1000 int rv; 1001 struct http_conn *httpc = &conn->proto.httpc; 1002 struct HTTP *stream = conn->data->req.protop; 1003 nghttp2_nv *nva; 1004 size_t nheader; 1005 size_t i; 1006 size_t authority_idx; 1007 char *hdbuf = (char*)mem; 1008 char *end; 1009 nghttp2_data_provider data_prd; 1010 int32_t stream_id; 1011 nghttp2_session *h2 = httpc->h2; 1012 1013 (void)sockindex; 1014 1015 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len)); 1016 1017 if(stream->stream_id != -1) { 1018 /* If stream_id != -1, we have dispatched request HEADERS, and now 1019 are going to send or sending request body in DATA frame */ 1020 stream->upload_mem = mem; 1021 stream->upload_len = len; 1022 nghttp2_session_resume_data(h2, stream->stream_id); 1023 rv = nghttp2_session_send(h2); 1024 if(nghttp2_is_fatal(rv)) { 1025 *err = CURLE_SEND_ERROR; 1026 return -1; 1027 } 1028 len -= stream->upload_len; 1029 1030 /* Nullify here because we call nghttp2_session_send() and they 1031 might refer to the old buffer. */ 1032 stream->upload_mem = NULL; 1033 stream->upload_len = 0; 1034 1035 if(stream->upload_left) { 1036 /* we are sure that we have more data to send here. Calling the 1037 following API will make nghttp2_session_want_write() return 1038 nonzero if remote window allows it, which then libcurl checks 1039 socket is writable or not. See http2_perform_getsock(). */ 1040 nghttp2_session_resume_data(h2, stream->stream_id); 1041 } 1042 1043 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len, 1044 stream->stream_id)); 1045 return len; 1046 } 1047 1048 /* Calculate number of headers contained in [mem, mem + len) */ 1049 /* Here, we assume the curl http code generate *correct* HTTP header 1050 field block */ 1051 nheader = 0; 1052 for(i = 0; i < len; ++i) { 1053 if(hdbuf[i] == 0x0a) { 1054 ++nheader; 1055 } 1056 } 1057 /* We counted additional 2 \n in the first and last line. We need 3 1058 new headers: :method, :path and :scheme. Therefore we need one 1059 more space. */ 1060 nheader += 1; 1061 nva = malloc(sizeof(nghttp2_nv) * nheader); 1062 if(nva == NULL) { 1063 *err = CURLE_OUT_OF_MEMORY; 1064 return -1; 1065 } 1066 /* Extract :method, :path from request line */ 1067 end = strchr(hdbuf, ' '); 1068 nva[0].name = (unsigned char *)":method"; 1069 nva[0].namelen = (uint16_t)strlen((char *)nva[0].name); 1070 nva[0].value = (unsigned char *)hdbuf; 1071 nva[0].valuelen = (uint16_t)(end - hdbuf); 1072 nva[0].flags = NGHTTP2_NV_FLAG_NONE; 1073 1074 hdbuf = end + 1; 1075 1076 end = strchr(hdbuf, ' '); 1077 nva[1].name = (unsigned char *)":path"; 1078 nva[1].namelen = (uint16_t)strlen((char *)nva[1].name); 1079 nva[1].value = (unsigned char *)hdbuf; 1080 nva[1].valuelen = (uint16_t)(end - hdbuf); 1081 nva[1].flags = NGHTTP2_NV_FLAG_NONE; 1082 1083 nva[2].name = (unsigned char *)":scheme"; 1084 nva[2].namelen = (uint16_t)strlen((char *)nva[2].name); 1085 if(conn->handler->flags & PROTOPT_SSL) 1086 nva[2].value = (unsigned char *)"https"; 1087 else 1088 nva[2].value = (unsigned char *)"http"; 1089 nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value); 1090 nva[2].flags = NGHTTP2_NV_FLAG_NONE; 1091 1092 hdbuf = strchr(hdbuf, 0x0a); 1093 ++hdbuf; 1094 1095 authority_idx = 0; 1096 1097 for(i = 3; i < nheader; ++i) { 1098 end = strchr(hdbuf, ':'); 1099 assert(end); 1100 if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) { 1101 authority_idx = i; 1102 nva[i].name = (unsigned char *)":authority"; 1103 nva[i].namelen = (uint16_t)strlen((char *)nva[i].name); 1104 } 1105 else { 1106 nva[i].name = (unsigned char *)hdbuf; 1107 nva[i].namelen = (uint16_t)(end - hdbuf); 1108 } 1109 hdbuf = end + 1; 1110 for(; *hdbuf == ' '; ++hdbuf); 1111 end = strchr(hdbuf, 0x0d); 1112 assert(end); 1113 nva[i].value = (unsigned char *)hdbuf; 1114 nva[i].valuelen = (uint16_t)(end - hdbuf); 1115 nva[i].flags = NGHTTP2_NV_FLAG_NONE; 1116 1117 hdbuf = end + 2; 1118 /* Inspect Content-Length header field and retrieve the request 1119 entity length so that we can set END_STREAM to the last DATA 1120 frame. */ 1121 if(nva[i].namelen == 14 && 1122 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) { 1123 size_t j; 1124 stream->upload_left = 0; 1125 for(j = 0; j < nva[i].valuelen; ++j) { 1126 stream->upload_left *= 10; 1127 stream->upload_left += nva[i].value[j] - '0'; 1128 } 1129 DEBUGF(infof(conn->data, 1130 "request content-length=%" 1131 CURL_FORMAT_CURL_OFF_T 1132 "\n", stream->upload_left)); 1133 } 1134 } 1135 1136 /* :authority must come before non-pseudo header fields */ 1137 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { 1138 nghttp2_nv authority = nva[authority_idx]; 1139 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { 1140 nva[i] = nva[i - 1]; 1141 } 1142 nva[i] = authority; 1143 } 1144 1145 switch(conn->data->set.httpreq) { 1146 case HTTPREQ_POST: 1147 case HTTPREQ_POST_FORM: 1148 case HTTPREQ_PUT: 1149 data_prd.read_callback = data_source_read_callback; 1150 data_prd.source.ptr = NULL; 1151 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, 1152 &data_prd, NULL); 1153 break; 1154 default: 1155 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader, 1156 NULL, NULL); 1157 } 1158 1159 free(nva); 1160 1161 if(stream_id < 0) { 1162 DEBUGF(infof(conn->data, "http2_send() send error\n")); 1163 *err = CURLE_SEND_ERROR; 1164 return -1; 1165 } 1166 1167 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n", 1168 stream_id, conn->data); 1169 stream->stream_id = stream_id; 1170 1171 /* put the SessionHandle in the hash with the stream_id as key */ 1172 if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id, sizeof(stream_id), 1173 conn->data)) { 1174 failf(conn->data, "Couldn't add stream to hash!"); 1175 *err = CURLE_OUT_OF_MEMORY; 1176 return -1; 1177 } 1178 1179 rv = nghttp2_session_send(h2); 1180 1181 if(rv != 0) { 1182 *err = CURLE_SEND_ERROR; 1183 return -1; 1184 } 1185 1186 if(stream->stream_id != -1) { 1187 /* If whole HEADERS frame was sent off to the underlying socket, 1188 the nghttp2 library calls data_source_read_callback. But only 1189 it found that no data available, so it deferred the DATA 1190 transmission. Which means that nghttp2_session_want_write() 1191 returns 0 on http2_perform_getsock(), which results that no 1192 writable socket check is performed. To workaround this, we 1193 issue nghttp2_session_resume_data() here to bring back DATA 1194 transmission from deferred state. */ 1195 nghttp2_session_resume_data(h2, stream->stream_id); 1196 } 1197 1198 return len; 1199 } 1200 1201 CURLcode Curl_http2_setup(struct connectdata *conn) 1202 { 1203 CURLcode result; 1204 struct http_conn *httpc = &conn->proto.httpc; 1205 struct HTTP *stream = conn->data->req.protop; 1206 1207 stream->stream_id = -1; 1208 1209 if(!stream->header_recvbuf) 1210 stream->header_recvbuf = Curl_add_buffer_init(); 1211 1212 if((conn->handler == &Curl_handler_http2_ssl) || 1213 (conn->handler == &Curl_handler_http2)) 1214 return CURLE_OK; /* already done */ 1215 1216 if(conn->handler->flags & PROTOPT_SSL) 1217 conn->handler = &Curl_handler_http2_ssl; 1218 else 1219 conn->handler = &Curl_handler_http2; 1220 1221 result = Curl_http2_init(conn); 1222 if(result) 1223 return result; 1224 1225 infof(conn->data, "Using HTTP2, server supports multi-use\n"); 1226 stream->upload_left = 0; 1227 stream->upload_mem = NULL; 1228 stream->upload_len = 0; 1229 1230 httpc->inbuflen = 0; 1231 httpc->nread_inbuf = 0; 1232 1233 httpc->pause_stream_id = 0; 1234 1235 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 1236 conn->httpversion = 20; 1237 conn->bundle->multiuse = BUNDLE_MULTIPLEX; 1238 1239 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n"); 1240 Curl_multi_connchanged(conn->data->multi); 1241 1242 return CURLE_OK; 1243 } 1244 1245 CURLcode Curl_http2_switched(struct connectdata *conn, 1246 const char *mem, size_t nread) 1247 { 1248 CURLcode result; 1249 struct http_conn *httpc = &conn->proto.httpc; 1250 int rv; 1251 ssize_t nproc; 1252 struct SessionHandle *data = conn->data; 1253 struct HTTP *stream = conn->data->req.protop; 1254 1255 result = Curl_http2_setup(conn); 1256 if(result) 1257 return result; 1258 1259 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; 1260 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; 1261 conn->recv[FIRSTSOCKET] = http2_recv; 1262 conn->send[FIRSTSOCKET] = http2_send; 1263 1264 if(conn->data->req.upgr101 == UPGR101_RECEIVED) { 1265 /* stream 1 is opened implicitly on upgrade */ 1266 stream->stream_id = 1; 1267 /* queue SETTINGS frame (again) */ 1268 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, 1269 httpc->binlen, NULL); 1270 if(rv != 0) { 1271 failf(data, "nghttp2_session_upgrade() failed: %s(%d)", 1272 nghttp2_strerror(rv), rv); 1273 return CURLE_HTTP2; 1274 } 1275 1276 /* put the SessionHandle in the hash with the stream->stream_id as key */ 1277 if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id, 1278 sizeof(stream->stream_id), conn->data)) { 1279 failf(conn->data, "Couldn't add stream to hash!"); 1280 return CURLE_OUT_OF_MEMORY; 1281 } 1282 } 1283 else { 1284 /* stream ID is unknown at this point */ 1285 stream->stream_id = -1; 1286 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); 1287 if(rv != 0) { 1288 failf(data, "nghttp2_submit_settings() failed: %s(%d)", 1289 nghttp2_strerror(rv), rv); 1290 return CURLE_HTTP2; 1291 } 1292 } 1293 1294 /* we are going to copy mem to httpc->inbuf. This is required since 1295 mem is part of buffer pointed by stream->mem, and callbacks 1296 called by nghttp2_session_mem_recv() will write stream specific 1297 data into stream->mem, overwriting data already there. */ 1298 if(H2_BUFSIZE < nread) { 1299 failf(data, "connection buffer size is too small to store data following " 1300 "HTTP Upgrade response header: buflen=%zu, datalen=%zu", 1301 H2_BUFSIZE, nread); 1302 return CURLE_HTTP2; 1303 } 1304 1305 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" 1306 " after upgrade: len=%zu\n", 1307 nread); 1308 1309 memcpy(httpc->inbuf, mem, nread); 1310 httpc->inbuflen = nread; 1311 1312 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, 1313 httpc->inbuflen); 1314 1315 if(nghttp2_is_fatal((int)nproc)) { 1316 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", 1317 nghttp2_strerror((int)nproc), (int)nproc); 1318 return CURLE_HTTP2; 1319 } 1320 1321 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); 1322 1323 if((ssize_t)nread == nproc) { 1324 httpc->inbuflen = 0; 1325 httpc->nread_inbuf = 0; 1326 } 1327 else { 1328 httpc->nread_inbuf += nproc; 1329 } 1330 1331 /* Try to send some frames since we may read SETTINGS already. */ 1332 rv = nghttp2_session_send(httpc->h2); 1333 1334 if(rv != 0) { 1335 failf(data, "nghttp2_session_send() failed: %s(%d)", 1336 nghttp2_strerror(rv), rv); 1337 return CURLE_HTTP2; 1338 } 1339 1340 return CURLE_OK; 1341 } 1342 1343 #endif 1344