1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@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.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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#ifdef USE_NGHTTP2 28#include <stdint.h> 29#include <nghttp2/nghttp2.h> 30#include "urldata.h" 31#include "bufq.h" 32#include "http1.h" 33#include "http2.h" 34#include "http.h" 35#include "sendf.h" 36#include "select.h" 37#include "curl_base64.h" 38#include "strcase.h" 39#include "multiif.h" 40#include "url.h" 41#include "urlapi-int.h" 42#include "cfilters.h" 43#include "connect.h" 44#include "rand.h" 45#include "strtoofft.h" 46#include "strdup.h" 47#include "transfer.h" 48#include "dynbuf.h" 49#include "headers.h" 50/* The last 3 #include files should be in this order */ 51#include "curl_printf.h" 52#include "curl_memory.h" 53#include "memdebug.h" 54 55#if (NGHTTP2_VERSION_NUM < 0x010c00) 56#error too old nghttp2 version, upgrade! 57#endif 58 59#ifdef CURL_DISABLE_VERBOSE_STRINGS 60#define nghttp2_session_callbacks_set_error_callback(x,y) 61#endif 62 63#if (NGHTTP2_VERSION_NUM >= 0x010c00) 64#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 65#endif 66 67 68/* buffer dimensioning: 69 * use 16K as chunk size, as that fits H2 DATA frames well */ 70#define H2_CHUNK_SIZE (16 * 1024) 71/* this is how much we want "in flight" for a stream */ 72#define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024) 73/* on receiving from TLS, we prep for holding a full stream window */ 74#define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) 75/* on send into TLS, we just want to accumulate small frames */ 76#define H2_NW_SEND_CHUNKS 1 77/* stream recv/send chunks are a result of window / chunk sizes */ 78#define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) 79/* keep smaller stream upload buffer (default h2 window size) to have 80 * our progress bars and "upload done" reporting closer to reality */ 81#define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE) 82/* spare chunks we keep for a full window */ 83#define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) 84 85/* We need to accommodate the max number of streams with their window 86 * sizes on the overall connection. Streams might become PAUSED which 87 * will block their received QUOTA in the connection window. And if we 88 * run out of space, the server is blocked from sending us any data. 89 * See #10988 for an issue with this. */ 90#define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE) 91 92#define H2_SETTINGS_IV_LEN 3 93#define H2_BINSETTINGS_LEN 80 94 95static int populate_settings(nghttp2_settings_entry *iv, 96 struct Curl_easy *data) 97{ 98 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; 99 iv[0].value = Curl_multi_max_concurrent_streams(data->multi); 100 101 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 102 iv[1].value = H2_STREAM_WINDOW_SIZE; 103 104 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 105 iv[2].value = data->multi->push_cb != NULL; 106 107 return 3; 108} 109 110static ssize_t populate_binsettings(uint8_t *binsettings, 111 struct Curl_easy *data) 112{ 113 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; 114 int ivlen; 115 116 ivlen = populate_settings(iv, data); 117 /* this returns number of bytes it wrote or a negative number on error. */ 118 return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, 119 iv, ivlen); 120} 121 122struct cf_h2_ctx { 123 nghttp2_session *h2; 124 uint32_t max_concurrent_streams; 125 /* The easy handle used in the current filter call, cleared at return */ 126 struct cf_call_data call_data; 127 128 struct bufq inbufq; /* network input */ 129 struct bufq outbufq; /* network output */ 130 struct bufc_pool stream_bufcp; /* spares for stream buffers */ 131 132 size_t drain_total; /* sum of all stream's UrlState drain */ 133 int32_t goaway_error; 134 int32_t last_stream_id; 135 BIT(conn_closed); 136 BIT(goaway); 137 BIT(enable_push); 138 BIT(nw_out_blocked); 139}; 140 141/* How to access `call_data` from a cf_h2 filter */ 142#undef CF_CTX_CALL_DATA 143#define CF_CTX_CALL_DATA(cf) \ 144 ((struct cf_h2_ctx *)(cf)->ctx)->call_data 145 146static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx) 147{ 148 struct cf_call_data save = ctx->call_data; 149 150 if(ctx->h2) { 151 nghttp2_session_del(ctx->h2); 152 } 153 Curl_bufq_free(&ctx->inbufq); 154 Curl_bufq_free(&ctx->outbufq); 155 Curl_bufcp_free(&ctx->stream_bufcp); 156 memset(ctx, 0, sizeof(*ctx)); 157 ctx->call_data = save; 158} 159 160static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) 161{ 162 if(ctx) { 163 cf_h2_ctx_clear(ctx); 164 free(ctx); 165 } 166} 167 168static CURLcode h2_progress_egress(struct Curl_cfilter *cf, 169 struct Curl_easy *data); 170 171/** 172 * All about the H3 internals of a stream 173 */ 174struct stream_ctx { 175 /*********** for HTTP/2 we store stream-local data here *************/ 176 int32_t id; /* HTTP/2 protocol identifier for stream */ 177 struct bufq recvbuf; /* response buffer */ 178 struct bufq sendbuf; /* request buffer */ 179 struct h1_req_parser h1; /* parsing the request */ 180 struct dynhds resp_trailers; /* response trailer fields */ 181 size_t resp_hds_len; /* amount of response header bytes in recvbuf */ 182 size_t upload_blocked_len; 183 curl_off_t upload_left; /* number of request bytes left to upload */ 184 185 char **push_headers; /* allocated array */ 186 size_t push_headers_used; /* number of entries filled in */ 187 size_t push_headers_alloc; /* number of entries allocated */ 188 189 int status_code; /* HTTP response status code */ 190 uint32_t error; /* stream error code */ 191 uint32_t local_window_size; /* the local recv window size */ 192 bool resp_hds_complete; /* we have a complete, final response */ 193 bool closed; /* TRUE on stream close */ 194 bool reset; /* TRUE on stream reset */ 195 bool close_handled; /* TRUE if stream closure is handled by libcurl */ 196 bool bodystarted; 197 bool send_closed; /* transfer is done sending, we might have still 198 buffered data in stream->sendbuf to upload. */ 199}; 200 201#define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \ 202 ((struct HTTP *)(d)->req.p.http)->h2_ctx \ 203 : NULL)) 204#define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx 205#define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \ 206 H2_STREAM_CTX(d)->id : -2) 207 208/* 209 * Mark this transfer to get "drained". 210 */ 211static void drain_stream(struct Curl_cfilter *cf, 212 struct Curl_easy *data, 213 struct stream_ctx *stream) 214{ 215 unsigned char bits; 216 217 (void)cf; 218 bits = CURL_CSELECT_IN; 219 if(!stream->send_closed && 220 (stream->upload_left || stream->upload_blocked_len)) 221 bits |= CURL_CSELECT_OUT; 222 if(data->state.select_bits != bits) { 223 CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", 224 stream->id, bits); 225 data->state.select_bits = bits; 226 Curl_expire(data, 0, EXPIRE_RUN_NOW); 227 } 228} 229 230static CURLcode http2_data_setup(struct Curl_cfilter *cf, 231 struct Curl_easy *data, 232 struct stream_ctx **pstream) 233{ 234 struct cf_h2_ctx *ctx = cf->ctx; 235 struct stream_ctx *stream; 236 237 (void)cf; 238 DEBUGASSERT(data); 239 if(!data->req.p.http) { 240 failf(data, "initialization failure, transfer not http initialized"); 241 return CURLE_FAILED_INIT; 242 } 243 stream = H2_STREAM_CTX(data); 244 if(stream) { 245 *pstream = stream; 246 return CURLE_OK; 247 } 248 249 stream = calloc(1, sizeof(*stream)); 250 if(!stream) 251 return CURLE_OUT_OF_MEMORY; 252 253 stream->id = -1; 254 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, 255 H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); 256 Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, 257 H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 258 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 259 Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST); 260 stream->resp_hds_len = 0; 261 stream->bodystarted = FALSE; 262 stream->status_code = -1; 263 stream->closed = FALSE; 264 stream->close_handled = FALSE; 265 stream->error = NGHTTP2_NO_ERROR; 266 stream->local_window_size = H2_STREAM_WINDOW_SIZE; 267 stream->upload_left = 0; 268 269 H2_STREAM_LCTX(data) = stream; 270 *pstream = stream; 271 return CURLE_OK; 272} 273 274static void free_push_headers(struct stream_ctx *stream) 275{ 276 size_t i; 277 for(i = 0; i<stream->push_headers_used; i++) 278 free(stream->push_headers[i]); 279 Curl_safefree(stream->push_headers); 280 stream->push_headers_used = 0; 281} 282 283static void http2_data_done(struct Curl_cfilter *cf, 284 struct Curl_easy *data, bool premature) 285{ 286 struct cf_h2_ctx *ctx = cf->ctx; 287 struct stream_ctx *stream = H2_STREAM_CTX(data); 288 289 DEBUGASSERT(ctx); 290 (void)premature; 291 if(!stream) 292 return; 293 294 if(ctx->h2) { 295 bool flush_egress = FALSE; 296 /* returns error if stream not known, which is fine here */ 297 (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL); 298 299 if(!stream->closed && stream->id > 0) { 300 /* RST_STREAM */ 301 CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream", 302 stream->id); 303 stream->closed = TRUE; 304 stream->reset = TRUE; 305 stream->send_closed = TRUE; 306 nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, 307 stream->id, NGHTTP2_STREAM_CLOSED); 308 flush_egress = TRUE; 309 } 310 if(!Curl_bufq_is_empty(&stream->recvbuf)) { 311 /* Anything in the recvbuf is still being counted 312 * in stream and connection window flow control. Need 313 * to free that space or the connection window might get 314 * exhausted eventually. */ 315 nghttp2_session_consume(ctx->h2, stream->id, 316 Curl_bufq_len(&stream->recvbuf)); 317 /* give WINDOW_UPATE a chance to be sent, but ignore any error */ 318 flush_egress = TRUE; 319 } 320 321 if(flush_egress) 322 nghttp2_session_send(ctx->h2); 323 } 324 325 Curl_bufq_free(&stream->sendbuf); 326 Curl_bufq_free(&stream->recvbuf); 327 Curl_h1_req_parse_free(&stream->h1); 328 Curl_dynhds_free(&stream->resp_trailers); 329 free_push_headers(stream); 330 free(stream); 331 H2_STREAM_LCTX(data) = NULL; 332} 333 334static int h2_client_new(struct Curl_cfilter *cf, 335 nghttp2_session_callbacks *cbs) 336{ 337 struct cf_h2_ctx *ctx = cf->ctx; 338 nghttp2_option *o; 339 340 int rc = nghttp2_option_new(&o); 341 if(rc) 342 return rc; 343 /* We handle window updates ourself to enforce buffer limits */ 344 nghttp2_option_set_no_auto_window_update(o, 1); 345#if NGHTTP2_VERSION_NUM >= 0x013200 346 /* with 1.50.0 */ 347 /* turn off RFC 9113 leading and trailing white spaces validation against 348 HTTP field value. */ 349 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); 350#endif 351 rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); 352 nghttp2_option_del(o); 353 return rc; 354} 355 356static ssize_t nw_in_reader(void *reader_ctx, 357 unsigned char *buf, size_t buflen, 358 CURLcode *err) 359{ 360 struct Curl_cfilter *cf = reader_ctx; 361 struct Curl_easy *data = CF_DATA_CURRENT(cf); 362 363 return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); 364} 365 366static ssize_t nw_out_writer(void *writer_ctx, 367 const unsigned char *buf, size_t buflen, 368 CURLcode *err) 369{ 370 struct Curl_cfilter *cf = writer_ctx; 371 struct Curl_easy *data = CF_DATA_CURRENT(cf); 372 373 if(data) { 374 ssize_t nwritten = Curl_conn_cf_send(cf->next, data, 375 (const char *)buf, buflen, err); 376 if(nwritten > 0) 377 CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); 378 return nwritten; 379 } 380 return 0; 381} 382 383static ssize_t send_callback(nghttp2_session *h2, 384 const uint8_t *mem, size_t length, int flags, 385 void *userp); 386static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 387 void *userp); 388#ifndef CURL_DISABLE_VERBOSE_STRINGS 389static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, 390 void *userp); 391#endif 392static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, 393 int32_t stream_id, 394 const uint8_t *mem, size_t len, void *userp); 395static int on_stream_close(nghttp2_session *session, int32_t stream_id, 396 uint32_t error_code, void *userp); 397static int on_begin_headers(nghttp2_session *session, 398 const nghttp2_frame *frame, void *userp); 399static int on_header(nghttp2_session *session, const nghttp2_frame *frame, 400 const uint8_t *name, size_t namelen, 401 const uint8_t *value, size_t valuelen, 402 uint8_t flags, 403 void *userp); 404static int error_callback(nghttp2_session *session, const char *msg, 405 size_t len, void *userp); 406 407/* 408 * Initialize the cfilter context 409 */ 410static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf, 411 struct Curl_easy *data, 412 bool via_h1_upgrade) 413{ 414 struct cf_h2_ctx *ctx = cf->ctx; 415 struct stream_ctx *stream; 416 CURLcode result = CURLE_OUT_OF_MEMORY; 417 int rc; 418 nghttp2_session_callbacks *cbs = NULL; 419 420 DEBUGASSERT(!ctx->h2); 421 Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); 422 Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); 423 Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); 424 ctx->last_stream_id = 2147483647; 425 426 rc = nghttp2_session_callbacks_new(&cbs); 427 if(rc) { 428 failf(data, "Couldn't initialize nghttp2 callbacks"); 429 goto out; 430 } 431 432 nghttp2_session_callbacks_set_send_callback(cbs, send_callback); 433 nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv); 434#ifndef CURL_DISABLE_VERBOSE_STRINGS 435 nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send); 436#endif 437 nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 438 cbs, on_data_chunk_recv); 439 nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close); 440 nghttp2_session_callbacks_set_on_begin_headers_callback( 441 cbs, on_begin_headers); 442 nghttp2_session_callbacks_set_on_header_callback(cbs, on_header); 443 nghttp2_session_callbacks_set_error_callback(cbs, error_callback); 444 445 /* The nghttp2 session is not yet setup, do it */ 446 rc = h2_client_new(cf, cbs); 447 if(rc) { 448 failf(data, "Couldn't initialize nghttp2"); 449 goto out; 450 } 451 ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS; 452 453 if(via_h1_upgrade) { 454 /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted 455 * in the H1 request and we upgrade from there. This stream 456 * is opened implicitly as #1. */ 457 uint8_t binsettings[H2_BINSETTINGS_LEN]; 458 ssize_t binlen; /* length of the binsettings data */ 459 460 binlen = populate_binsettings(binsettings, data); 461 if(binlen <= 0) { 462 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); 463 result = CURLE_FAILED_INIT; 464 goto out; 465 } 466 467 result = http2_data_setup(cf, data, &stream); 468 if(result) 469 goto out; 470 DEBUGASSERT(stream); 471 stream->id = 1; 472 /* queue SETTINGS frame (again) */ 473 rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen, 474 data->state.httpreq == HTTPREQ_HEAD, 475 NULL); 476 if(rc) { 477 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", 478 nghttp2_strerror(rc), rc); 479 result = CURLE_HTTP2; 480 goto out; 481 } 482 483 rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, 484 data); 485 if(rc) { 486 infof(data, "http/2: failed to set user_data for stream %u", 487 stream->id); 488 DEBUGASSERT(0); 489 } 490 CURL_TRC_CF(data, cf, "created session via Upgrade"); 491 } 492 else { 493 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; 494 int ivlen; 495 496 ivlen = populate_settings(iv, data); 497 rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, 498 iv, ivlen); 499 if(rc) { 500 failf(data, "nghttp2_submit_settings() failed: %s(%d)", 501 nghttp2_strerror(rc), rc); 502 result = CURLE_HTTP2; 503 goto out; 504 } 505 } 506 507 rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, 508 HTTP2_HUGE_WINDOW_SIZE); 509 if(rc) { 510 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", 511 nghttp2_strerror(rc), rc); 512 result = CURLE_HTTP2; 513 goto out; 514 } 515 516 /* all set, traffic will be send on connect */ 517 result = CURLE_OK; 518 CURL_TRC_CF(data, cf, "[0] created h2 session%s", 519 via_h1_upgrade? " (via h1 upgrade)" : ""); 520 521out: 522 if(cbs) 523 nghttp2_session_callbacks_del(cbs); 524 return result; 525} 526 527/* 528 * Returns nonzero if current HTTP/2 session should be closed. 529 */ 530static int should_close_session(struct cf_h2_ctx *ctx) 531{ 532 return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) && 533 !nghttp2_session_want_write(ctx->h2); 534} 535 536/* 537 * Processes pending input left in network input buffer. 538 * This function returns 0 if it succeeds, or -1 and error code will 539 * be assigned to *err. 540 */ 541static int h2_process_pending_input(struct Curl_cfilter *cf, 542 struct Curl_easy *data, 543 CURLcode *err) 544{ 545 struct cf_h2_ctx *ctx = cf->ctx; 546 const unsigned char *buf; 547 size_t blen; 548 ssize_t rv; 549 550 while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { 551 552 rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); 553 if(rv < 0) { 554 failf(data, 555 "process_pending_input: nghttp2_session_mem_recv() returned " 556 "%zd:%s", rv, nghttp2_strerror((int)rv)); 557 *err = CURLE_RECV_ERROR; 558 return -1; 559 } 560 Curl_bufq_skip(&ctx->inbufq, (size_t)rv); 561 if(Curl_bufq_is_empty(&ctx->inbufq)) { 562 break; 563 } 564 else { 565 CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left " 566 "in connection buffer", Curl_bufq_len(&ctx->inbufq)); 567 } 568 } 569 570 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { 571 /* No more requests are allowed in the current session, so 572 the connection may not be reused. This is set when a 573 GOAWAY frame has been received or when the limit of stream 574 identifiers has been reached. */ 575 connclose(cf->conn, "http/2: No new requests allowed"); 576 } 577 578 return 0; 579} 580 581/* 582 * The server may send us data at any point (e.g. PING frames). Therefore, 583 * we cannot assume that an HTTP/2 socket is dead just because it is readable. 584 * 585 * Check the lower filters first and, if successful, peek at the socket 586 * and distinguish between closed and data. 587 */ 588static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, 589 bool *input_pending) 590{ 591 struct cf_h2_ctx *ctx = cf->ctx; 592 bool alive = TRUE; 593 594 *input_pending = FALSE; 595 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 596 return FALSE; 597 598 if(*input_pending) { 599 /* This happens before we've sent off a request and the connection is 600 not in use by any other transfer, there shouldn't be any data here, 601 only "protocol frames" */ 602 CURLcode result; 603 ssize_t nread = -1; 604 605 *input_pending = FALSE; 606 nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); 607 if(nread != -1) { 608 CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying " 609 "h2 connection", nread); 610 if(h2_process_pending_input(cf, data, &result) < 0) 611 /* immediate error, considered dead */ 612 alive = FALSE; 613 else { 614 alive = !should_close_session(ctx); 615 } 616 } 617 else if(result != CURLE_AGAIN) { 618 /* the read failed so let's say this is dead anyway */ 619 alive = FALSE; 620 } 621 } 622 623 return alive; 624} 625 626static CURLcode http2_send_ping(struct Curl_cfilter *cf, 627 struct Curl_easy *data) 628{ 629 struct cf_h2_ctx *ctx = cf->ctx; 630 int rc; 631 632 rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL); 633 if(rc) { 634 failf(data, "nghttp2_submit_ping() failed: %s(%d)", 635 nghttp2_strerror(rc), rc); 636 return CURLE_HTTP2; 637 } 638 639 rc = nghttp2_session_send(ctx->h2); 640 if(rc) { 641 failf(data, "nghttp2_session_send() failed: %s(%d)", 642 nghttp2_strerror(rc), rc); 643 return CURLE_SEND_ERROR; 644 } 645 return CURLE_OK; 646} 647 648/* 649 * Store nghttp2 version info in this buffer. 650 */ 651void Curl_http2_ver(char *p, size_t len) 652{ 653 nghttp2_info *h2 = nghttp2_version(0); 654 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str); 655} 656 657static CURLcode nw_out_flush(struct Curl_cfilter *cf, 658 struct Curl_easy *data) 659{ 660 struct cf_h2_ctx *ctx = cf->ctx; 661 ssize_t nwritten; 662 CURLcode result; 663 664 (void)data; 665 if(Curl_bufq_is_empty(&ctx->outbufq)) 666 return CURLE_OK; 667 668 nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); 669 if(nwritten < 0) { 670 if(result == CURLE_AGAIN) { 671 CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", 672 Curl_bufq_len(&ctx->outbufq)); 673 ctx->nw_out_blocked = 1; 674 } 675 return result; 676 } 677 return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN; 678} 679 680/* 681 * The implementation of nghttp2_send_callback type. Here we write |data| with 682 * size |length| to the network and return the number of bytes actually 683 * written. See the documentation of nghttp2_send_callback for the details. 684 */ 685static ssize_t send_callback(nghttp2_session *h2, 686 const uint8_t *buf, size_t blen, int flags, 687 void *userp) 688{ 689 struct Curl_cfilter *cf = userp; 690 struct cf_h2_ctx *ctx = cf->ctx; 691 struct Curl_easy *data = CF_DATA_CURRENT(cf); 692 ssize_t nwritten; 693 CURLcode result = CURLE_OK; 694 695 (void)h2; 696 (void)flags; 697 DEBUGASSERT(data); 698 699 nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, 700 nw_out_writer, cf, &result); 701 if(nwritten < 0) { 702 if(result == CURLE_AGAIN) { 703 ctx->nw_out_blocked = 1; 704 return NGHTTP2_ERR_WOULDBLOCK; 705 } 706 failf(data, "Failed sending HTTP2 data"); 707 return NGHTTP2_ERR_CALLBACK_FAILURE; 708 } 709 710 if(!nwritten) { 711 ctx->nw_out_blocked = 1; 712 return NGHTTP2_ERR_WOULDBLOCK; 713 } 714 return nwritten; 715} 716 717 718/* We pass a pointer to this struct in the push callback, but the contents of 719 the struct are hidden from the user. */ 720struct curl_pushheaders { 721 struct Curl_easy *data; 722 const nghttp2_push_promise *frame; 723}; 724 725/* 726 * push header access function. Only to be used from within the push callback 727 */ 728char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) 729{ 730 /* Verify that we got a good easy handle in the push header struct, mostly to 731 detect rubbish input fast(er). */ 732 if(!h || !GOOD_EASY_HANDLE(h->data)) 733 return NULL; 734 else { 735 struct stream_ctx *stream = H2_STREAM_CTX(h->data); 736 if(stream && num < stream->push_headers_used) 737 return stream->push_headers[num]; 738 } 739 return NULL; 740} 741 742/* 743 * push header access function. Only to be used from within the push callback 744 */ 745char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) 746{ 747 struct stream_ctx *stream; 748 size_t len; 749 size_t i; 750 /* Verify that we got a good easy handle in the push header struct, 751 mostly to detect rubbish input fast(er). Also empty header name 752 is just a rubbish too. We have to allow ":" at the beginning of 753 the header, but header == ":" must be rejected. If we have ':' in 754 the middle of header, it could be matched in middle of the value, 755 this is because we do prefix match.*/ 756 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] || 757 !strcmp(header, ":") || strchr(header + 1, ':')) 758 return NULL; 759 760 stream = H2_STREAM_CTX(h->data); 761 if(!stream) 762 return NULL; 763 764 len = strlen(header); 765 for(i = 0; i<stream->push_headers_used; i++) { 766 if(!strncmp(header, stream->push_headers[i], len)) { 767 /* sub-match, make sure that it is followed by a colon */ 768 if(stream->push_headers[i][len] != ':') 769 continue; 770 return &stream->push_headers[i][len + 1]; 771 } 772 } 773 return NULL; 774} 775 776static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, 777 struct Curl_easy *data) 778{ 779 struct Curl_easy *second = curl_easy_duphandle(data); 780 if(second) { 781 /* setup the request struct */ 782 struct HTTP *http = calloc(1, sizeof(struct HTTP)); 783 if(!http) { 784 (void)Curl_close(&second); 785 } 786 else { 787 struct stream_ctx *second_stream; 788 789 second->req.p.http = http; 790 http2_data_setup(cf, second, &second_stream); 791 second->state.priority.weight = data->state.priority.weight; 792 } 793 } 794 return second; 795} 796 797static int set_transfer_url(struct Curl_easy *data, 798 struct curl_pushheaders *hp) 799{ 800 const char *v; 801 CURLUcode uc; 802 char *url = NULL; 803 int rc = 0; 804 CURLU *u = curl_url(); 805 806 if(!u) 807 return 5; 808 809 v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME); 810 if(v) { 811 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); 812 if(uc) { 813 rc = 1; 814 goto fail; 815 } 816 } 817 818 v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY); 819 if(v) { 820 uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER); 821 if(uc) { 822 rc = 2; 823 goto fail; 824 } 825 } 826 827 v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH); 828 if(v) { 829 uc = curl_url_set(u, CURLUPART_PATH, v, 0); 830 if(uc) { 831 rc = 3; 832 goto fail; 833 } 834 } 835 836 uc = curl_url_get(u, CURLUPART_URL, &url, 0); 837 if(uc) 838 rc = 4; 839fail: 840 curl_url_cleanup(u); 841 if(rc) 842 return rc; 843 844 if(data->state.url_alloc) 845 free(data->state.url); 846 data->state.url_alloc = TRUE; 847 data->state.url = url; 848 return 0; 849} 850 851static void discard_newhandle(struct Curl_cfilter *cf, 852 struct Curl_easy *newhandle) 853{ 854 if(!newhandle->req.p.http) { 855 http2_data_done(cf, newhandle, TRUE); 856 newhandle->req.p.http = NULL; 857 } 858 (void)Curl_close(&newhandle); 859} 860 861static int push_promise(struct Curl_cfilter *cf, 862 struct Curl_easy *data, 863 const nghttp2_push_promise *frame) 864{ 865 struct cf_h2_ctx *ctx = cf->ctx; 866 int rv; /* one of the CURL_PUSH_* defines */ 867 868 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received", 869 frame->promised_stream_id); 870 if(data->multi->push_cb) { 871 struct stream_ctx *stream; 872 struct stream_ctx *newstream; 873 struct curl_pushheaders heads; 874 CURLMcode rc; 875 CURLcode result; 876 /* clone the parent */ 877 struct Curl_easy *newhandle = h2_duphandle(cf, data); 878 if(!newhandle) { 879 infof(data, "failed to duplicate handle"); 880 rv = CURL_PUSH_DENY; /* FAIL HARD */ 881 goto fail; 882 } 883 884 heads.data = data; 885 heads.frame = frame; 886 /* ask the application */ 887 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application"); 888 889 stream = H2_STREAM_CTX(data); 890 if(!stream) { 891 failf(data, "Internal NULL stream"); 892 discard_newhandle(cf, newhandle); 893 rv = CURL_PUSH_DENY; 894 goto fail; 895 } 896 897 rv = set_transfer_url(newhandle, &heads); 898 if(rv) { 899 discard_newhandle(cf, newhandle); 900 rv = CURL_PUSH_DENY; 901 goto fail; 902 } 903 904 result = http2_data_setup(cf, newhandle, &newstream); 905 if(result) { 906 failf(data, "error setting up stream: %d", result); 907 discard_newhandle(cf, newhandle); 908 rv = CURL_PUSH_DENY; 909 goto fail; 910 } 911 DEBUGASSERT(stream); 912 913 Curl_set_in_callback(data, true); 914 rv = data->multi->push_cb(data, newhandle, 915 stream->push_headers_used, &heads, 916 data->multi->push_userp); 917 Curl_set_in_callback(data, false); 918 919 /* free the headers again */ 920 free_push_headers(stream); 921 922 if(rv) { 923 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); 924 /* denied, kill off the new handle again */ 925 discard_newhandle(cf, newhandle); 926 goto fail; 927 } 928 929 newstream->id = frame->promised_stream_id; 930 newhandle->req.maxdownload = -1; 931 newhandle->req.size = -1; 932 933 /* approved, add to the multi handle and immediately switch to PERFORM 934 state with the given connection !*/ 935 rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn); 936 if(rc) { 937 infof(data, "failed to add handle to multi"); 938 discard_newhandle(cf, newhandle); 939 rv = CURL_PUSH_DENY; 940 goto fail; 941 } 942 943 rv = nghttp2_session_set_stream_user_data(ctx->h2, 944 newstream->id, 945 newhandle); 946 if(rv) { 947 infof(data, "failed to set user_data for stream %u", 948 newstream->id); 949 DEBUGASSERT(0); 950 rv = CURL_PUSH_DENY; 951 goto fail; 952 } 953 } 954 else { 955 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it"); 956 rv = CURL_PUSH_DENY; 957 } 958fail: 959 return rv; 960} 961 962static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf, 963 struct Curl_easy *data, 964 const char *buf, size_t blen) 965{ 966 struct stream_ctx *stream = H2_STREAM_CTX(data); 967 ssize_t nwritten; 968 CURLcode result; 969 970 (void)cf; 971 nwritten = Curl_bufq_write(&stream->recvbuf, 972 (const unsigned char *)buf, blen, &result); 973 if(nwritten < 0) 974 return result; 975 stream->resp_hds_len += (size_t)nwritten; 976 DEBUGASSERT((size_t)nwritten == blen); 977 return CURLE_OK; 978} 979 980static CURLcode on_stream_frame(struct Curl_cfilter *cf, 981 struct Curl_easy *data, 982 const nghttp2_frame *frame) 983{ 984 struct cf_h2_ctx *ctx = cf->ctx; 985 struct stream_ctx *stream = H2_STREAM_CTX(data); 986 int32_t stream_id = frame->hd.stream_id; 987 CURLcode result; 988 size_t rbuflen; 989 int rv; 990 991 if(!stream) { 992 CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id); 993 return CURLE_FAILED_INIT; 994 } 995 996 switch(frame->hd.type) { 997 case NGHTTP2_DATA: 998 rbuflen = Curl_bufq_len(&stream->recvbuf); 999 CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d", 1000 stream_id, rbuflen, 1001 nghttp2_session_get_stream_effective_recv_data_length( 1002 ctx->h2, stream->id), 1003 nghttp2_session_get_stream_effective_local_window_size( 1004 ctx->h2, stream->id)); 1005 /* If !body started on this stream, then receiving DATA is illegal. */ 1006 if(!stream->bodystarted) { 1007 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, 1008 stream_id, NGHTTP2_PROTOCOL_ERROR); 1009 1010 if(nghttp2_is_fatal(rv)) { 1011 return CURLE_RECV_ERROR; 1012 } 1013 } 1014 if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { 1015 drain_stream(cf, data, stream); 1016 } 1017 else if(rbuflen > stream->local_window_size) { 1018 int32_t wsize = nghttp2_session_get_stream_local_window_size( 1019 ctx->h2, stream->id); 1020 if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) { 1021 /* H2 flow control is not absolute, as the server might not have the 1022 * same view, yet. When we receive more than we want, we enforce 1023 * the local window size again to make nghttp2 send WINDOW_UPATEs 1024 * accordingly. */ 1025 nghttp2_session_set_local_window_size(ctx->h2, 1026 NGHTTP2_FLAG_NONE, 1027 stream->id, 1028 stream->local_window_size); 1029 } 1030 } 1031 break; 1032 case NGHTTP2_HEADERS: 1033 if(stream->bodystarted) { 1034 /* Only valid HEADERS after body started is trailer HEADERS. We 1035 buffer them in on_header callback. */ 1036 break; 1037 } 1038 1039 /* nghttp2 guarantees that :status is received, and we store it to 1040 stream->status_code. Fuzzing has proven this can still be reached 1041 without status code having been set. */ 1042 if(stream->status_code == -1) 1043 return CURLE_RECV_ERROR; 1044 1045 /* Only final status code signals the end of header */ 1046 if(stream->status_code / 100 != 1) { 1047 stream->bodystarted = TRUE; 1048 stream->status_code = -1; 1049 } 1050 1051 result = recvbuf_write_hds(cf, data, STRCONST("\r\n")); 1052 if(result) 1053 return result; 1054 1055 if(stream->status_code / 100 != 1) { 1056 stream->resp_hds_complete = TRUE; 1057 } 1058 drain_stream(cf, data, stream); 1059 break; 1060 case NGHTTP2_PUSH_PROMISE: 1061 rv = push_promise(cf, data, &frame->push_promise); 1062 if(rv) { /* deny! */ 1063 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT)); 1064 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, 1065 frame->push_promise.promised_stream_id, 1066 NGHTTP2_CANCEL); 1067 if(nghttp2_is_fatal(rv)) 1068 return CURLE_SEND_ERROR; 1069 else if(rv == CURL_PUSH_ERROROUT) { 1070 CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received", 1071 stream_id); 1072 return CURLE_RECV_ERROR; 1073 } 1074 } 1075 break; 1076 case NGHTTP2_RST_STREAM: 1077 stream->closed = TRUE; 1078 if(frame->rst_stream.error_code) { 1079 stream->reset = TRUE; 1080 } 1081 stream->send_closed = TRUE; 1082 drain_stream(cf, data, stream); 1083 break; 1084 case NGHTTP2_WINDOW_UPDATE: 1085 if(CURL_WANT_SEND(data)) { 1086 drain_stream(cf, data, stream); 1087 } 1088 break; 1089 default: 1090 break; 1091 } 1092 return CURLE_OK; 1093} 1094 1095#ifndef CURL_DISABLE_VERBOSE_STRINGS 1096static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen) 1097{ 1098 switch(frame->hd.type) { 1099 case NGHTTP2_DATA: { 1100 return msnprintf(buffer, blen, 1101 "FRAME[DATA, len=%d, eos=%d, padlen=%d]", 1102 (int)frame->hd.length, 1103 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), 1104 (int)frame->data.padlen); 1105 } 1106 case NGHTTP2_HEADERS: { 1107 return msnprintf(buffer, blen, 1108 "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", 1109 (int)frame->hd.length, 1110 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), 1111 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); 1112 } 1113 case NGHTTP2_PRIORITY: { 1114 return msnprintf(buffer, blen, 1115 "FRAME[PRIORITY, len=%d, flags=%d]", 1116 (int)frame->hd.length, frame->hd.flags); 1117 } 1118 case NGHTTP2_RST_STREAM: { 1119 return msnprintf(buffer, blen, 1120 "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", 1121 (int)frame->hd.length, frame->hd.flags, 1122 frame->rst_stream.error_code); 1123 } 1124 case NGHTTP2_SETTINGS: { 1125 if(frame->hd.flags & NGHTTP2_FLAG_ACK) { 1126 return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); 1127 } 1128 return msnprintf(buffer, blen, 1129 "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); 1130 } 1131 case NGHTTP2_PUSH_PROMISE: { 1132 return msnprintf(buffer, blen, 1133 "FRAME[PUSH_PROMISE, len=%d, hend=%d]", 1134 (int)frame->hd.length, 1135 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); 1136 } 1137 case NGHTTP2_PING: { 1138 return msnprintf(buffer, blen, 1139 "FRAME[PING, len=%d, ack=%d]", 1140 (int)frame->hd.length, 1141 frame->hd.flags&NGHTTP2_FLAG_ACK); 1142 } 1143 case NGHTTP2_GOAWAY: { 1144 char scratch[128]; 1145 size_t s_len = sizeof(scratch)/sizeof(scratch[0]); 1146 size_t len = (frame->goaway.opaque_data_len < s_len)? 1147 frame->goaway.opaque_data_len : s_len-1; 1148 if(len) 1149 memcpy(scratch, frame->goaway.opaque_data, len); 1150 scratch[len] = '\0'; 1151 return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " 1152 "last_stream=%d]", frame->goaway.error_code, 1153 scratch, frame->goaway.last_stream_id); 1154 } 1155 case NGHTTP2_WINDOW_UPDATE: { 1156 return msnprintf(buffer, blen, 1157 "FRAME[WINDOW_UPDATE, incr=%d]", 1158 frame->window_update.window_size_increment); 1159 } 1160 default: 1161 return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", 1162 frame->hd.type, (int)frame->hd.length, 1163 frame->hd.flags); 1164 } 1165} 1166 1167static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame, 1168 void *userp) 1169{ 1170 struct Curl_cfilter *cf = userp; 1171 struct Curl_easy *data = CF_DATA_CURRENT(cf); 1172 1173 (void)session; 1174 DEBUGASSERT(data); 1175 if(data && Curl_trc_cf_is_verbose(cf, data)) { 1176 char buffer[256]; 1177 int len; 1178 len = fr_print(frame, buffer, sizeof(buffer)-1); 1179 buffer[len] = 0; 1180 CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); 1181 } 1182 return 0; 1183} 1184#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 1185 1186static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, 1187 void *userp) 1188{ 1189 struct Curl_cfilter *cf = userp; 1190 struct cf_h2_ctx *ctx = cf->ctx; 1191 struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s; 1192 int32_t stream_id = frame->hd.stream_id; 1193 1194 DEBUGASSERT(data); 1195#ifndef CURL_DISABLE_VERBOSE_STRINGS 1196 if(Curl_trc_cf_is_verbose(cf, data)) { 1197 char buffer[256]; 1198 int len; 1199 len = fr_print(frame, buffer, sizeof(buffer)-1); 1200 buffer[len] = 0; 1201 CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); 1202 } 1203#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 1204 1205 if(!stream_id) { 1206 /* stream ID zero is for connection-oriented stuff */ 1207 DEBUGASSERT(data); 1208 switch(frame->hd.type) { 1209 case NGHTTP2_SETTINGS: { 1210 if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) { 1211 uint32_t max_conn = ctx->max_concurrent_streams; 1212 ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( 1213 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); 1214 ctx->enable_push = nghttp2_session_get_remote_settings( 1215 session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; 1216 CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", 1217 ctx->max_concurrent_streams); 1218 CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", 1219 ctx->enable_push ? "TRUE" : "false"); 1220 if(data && max_conn != ctx->max_concurrent_streams) { 1221 /* only signal change if the value actually changed */ 1222 CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u", 1223 ctx->max_concurrent_streams); 1224 Curl_multi_connchanged(data->multi); 1225 } 1226 /* Since the initial stream window is 64K, a request might be on HOLD, 1227 * due to exhaustion. The (initial) SETTINGS may announce a much larger 1228 * window and *assume* that we treat this like a WINDOW_UPDATE. Some 1229 * servers send an explicit WINDOW_UPDATE, but not all seem to do that. 1230 * To be safe, we UNHOLD a stream in order not to stall. */ 1231 if(CURL_WANT_SEND(data)) { 1232 struct stream_ctx *stream = H2_STREAM_CTX(data); 1233 if(stream) 1234 drain_stream(cf, data, stream); 1235 } 1236 } 1237 break; 1238 } 1239 case NGHTTP2_GOAWAY: 1240 ctx->goaway = TRUE; 1241 ctx->goaway_error = frame->goaway.error_code; 1242 ctx->last_stream_id = frame->goaway.last_stream_id; 1243 if(data) { 1244 infof(data, "received GOAWAY, error=%d, last_stream=%u", 1245 ctx->goaway_error, ctx->last_stream_id); 1246 Curl_multi_connchanged(data->multi); 1247 } 1248 break; 1249 default: 1250 break; 1251 } 1252 return 0; 1253 } 1254 1255 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 1256 if(!data_s) { 1257 CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id); 1258 return 0; 1259 } 1260 1261 return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0; 1262} 1263 1264static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, 1265 int32_t stream_id, 1266 const uint8_t *mem, size_t len, void *userp) 1267{ 1268 struct Curl_cfilter *cf = userp; 1269 struct stream_ctx *stream; 1270 struct Curl_easy *data_s; 1271 ssize_t nwritten; 1272 CURLcode result; 1273 (void)flags; 1274 1275 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 1276 DEBUGASSERT(CF_DATA_CURRENT(cf)); 1277 1278 /* get the stream from the hash based on Stream ID */ 1279 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 1280 if(!data_s) { 1281 /* Receiving a Stream ID not in the hash should not happen - unless 1282 we have aborted a transfer artificially and there were more data 1283 in the pipeline. Silently ignore. */ 1284 CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", 1285 stream_id); 1286 /* consumed explicitly as no one will read it */ 1287 nghttp2_session_consume(session, stream_id, len); 1288 return 0; 1289 } 1290 1291 stream = H2_STREAM_CTX(data_s); 1292 if(!stream) 1293 return NGHTTP2_ERR_CALLBACK_FAILURE; 1294 1295 nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result); 1296 if(nwritten < 0) { 1297 if(result != CURLE_AGAIN) 1298 return NGHTTP2_ERR_CALLBACK_FAILURE; 1299 1300 nwritten = 0; 1301 } 1302 1303 /* if we receive data for another handle, wake that up */ 1304 drain_stream(cf, data_s, stream); 1305 1306 DEBUGASSERT((size_t)nwritten == len); 1307 return 0; 1308} 1309 1310static int on_stream_close(nghttp2_session *session, int32_t stream_id, 1311 uint32_t error_code, void *userp) 1312{ 1313 struct Curl_cfilter *cf = userp; 1314 struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf); 1315 struct stream_ctx *stream; 1316 int rv; 1317 (void)session; 1318 1319 DEBUGASSERT(call_data); 1320 /* get the stream from the hash based on Stream ID, stream ID zero is for 1321 connection-oriented stuff */ 1322 data_s = stream_id? 1323 nghttp2_session_get_stream_user_data(session, stream_id) : NULL; 1324 if(!data_s) { 1325 CURL_TRC_CF(call_data, cf, 1326 "[%d] on_stream_close, no easy set on stream", stream_id); 1327 return 0; 1328 } 1329 if(!GOOD_EASY_HANDLE(data_s)) { 1330 /* nghttp2 still has an easy registered for the stream which has 1331 * been freed be libcurl. This points to a code path that does not 1332 * trigger DONE or DETACH events as it must. */ 1333 CURL_TRC_CF(call_data, cf, 1334 "[%d] on_stream_close, not a GOOD easy on stream", stream_id); 1335 (void)nghttp2_session_set_stream_user_data(session, stream_id, 0); 1336 return NGHTTP2_ERR_CALLBACK_FAILURE; 1337 } 1338 stream = H2_STREAM_CTX(data_s); 1339 if(!stream) { 1340 CURL_TRC_CF(data_s, cf, 1341 "[%d] on_stream_close, GOOD easy but no stream", stream_id); 1342 return NGHTTP2_ERR_CALLBACK_FAILURE; 1343 } 1344 1345 stream->closed = TRUE; 1346 stream->error = error_code; 1347 if(stream->error) { 1348 stream->reset = TRUE; 1349 stream->send_closed = TRUE; 1350 } 1351 1352 if(stream->error) 1353 CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", 1354 stream_id, nghttp2_http2_strerror(error_code), error_code); 1355 else 1356 CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); 1357 drain_stream(cf, data_s, stream); 1358 1359 /* remove `data_s` from the nghttp2 stream */ 1360 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); 1361 if(rv) { 1362 infof(data_s, "http/2: failed to clear user_data for stream %u", 1363 stream_id); 1364 DEBUGASSERT(0); 1365 } 1366 return 0; 1367} 1368 1369static int on_begin_headers(nghttp2_session *session, 1370 const nghttp2_frame *frame, void *userp) 1371{ 1372 struct Curl_cfilter *cf = userp; 1373 struct stream_ctx *stream; 1374 struct Curl_easy *data_s = NULL; 1375 1376 (void)cf; 1377 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 1378 if(!data_s) { 1379 return 0; 1380 } 1381 1382 if(frame->hd.type != NGHTTP2_HEADERS) { 1383 return 0; 1384 } 1385 1386 stream = H2_STREAM_CTX(data_s); 1387 if(!stream || !stream->bodystarted) { 1388 return 0; 1389 } 1390 1391 return 0; 1392} 1393 1394/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */ 1395static int on_header(nghttp2_session *session, const nghttp2_frame *frame, 1396 const uint8_t *name, size_t namelen, 1397 const uint8_t *value, size_t valuelen, 1398 uint8_t flags, 1399 void *userp) 1400{ 1401 struct Curl_cfilter *cf = userp; 1402 struct stream_ctx *stream; 1403 struct Curl_easy *data_s; 1404 int32_t stream_id = frame->hd.stream_id; 1405 CURLcode result; 1406 (void)flags; 1407 1408 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 1409 1410 /* get the stream from the hash based on Stream ID */ 1411 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 1412 if(!data_s) 1413 /* Receiving a Stream ID not in the hash should not happen, this is an 1414 internal error more than anything else! */ 1415 return NGHTTP2_ERR_CALLBACK_FAILURE; 1416 1417 stream = H2_STREAM_CTX(data_s); 1418 if(!stream) { 1419 failf(data_s, "Internal NULL stream"); 1420 return NGHTTP2_ERR_CALLBACK_FAILURE; 1421 } 1422 1423 /* Store received PUSH_PROMISE headers to be used when the subsequent 1424 PUSH_PROMISE callback comes */ 1425 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { 1426 char *h; 1427 1428 if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) { 1429 /* pseudo headers are lower case */ 1430 int rc = 0; 1431 char *check = aprintf("%s:%d", cf->conn->host.name, 1432 cf->conn->remote_port); 1433 if(!check) 1434 /* no memory */ 1435 return NGHTTP2_ERR_CALLBACK_FAILURE; 1436 if(!strcasecompare(check, (const char *)value) && 1437 ((cf->conn->remote_port != cf->conn->given->defport) || 1438 !strcasecompare(cf->conn->host.name, (const char *)value))) { 1439 /* This is push is not for the same authority that was asked for in 1440 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a 1441 * PUSH_PROMISE for which the server is not authoritative as a stream 1442 * error of type PROTOCOL_ERROR." 1443 */ 1444 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 1445 stream_id, NGHTTP2_PROTOCOL_ERROR); 1446 rc = NGHTTP2_ERR_CALLBACK_FAILURE; 1447 } 1448 free(check); 1449 if(rc) 1450 return rc; 1451 } 1452 1453 if(!stream->push_headers) { 1454 stream->push_headers_alloc = 10; 1455 stream->push_headers = malloc(stream->push_headers_alloc * 1456 sizeof(char *)); 1457 if(!stream->push_headers) 1458 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 1459 stream->push_headers_used = 0; 1460 } 1461 else if(stream->push_headers_used == 1462 stream->push_headers_alloc) { 1463 char **headp; 1464 if(stream->push_headers_alloc > 1000) { 1465 /* this is beyond crazy many headers, bail out */ 1466 failf(data_s, "Too many PUSH_PROMISE headers"); 1467 free_push_headers(stream); 1468 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 1469 } 1470 stream->push_headers_alloc *= 2; 1471 headp = realloc(stream->push_headers, 1472 stream->push_headers_alloc * sizeof(char *)); 1473 if(!headp) { 1474 free_push_headers(stream); 1475 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; 1476 } 1477 stream->push_headers = headp; 1478 } 1479 h = aprintf("%s:%s", name, value); 1480 if(h) 1481 stream->push_headers[stream->push_headers_used++] = h; 1482 return 0; 1483 } 1484 1485 if(stream->bodystarted) { 1486 /* This is a trailer */ 1487 CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", 1488 stream->id, (int)namelen, name, (int)valuelen, value); 1489 result = Curl_dynhds_add(&stream->resp_trailers, 1490 (const char *)name, namelen, 1491 (const char *)value, valuelen); 1492 if(result) 1493 return NGHTTP2_ERR_CALLBACK_FAILURE; 1494 1495 return 0; 1496 } 1497 1498 if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && 1499 memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { 1500 /* nghttp2 guarantees :status is received first and only once. */ 1501 char buffer[32]; 1502 result = Curl_http_decode_status(&stream->status_code, 1503 (const char *)value, valuelen); 1504 if(result) 1505 return NGHTTP2_ERR_CALLBACK_FAILURE; 1506 msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", 1507 stream->status_code); 1508 result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); 1509 if(result) 1510 return NGHTTP2_ERR_CALLBACK_FAILURE; 1511 result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 ")); 1512 if(result) 1513 return NGHTTP2_ERR_CALLBACK_FAILURE; 1514 result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); 1515 if(result) 1516 return NGHTTP2_ERR_CALLBACK_FAILURE; 1517 /* the space character after the status code is mandatory */ 1518 result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n")); 1519 if(result) 1520 return NGHTTP2_ERR_CALLBACK_FAILURE; 1521 /* if we receive data for another handle, wake that up */ 1522 if(CF_DATA_CURRENT(cf) != data_s) 1523 Curl_expire(data_s, 0, EXPIRE_RUN_NOW); 1524 1525 CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", 1526 stream->id, stream->status_code); 1527 return 0; 1528 } 1529 1530 /* nghttp2 guarantees that namelen > 0, and :status was already 1531 received, and this is not pseudo-header field . */ 1532 /* convert to an HTTP1-style header */ 1533 result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen); 1534 if(result) 1535 return NGHTTP2_ERR_CALLBACK_FAILURE; 1536 result = recvbuf_write_hds(cf, data_s, STRCONST(": ")); 1537 if(result) 1538 return NGHTTP2_ERR_CALLBACK_FAILURE; 1539 result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen); 1540 if(result) 1541 return NGHTTP2_ERR_CALLBACK_FAILURE; 1542 result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n")); 1543 if(result) 1544 return NGHTTP2_ERR_CALLBACK_FAILURE; 1545 /* if we receive data for another handle, wake that up */ 1546 if(CF_DATA_CURRENT(cf) != data_s) 1547 Curl_expire(data_s, 0, EXPIRE_RUN_NOW); 1548 1549 CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", 1550 stream->id, (int)namelen, name, (int)valuelen, value); 1551 1552 return 0; /* 0 is successful */ 1553} 1554 1555static ssize_t req_body_read_callback(nghttp2_session *session, 1556 int32_t stream_id, 1557 uint8_t *buf, size_t length, 1558 uint32_t *data_flags, 1559 nghttp2_data_source *source, 1560 void *userp) 1561{ 1562 struct Curl_cfilter *cf = userp; 1563 struct Curl_easy *data_s; 1564 struct stream_ctx *stream = NULL; 1565 CURLcode result; 1566 ssize_t nread; 1567 (void)source; 1568 1569 (void)cf; 1570 if(stream_id) { 1571 /* get the stream from the hash based on Stream ID, stream ID zero is for 1572 connection-oriented stuff */ 1573 data_s = nghttp2_session_get_stream_user_data(session, stream_id); 1574 if(!data_s) 1575 /* Receiving a Stream ID not in the hash should not happen, this is an 1576 internal error more than anything else! */ 1577 return NGHTTP2_ERR_CALLBACK_FAILURE; 1578 1579 stream = H2_STREAM_CTX(data_s); 1580 if(!stream) 1581 return NGHTTP2_ERR_CALLBACK_FAILURE; 1582 } 1583 else 1584 return NGHTTP2_ERR_INVALID_ARGUMENT; 1585 1586 nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result); 1587 if(nread < 0) { 1588 if(result != CURLE_AGAIN) 1589 return NGHTTP2_ERR_CALLBACK_FAILURE; 1590 nread = 0; 1591 } 1592 1593 if(nread > 0 && stream->upload_left != -1) 1594 stream->upload_left -= nread; 1595 1596 CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%" 1597 CURL_FORMAT_CURL_OFF_T " -> %zd, %d", 1598 stream_id, length, stream->upload_left, nread, result); 1599 1600 if(stream->upload_left == 0) 1601 *data_flags = NGHTTP2_DATA_FLAG_EOF; 1602 else if(nread == 0) 1603 return NGHTTP2_ERR_DEFERRED; 1604 1605 return nread; 1606} 1607 1608#if !defined(CURL_DISABLE_VERBOSE_STRINGS) 1609static int error_callback(nghttp2_session *session, 1610 const char *msg, 1611 size_t len, 1612 void *userp) 1613{ 1614 struct Curl_cfilter *cf = userp; 1615 struct Curl_easy *data = CF_DATA_CURRENT(cf); 1616 (void)session; 1617 failf(data, "%.*s", (int)len, msg); 1618 return 0; 1619} 1620#endif 1621 1622/* 1623 * Append headers to ask for an HTTP1.1 to HTTP2 upgrade. 1624 */ 1625CURLcode Curl_http2_request_upgrade(struct dynbuf *req, 1626 struct Curl_easy *data) 1627{ 1628 CURLcode result; 1629 char *base64; 1630 size_t blen; 1631 struct SingleRequest *k = &data->req; 1632 uint8_t binsettings[H2_BINSETTINGS_LEN]; 1633 ssize_t binlen; /* length of the binsettings data */ 1634 1635 binlen = populate_binsettings(binsettings, data); 1636 if(binlen <= 0) { 1637 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); 1638 Curl_dyn_free(req); 1639 return CURLE_FAILED_INIT; 1640 } 1641 1642 result = Curl_base64url_encode((const char *)binsettings, binlen, 1643 &base64, &blen); 1644 if(result) { 1645 Curl_dyn_free(req); 1646 return result; 1647 } 1648 1649 result = Curl_dyn_addf(req, 1650 "Connection: Upgrade, HTTP2-Settings\r\n" 1651 "Upgrade: %s\r\n" 1652 "HTTP2-Settings: %s\r\n", 1653 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); 1654 free(base64); 1655 1656 k->upgr101 = UPGR101_H2; 1657 1658 return result; 1659} 1660 1661static CURLcode http2_data_done_send(struct Curl_cfilter *cf, 1662 struct Curl_easy *data) 1663{ 1664 struct cf_h2_ctx *ctx = cf->ctx; 1665 CURLcode result = CURLE_OK; 1666 struct stream_ctx *stream = H2_STREAM_CTX(data); 1667 1668 if(!ctx || !ctx->h2 || !stream) 1669 goto out; 1670 1671 CURL_TRC_CF(data, cf, "[%d] data done send", stream->id); 1672 if(!stream->send_closed) { 1673 stream->send_closed = TRUE; 1674 if(stream->upload_left) { 1675 /* we now know that everything that is buffered is all there is. */ 1676 stream->upload_left = Curl_bufq_len(&stream->sendbuf); 1677 /* resume sending here to trigger the callback to get called again so 1678 that it can signal EOF to nghttp2 */ 1679 (void)nghttp2_session_resume_data(ctx->h2, stream->id); 1680 drain_stream(cf, data, stream); 1681 } 1682 } 1683 1684out: 1685 return result; 1686} 1687 1688static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, 1689 struct Curl_easy *data, 1690 struct stream_ctx *stream, 1691 CURLcode *err) 1692{ 1693 ssize_t rv = 0; 1694 1695 if(stream->error == NGHTTP2_REFUSED_STREAM) { 1696 CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " 1697 "connection", stream->id); 1698 connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */ 1699 data->state.refused_stream = TRUE; 1700 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ 1701 return -1; 1702 } 1703 else if(stream->error != NGHTTP2_NO_ERROR) { 1704 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", 1705 stream->id, nghttp2_http2_strerror(stream->error), 1706 stream->error); 1707 *err = CURLE_HTTP2_STREAM; 1708 return -1; 1709 } 1710 else if(stream->reset) { 1711 failf(data, "HTTP/2 stream %u was reset", stream->id); 1712 *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; 1713 return -1; 1714 } 1715 1716 if(!stream->bodystarted) { 1717 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " 1718 " all response header fields, treated as error", 1719 stream->id); 1720 *err = CURLE_HTTP2_STREAM; 1721 return -1; 1722 } 1723 1724 if(Curl_dynhds_count(&stream->resp_trailers)) { 1725 struct dynhds_entry *e; 1726 struct dynbuf dbuf; 1727 size_t i; 1728 1729 *err = CURLE_OK; 1730 Curl_dyn_init(&dbuf, DYN_TRAILERS); 1731 for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) { 1732 e = Curl_dynhds_getn(&stream->resp_trailers, i); 1733 if(!e) 1734 break; 1735 Curl_dyn_reset(&dbuf); 1736 *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", 1737 (int)e->namelen, e->name, 1738 (int)e->valuelen, e->value); 1739 if(*err) 1740 break; 1741 Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf), 1742 Curl_dyn_len(&dbuf)); 1743 *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, 1744 Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf)); 1745 if(*err) 1746 break; 1747 } 1748 Curl_dyn_free(&dbuf); 1749 if(*err) 1750 goto out; 1751 } 1752 1753 stream->close_handled = TRUE; 1754 *err = CURLE_OK; 1755 rv = 0; 1756 1757out: 1758 CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err); 1759 return rv; 1760} 1761 1762static int sweight_wanted(const struct Curl_easy *data) 1763{ 1764 /* 0 weight is not set by user and we take the nghttp2 default one */ 1765 return data->set.priority.weight? 1766 data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT; 1767} 1768 1769static int sweight_in_effect(const struct Curl_easy *data) 1770{ 1771 /* 0 weight is not set by user and we take the nghttp2 default one */ 1772 return data->state.priority.weight? 1773 data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT; 1774} 1775 1776/* 1777 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight 1778 * and dependency to the peer. It also stores the updated values in the state 1779 * struct. 1780 */ 1781 1782static void h2_pri_spec(struct Curl_easy *data, 1783 nghttp2_priority_spec *pri_spec) 1784{ 1785 struct Curl_data_priority *prio = &data->set.priority; 1786 struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent); 1787 int32_t depstream_id = depstream? depstream->id:0; 1788 nghttp2_priority_spec_init(pri_spec, depstream_id, 1789 sweight_wanted(data), 1790 data->set.priority.exclusive); 1791 data->state.priority = *prio; 1792} 1793 1794/* 1795 * Check if there's been an update in the priority / 1796 * dependency settings and if so it submits a PRIORITY frame with the updated 1797 * info. 1798 * Flush any out data pending in the network buffer. 1799 */ 1800static CURLcode h2_progress_egress(struct Curl_cfilter *cf, 1801 struct Curl_easy *data) 1802{ 1803 struct cf_h2_ctx *ctx = cf->ctx; 1804 struct stream_ctx *stream = H2_STREAM_CTX(data); 1805 int rv = 0; 1806 1807 if(stream && stream->id > 0 && 1808 ((sweight_wanted(data) != sweight_in_effect(data)) || 1809 (data->set.priority.exclusive != data->state.priority.exclusive) || 1810 (data->set.priority.parent != data->state.priority.parent)) ) { 1811 /* send new weight and/or dependency */ 1812 nghttp2_priority_spec pri_spec; 1813 1814 h2_pri_spec(data, &pri_spec); 1815 CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); 1816 DEBUGASSERT(stream->id != -1); 1817 rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, 1818 stream->id, &pri_spec); 1819 if(rv) 1820 goto out; 1821 } 1822 1823 ctx->nw_out_blocked = 0; 1824 while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) 1825 rv = nghttp2_session_send(ctx->h2); 1826 1827out: 1828 if(nghttp2_is_fatal(rv)) { 1829 CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d", 1830 nghttp2_strerror(rv), rv); 1831 return CURLE_SEND_ERROR; 1832 } 1833 return nw_out_flush(cf, data); 1834} 1835 1836static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 1837 struct stream_ctx *stream, 1838 char *buf, size_t len, CURLcode *err) 1839{ 1840 struct cf_h2_ctx *ctx = cf->ctx; 1841 ssize_t nread = -1; 1842 1843 *err = CURLE_AGAIN; 1844 if(!Curl_bufq_is_empty(&stream->recvbuf)) { 1845 nread = Curl_bufq_read(&stream->recvbuf, 1846 (unsigned char *)buf, len, err); 1847 if(nread < 0) 1848 goto out; 1849 DEBUGASSERT(nread > 0); 1850 } 1851 1852 if(nread < 0) { 1853 if(stream->closed) { 1854 CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); 1855 nread = http2_handle_stream_close(cf, data, stream, err); 1856 } 1857 else if(stream->reset || 1858 (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || 1859 (ctx->goaway && ctx->last_stream_id < stream->id)) { 1860 CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); 1861 *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR; 1862 nread = -1; 1863 } 1864 } 1865 else if(nread == 0) { 1866 *err = CURLE_AGAIN; 1867 nread = -1; 1868 } 1869 1870out: 1871 if(nread < 0 && *err != CURLE_AGAIN) 1872 CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", 1873 stream->id, len, nread, *err); 1874 return nread; 1875} 1876 1877static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, 1878 struct Curl_easy *data) 1879{ 1880 struct cf_h2_ctx *ctx = cf->ctx; 1881 struct stream_ctx *stream; 1882 CURLcode result = CURLE_OK; 1883 ssize_t nread; 1884 1885 /* Process network input buffer fist */ 1886 if(!Curl_bufq_is_empty(&ctx->inbufq)) { 1887 CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer", 1888 Curl_bufq_len(&ctx->inbufq)); 1889 if(h2_process_pending_input(cf, data, &result) < 0) 1890 return result; 1891 } 1892 1893 /* Receive data from the "lower" filters, e.g. network until 1894 * it is time to stop due to connection close or us not processing 1895 * all network input */ 1896 while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { 1897 stream = H2_STREAM_CTX(data); 1898 if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) { 1899 /* We would like to abort here and stop processing, so that 1900 * the transfer loop can handle the data/close here. However, 1901 * this may leave data in underlying buffers that will not 1902 * be consumed. */ 1903 if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) 1904 break; 1905 } 1906 1907 nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); 1908 if(nread < 0) { 1909 if(result != CURLE_AGAIN) { 1910 failf(data, "Failed receiving HTTP2 data: %d(%s)", result, 1911 curl_easy_strerror(result)); 1912 return result; 1913 } 1914 break; 1915 } 1916 else if(nread == 0) { 1917 CURL_TRC_CF(data, cf, "[0] ingress: connection closed"); 1918 ctx->conn_closed = TRUE; 1919 break; 1920 } 1921 else { 1922 CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", 1923 nread); 1924 } 1925 1926 if(h2_process_pending_input(cf, data, &result)) 1927 return result; 1928 } 1929 1930 if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { 1931 connclose(cf->conn, "GOAWAY received"); 1932 } 1933 1934 return CURLE_OK; 1935} 1936 1937static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 1938 char *buf, size_t len, CURLcode *err) 1939{ 1940 struct cf_h2_ctx *ctx = cf->ctx; 1941 struct stream_ctx *stream = H2_STREAM_CTX(data); 1942 ssize_t nread = -1; 1943 CURLcode result; 1944 struct cf_call_data save; 1945 1946 if(!stream) { 1947 /* Abnormal call sequence: either this transfer has never opened a stream 1948 * (unlikely) or the transfer has been done, cleaned up its resources, but 1949 * a read() is called anyway. It is not clear what the calling sequence 1950 * is for such a case. */ 1951 failf(data, "[%zd-%zd], http/2 recv on a transfer never opened " 1952 "or already cleared", (ssize_t)data->id, 1953 (ssize_t)cf->conn->connection_id); 1954 *err = CURLE_HTTP2; 1955 return -1; 1956 } 1957 1958 CF_DATA_SAVE(save, cf, data); 1959 1960 nread = stream_recv(cf, data, stream, buf, len, err); 1961 if(nread < 0 && *err != CURLE_AGAIN) 1962 goto out; 1963 1964 if(nread < 0) { 1965 *err = h2_progress_ingress(cf, data); 1966 if(*err) 1967 goto out; 1968 1969 nread = stream_recv(cf, data, stream, buf, len, err); 1970 } 1971 1972 if(nread > 0) { 1973 size_t data_consumed = (size_t)nread; 1974 /* Now that we transferred this to the upper layer, we report 1975 * the actual amount of DATA consumed to the H2 session, so 1976 * that it adjusts stream flow control */ 1977 if(stream->resp_hds_len >= data_consumed) { 1978 stream->resp_hds_len -= data_consumed; /* no DATA */ 1979 } 1980 else { 1981 if(stream->resp_hds_len) { 1982 data_consumed -= stream->resp_hds_len; 1983 stream->resp_hds_len = 0; 1984 } 1985 if(data_consumed) { 1986 nghttp2_session_consume(ctx->h2, stream->id, data_consumed); 1987 } 1988 } 1989 1990 if(stream->closed) { 1991 CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); 1992 drain_stream(cf, data, stream); 1993 } 1994 } 1995 1996out: 1997 result = h2_progress_egress(cf, data); 1998 if(result == CURLE_AGAIN) { 1999 /* pending data to send, need to be called again. Ideally, we'd 2000 * monitor the socket for POLLOUT, but we might not be in SENDING 2001 * transfer state any longer and are unable to make this happen. 2002 */ 2003 drain_stream(cf, data, stream); 2004 } 2005 else if(result) { 2006 *err = result; 2007 nread = -1; 2008 } 2009 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " 2010 "buffered=%zu, window=%d/%d, connection %d/%d", 2011 stream->id, len, nread, *err, 2012 Curl_bufq_len(&stream->recvbuf), 2013 nghttp2_session_get_stream_effective_recv_data_length( 2014 ctx->h2, stream->id), 2015 nghttp2_session_get_stream_effective_local_window_size( 2016 ctx->h2, stream->id), 2017 nghttp2_session_get_local_window_size(ctx->h2), 2018 HTTP2_HUGE_WINDOW_SIZE); 2019 2020 CF_DATA_RESTORE(cf, save); 2021 return nread; 2022} 2023 2024static ssize_t h2_submit(struct stream_ctx **pstream, 2025 struct Curl_cfilter *cf, struct Curl_easy *data, 2026 const void *buf, size_t len, CURLcode *err) 2027{ 2028 struct cf_h2_ctx *ctx = cf->ctx; 2029 struct stream_ctx *stream = NULL; 2030 struct dynhds h2_headers; 2031 nghttp2_nv *nva = NULL; 2032 const void *body = NULL; 2033 size_t nheader, bodylen, i; 2034 nghttp2_data_provider data_prd; 2035 int32_t stream_id; 2036 nghttp2_priority_spec pri_spec; 2037 ssize_t nwritten; 2038 2039 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 2040 2041 *err = http2_data_setup(cf, data, &stream); 2042 if(*err) { 2043 nwritten = -1; 2044 goto out; 2045 } 2046 2047 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); 2048 if(nwritten < 0) 2049 goto out; 2050 if(!stream->h1.done) { 2051 /* need more data */ 2052 goto out; 2053 } 2054 DEBUGASSERT(stream->h1.req); 2055 2056 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); 2057 if(*err) { 2058 nwritten = -1; 2059 goto out; 2060 } 2061 /* no longer needed */ 2062 Curl_h1_req_parse_free(&stream->h1); 2063 2064 nva = Curl_dynhds_to_nva(&h2_headers, &nheader); 2065 if(!nva) { 2066 *err = CURLE_OUT_OF_MEMORY; 2067 nwritten = -1; 2068 goto out; 2069 } 2070 2071 h2_pri_spec(data, &pri_spec); 2072 if(!nghttp2_session_check_request_allowed(ctx->h2)) 2073 CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); 2074 2075 switch(data->state.httpreq) { 2076 case HTTPREQ_POST: 2077 case HTTPREQ_POST_FORM: 2078 case HTTPREQ_POST_MIME: 2079 case HTTPREQ_PUT: 2080 if(data->state.infilesize != -1) 2081 stream->upload_left = data->state.infilesize; 2082 else 2083 /* data sending without specifying the data amount up front */ 2084 stream->upload_left = -1; /* unknown */ 2085 2086 data_prd.read_callback = req_body_read_callback; 2087 data_prd.source.ptr = NULL; 2088 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, 2089 &data_prd, data); 2090 break; 2091 default: 2092 stream->upload_left = 0; /* no request body */ 2093 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader, 2094 NULL, data); 2095 } 2096 2097 if(stream_id < 0) { 2098 CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", 2099 nghttp2_strerror(stream_id), stream_id); 2100 *err = CURLE_SEND_ERROR; 2101 nwritten = -1; 2102 goto out; 2103 } 2104 2105#define MAX_ACC 60000 /* <64KB to account for some overhead */ 2106 if(Curl_trc_is_verbose(data)) { 2107 size_t acc = 0; 2108 2109 infof(data, "[HTTP/2] [%d] OPENED stream for %s", 2110 stream_id, data->state.url); 2111 for(i = 0; i < nheader; ++i) { 2112 acc += nva[i].namelen + nva[i].valuelen; 2113 2114 infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id, 2115 (int)nva[i].namelen, nva[i].name, 2116 (int)nva[i].valuelen, nva[i].value); 2117 } 2118 2119 if(acc > MAX_ACC) { 2120 infof(data, "[HTTP/2] Warning: The cumulative length of all " 2121 "headers exceeds %d bytes and that could cause the " 2122 "stream to be rejected.", MAX_ACC); 2123 } 2124 } 2125 2126 stream->id = stream_id; 2127 stream->local_window_size = H2_STREAM_WINDOW_SIZE; 2128 if(data->set.max_recv_speed) { 2129 /* We are asked to only receive `max_recv_speed` bytes per second. 2130 * Let's limit our stream window size around that, otherwise the server 2131 * will send in large bursts only. We make the window 50% larger to 2132 * allow for data in flight and avoid stalling. */ 2133 curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1); 2134 n += CURLMAX((n/2), 1); 2135 if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) && 2136 n < (UINT_MAX / H2_CHUNK_SIZE)) { 2137 stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE; 2138 } 2139 } 2140 2141 body = (const char *)buf + nwritten; 2142 bodylen = len - nwritten; 2143 2144 if(bodylen) { 2145 /* We have request body to send in DATA frame */ 2146 ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err); 2147 if(n < 0) { 2148 *err = CURLE_SEND_ERROR; 2149 nwritten = -1; 2150 goto out; 2151 } 2152 nwritten += n; 2153 } 2154 2155out: 2156 CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d", 2157 stream? stream->id : -1, nwritten, *err); 2158 Curl_safefree(nva); 2159 *pstream = stream; 2160 Curl_dynhds_free(&h2_headers); 2161 return nwritten; 2162} 2163 2164static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, 2165 const void *buf, size_t len, CURLcode *err) 2166{ 2167 struct cf_h2_ctx *ctx = cf->ctx; 2168 struct stream_ctx *stream = H2_STREAM_CTX(data); 2169 struct cf_call_data save; 2170 int rv; 2171 ssize_t nwritten; 2172 CURLcode result; 2173 int blocked = 0, was_blocked = 0; 2174 2175 CF_DATA_SAVE(save, cf, data); 2176 2177 if(stream && stream->id != -1) { 2178 if(stream->upload_blocked_len) { 2179 /* the data in `buf` has already been submitted or added to the 2180 * buffers, but have been EAGAINed on the last invocation. */ 2181 /* TODO: this assertion triggers in OSSFuzz runs and it is not 2182 * clear why. Disable for now to let OSSFuzz continue its tests. */ 2183 DEBUGASSERT(len >= stream->upload_blocked_len); 2184 if(len < stream->upload_blocked_len) { 2185 /* Did we get called again with a smaller `len`? This should not 2186 * happen. We are not prepared to handle that. */ 2187 failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)", 2188 len, stream->upload_blocked_len); 2189 *err = CURLE_HTTP2; 2190 nwritten = -1; 2191 goto out; 2192 } 2193 nwritten = (ssize_t)stream->upload_blocked_len; 2194 stream->upload_blocked_len = 0; 2195 was_blocked = 1; 2196 } 2197 else if(stream->closed) { 2198 if(stream->resp_hds_complete) { 2199 /* Server decided to close the stream after having sent us a findl 2200 * response. This is valid if it is not interested in the request 2201 * body. This happens on 30x or 40x responses. 2202 * We silently discard the data sent, since this is not a transport 2203 * error situation. */ 2204 CURL_TRC_CF(data, cf, "[%d] discarding data" 2205 "on closed stream with response", stream->id); 2206 *err = CURLE_OK; 2207 nwritten = (ssize_t)len; 2208 goto out; 2209 } 2210 infof(data, "stream %u closed", stream->id); 2211 *err = CURLE_SEND_ERROR; 2212 nwritten = -1; 2213 goto out; 2214 } 2215 else { 2216 /* If stream_id != -1, we have dispatched request HEADERS and 2217 * optionally request body, and now are going to send or sending 2218 * more request body in DATA frame */ 2219 nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); 2220 if(nwritten < 0 && *err != CURLE_AGAIN) 2221 goto out; 2222 } 2223 2224 if(!Curl_bufq_is_empty(&stream->sendbuf)) { 2225 /* req body data is buffered, resume the potentially suspended stream */ 2226 rv = nghttp2_session_resume_data(ctx->h2, stream->id); 2227 if(nghttp2_is_fatal(rv)) { 2228 *err = CURLE_SEND_ERROR; 2229 nwritten = -1; 2230 goto out; 2231 } 2232 } 2233 } 2234 else { 2235 nwritten = h2_submit(&stream, cf, data, buf, len, err); 2236 if(nwritten < 0) { 2237 goto out; 2238 } 2239 DEBUGASSERT(stream); 2240 } 2241 2242 /* Call the nghttp2 send loop and flush to write ALL buffered data, 2243 * headers and/or request body completely out to the network */ 2244 result = h2_progress_egress(cf, data); 2245 /* if the stream has been closed in egress handling (nghttp2 does that 2246 * when it does not like the headers, for example */ 2247 if(stream && stream->closed && !was_blocked) { 2248 infof(data, "stream %u closed", stream->id); 2249 *err = CURLE_SEND_ERROR; 2250 nwritten = -1; 2251 goto out; 2252 } 2253 else if(result == CURLE_AGAIN) { 2254 blocked = 1; 2255 } 2256 else if(result) { 2257 *err = result; 2258 nwritten = -1; 2259 goto out; 2260 } 2261 else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) { 2262 /* although we wrote everything that nghttp2 wants to send now, 2263 * there is data left in our stream send buffer unwritten. This may 2264 * be due to the stream's HTTP/2 flow window being exhausted. */ 2265 blocked = 1; 2266 } 2267 2268 if(stream && blocked && nwritten > 0) { 2269 /* Unable to send all data, due to connection blocked or H2 window 2270 * exhaustion. Data is left in our stream buffer, or nghttp2's internal 2271 * frame buffer or our network out buffer. */ 2272 size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2, 2273 stream->id); 2274 /* Whatever the cause, we need to return CURL_EAGAIN for this call. 2275 * We have unwritten state that needs us being invoked again and EAGAIN 2276 * is the only way to ensure that. */ 2277 stream->upload_blocked_len = nwritten; 2278 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu " 2279 "blocked_len=%zu", 2280 stream->id, len, 2281 nghttp2_session_get_remote_window_size(ctx->h2), rwin, 2282 nwritten); 2283 *err = CURLE_AGAIN; 2284 nwritten = -1; 2285 goto out; 2286 } 2287 else if(should_close_session(ctx)) { 2288 /* nghttp2 thinks this session is done. If the stream has not been 2289 * closed, this is an error state for out transfer */ 2290 if(stream->closed) { 2291 nwritten = http2_handle_stream_close(cf, data, stream, err); 2292 } 2293 else { 2294 CURL_TRC_CF(data, cf, "send: nothing to do in this session"); 2295 *err = CURLE_HTTP2; 2296 nwritten = -1; 2297 } 2298 } 2299 2300out: 2301 if(stream) { 2302 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " 2303 "upload_left=%" CURL_FORMAT_CURL_OFF_T ", " 2304 "h2 windows %d-%d (stream-conn), " 2305 "buffers %zu-%zu (stream-conn)", 2306 stream->id, len, nwritten, *err, 2307 stream->upload_left, 2308 nghttp2_session_get_stream_remote_window_size( 2309 ctx->h2, stream->id), 2310 nghttp2_session_get_remote_window_size(ctx->h2), 2311 Curl_bufq_len(&stream->sendbuf), 2312 Curl_bufq_len(&ctx->outbufq)); 2313 } 2314 else { 2315 CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " 2316 "connection-window=%d, nw_send_buffer(%zu)", 2317 len, nwritten, *err, 2318 nghttp2_session_get_remote_window_size(ctx->h2), 2319 Curl_bufq_len(&ctx->outbufq)); 2320 } 2321 CF_DATA_RESTORE(cf, save); 2322 return nwritten; 2323} 2324 2325static void cf_h2_adjust_pollset(struct Curl_cfilter *cf, 2326 struct Curl_easy *data, 2327 struct easy_pollset *ps) 2328{ 2329 struct cf_h2_ctx *ctx = cf->ctx; 2330 curl_socket_t sock; 2331 bool want_recv, want_send; 2332 2333 if(!ctx->h2) 2334 return; 2335 2336 sock = Curl_conn_cf_get_socket(cf, data); 2337 Curl_pollset_check(data, ps, sock, &want_recv, &want_send); 2338 if(want_recv || want_send) { 2339 struct stream_ctx *stream = H2_STREAM_CTX(data); 2340 struct cf_call_data save; 2341 bool c_exhaust, s_exhaust; 2342 2343 CF_DATA_SAVE(save, cf, data); 2344 c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2); 2345 s_exhaust = want_send && stream && stream->id >= 0 && 2346 !nghttp2_session_get_stream_remote_window_size(ctx->h2, 2347 stream->id); 2348 want_recv = (want_recv || c_exhaust || s_exhaust); 2349 want_send = (!s_exhaust && want_send) || 2350 (!c_exhaust && nghttp2_session_want_write(ctx->h2)); 2351 2352 Curl_pollset_set(data, ps, sock, want_recv, want_send); 2353 CF_DATA_RESTORE(cf, save); 2354 } 2355} 2356 2357static CURLcode cf_h2_connect(struct Curl_cfilter *cf, 2358 struct Curl_easy *data, 2359 bool blocking, bool *done) 2360{ 2361 struct cf_h2_ctx *ctx = cf->ctx; 2362 CURLcode result = CURLE_OK; 2363 struct cf_call_data save; 2364 2365 if(cf->connected) { 2366 *done = TRUE; 2367 return CURLE_OK; 2368 } 2369 2370 /* Connect the lower filters first */ 2371 if(!cf->next->connected) { 2372 result = Curl_conn_cf_connect(cf->next, data, blocking, done); 2373 if(result || !*done) 2374 return result; 2375 } 2376 2377 *done = FALSE; 2378 2379 CF_DATA_SAVE(save, cf, data); 2380 if(!ctx->h2) { 2381 result = cf_h2_ctx_init(cf, data, FALSE); 2382 if(result) 2383 goto out; 2384 } 2385 2386 result = h2_progress_ingress(cf, data); 2387 if(result) 2388 goto out; 2389 2390 /* Send out our SETTINGS and ACKs and such. If that blocks, we 2391 * have it buffered and can count this filter as being connected */ 2392 result = h2_progress_egress(cf, data); 2393 if(result == CURLE_AGAIN) 2394 result = CURLE_OK; 2395 else if(result) 2396 goto out; 2397 2398 *done = TRUE; 2399 cf->connected = TRUE; 2400 result = CURLE_OK; 2401 2402out: 2403 CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); 2404 CF_DATA_RESTORE(cf, save); 2405 return result; 2406} 2407 2408static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) 2409{ 2410 struct cf_h2_ctx *ctx = cf->ctx; 2411 2412 if(ctx) { 2413 struct cf_call_data save; 2414 2415 CF_DATA_SAVE(save, cf, data); 2416 cf_h2_ctx_clear(ctx); 2417 CF_DATA_RESTORE(cf, save); 2418 } 2419 if(cf->next) 2420 cf->next->cft->do_close(cf->next, data); 2421} 2422 2423static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 2424{ 2425 struct cf_h2_ctx *ctx = cf->ctx; 2426 2427 (void)data; 2428 if(ctx) { 2429 cf_h2_ctx_free(ctx); 2430 cf->ctx = NULL; 2431 } 2432} 2433 2434static CURLcode http2_data_pause(struct Curl_cfilter *cf, 2435 struct Curl_easy *data, 2436 bool pause) 2437{ 2438#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 2439 struct cf_h2_ctx *ctx = cf->ctx; 2440 struct stream_ctx *stream = H2_STREAM_CTX(data); 2441 2442 DEBUGASSERT(data); 2443 if(ctx && ctx->h2 && stream) { 2444 uint32_t window = pause? 0 : stream->local_window_size; 2445 2446 int rv = nghttp2_session_set_local_window_size(ctx->h2, 2447 NGHTTP2_FLAG_NONE, 2448 stream->id, 2449 window); 2450 if(rv) { 2451 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", 2452 nghttp2_strerror(rv), rv); 2453 return CURLE_HTTP2; 2454 } 2455 2456 if(!pause) 2457 drain_stream(cf, data, stream); 2458 2459 /* attempt to send the window update */ 2460 (void)h2_progress_egress(cf, data); 2461 2462 if(!pause) { 2463 /* Unpausing a h2 transfer, requires it to be run again. The server 2464 * may send new DATA on us increasing the flow window, and it may 2465 * not. We may have already buffered and exhausted the new window 2466 * by operating on things in flight during the handling of other 2467 * transfers. */ 2468 drain_stream(cf, data, stream); 2469 Curl_expire(data, 0, EXPIRE_RUN_NOW); 2470 } 2471 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u", 2472 window, stream->id)); 2473 2474#ifdef DEBUGBUILD 2475 { 2476 /* read out the stream local window again */ 2477 uint32_t window2 = 2478 nghttp2_session_get_stream_local_window_size(ctx->h2, 2479 stream->id); 2480 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u", 2481 window2, stream->id)); 2482 } 2483#endif 2484 } 2485#endif 2486 return CURLE_OK; 2487} 2488 2489static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, 2490 struct Curl_easy *data, 2491 int event, int arg1, void *arg2) 2492{ 2493 CURLcode result = CURLE_OK; 2494 struct cf_call_data save; 2495 2496 (void)arg2; 2497 2498 CF_DATA_SAVE(save, cf, data); 2499 switch(event) { 2500 case CF_CTRL_DATA_SETUP: 2501 break; 2502 case CF_CTRL_DATA_PAUSE: 2503 result = http2_data_pause(cf, data, (arg1 != 0)); 2504 break; 2505 case CF_CTRL_DATA_DONE_SEND: 2506 result = http2_data_done_send(cf, data); 2507 break; 2508 case CF_CTRL_DATA_DETACH: 2509 http2_data_done(cf, data, TRUE); 2510 break; 2511 case CF_CTRL_DATA_DONE: 2512 http2_data_done(cf, data, arg1 != 0); 2513 break; 2514 default: 2515 break; 2516 } 2517 CF_DATA_RESTORE(cf, save); 2518 return result; 2519} 2520 2521static bool cf_h2_data_pending(struct Curl_cfilter *cf, 2522 const struct Curl_easy *data) 2523{ 2524 struct cf_h2_ctx *ctx = cf->ctx; 2525 struct stream_ctx *stream = H2_STREAM_CTX(data); 2526 2527 if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq) 2528 || (stream && !Curl_bufq_is_empty(&stream->sendbuf)) 2529 || (stream && !Curl_bufq_is_empty(&stream->recvbuf)))) 2530 return TRUE; 2531 return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE; 2532} 2533 2534static bool cf_h2_is_alive(struct Curl_cfilter *cf, 2535 struct Curl_easy *data, 2536 bool *input_pending) 2537{ 2538 struct cf_h2_ctx *ctx = cf->ctx; 2539 CURLcode result; 2540 struct cf_call_data save; 2541 2542 CF_DATA_SAVE(save, cf, data); 2543 result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); 2544 CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d", 2545 result, *input_pending); 2546 CF_DATA_RESTORE(cf, save); 2547 return result; 2548} 2549 2550static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf, 2551 struct Curl_easy *data) 2552{ 2553 CURLcode result; 2554 struct cf_call_data save; 2555 2556 CF_DATA_SAVE(save, cf, data); 2557 result = http2_send_ping(cf, data); 2558 CF_DATA_RESTORE(cf, save); 2559 return result; 2560} 2561 2562static CURLcode cf_h2_query(struct Curl_cfilter *cf, 2563 struct Curl_easy *data, 2564 int query, int *pres1, void *pres2) 2565{ 2566 struct cf_h2_ctx *ctx = cf->ctx; 2567 struct cf_call_data save; 2568 size_t effective_max; 2569 2570 switch(query) { 2571 case CF_QUERY_MAX_CONCURRENT: 2572 DEBUGASSERT(pres1); 2573 2574 CF_DATA_SAVE(save, cf, data); 2575 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { 2576 /* the limit is what we have in use right now */ 2577 effective_max = CONN_INUSE(cf->conn); 2578 } 2579 else { 2580 effective_max = ctx->max_concurrent_streams; 2581 } 2582 *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max; 2583 CF_DATA_RESTORE(cf, save); 2584 return CURLE_OK; 2585 default: 2586 break; 2587 } 2588 return cf->next? 2589 cf->next->cft->query(cf->next, data, query, pres1, pres2) : 2590 CURLE_UNKNOWN_OPTION; 2591} 2592 2593struct Curl_cftype Curl_cft_nghttp2 = { 2594 "HTTP/2", 2595 CF_TYPE_MULTIPLEX, 2596 CURL_LOG_LVL_NONE, 2597 cf_h2_destroy, 2598 cf_h2_connect, 2599 cf_h2_close, 2600 Curl_cf_def_get_host, 2601 cf_h2_adjust_pollset, 2602 cf_h2_data_pending, 2603 cf_h2_send, 2604 cf_h2_recv, 2605 cf_h2_cntrl, 2606 cf_h2_is_alive, 2607 cf_h2_keep_alive, 2608 cf_h2_query, 2609}; 2610 2611static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, 2612 struct Curl_easy *data, 2613 struct connectdata *conn, 2614 int sockindex) 2615{ 2616 struct Curl_cfilter *cf = NULL; 2617 struct cf_h2_ctx *ctx; 2618 CURLcode result = CURLE_OUT_OF_MEMORY; 2619 2620 DEBUGASSERT(data->conn); 2621 ctx = calloc(1, sizeof(*ctx)); 2622 if(!ctx) 2623 goto out; 2624 2625 result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx); 2626 if(result) 2627 goto out; 2628 2629 Curl_conn_cf_add(data, conn, sockindex, cf); 2630 result = CURLE_OK; 2631 2632out: 2633 if(result) 2634 cf_h2_ctx_free(ctx); 2635 *pcf = result? NULL : cf; 2636 return result; 2637} 2638 2639static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, 2640 struct Curl_easy *data) 2641{ 2642 struct Curl_cfilter *cf_h2 = NULL; 2643 struct cf_h2_ctx *ctx; 2644 CURLcode result = CURLE_OUT_OF_MEMORY; 2645 2646 (void)data; 2647 ctx = calloc(1, sizeof(*ctx)); 2648 if(!ctx) 2649 goto out; 2650 2651 result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx); 2652 if(result) 2653 goto out; 2654 2655 Curl_conn_cf_insert_after(cf, cf_h2); 2656 result = CURLE_OK; 2657 2658out: 2659 if(result) 2660 cf_h2_ctx_free(ctx); 2661 return result; 2662} 2663 2664static bool Curl_cf_is_http2(struct Curl_cfilter *cf, 2665 const struct Curl_easy *data) 2666{ 2667 (void)data; 2668 for(; cf; cf = cf->next) { 2669 if(cf->cft == &Curl_cft_nghttp2) 2670 return TRUE; 2671 if(cf->cft->flags & CF_TYPE_IP_CONNECT) 2672 return FALSE; 2673 } 2674 return FALSE; 2675} 2676 2677bool Curl_conn_is_http2(const struct Curl_easy *data, 2678 const struct connectdata *conn, 2679 int sockindex) 2680{ 2681 return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE; 2682} 2683 2684bool Curl_http2_may_switch(struct Curl_easy *data, 2685 struct connectdata *conn, 2686 int sockindex) 2687{ 2688 (void)sockindex; 2689 if(!Curl_conn_is_http2(data, conn, sockindex) && 2690 data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { 2691#ifndef CURL_DISABLE_PROXY 2692 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { 2693 /* We don't support HTTP/2 proxies yet. Also it's debatable 2694 whether or not this setting should apply to HTTP/2 proxies. */ 2695 infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); 2696 return FALSE; 2697 } 2698#endif 2699 return TRUE; 2700 } 2701 return FALSE; 2702} 2703 2704CURLcode Curl_http2_switch(struct Curl_easy *data, 2705 struct connectdata *conn, int sockindex) 2706{ 2707 struct Curl_cfilter *cf; 2708 CURLcode result; 2709 2710 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); 2711 DEBUGF(infof(data, "switching to HTTP/2")); 2712 2713 result = http2_cfilter_add(&cf, data, conn, sockindex); 2714 if(result) 2715 return result; 2716 2717 result = cf_h2_ctx_init(cf, data, FALSE); 2718 if(result) 2719 return result; 2720 2721 conn->httpversion = 20; /* we know we're on HTTP/2 now */ 2722 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 2723 conn->bundle->multiuse = BUNDLE_MULTIPLEX; 2724 Curl_multi_connchanged(data->multi); 2725 2726 if(cf->next) { 2727 bool done; 2728 return Curl_conn_cf_connect(cf, data, FALSE, &done); 2729 } 2730 return CURLE_OK; 2731} 2732 2733CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) 2734{ 2735 struct Curl_cfilter *cf_h2; 2736 CURLcode result; 2737 2738 DEBUGASSERT(!Curl_cf_is_http2(cf, data)); 2739 2740 result = http2_cfilter_insert_after(cf, data); 2741 if(result) 2742 return result; 2743 2744 cf_h2 = cf->next; 2745 result = cf_h2_ctx_init(cf_h2, data, FALSE); 2746 if(result) 2747 return result; 2748 2749 cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */ 2750 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 2751 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; 2752 Curl_multi_connchanged(data->multi); 2753 2754 if(cf_h2->next) { 2755 bool done; 2756 return Curl_conn_cf_connect(cf_h2, data, FALSE, &done); 2757 } 2758 return CURLE_OK; 2759} 2760 2761CURLcode Curl_http2_upgrade(struct Curl_easy *data, 2762 struct connectdata *conn, int sockindex, 2763 const char *mem, size_t nread) 2764{ 2765 struct Curl_cfilter *cf; 2766 struct cf_h2_ctx *ctx; 2767 CURLcode result; 2768 2769 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex)); 2770 DEBUGF(infof(data, "upgrading to HTTP/2")); 2771 DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); 2772 2773 result = http2_cfilter_add(&cf, data, conn, sockindex); 2774 if(result) 2775 return result; 2776 2777 DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); 2778 ctx = cf->ctx; 2779 2780 result = cf_h2_ctx_init(cf, data, TRUE); 2781 if(result) 2782 return result; 2783 2784 if(nread > 0) { 2785 /* Remaining data from the protocol switch reply is already using 2786 * the switched protocol, ie. HTTP/2. We add that to the network 2787 * inbufq. */ 2788 ssize_t copied; 2789 2790 copied = Curl_bufq_write(&ctx->inbufq, 2791 (const unsigned char *)mem, nread, &result); 2792 if(copied < 0) { 2793 failf(data, "error on copying HTTP Upgrade response: %d", result); 2794 return CURLE_RECV_ERROR; 2795 } 2796 if((size_t)copied < nread) { 2797 failf(data, "connection buffer size could not take all data " 2798 "from HTTP Upgrade response header: copied=%zd, datalen=%zu", 2799 copied, nread); 2800 return CURLE_HTTP2; 2801 } 2802 infof(data, "Copied HTTP/2 data in stream buffer to connection buffer" 2803 " after upgrade: len=%zu", nread); 2804 } 2805 2806 conn->httpversion = 20; /* we know we're on HTTP/2 now */ 2807 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 2808 conn->bundle->multiuse = BUNDLE_MULTIPLEX; 2809 Curl_multi_connchanged(data->multi); 2810 2811 if(cf->next) { 2812 bool done; 2813 return Curl_conn_cf_connect(cf, data, FALSE, &done); 2814 } 2815 return CURLE_OK; 2816} 2817 2818/* Only call this function for a transfer that already got an HTTP/2 2819 CURLE_HTTP2_STREAM error! */ 2820bool Curl_h2_http_1_1_error(struct Curl_easy *data) 2821{ 2822 struct stream_ctx *stream = H2_STREAM_CTX(data); 2823 return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED); 2824} 2825 2826#else /* !USE_NGHTTP2 */ 2827 2828/* Satisfy external references even if http2 is not compiled in. */ 2829#include <curl/curl.h> 2830 2831char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) 2832{ 2833 (void) h; 2834 (void) num; 2835 return NULL; 2836} 2837 2838char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header) 2839{ 2840 (void) h; 2841 (void) header; 2842 return NULL; 2843} 2844 2845#endif /* USE_NGHTTP2 */ 2846