cf-h2-proxy.c (49961B)
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 #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) 28 29 #include <nghttp2/nghttp2.h> 30 #include "urldata.h" 31 #include "url.h" 32 #include "cfilters.h" 33 #include "connect.h" 34 #include "curl_trc.h" 35 #include "bufq.h" 36 #include "curlx/dynbuf.h" 37 #include "dynhds.h" 38 #include "http1.h" 39 #include "http2.h" 40 #include "http_proxy.h" 41 #include "multiif.h" 42 #include "sendf.h" 43 #include "cf-h2-proxy.h" 44 45 /* The last 3 #include files should be in this order */ 46 #include "curl_printf.h" 47 #include "curl_memory.h" 48 #include "memdebug.h" 49 50 #define PROXY_H2_CHUNK_SIZE (16*1024) 51 52 #define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) 53 #define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) 54 55 #define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) 56 #define PROXY_H2_NW_SEND_CHUNKS 1 57 58 #define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) 59 #define H2_TUNNEL_SEND_CHUNKS ((128 * 1024) / PROXY_H2_CHUNK_SIZE) 60 61 62 typedef enum { 63 H2_TUNNEL_INIT, /* init/default/no tunnel state */ 64 H2_TUNNEL_CONNECT, /* CONNECT request is being send */ 65 H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ 66 H2_TUNNEL_ESTABLISHED, 67 H2_TUNNEL_FAILED 68 } h2_tunnel_state; 69 70 struct tunnel_stream { 71 struct http_resp *resp; 72 struct bufq recvbuf; 73 struct bufq sendbuf; 74 char *authority; 75 int32_t stream_id; 76 uint32_t error; 77 h2_tunnel_state state; 78 BIT(has_final_response); 79 BIT(closed); 80 BIT(reset); 81 }; 82 83 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, 84 struct tunnel_stream *ts) 85 { 86 const char *hostname; 87 int port; 88 bool ipv6_ip; 89 CURLcode result; 90 91 ts->state = H2_TUNNEL_INIT; 92 ts->stream_id = -1; 93 Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, 94 BUFQ_OPT_SOFT_LIMIT); 95 Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); 96 97 result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); 98 if(result) 99 return result; 100 101 ts->authority = /* host:port with IPv6 support */ 102 aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname, 103 ipv6_ip ? "]" : "", port); 104 if(!ts->authority) 105 return CURLE_OUT_OF_MEMORY; 106 107 return CURLE_OK; 108 } 109 110 static void tunnel_stream_clear(struct tunnel_stream *ts) 111 { 112 Curl_http_resp_free(ts->resp); 113 Curl_bufq_free(&ts->recvbuf); 114 Curl_bufq_free(&ts->sendbuf); 115 Curl_safefree(ts->authority); 116 memset(ts, 0, sizeof(*ts)); 117 ts->state = H2_TUNNEL_INIT; 118 } 119 120 static void h2_tunnel_go_state(struct Curl_cfilter *cf, 121 struct tunnel_stream *ts, 122 h2_tunnel_state new_state, 123 struct Curl_easy *data) 124 { 125 (void)cf; 126 127 if(ts->state == new_state) 128 return; 129 /* leaving this one */ 130 switch(ts->state) { 131 case H2_TUNNEL_CONNECT: 132 data->req.ignorebody = FALSE; 133 break; 134 default: 135 break; 136 } 137 /* entering this one */ 138 switch(new_state) { 139 case H2_TUNNEL_INIT: 140 CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); 141 tunnel_stream_clear(ts); 142 break; 143 144 case H2_TUNNEL_CONNECT: 145 CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id); 146 ts->state = H2_TUNNEL_CONNECT; 147 break; 148 149 case H2_TUNNEL_RESPONSE: 150 CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id); 151 ts->state = H2_TUNNEL_RESPONSE; 152 break; 153 154 case H2_TUNNEL_ESTABLISHED: 155 CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", 156 ts->stream_id); 157 infof(data, "CONNECT phase completed"); 158 data->state.authproxy.done = TRUE; 159 data->state.authproxy.multipass = FALSE; 160 FALLTHROUGH(); 161 case H2_TUNNEL_FAILED: 162 if(new_state == H2_TUNNEL_FAILED) 163 CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id); 164 ts->state = new_state; 165 /* If a proxy-authorization header was used for the proxy, then we should 166 make sure that it is not accidentally used for the document request 167 after we have connected. So let's free and clear it here. */ 168 Curl_safefree(data->state.aptr.proxyuserpwd); 169 break; 170 } 171 } 172 173 struct cf_h2_proxy_ctx { 174 nghttp2_session *h2; 175 /* The easy handle used in the current filter call, cleared at return */ 176 struct cf_call_data call_data; 177 178 struct bufq inbufq; /* network receive buffer */ 179 struct bufq outbufq; /* network send buffer */ 180 181 struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ 182 int32_t goaway_error; 183 int32_t last_stream_id; 184 BIT(conn_closed); 185 BIT(rcvd_goaway); 186 BIT(sent_goaway); 187 BIT(nw_out_blocked); 188 }; 189 190 /* How to access `call_data` from a cf_h2 filter */ 191 #undef CF_CTX_CALL_DATA 192 #define CF_CTX_CALL_DATA(cf) \ 193 ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data 194 195 static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) 196 { 197 struct cf_call_data save = ctx->call_data; 198 199 if(ctx->h2) { 200 nghttp2_session_del(ctx->h2); 201 } 202 Curl_bufq_free(&ctx->inbufq); 203 Curl_bufq_free(&ctx->outbufq); 204 tunnel_stream_clear(&ctx->tunnel); 205 memset(ctx, 0, sizeof(*ctx)); 206 ctx->call_data = save; 207 } 208 209 static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) 210 { 211 if(ctx) { 212 cf_h2_proxy_ctx_clear(ctx); 213 free(ctx); 214 } 215 } 216 217 static void drain_tunnel(struct Curl_cfilter *cf, 218 struct Curl_easy *data, 219 struct tunnel_stream *tunnel) 220 { 221 struct cf_h2_proxy_ctx *ctx = cf->ctx; 222 (void)cf; 223 if(!tunnel->closed && !tunnel->reset && 224 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) 225 Curl_multi_mark_dirty(data); 226 } 227 228 static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, 229 const unsigned char *buf, size_t buflen, 230 size_t *pnwritten) 231 { 232 struct Curl_cfilter *cf = writer_ctx; 233 *pnwritten = 0; 234 if(cf) { 235 struct Curl_easy *data = CF_DATA_CURRENT(cf); 236 CURLcode result; 237 result = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, 238 FALSE, pnwritten); 239 CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", 240 buflen, result, *pnwritten); 241 return result; 242 } 243 return CURLE_FAILED_INIT; 244 } 245 246 static int proxy_h2_client_new(struct Curl_cfilter *cf, 247 nghttp2_session_callbacks *cbs) 248 { 249 struct cf_h2_proxy_ctx *ctx = cf->ctx; 250 nghttp2_option *o; 251 nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, 252 Curl_nghttp2_calloc, Curl_nghttp2_realloc}; 253 254 int rc = nghttp2_option_new(&o); 255 if(rc) 256 return rc; 257 /* We handle window updates ourself to enforce buffer limits */ 258 nghttp2_option_set_no_auto_window_update(o, 1); 259 #if NGHTTP2_VERSION_NUM >= 0x013200 260 /* with 1.50.0 */ 261 /* turn off RFC 9113 leading and trailing white spaces validation against 262 HTTP field value. */ 263 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); 264 #endif 265 rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem); 266 nghttp2_option_del(o); 267 return rc; 268 } 269 270 static ssize_t on_session_send(nghttp2_session *h2, 271 const uint8_t *buf, size_t blen, 272 int flags, void *userp); 273 static int proxy_h2_on_frame_recv(nghttp2_session *session, 274 const nghttp2_frame *frame, 275 void *userp); 276 #ifndef CURL_DISABLE_VERBOSE_STRINGS 277 static int proxy_h2_on_frame_send(nghttp2_session *session, 278 const nghttp2_frame *frame, 279 void *userp); 280 #endif 281 static int proxy_h2_on_stream_close(nghttp2_session *session, 282 int32_t stream_id, 283 uint32_t error_code, void *userp); 284 static int proxy_h2_on_header(nghttp2_session *session, 285 const nghttp2_frame *frame, 286 const uint8_t *name, size_t namelen, 287 const uint8_t *value, size_t valuelen, 288 uint8_t flags, 289 void *userp); 290 static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 291 int32_t stream_id, 292 const uint8_t *mem, size_t len, void *userp); 293 294 /* 295 * Initialize the cfilter context 296 */ 297 static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, 298 struct Curl_easy *data) 299 { 300 struct cf_h2_proxy_ctx *ctx = cf->ctx; 301 CURLcode result = CURLE_OUT_OF_MEMORY; 302 nghttp2_session_callbacks *cbs = NULL; 303 int rc; 304 305 DEBUGASSERT(!ctx->h2); 306 memset(&ctx->tunnel, 0, sizeof(ctx->tunnel)); 307 308 Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS); 309 Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS); 310 311 if(tunnel_stream_init(cf, &ctx->tunnel)) 312 goto out; 313 314 rc = nghttp2_session_callbacks_new(&cbs); 315 if(rc) { 316 failf(data, "Couldn't initialize nghttp2 callbacks"); 317 goto out; 318 } 319 320 nghttp2_session_callbacks_set_send_callback(cbs, on_session_send); 321 nghttp2_session_callbacks_set_on_frame_recv_callback( 322 cbs, proxy_h2_on_frame_recv); 323 #ifndef CURL_DISABLE_VERBOSE_STRINGS 324 nghttp2_session_callbacks_set_on_frame_send_callback(cbs, 325 proxy_h2_on_frame_send); 326 #endif 327 nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 328 cbs, tunnel_recv_callback); 329 nghttp2_session_callbacks_set_on_stream_close_callback( 330 cbs, proxy_h2_on_stream_close); 331 nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header); 332 333 /* The nghttp2 session is not yet setup, do it */ 334 rc = proxy_h2_client_new(cf, cbs); 335 if(rc) { 336 failf(data, "Couldn't initialize nghttp2"); 337 goto out; 338 } 339 340 { 341 nghttp2_settings_entry iv[3]; 342 343 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; 344 iv[0].value = Curl_multi_max_concurrent_streams(data->multi); 345 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; 346 iv[1].value = H2_TUNNEL_WINDOW_SIZE; 347 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; 348 iv[2].value = 0; 349 rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, iv, 3); 350 if(rc) { 351 failf(data, "nghttp2_submit_settings() failed: %s(%d)", 352 nghttp2_strerror(rc), rc); 353 result = CURLE_HTTP2; 354 goto out; 355 } 356 } 357 358 rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0, 359 PROXY_HTTP2_HUGE_WINDOW_SIZE); 360 if(rc) { 361 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)", 362 nghttp2_strerror(rc), rc); 363 result = CURLE_HTTP2; 364 goto out; 365 } 366 367 368 /* all set, traffic will be send on connect */ 369 result = CURLE_OK; 370 371 out: 372 if(cbs) 373 nghttp2_session_callbacks_del(cbs); 374 CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); 375 return result; 376 } 377 378 static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx) 379 { 380 return !nghttp2_session_want_read(ctx->h2) && 381 !nghttp2_session_want_write(ctx->h2); 382 } 383 384 static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, 385 struct Curl_easy *data) 386 { 387 struct cf_h2_proxy_ctx *ctx = cf->ctx; 388 size_t nwritten; 389 CURLcode result; 390 391 (void)data; 392 if(Curl_bufq_is_empty(&ctx->outbufq)) 393 return CURLE_OK; 394 395 result = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, 396 &nwritten); 397 if(result) { 398 if(result == CURLE_AGAIN) { 399 CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", 400 Curl_bufq_len(&ctx->outbufq)); 401 ctx->nw_out_blocked = 1; 402 } 403 return result; 404 } 405 CURL_TRC_CF(data, cf, "[0] nw send buffer flushed"); 406 return Curl_bufq_is_empty(&ctx->outbufq) ? CURLE_OK : CURLE_AGAIN; 407 } 408 409 /* 410 * Processes pending input left in network input buffer. 411 * This function returns 0 if it succeeds, or -1 and error code will 412 * be assigned to *err. 413 */ 414 static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, 415 struct Curl_easy *data, 416 CURLcode *err) 417 { 418 struct cf_h2_proxy_ctx *ctx = cf->ctx; 419 const unsigned char *buf; 420 size_t blen; 421 ssize_t rv; 422 423 while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { 424 425 rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); 426 CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); 427 if(rv < 0) { 428 failf(data, 429 "process_pending_input: nghttp2_session_mem_recv() returned " 430 "%zd:%s", rv, nghttp2_strerror((int)rv)); 431 *err = CURLE_RECV_ERROR; 432 return -1; 433 } 434 Curl_bufq_skip(&ctx->inbufq, (size_t)rv); 435 if(Curl_bufq_is_empty(&ctx->inbufq)) { 436 CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); 437 break; 438 } 439 else { 440 CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left " 441 "in connection buffer", Curl_bufq_len(&ctx->inbufq)); 442 } 443 } 444 445 return 0; 446 } 447 448 static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, 449 struct Curl_easy *data) 450 { 451 struct cf_h2_proxy_ctx *ctx = cf->ctx; 452 CURLcode result = CURLE_OK; 453 size_t nread; 454 455 /* Process network input buffer fist */ 456 if(!Curl_bufq_is_empty(&ctx->inbufq)) { 457 CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", 458 Curl_bufq_len(&ctx->inbufq)); 459 if(proxy_h2_process_pending_input(cf, data, &result) < 0) 460 return result; 461 } 462 463 /* Receive data from the "lower" filters, e.g. network until 464 * it is time to stop or we have enough data for this stream */ 465 while(!ctx->conn_closed && /* not closed the connection */ 466 !ctx->tunnel.closed && /* nor the tunnel */ 467 Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ 468 !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { 469 470 result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); 471 CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu", 472 Curl_bufq_len(&ctx->inbufq), result, nread); 473 if(result) { 474 if(result != CURLE_AGAIN) { 475 failf(data, "Failed receiving HTTP2 data"); 476 return result; 477 } 478 break; 479 } 480 else if(nread == 0) { 481 ctx->conn_closed = TRUE; 482 break; 483 } 484 485 if(proxy_h2_process_pending_input(cf, data, &result)) 486 return result; 487 } 488 489 if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { 490 connclose(cf->conn, "GOAWAY received"); 491 } 492 493 return CURLE_OK; 494 } 495 496 static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf, 497 struct Curl_easy *data) 498 { 499 struct cf_h2_proxy_ctx *ctx = cf->ctx; 500 int rv = 0; 501 502 ctx->nw_out_blocked = 0; 503 while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2)) 504 rv = nghttp2_session_send(ctx->h2); 505 506 if(nghttp2_is_fatal(rv)) { 507 CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d", 508 nghttp2_strerror(rv), rv); 509 return CURLE_SEND_ERROR; 510 } 511 return proxy_h2_nw_out_flush(cf, data); 512 } 513 514 static ssize_t on_session_send(nghttp2_session *h2, 515 const uint8_t *buf, size_t blen, int flags, 516 void *userp) 517 { 518 struct Curl_cfilter *cf = userp; 519 struct cf_h2_proxy_ctx *ctx = cf->ctx; 520 struct Curl_easy *data = CF_DATA_CURRENT(cf); 521 size_t nwritten; 522 CURLcode result = CURLE_OK; 523 524 (void)h2; 525 (void)flags; 526 DEBUGASSERT(data); 527 528 result = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, 529 proxy_h2_nw_out_writer, cf, &nwritten); 530 if(result) { 531 if(result == CURLE_AGAIN) { 532 ctx->nw_out_blocked = 1; 533 return NGHTTP2_ERR_WOULDBLOCK; 534 } 535 failf(data, "Failed sending HTTP2 data"); 536 return NGHTTP2_ERR_CALLBACK_FAILURE; 537 } 538 539 if(!nwritten) 540 return NGHTTP2_ERR_WOULDBLOCK; 541 542 return (nwritten > SSIZE_T_MAX) ? 543 NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; 544 } 545 546 #ifndef CURL_DISABLE_VERBOSE_STRINGS 547 static int proxy_h2_fr_print(const nghttp2_frame *frame, 548 char *buffer, size_t blen) 549 { 550 switch(frame->hd.type) { 551 case NGHTTP2_DATA: { 552 return msnprintf(buffer, blen, 553 "FRAME[DATA, len=%d, eos=%d, padlen=%d]", 554 (int)frame->hd.length, 555 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM), 556 (int)frame->data.padlen); 557 } 558 case NGHTTP2_HEADERS: { 559 return msnprintf(buffer, blen, 560 "FRAME[HEADERS, len=%d, hend=%d, eos=%d]", 561 (int)frame->hd.length, 562 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS), 563 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)); 564 } 565 case NGHTTP2_PRIORITY: { 566 return msnprintf(buffer, blen, 567 "FRAME[PRIORITY, len=%d, flags=%d]", 568 (int)frame->hd.length, frame->hd.flags); 569 } 570 case NGHTTP2_RST_STREAM: { 571 return msnprintf(buffer, blen, 572 "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]", 573 (int)frame->hd.length, frame->hd.flags, 574 frame->rst_stream.error_code); 575 } 576 case NGHTTP2_SETTINGS: { 577 if(frame->hd.flags & NGHTTP2_FLAG_ACK) { 578 return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); 579 } 580 return msnprintf(buffer, blen, 581 "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); 582 } 583 case NGHTTP2_PUSH_PROMISE: 584 return msnprintf(buffer, blen, 585 "FRAME[PUSH_PROMISE, len=%d, hend=%d]", 586 (int)frame->hd.length, 587 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)); 588 case NGHTTP2_PING: 589 return msnprintf(buffer, blen, 590 "FRAME[PING, len=%d, ack=%d]", 591 (int)frame->hd.length, 592 frame->hd.flags & NGHTTP2_FLAG_ACK); 593 case NGHTTP2_GOAWAY: { 594 char scratch[128]; 595 size_t s_len = CURL_ARRAYSIZE(scratch); 596 size_t len = (frame->goaway.opaque_data_len < s_len) ? 597 frame->goaway.opaque_data_len : s_len-1; 598 if(len) 599 memcpy(scratch, frame->goaway.opaque_data, len); 600 scratch[len] = '\0'; 601 return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', " 602 "last_stream=%d]", frame->goaway.error_code, 603 scratch, frame->goaway.last_stream_id); 604 } 605 case NGHTTP2_WINDOW_UPDATE: { 606 return msnprintf(buffer, blen, 607 "FRAME[WINDOW_UPDATE, incr=%d]", 608 frame->window_update.window_size_increment); 609 } 610 default: 611 return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", 612 frame->hd.type, (int)frame->hd.length, 613 frame->hd.flags); 614 } 615 } 616 617 static int proxy_h2_on_frame_send(nghttp2_session *session, 618 const nghttp2_frame *frame, 619 void *userp) 620 { 621 struct Curl_cfilter *cf = userp; 622 struct Curl_easy *data = CF_DATA_CURRENT(cf); 623 624 (void)session; 625 DEBUGASSERT(data); 626 if(data && Curl_trc_cf_is_verbose(cf, data)) { 627 char buffer[256]; 628 int len; 629 len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); 630 buffer[len] = 0; 631 CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer); 632 } 633 return 0; 634 } 635 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 636 637 static int proxy_h2_on_frame_recv(nghttp2_session *session, 638 const nghttp2_frame *frame, 639 void *userp) 640 { 641 struct Curl_cfilter *cf = userp; 642 struct cf_h2_proxy_ctx *ctx = cf->ctx; 643 struct Curl_easy *data = CF_DATA_CURRENT(cf); 644 int32_t stream_id = frame->hd.stream_id; 645 646 (void)session; 647 DEBUGASSERT(data); 648 #ifndef CURL_DISABLE_VERBOSE_STRINGS 649 if(Curl_trc_cf_is_verbose(cf, data)) { 650 char buffer[256]; 651 int len; 652 len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1); 653 buffer[len] = 0; 654 CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer); 655 } 656 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ 657 658 if(!stream_id) { 659 /* stream ID zero is for connection-oriented stuff */ 660 DEBUGASSERT(data); 661 switch(frame->hd.type) { 662 case NGHTTP2_SETTINGS: 663 /* Since the initial stream window is 64K, a request might be on HOLD, 664 * due to exhaustion. The (initial) SETTINGS may announce a much larger 665 * window and *assume* that we treat this like a WINDOW_UPDATE. Some 666 * servers send an explicit WINDOW_UPDATE, but not all seem to do that. 667 * To be safe, we UNHOLD a stream in order not to stall. */ 668 if(CURL_WANT_SEND(data)) { 669 drain_tunnel(cf, data, &ctx->tunnel); 670 } 671 break; 672 case NGHTTP2_GOAWAY: 673 ctx->rcvd_goaway = TRUE; 674 break; 675 default: 676 break; 677 } 678 return 0; 679 } 680 681 if(stream_id != ctx->tunnel.stream_id) { 682 CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id); 683 return NGHTTP2_ERR_CALLBACK_FAILURE; 684 } 685 686 switch(frame->hd.type) { 687 case NGHTTP2_HEADERS: 688 /* nghttp2 guarantees that :status is received, and we store it to 689 stream->status_code. Fuzzing has proven this can still be reached 690 without status code having been set. */ 691 if(!ctx->tunnel.resp) 692 return NGHTTP2_ERR_CALLBACK_FAILURE; 693 /* Only final status code signals the end of header */ 694 CURL_TRC_CF(data, cf, "[%d] got http status: %d", 695 stream_id, ctx->tunnel.resp->status); 696 if(!ctx->tunnel.has_final_response) { 697 if(ctx->tunnel.resp->status / 100 != 1) { 698 ctx->tunnel.has_final_response = TRUE; 699 } 700 } 701 break; 702 case NGHTTP2_WINDOW_UPDATE: 703 if(CURL_WANT_SEND(data)) { 704 drain_tunnel(cf, data, &ctx->tunnel); 705 } 706 break; 707 default: 708 break; 709 } 710 return 0; 711 } 712 713 static int proxy_h2_on_header(nghttp2_session *session, 714 const nghttp2_frame *frame, 715 const uint8_t *name, size_t namelen, 716 const uint8_t *value, size_t valuelen, 717 uint8_t flags, 718 void *userp) 719 { 720 struct Curl_cfilter *cf = userp; 721 struct cf_h2_proxy_ctx *ctx = cf->ctx; 722 struct Curl_easy *data = CF_DATA_CURRENT(cf); 723 int32_t stream_id = frame->hd.stream_id; 724 CURLcode result; 725 726 (void)flags; 727 (void)data; 728 (void)session; 729 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 730 if(stream_id != ctx->tunnel.stream_id) { 731 CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: " 732 "%.*s: %.*s", stream_id, 733 (int)namelen, name, (int)valuelen, value); 734 return NGHTTP2_ERR_CALLBACK_FAILURE; 735 } 736 737 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) 738 return NGHTTP2_ERR_CALLBACK_FAILURE; 739 740 if(ctx->tunnel.has_final_response) { 741 /* we do not do anything with trailers for tunnel streams */ 742 return 0; 743 } 744 745 if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && 746 memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { 747 int http_status; 748 struct http_resp *resp; 749 750 /* status: always comes first, we might get more than one response, 751 * link the previous ones for keepers */ 752 result = Curl_http_decode_status(&http_status, 753 (const char *)value, valuelen); 754 if(result) 755 return NGHTTP2_ERR_CALLBACK_FAILURE; 756 result = Curl_http_resp_make(&resp, http_status, NULL); 757 if(result) 758 return NGHTTP2_ERR_CALLBACK_FAILURE; 759 resp->prev = ctx->tunnel.resp; 760 ctx->tunnel.resp = resp; 761 CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", 762 stream_id, ctx->tunnel.resp->status); 763 return 0; 764 } 765 766 if(!ctx->tunnel.resp) 767 return NGHTTP2_ERR_CALLBACK_FAILURE; 768 769 result = Curl_dynhds_add(&ctx->tunnel.resp->headers, 770 (const char *)name, namelen, 771 (const char *)value, valuelen); 772 if(result) 773 return NGHTTP2_ERR_CALLBACK_FAILURE; 774 775 CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", 776 stream_id, (int)namelen, name, (int)valuelen, value); 777 778 return 0; /* 0 is successful */ 779 } 780 781 static ssize_t tunnel_send_callback(nghttp2_session *session, 782 int32_t stream_id, 783 uint8_t *buf, size_t length, 784 uint32_t *data_flags, 785 nghttp2_data_source *source, 786 void *userp) 787 { 788 struct Curl_cfilter *cf = userp; 789 struct cf_h2_proxy_ctx *ctx = cf->ctx; 790 struct Curl_easy *data = CF_DATA_CURRENT(cf); 791 struct tunnel_stream *ts; 792 CURLcode result; 793 size_t nread; 794 795 (void)source; 796 (void)data; 797 (void)ctx; 798 799 if(!stream_id) 800 return NGHTTP2_ERR_INVALID_ARGUMENT; 801 802 ts = nghttp2_session_get_stream_user_data(session, stream_id); 803 if(!ts) 804 return NGHTTP2_ERR_CALLBACK_FAILURE; 805 DEBUGASSERT(ts == &ctx->tunnel); 806 807 result = Curl_bufq_read(&ts->sendbuf, buf, length, &nread); 808 if(result) { 809 if(result != CURLE_AGAIN) 810 return NGHTTP2_ERR_CALLBACK_FAILURE; 811 return NGHTTP2_ERR_DEFERRED; 812 } 813 if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) 814 *data_flags = NGHTTP2_DATA_FLAG_EOF; 815 816 CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", 817 ts->stream_id, nread); 818 return (nread > SSIZE_T_MAX) ? 819 NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread; 820 } 821 822 static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, 823 int32_t stream_id, 824 const uint8_t *mem, size_t len, void *userp) 825 { 826 struct Curl_cfilter *cf = userp; 827 struct cf_h2_proxy_ctx *ctx = cf->ctx; 828 size_t nwritten; 829 CURLcode result; 830 831 (void)flags; 832 (void)session; 833 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ 834 835 if(stream_id != ctx->tunnel.stream_id) 836 return NGHTTP2_ERR_CALLBACK_FAILURE; 837 838 result = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &nwritten); 839 if(result) { 840 if(result != CURLE_AGAIN) 841 return NGHTTP2_ERR_CALLBACK_FAILURE; 842 #ifdef DEBUGBUILD 843 nwritten = 0; 844 #endif 845 } 846 /* tunnel.recbuf has soft limit, any success MUST add all data */ 847 DEBUGASSERT((size_t)nwritten == len); 848 return 0; 849 } 850 851 static int proxy_h2_on_stream_close(nghttp2_session *session, 852 int32_t stream_id, 853 uint32_t error_code, void *userp) 854 { 855 struct Curl_cfilter *cf = userp; 856 struct cf_h2_proxy_ctx *ctx = cf->ctx; 857 struct Curl_easy *data = CF_DATA_CURRENT(cf); 858 859 (void)session; 860 (void)data; 861 862 if(stream_id != ctx->tunnel.stream_id) 863 return 0; 864 865 CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", 866 stream_id, nghttp2_http2_strerror(error_code), error_code); 867 ctx->tunnel.closed = TRUE; 868 ctx->tunnel.error = error_code; 869 870 return 0; 871 } 872 873 static CURLcode proxy_h2_submit(int32_t *pstream_id, 874 struct Curl_cfilter *cf, 875 struct Curl_easy *data, 876 nghttp2_session *h2, 877 struct httpreq *req, 878 const nghttp2_priority_spec *pri_spec, 879 void *stream_user_data, 880 nghttp2_data_source_read_callback read_callback, 881 void *read_ctx) 882 { 883 struct dynhds h2_headers; 884 nghttp2_nv *nva = NULL; 885 int32_t stream_id = -1; 886 size_t nheader; 887 CURLcode result; 888 889 (void)cf; 890 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 891 result = Curl_http_req_to_h2(&h2_headers, req, data); 892 if(result) 893 goto out; 894 895 nva = Curl_dynhds_to_nva(&h2_headers, &nheader); 896 if(!nva) { 897 result = CURLE_OUT_OF_MEMORY; 898 goto out; 899 } 900 901 if(read_callback) { 902 nghttp2_data_provider data_prd; 903 904 data_prd.read_callback = read_callback; 905 data_prd.source.ptr = read_ctx; 906 stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 907 &data_prd, stream_user_data); 908 } 909 else { 910 stream_id = nghttp2_submit_request(h2, pri_spec, nva, nheader, 911 NULL, stream_user_data); 912 } 913 914 if(stream_id < 0) { 915 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)", 916 nghttp2_strerror(stream_id), stream_id); 917 result = CURLE_SEND_ERROR; 918 goto out; 919 } 920 result = CURLE_OK; 921 922 out: 923 free(nva); 924 Curl_dynhds_free(&h2_headers); 925 *pstream_id = stream_id; 926 return result; 927 } 928 929 static CURLcode submit_CONNECT(struct Curl_cfilter *cf, 930 struct Curl_easy *data, 931 struct tunnel_stream *ts) 932 { 933 struct cf_h2_proxy_ctx *ctx = cf->ctx; 934 CURLcode result; 935 struct httpreq *req = NULL; 936 937 result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); 938 if(result) 939 goto out; 940 result = Curl_creader_set_null(data); 941 if(result) 942 goto out; 943 944 infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); 945 946 result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, 947 NULL, ts, tunnel_send_callback, cf); 948 if(result) { 949 CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s", 950 ts->stream_id, nghttp2_strerror(ts->stream_id)); 951 } 952 953 out: 954 if(req) 955 Curl_http_req_free(req); 956 if(result) 957 failf(data, "Failed sending CONNECT to proxy"); 958 return result; 959 } 960 961 static CURLcode inspect_response(struct Curl_cfilter *cf, 962 struct Curl_easy *data, 963 struct tunnel_stream *ts) 964 { 965 CURLcode result = CURLE_OK; 966 struct dynhds_entry *auth_reply = NULL; 967 (void)cf; 968 969 DEBUGASSERT(ts->resp); 970 if(ts->resp->status/100 == 2) { 971 infof(data, "CONNECT tunnel established, response %d", ts->resp->status); 972 h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); 973 return CURLE_OK; 974 } 975 976 if(ts->resp->status == 401) { 977 auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); 978 } 979 else if(ts->resp->status == 407) { 980 auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); 981 } 982 983 if(auth_reply) { 984 CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", 985 auth_reply->value); 986 result = Curl_http_input_auth(data, ts->resp->status == 407, 987 auth_reply->value); 988 if(result) 989 return result; 990 if(data->req.newurl) { 991 /* Indicator that we should try again */ 992 Curl_safefree(data->req.newurl); 993 h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); 994 return CURLE_OK; 995 } 996 } 997 998 /* Seems to have failed */ 999 return CURLE_RECV_ERROR; 1000 } 1001 1002 static CURLcode H2_CONNECT(struct Curl_cfilter *cf, 1003 struct Curl_easy *data, 1004 struct tunnel_stream *ts) 1005 { 1006 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1007 CURLcode result = CURLE_OK; 1008 1009 DEBUGASSERT(ts); 1010 DEBUGASSERT(ts->authority); 1011 do { 1012 switch(ts->state) { 1013 case H2_TUNNEL_INIT: 1014 /* Prepare the CONNECT request and make a first attempt to send. */ 1015 CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); 1016 result = submit_CONNECT(cf, data, ts); 1017 if(result) 1018 goto out; 1019 h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); 1020 FALLTHROUGH(); 1021 1022 case H2_TUNNEL_CONNECT: 1023 /* see that the request is completely sent */ 1024 result = proxy_h2_progress_ingress(cf, data); 1025 if(!result) 1026 result = proxy_h2_progress_egress(cf, data); 1027 if(result && result != CURLE_AGAIN) { 1028 h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); 1029 break; 1030 } 1031 1032 if(ts->has_final_response) { 1033 h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); 1034 } 1035 else { 1036 result = CURLE_OK; 1037 goto out; 1038 } 1039 FALLTHROUGH(); 1040 1041 case H2_TUNNEL_RESPONSE: 1042 DEBUGASSERT(ts->has_final_response); 1043 result = inspect_response(cf, data, ts); 1044 if(result) 1045 goto out; 1046 break; 1047 1048 case H2_TUNNEL_ESTABLISHED: 1049 return CURLE_OK; 1050 1051 case H2_TUNNEL_FAILED: 1052 return CURLE_RECV_ERROR; 1053 1054 default: 1055 break; 1056 } 1057 1058 } while(ts->state == H2_TUNNEL_INIT); 1059 1060 out: 1061 if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) 1062 h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); 1063 return result; 1064 } 1065 1066 static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, 1067 struct Curl_easy *data, 1068 bool *done) 1069 { 1070 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1071 CURLcode result = CURLE_OK; 1072 struct cf_call_data save; 1073 timediff_t check; 1074 struct tunnel_stream *ts = &ctx->tunnel; 1075 1076 if(cf->connected) { 1077 *done = TRUE; 1078 return CURLE_OK; 1079 } 1080 1081 /* Connect the lower filters first */ 1082 if(!cf->next->connected) { 1083 result = Curl_conn_cf_connect(cf->next, data, done); 1084 if(result || !*done) 1085 return result; 1086 } 1087 1088 *done = FALSE; 1089 1090 CF_DATA_SAVE(save, cf, data); 1091 if(!ctx->h2) { 1092 result = cf_h2_proxy_ctx_init(cf, data); 1093 if(result) 1094 goto out; 1095 } 1096 DEBUGASSERT(ts->authority); 1097 1098 check = Curl_timeleft(data, NULL, TRUE); 1099 if(check <= 0) { 1100 failf(data, "Proxy CONNECT aborted due to timeout"); 1101 result = CURLE_OPERATION_TIMEDOUT; 1102 goto out; 1103 } 1104 1105 /* for the secondary socket (FTP), use the "connect to host" 1106 * but ignore the "connect to port" (use the secondary port) 1107 */ 1108 result = H2_CONNECT(cf, data, ts); 1109 1110 out: 1111 *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED); 1112 if(*done) { 1113 cf->connected = TRUE; 1114 /* The real request will follow the CONNECT, reset request partially */ 1115 Curl_req_soft_reset(&data->req, data); 1116 Curl_client_reset(data); 1117 } 1118 CF_DATA_RESTORE(cf, save); 1119 return result; 1120 } 1121 1122 static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) 1123 { 1124 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1125 1126 if(ctx) { 1127 struct cf_call_data save; 1128 1129 CF_DATA_SAVE(save, cf, data); 1130 cf_h2_proxy_ctx_clear(ctx); 1131 CF_DATA_RESTORE(cf, save); 1132 } 1133 if(cf->next) 1134 cf->next->cft->do_close(cf->next, data); 1135 } 1136 1137 static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, 1138 struct Curl_easy *data) 1139 { 1140 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1141 1142 (void)data; 1143 if(ctx) { 1144 cf_h2_proxy_ctx_free(ctx); 1145 cf->ctx = NULL; 1146 } 1147 } 1148 1149 static CURLcode cf_h2_proxy_shutdown(struct Curl_cfilter *cf, 1150 struct Curl_easy *data, bool *done) 1151 { 1152 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1153 struct cf_call_data save; 1154 CURLcode result; 1155 int rv; 1156 1157 if(!cf->connected || !ctx->h2 || cf->shutdown || ctx->conn_closed) { 1158 *done = TRUE; 1159 return CURLE_OK; 1160 } 1161 1162 CF_DATA_SAVE(save, cf, data); 1163 1164 if(!ctx->sent_goaway) { 1165 rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE, 1166 0, 0, 1167 (const uint8_t *)"shutdown", 1168 sizeof("shutdown")); 1169 if(rv) { 1170 failf(data, "nghttp2_submit_goaway() failed: %s(%d)", 1171 nghttp2_strerror(rv), rv); 1172 result = CURLE_SEND_ERROR; 1173 goto out; 1174 } 1175 ctx->sent_goaway = TRUE; 1176 } 1177 /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */ 1178 result = CURLE_OK; 1179 if(nghttp2_session_want_write(ctx->h2)) 1180 result = proxy_h2_progress_egress(cf, data); 1181 if(!result && nghttp2_session_want_read(ctx->h2)) 1182 result = proxy_h2_progress_ingress(cf, data); 1183 1184 *done = (ctx->conn_closed || 1185 (!result && !nghttp2_session_want_write(ctx->h2) && 1186 !nghttp2_session_want_read(ctx->h2))); 1187 out: 1188 CF_DATA_RESTORE(cf, save); 1189 cf->shutdown = (result || *done); 1190 return result; 1191 } 1192 1193 static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf, 1194 const struct Curl_easy *data) 1195 { 1196 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1197 if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) || 1198 (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED && 1199 !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) 1200 return TRUE; 1201 return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; 1202 } 1203 1204 static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, 1205 struct Curl_easy *data, 1206 struct easy_pollset *ps) 1207 { 1208 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1209 struct cf_call_data save; 1210 curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); 1211 bool want_recv, want_send; 1212 1213 if(!cf->connected && ctx->h2) { 1214 want_send = nghttp2_session_want_write(ctx->h2) || 1215 !Curl_bufq_is_empty(&ctx->outbufq) || 1216 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); 1217 want_recv = nghttp2_session_want_read(ctx->h2); 1218 } 1219 else 1220 Curl_pollset_check(data, ps, sock, &want_recv, &want_send); 1221 1222 if(ctx->h2 && (want_recv || want_send)) { 1223 bool c_exhaust, s_exhaust; 1224 1225 CF_DATA_SAVE(save, cf, data); 1226 c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); 1227 s_exhaust = ctx->tunnel.stream_id >= 0 && 1228 !nghttp2_session_get_stream_remote_window_size( 1229 ctx->h2, ctx->tunnel.stream_id); 1230 want_recv = (want_recv || c_exhaust || s_exhaust); 1231 want_send = (!s_exhaust && want_send) || 1232 (!c_exhaust && nghttp2_session_want_write(ctx->h2)) || 1233 !Curl_bufq_is_empty(&ctx->outbufq) || 1234 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); 1235 1236 Curl_pollset_set(data, ps, sock, want_recv, want_send); 1237 CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", 1238 want_recv, want_send); 1239 CF_DATA_RESTORE(cf, save); 1240 } 1241 else if(ctx->sent_goaway && !cf->shutdown) { 1242 /* shutdown in progress */ 1243 CF_DATA_SAVE(save, cf, data); 1244 want_send = nghttp2_session_want_write(ctx->h2) || 1245 !Curl_bufq_is_empty(&ctx->outbufq) || 1246 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf); 1247 want_recv = nghttp2_session_want_read(ctx->h2); 1248 Curl_pollset_set(data, ps, sock, want_recv, want_send); 1249 CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d", 1250 want_recv, want_send); 1251 CF_DATA_RESTORE(cf, save); 1252 } 1253 } 1254 1255 static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf, 1256 struct Curl_easy *data, 1257 size_t *pnread) 1258 { 1259 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1260 1261 *pnread = 0; 1262 if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { 1263 CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " 1264 "connection", ctx->tunnel.stream_id); 1265 connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ 1266 return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ 1267 } 1268 else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { 1269 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", 1270 ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), 1271 ctx->tunnel.error); 1272 return CURLE_HTTP2_STREAM; 1273 } 1274 else if(ctx->tunnel.reset) { 1275 failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); 1276 return CURLE_RECV_ERROR; 1277 } 1278 1279 CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> 0", 1280 ctx->tunnel.stream_id); 1281 return CURLE_OK; 1282 } 1283 1284 static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 1285 char *buf, size_t len, size_t *pnread) 1286 { 1287 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1288 CURLcode result = CURLE_AGAIN; 1289 1290 *pnread = 0; 1291 if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) 1292 result = Curl_bufq_cread(&ctx->tunnel.recvbuf, buf, len, pnread); 1293 else { 1294 if(ctx->tunnel.closed) { 1295 result = h2_handle_tunnel_close(cf, data, pnread); 1296 } 1297 else if(ctx->tunnel.reset || 1298 (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || 1299 (ctx->rcvd_goaway && 1300 ctx->last_stream_id < ctx->tunnel.stream_id)) { 1301 result = CURLE_RECV_ERROR; 1302 } 1303 else 1304 result = CURLE_AGAIN; 1305 } 1306 1307 CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu", 1308 ctx->tunnel.stream_id, len, result, *pnread); 1309 return result; 1310 } 1311 1312 static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, 1313 struct Curl_easy *data, 1314 char *buf, size_t len, 1315 size_t *pnread) 1316 { 1317 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1318 struct cf_call_data save; 1319 CURLcode result; 1320 1321 *pnread = 0; 1322 CF_DATA_SAVE(save, cf, data); 1323 1324 if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { 1325 result = CURLE_RECV_ERROR; 1326 goto out; 1327 } 1328 1329 if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { 1330 result = proxy_h2_progress_ingress(cf, data); 1331 if(result) 1332 goto out; 1333 } 1334 1335 result = tunnel_recv(cf, data, buf, len, pnread); 1336 1337 if(!result) { 1338 CURL_TRC_CF(data, cf, "[%d] increase window by %zu", 1339 ctx->tunnel.stream_id, *pnread); 1340 nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, *pnread); 1341 } 1342 1343 result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); 1344 1345 out: 1346 if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && 1347 (!result || (result == CURLE_AGAIN))) { 1348 /* data pending and no fatal error to report. Need to trigger 1349 * draining to avoid stalling when no socket events happen. */ 1350 drain_tunnel(cf, data, &ctx->tunnel); 1351 } 1352 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu", 1353 ctx->tunnel.stream_id, len, result, *pnread); 1354 CF_DATA_RESTORE(cf, save); 1355 return result; 1356 } 1357 1358 static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, 1359 struct Curl_easy *data, 1360 const void *buf, size_t len, bool eos, 1361 size_t *pnwritten) 1362 { 1363 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1364 struct cf_call_data save; 1365 int rv; 1366 CURLcode result; 1367 1368 (void)eos; 1369 *pnwritten = 0; 1370 CF_DATA_SAVE(save, cf, data); 1371 1372 if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { 1373 result = CURLE_SEND_ERROR; 1374 goto out; 1375 } 1376 1377 if(ctx->tunnel.closed) { 1378 result = CURLE_SEND_ERROR; 1379 goto out; 1380 } 1381 1382 result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); 1383 CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten); 1384 if(result && (result != CURLE_AGAIN)) 1385 goto out; 1386 1387 if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { 1388 /* req body data is buffered, resume the potentially suspended stream */ 1389 rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); 1390 if(nghttp2_is_fatal(rv)) { 1391 result = CURLE_SEND_ERROR; 1392 goto out; 1393 } 1394 } 1395 1396 result = Curl_1st_fatal(result, proxy_h2_progress_ingress(cf, data)); 1397 result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); 1398 1399 if(!result && proxy_h2_should_close_session(ctx)) { 1400 /* nghttp2 thinks this session is done. If the stream has not been 1401 * closed, this is an error state for out transfer */ 1402 if(ctx->tunnel.closed) { 1403 result = CURLE_SEND_ERROR; 1404 } 1405 else { 1406 CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); 1407 result = CURLE_HTTP2; 1408 } 1409 } 1410 1411 out: 1412 if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && 1413 (!result || (result == CURLE_AGAIN))) { 1414 /* data pending and no fatal error to report. Need to trigger 1415 * draining to avoid stalling when no socket events happen. */ 1416 drain_tunnel(cf, data, &ctx->tunnel); 1417 } 1418 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " 1419 "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", 1420 ctx->tunnel.stream_id, len, result, *pnwritten, 1421 nghttp2_session_get_stream_remote_window_size( 1422 ctx->h2, ctx->tunnel.stream_id), 1423 nghttp2_session_get_remote_window_size(ctx->h2), 1424 Curl_bufq_len(&ctx->tunnel.sendbuf), 1425 Curl_bufq_len(&ctx->outbufq)); 1426 CF_DATA_RESTORE(cf, save); 1427 return result; 1428 } 1429 1430 static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, 1431 struct Curl_easy *data) 1432 { 1433 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1434 struct cf_call_data save; 1435 CURLcode result = CURLE_OK; 1436 1437 CF_DATA_SAVE(save, cf, data); 1438 if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { 1439 /* resume the potentially suspended tunnel */ 1440 int rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); 1441 if(nghttp2_is_fatal(rv)) { 1442 result = CURLE_SEND_ERROR; 1443 goto out; 1444 } 1445 } 1446 1447 result = proxy_h2_progress_egress(cf, data); 1448 1449 out: 1450 CURL_TRC_CF(data, cf, "[%d] flush -> %d, " 1451 "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", 1452 ctx->tunnel.stream_id, result, 1453 nghttp2_session_get_stream_remote_window_size( 1454 ctx->h2, ctx->tunnel.stream_id), 1455 nghttp2_session_get_remote_window_size(ctx->h2), 1456 Curl_bufq_len(&ctx->tunnel.sendbuf), 1457 Curl_bufq_len(&ctx->outbufq)); 1458 CF_DATA_RESTORE(cf, save); 1459 return result; 1460 } 1461 1462 static bool proxy_h2_connisalive(struct Curl_cfilter *cf, 1463 struct Curl_easy *data, 1464 bool *input_pending) 1465 { 1466 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1467 bool alive = TRUE; 1468 1469 *input_pending = FALSE; 1470 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 1471 return FALSE; 1472 1473 if(*input_pending) { 1474 /* This happens before we have sent off a request and the connection is 1475 not in use by any other transfer, there should not be any data here, 1476 only "protocol frames" */ 1477 CURLcode result; 1478 size_t nread; 1479 1480 *input_pending = FALSE; 1481 result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); 1482 if(!result) { 1483 if(proxy_h2_process_pending_input(cf, data, &result) < 0) 1484 /* immediate error, considered dead */ 1485 alive = FALSE; 1486 else { 1487 alive = !proxy_h2_should_close_session(ctx); 1488 } 1489 } 1490 else if(result != CURLE_AGAIN) { 1491 /* the read failed so let's say this is dead anyway */ 1492 alive = FALSE; 1493 } 1494 } 1495 1496 return alive; 1497 } 1498 1499 static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, 1500 struct Curl_easy *data, 1501 bool *input_pending) 1502 { 1503 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1504 bool alive; 1505 struct cf_call_data save; 1506 1507 *input_pending = FALSE; 1508 CF_DATA_SAVE(save, cf, data); 1509 alive = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); 1510 CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", 1511 alive, *input_pending); 1512 CF_DATA_RESTORE(cf, save); 1513 return alive; 1514 } 1515 1516 static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, 1517 struct Curl_easy *data, 1518 int query, int *pres1, void *pres2) 1519 { 1520 struct cf_h2_proxy_ctx *ctx = cf->ctx; 1521 1522 switch(query) { 1523 case CF_QUERY_HOST_PORT: 1524 *pres1 = (int)cf->conn->http_proxy.port; 1525 *((const char **)pres2) = cf->conn->http_proxy.host.name; 1526 return CURLE_OK; 1527 case CF_QUERY_NEED_FLUSH: { 1528 if(!Curl_bufq_is_empty(&ctx->outbufq) || 1529 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { 1530 CURL_TRC_CF(data, cf, "needs flush"); 1531 *pres1 = TRUE; 1532 return CURLE_OK; 1533 } 1534 break; 1535 } 1536 default: 1537 break; 1538 } 1539 return cf->next ? 1540 cf->next->cft->query(cf->next, data, query, pres1, pres2) : 1541 CURLE_UNKNOWN_OPTION; 1542 } 1543 1544 static CURLcode cf_h2_proxy_cntrl(struct Curl_cfilter *cf, 1545 struct Curl_easy *data, 1546 int event, int arg1, void *arg2) 1547 { 1548 CURLcode result = CURLE_OK; 1549 struct cf_call_data save; 1550 1551 (void)arg1; 1552 (void)arg2; 1553 1554 switch(event) { 1555 case CF_CTRL_FLUSH: 1556 CF_DATA_SAVE(save, cf, data); 1557 result = cf_h2_proxy_flush(cf, data); 1558 CF_DATA_RESTORE(cf, save); 1559 break; 1560 default: 1561 break; 1562 } 1563 return result; 1564 } 1565 1566 struct Curl_cftype Curl_cft_h2_proxy = { 1567 "H2-PROXY", 1568 CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 1569 CURL_LOG_LVL_NONE, 1570 cf_h2_proxy_destroy, 1571 cf_h2_proxy_connect, 1572 cf_h2_proxy_close, 1573 cf_h2_proxy_shutdown, 1574 cf_h2_proxy_adjust_pollset, 1575 cf_h2_proxy_data_pending, 1576 cf_h2_proxy_send, 1577 cf_h2_proxy_recv, 1578 cf_h2_proxy_cntrl, 1579 cf_h2_proxy_is_alive, 1580 Curl_cf_def_conn_keep_alive, 1581 cf_h2_proxy_query, 1582 }; 1583 1584 CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, 1585 struct Curl_easy *data) 1586 { 1587 struct Curl_cfilter *cf_h2_proxy = NULL; 1588 struct cf_h2_proxy_ctx *ctx; 1589 CURLcode result = CURLE_OUT_OF_MEMORY; 1590 1591 (void)data; 1592 ctx = calloc(1, sizeof(*ctx)); 1593 if(!ctx) 1594 goto out; 1595 1596 result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); 1597 if(result) 1598 goto out; 1599 1600 Curl_conn_cf_insert_after(cf, cf_h2_proxy); 1601 result = CURLE_OK; 1602 1603 out: 1604 if(result) 1605 cf_h2_proxy_ctx_free(ctx); 1606 return result; 1607 } 1608 1609 #endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */