cf-h1-proxy.c (23121B)
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(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) 28 29 #include <curl/curl.h> 30 #include "urldata.h" 31 #include "curlx/dynbuf.h" 32 #include "sendf.h" 33 #include "http.h" 34 #include "http1.h" 35 #include "http_proxy.h" 36 #include "url.h" 37 #include "select.h" 38 #include "progress.h" 39 #include "cfilters.h" 40 #include "cf-h1-proxy.h" 41 #include "connect.h" 42 #include "curl_trc.h" 43 #include "strcase.h" 44 #include "vtls/vtls.h" 45 #include "transfer.h" 46 #include "multiif.h" 47 #include "curlx/strparse.h" 48 49 /* The last 3 #include files should be in this order */ 50 #include "curl_printf.h" 51 #include "curl_memory.h" 52 #include "memdebug.h" 53 54 55 typedef enum { 56 H1_TUNNEL_INIT, /* init/default/no tunnel state */ 57 H1_TUNNEL_CONNECT, /* CONNECT request is being send */ 58 H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ 59 H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ 60 H1_TUNNEL_ESTABLISHED, 61 H1_TUNNEL_FAILED 62 } h1_tunnel_state; 63 64 /* struct for HTTP CONNECT tunneling */ 65 struct h1_tunnel_state { 66 struct dynbuf rcvbuf; 67 struct dynbuf request_data; 68 size_t nsent; 69 size_t headerlines; 70 struct Curl_chunker ch; 71 enum keeponval { 72 KEEPON_DONE, 73 KEEPON_CONNECT, 74 KEEPON_IGNORE 75 } keepon; 76 curl_off_t cl; /* size of content to read and ignore */ 77 h1_tunnel_state tunnel_state; 78 BIT(chunked_encoding); 79 BIT(close_connection); 80 }; 81 82 83 static bool tunnel_is_established(struct h1_tunnel_state *ts) 84 { 85 return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); 86 } 87 88 static bool tunnel_is_failed(struct h1_tunnel_state *ts) 89 { 90 return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); 91 } 92 93 static CURLcode tunnel_reinit(struct Curl_cfilter *cf, 94 struct Curl_easy *data, 95 struct h1_tunnel_state *ts) 96 { 97 (void)data; 98 (void)cf; 99 DEBUGASSERT(ts); 100 curlx_dyn_reset(&ts->rcvbuf); 101 curlx_dyn_reset(&ts->request_data); 102 ts->tunnel_state = H1_TUNNEL_INIT; 103 ts->keepon = KEEPON_CONNECT; 104 ts->cl = 0; 105 ts->close_connection = FALSE; 106 return CURLE_OK; 107 } 108 109 static CURLcode tunnel_init(struct Curl_cfilter *cf, 110 struct Curl_easy *data, 111 struct h1_tunnel_state **pts) 112 { 113 struct h1_tunnel_state *ts; 114 115 if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { 116 failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); 117 return CURLE_UNSUPPORTED_PROTOCOL; 118 } 119 120 ts = calloc(1, sizeof(*ts)); 121 if(!ts) 122 return CURLE_OUT_OF_MEMORY; 123 124 infof(data, "allocate connect buffer"); 125 126 curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); 127 curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); 128 Curl_httpchunk_init(data, &ts->ch, TRUE); 129 130 *pts = ts; 131 connkeep(cf->conn, "HTTP proxy CONNECT"); 132 return tunnel_reinit(cf, data, ts); 133 } 134 135 static void h1_tunnel_go_state(struct Curl_cfilter *cf, 136 struct h1_tunnel_state *ts, 137 h1_tunnel_state new_state, 138 struct Curl_easy *data) 139 { 140 if(ts->tunnel_state == new_state) 141 return; 142 /* entering this one */ 143 switch(new_state) { 144 case H1_TUNNEL_INIT: 145 CURL_TRC_CF(data, cf, "new tunnel state 'init'"); 146 tunnel_reinit(cf, data, ts); 147 break; 148 149 case H1_TUNNEL_CONNECT: 150 CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); 151 ts->tunnel_state = H1_TUNNEL_CONNECT; 152 ts->keepon = KEEPON_CONNECT; 153 curlx_dyn_reset(&ts->rcvbuf); 154 break; 155 156 case H1_TUNNEL_RECEIVE: 157 CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); 158 ts->tunnel_state = H1_TUNNEL_RECEIVE; 159 break; 160 161 case H1_TUNNEL_RESPONSE: 162 CURL_TRC_CF(data, cf, "new tunnel state 'response'"); 163 ts->tunnel_state = H1_TUNNEL_RESPONSE; 164 break; 165 166 case H1_TUNNEL_ESTABLISHED: 167 CURL_TRC_CF(data, cf, "new tunnel state 'established'"); 168 infof(data, "CONNECT phase completed"); 169 data->state.authproxy.done = TRUE; 170 data->state.authproxy.multipass = FALSE; 171 FALLTHROUGH(); 172 case H1_TUNNEL_FAILED: 173 if(new_state == H1_TUNNEL_FAILED) 174 CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); 175 ts->tunnel_state = new_state; 176 curlx_dyn_reset(&ts->rcvbuf); 177 curlx_dyn_reset(&ts->request_data); 178 /* restore the protocol pointer */ 179 data->info.httpcode = 0; /* clear it as it might've been used for the 180 proxy */ 181 /* If a proxy-authorization header was used for the proxy, then we should 182 make sure that it is not accidentally used for the document request 183 after we have connected. So let's free and clear it here. */ 184 Curl_safefree(data->state.aptr.proxyuserpwd); 185 break; 186 } 187 } 188 189 static void tunnel_free(struct Curl_cfilter *cf, 190 struct Curl_easy *data) 191 { 192 if(cf) { 193 struct h1_tunnel_state *ts = cf->ctx; 194 if(ts) { 195 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 196 curlx_dyn_free(&ts->rcvbuf); 197 curlx_dyn_free(&ts->request_data); 198 Curl_httpchunk_free(data, &ts->ch); 199 free(ts); 200 cf->ctx = NULL; 201 } 202 } 203 } 204 205 static bool tunnel_want_send(struct h1_tunnel_state *ts) 206 { 207 return ts->tunnel_state == H1_TUNNEL_CONNECT; 208 } 209 210 static CURLcode start_CONNECT(struct Curl_cfilter *cf, 211 struct Curl_easy *data, 212 struct h1_tunnel_state *ts) 213 { 214 struct httpreq *req = NULL; 215 int http_minor; 216 CURLcode result; 217 218 /* This only happens if we have looped here due to authentication 219 reasons, and we do not really use the newly cloned URL here 220 then. Just free() it. */ 221 Curl_safefree(data->req.newurl); 222 223 result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); 224 if(result) 225 goto out; 226 227 infof(data, "Establish HTTP proxy tunnel to %s", req->authority); 228 229 curlx_dyn_reset(&ts->request_data); 230 ts->nsent = 0; 231 ts->headerlines = 0; 232 http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; 233 234 result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); 235 if(!result) 236 result = Curl_creader_set_null(data); 237 238 out: 239 if(result) 240 failf(data, "Failed sending CONNECT to proxy"); 241 if(req) 242 Curl_http_req_free(req); 243 return result; 244 } 245 246 static CURLcode send_CONNECT(struct Curl_cfilter *cf, 247 struct Curl_easy *data, 248 struct h1_tunnel_state *ts, 249 bool *done) 250 { 251 char *buf = curlx_dyn_ptr(&ts->request_data); 252 size_t request_len = curlx_dyn_len(&ts->request_data); 253 size_t blen = request_len; 254 CURLcode result = CURLE_OK; 255 size_t nwritten; 256 257 if(blen <= ts->nsent) 258 goto out; /* we are done */ 259 260 blen -= ts->nsent; 261 buf += ts->nsent; 262 263 result = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &nwritten); 264 if(result) { 265 if(result == CURLE_AGAIN) 266 result = CURLE_OK; 267 goto out; 268 } 269 270 DEBUGASSERT(blen >= nwritten); 271 ts->nsent += nwritten; 272 Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); 273 274 out: 275 if(result) 276 failf(data, "Failed sending CONNECT to proxy"); 277 *done = (!result && (ts->nsent >= request_len)); 278 return result; 279 } 280 281 static CURLcode on_resp_header(struct Curl_cfilter *cf, 282 struct Curl_easy *data, 283 struct h1_tunnel_state *ts, 284 const char *header) 285 { 286 CURLcode result = CURLE_OK; 287 struct SingleRequest *k = &data->req; 288 (void)cf; 289 290 if((checkprefix("WWW-Authenticate:", header) && 291 (401 == k->httpcode)) || 292 (checkprefix("Proxy-authenticate:", header) && 293 (407 == k->httpcode))) { 294 295 bool proxy = (k->httpcode == 407); 296 char *auth = Curl_copy_header_value(header); 297 if(!auth) 298 return CURLE_OUT_OF_MEMORY; 299 300 CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); 301 result = Curl_http_input_auth(data, proxy, auth); 302 303 free(auth); 304 305 if(result) 306 return result; 307 } 308 else if(checkprefix("Content-Length:", header)) { 309 if(k->httpcode/100 == 2) { 310 /* A client MUST ignore any Content-Length or Transfer-Encoding 311 header fields received in a successful response to CONNECT. 312 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 313 infof(data, "Ignoring Content-Length in CONNECT %03d response", 314 k->httpcode); 315 } 316 else { 317 const char *p = header + strlen("Content-Length:"); 318 if(curlx_str_numblanks(&p, &ts->cl)) { 319 failf(data, "Unsupported Content-Length value"); 320 return CURLE_WEIRD_SERVER_REPLY; 321 } 322 } 323 } 324 else if(Curl_compareheader(header, 325 STRCONST("Connection:"), STRCONST("close"))) 326 ts->close_connection = TRUE; 327 else if(checkprefix("Transfer-Encoding:", header)) { 328 if(k->httpcode/100 == 2) { 329 /* A client MUST ignore any Content-Length or Transfer-Encoding 330 header fields received in a successful response to CONNECT. 331 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 332 infof(data, "Ignoring Transfer-Encoding in " 333 "CONNECT %03d response", k->httpcode); 334 } 335 else if(Curl_compareheader(header, 336 STRCONST("Transfer-Encoding:"), 337 STRCONST("chunked"))) { 338 infof(data, "CONNECT responded chunked"); 339 ts->chunked_encoding = TRUE; 340 /* reset our chunky engine */ 341 Curl_httpchunk_reset(data, &ts->ch, TRUE); 342 } 343 } 344 else if(Curl_compareheader(header, 345 STRCONST("Proxy-Connection:"), 346 STRCONST("close"))) 347 ts->close_connection = TRUE; 348 else if(!strncmp(header, "HTTP/1.", 7) && 349 ((header[7] == '0') || (header[7] == '1')) && 350 (header[8] == ' ') && 351 ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && 352 !ISDIGIT(header[12])) { 353 /* store the HTTP code from the proxy */ 354 data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + 355 (header[10] - '0') * 10 + (header[11] - '0'); 356 } 357 return result; 358 } 359 360 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, 361 struct Curl_easy *data, 362 struct h1_tunnel_state *ts, 363 bool *done) 364 { 365 CURLcode result = CURLE_OK; 366 struct SingleRequest *k = &data->req; 367 char *linep; 368 size_t line_len; 369 int error, writetype; 370 371 #define SELECT_OK 0 372 #define SELECT_ERROR 1 373 374 error = SELECT_OK; 375 *done = FALSE; 376 377 while(ts->keepon) { 378 size_t nread; 379 char byte; 380 381 /* Read one byte at a time to avoid a race condition. Wait at most one 382 second before looping to ensure continuous pgrsUpdates. */ 383 result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread); 384 if(result == CURLE_AGAIN) 385 /* socket buffer drained, return */ 386 return CURLE_OK; 387 388 if(Curl_pgrsUpdate(data)) 389 return CURLE_ABORTED_BY_CALLBACK; 390 391 if(result) { 392 ts->keepon = KEEPON_DONE; 393 break; 394 } 395 396 if(!nread) { 397 if(data->set.proxyauth && data->state.authproxy.avail && 398 data->state.aptr.proxyuserpwd) { 399 /* proxy auth was requested and there was proxy auth available, 400 then deem this as "mere" proxy disconnect */ 401 ts->close_connection = TRUE; 402 infof(data, "Proxy CONNECT connection closed"); 403 } 404 else { 405 error = SELECT_ERROR; 406 failf(data, "Proxy CONNECT aborted"); 407 } 408 ts->keepon = KEEPON_DONE; 409 break; 410 } 411 412 if(ts->keepon == KEEPON_IGNORE) { 413 /* This means we are currently ignoring a response-body */ 414 415 if(ts->cl) { 416 /* A Content-Length based body: simply count down the counter 417 and make sure to break out of the loop when we are done! */ 418 ts->cl--; 419 if(ts->cl <= 0) { 420 ts->keepon = KEEPON_DONE; 421 break; 422 } 423 } 424 else if(ts->chunked_encoding) { 425 /* chunked-encoded body, so we need to do the chunked dance 426 properly to know when the end of the body is reached */ 427 size_t consumed = 0; 428 429 /* now parse the chunked piece of data so that we can 430 properly tell when the stream ends */ 431 result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed); 432 if(result) 433 return result; 434 if(Curl_httpchunk_is_done(data, &ts->ch)) { 435 /* we are done reading chunks! */ 436 infof(data, "chunk reading DONE"); 437 ts->keepon = KEEPON_DONE; 438 } 439 } 440 continue; 441 } 442 443 if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) { 444 failf(data, "CONNECT response too large"); 445 return CURLE_RECV_ERROR; 446 } 447 448 /* if this is not the end of a header line then continue */ 449 if(byte != 0x0a) 450 continue; 451 452 ts->headerlines++; 453 linep = curlx_dyn_ptr(&ts->rcvbuf); 454 line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ 455 456 /* output debug if that is requested */ 457 Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); 458 459 /* send the header to the callback */ 460 writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | 461 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); 462 result = Curl_client_write(data, writetype, linep, line_len); 463 if(result) 464 return result; 465 466 result = Curl_bump_headersize(data, line_len, TRUE); 467 if(result) 468 return result; 469 470 /* Newlines are CRLF, so the CR is ignored as the line is not 471 really terminated until the LF comes. Treat a following CR 472 as end-of-headers as well.*/ 473 474 if(('\r' == linep[0]) || 475 ('\n' == linep[0])) { 476 /* end of response-headers from the proxy */ 477 478 if((407 == k->httpcode) && !data->state.authproblem) { 479 /* If we get a 407 response code with content length 480 when we have no auth problem, we must ignore the 481 whole response-body */ 482 ts->keepon = KEEPON_IGNORE; 483 484 if(ts->cl) { 485 infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl); 486 } 487 else if(ts->chunked_encoding) { 488 infof(data, "Ignore chunked response-body"); 489 } 490 else { 491 /* without content-length or chunked encoding, we 492 cannot keep the connection alive since the close is 493 the end signal so we bail out at once instead */ 494 CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); 495 ts->keepon = KEEPON_DONE; 496 } 497 } 498 else { 499 ts->keepon = KEEPON_DONE; 500 } 501 502 DEBUGASSERT(ts->keepon == KEEPON_IGNORE 503 || ts->keepon == KEEPON_DONE); 504 continue; 505 } 506 507 result = on_resp_header(cf, data, ts, linep); 508 if(result) 509 return result; 510 511 curlx_dyn_reset(&ts->rcvbuf); 512 } /* while there is buffer left and loop is requested */ 513 514 if(error) 515 result = CURLE_RECV_ERROR; 516 *done = (ts->keepon == KEEPON_DONE); 517 if(!result && *done && data->info.httpproxycode/100 != 2) { 518 /* Deal with the possibly already received authenticate 519 headers. 'newurl' is set to a new URL if we must loop. */ 520 result = Curl_http_auth_act(data); 521 } 522 return result; 523 } 524 525 static CURLcode H1_CONNECT(struct Curl_cfilter *cf, 526 struct Curl_easy *data, 527 struct h1_tunnel_state *ts) 528 { 529 struct connectdata *conn = cf->conn; 530 CURLcode result; 531 bool done; 532 533 if(tunnel_is_established(ts)) 534 return CURLE_OK; 535 if(tunnel_is_failed(ts)) 536 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ 537 538 do { 539 timediff_t check; 540 541 check = Curl_timeleft(data, NULL, TRUE); 542 if(check <= 0) { 543 failf(data, "Proxy CONNECT aborted due to timeout"); 544 result = CURLE_OPERATION_TIMEDOUT; 545 goto out; 546 } 547 548 switch(ts->tunnel_state) { 549 case H1_TUNNEL_INIT: 550 /* Prepare the CONNECT request and make a first attempt to send. */ 551 CURL_TRC_CF(data, cf, "CONNECT start"); 552 result = start_CONNECT(cf, data, ts); 553 if(result) 554 goto out; 555 h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); 556 FALLTHROUGH(); 557 558 case H1_TUNNEL_CONNECT: 559 /* see that the request is completely sent */ 560 CURL_TRC_CF(data, cf, "CONNECT send"); 561 result = send_CONNECT(cf, data, ts, &done); 562 if(result || !done) 563 goto out; 564 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); 565 FALLTHROUGH(); 566 567 case H1_TUNNEL_RECEIVE: 568 /* read what is there */ 569 CURL_TRC_CF(data, cf, "CONNECT receive"); 570 result = recv_CONNECT_resp(cf, data, ts, &done); 571 if(Curl_pgrsUpdate(data)) { 572 result = CURLE_ABORTED_BY_CALLBACK; 573 goto out; 574 } 575 /* error or not complete yet. return for more multi-multi */ 576 if(result || !done) 577 goto out; 578 /* got it */ 579 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); 580 FALLTHROUGH(); 581 582 case H1_TUNNEL_RESPONSE: 583 CURL_TRC_CF(data, cf, "CONNECT response"); 584 if(data->req.newurl) { 585 /* not the "final" response, we need to do a follow up request. 586 * If the other side indicated a connection close, or if someone 587 * else told us to close this connection, do so now. 588 */ 589 Curl_req_soft_reset(&data->req, data); 590 if(ts->close_connection || conn->bits.close) { 591 /* Close this filter and the sub-chain, re-connect the 592 * sub-chain and continue. Closing this filter will 593 * reset our tunnel state. To avoid recursion, we return 594 * and expect to be called again. 595 */ 596 CURL_TRC_CF(data, cf, "CONNECT need to close+open"); 597 infof(data, "Connect me again please"); 598 Curl_conn_cf_close(cf, data); 599 connkeep(conn, "HTTP proxy CONNECT"); 600 result = Curl_conn_cf_connect(cf->next, data, &done); 601 goto out; 602 } 603 else { 604 /* staying on this connection, reset state */ 605 h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); 606 } 607 } 608 break; 609 610 default: 611 break; 612 } 613 614 } while(data->req.newurl); 615 616 DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); 617 if(data->info.httpproxycode/100 != 2) { 618 /* a non-2xx response and we have no next URL to try. */ 619 Curl_safefree(data->req.newurl); 620 /* failure, close this connection to avoid reuse */ 621 streamclose(conn, "proxy CONNECT failure"); 622 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 623 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); 624 return CURLE_RECV_ERROR; 625 } 626 /* 2xx response, SUCCESS! */ 627 h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); 628 infof(data, "CONNECT tunnel established, response %d", 629 data->info.httpproxycode); 630 result = CURLE_OK; 631 632 out: 633 if(result) 634 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 635 return result; 636 } 637 638 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, 639 struct Curl_easy *data, 640 bool *done) 641 { 642 CURLcode result; 643 struct h1_tunnel_state *ts = cf->ctx; 644 645 if(cf->connected) { 646 *done = TRUE; 647 return CURLE_OK; 648 } 649 650 CURL_TRC_CF(data, cf, "connect"); 651 result = cf->next->cft->do_connect(cf->next, data, done); 652 if(result || !*done) 653 return result; 654 655 *done = FALSE; 656 if(!ts) { 657 result = tunnel_init(cf, data, &ts); 658 if(result) 659 return result; 660 cf->ctx = ts; 661 } 662 663 /* We want "seamless" operations through HTTP proxy tunnel */ 664 665 result = H1_CONNECT(cf, data, ts); 666 if(result) 667 goto out; 668 Curl_safefree(data->state.aptr.proxyuserpwd); 669 670 out: 671 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); 672 if(*done) { 673 cf->connected = TRUE; 674 /* The real request will follow the CONNECT, reset request partially */ 675 Curl_req_soft_reset(&data->req, data); 676 Curl_client_reset(data); 677 Curl_pgrsSetUploadCounter(data, 0); 678 Curl_pgrsSetDownloadCounter(data, 0); 679 680 tunnel_free(cf, data); 681 } 682 return result; 683 } 684 685 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, 686 struct Curl_easy *data, 687 struct easy_pollset *ps) 688 { 689 struct h1_tunnel_state *ts = cf->ctx; 690 691 if(!cf->connected) { 692 /* If we are not connected, but the filter "below" is 693 * and not waiting on something, we are tunneling. */ 694 curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); 695 if(ts) { 696 /* when we have sent a CONNECT to a proxy, we should rather either 697 wait for the socket to become readable to be able to get the 698 response headers or if we are still sending the request, wait 699 for write. */ 700 if(tunnel_want_send(ts)) 701 Curl_pollset_set_out_only(data, ps, sock); 702 else 703 Curl_pollset_set_in_only(data, ps, sock); 704 } 705 else 706 Curl_pollset_set_out_only(data, ps, sock); 707 } 708 } 709 710 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, 711 struct Curl_easy *data) 712 { 713 CURL_TRC_CF(data, cf, "destroy"); 714 tunnel_free(cf, data); 715 } 716 717 static void cf_h1_proxy_close(struct Curl_cfilter *cf, 718 struct Curl_easy *data) 719 { 720 CURL_TRC_CF(data, cf, "close"); 721 if(cf) { 722 cf->connected = FALSE; 723 if(cf->ctx) { 724 h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); 725 } 726 if(cf->next) 727 cf->next->cft->do_close(cf->next, data); 728 } 729 } 730 731 732 struct Curl_cftype Curl_cft_h1_proxy = { 733 "H1-PROXY", 734 CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, 735 0, 736 cf_h1_proxy_destroy, 737 cf_h1_proxy_connect, 738 cf_h1_proxy_close, 739 Curl_cf_def_shutdown, 740 cf_h1_proxy_adjust_pollset, 741 Curl_cf_def_data_pending, 742 Curl_cf_def_send, 743 Curl_cf_def_recv, 744 Curl_cf_def_cntrl, 745 Curl_cf_def_conn_is_alive, 746 Curl_cf_def_conn_keep_alive, 747 Curl_cf_http_proxy_query, 748 }; 749 750 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, 751 struct Curl_easy *data) 752 { 753 struct Curl_cfilter *cf; 754 CURLcode result; 755 756 (void)data; 757 result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); 758 if(!result) 759 Curl_conn_cf_insert_after(cf_at, cf); 760 return result; 761 } 762 763 #endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */