VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/cf-h1-proxy.c

最後變更 在這個檔案是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

檔案大小: 31.7 KB
 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, 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#ifdef USE_HYPER
31#include <hyper.h>
32#endif
33#include "urldata.h"
34#include "dynbuf.h"
35#include "sendf.h"
36#include "http.h"
37#include "http1.h"
38#include "http_proxy.h"
39#include "url.h"
40#include "select.h"
41#include "progress.h"
42#include "cfilters.h"
43#include "cf-h1-proxy.h"
44#include "connect.h"
45#include "curl_trc.h"
46#include "curlx.h"
47#include "vtls/vtls.h"
48#include "transfer.h"
49#include "multiif.h"
50
51/* The last 3 #include files should be in this order */
52#include "curl_printf.h"
53#include "curl_memory.h"
54#include "memdebug.h"
55
56
57typedef enum {
58 H1_TUNNEL_INIT, /* init/default/no tunnel state */
59 H1_TUNNEL_CONNECT, /* CONNECT request is being send */
60 H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */
61 H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
62 H1_TUNNEL_ESTABLISHED,
63 H1_TUNNEL_FAILED
64} h1_tunnel_state;
65
66/* struct for HTTP CONNECT tunneling */
67struct h1_tunnel_state {
68 struct HTTP CONNECT;
69 struct dynbuf rcvbuf;
70 struct dynbuf request_data;
71 size_t nsent;
72 size_t headerlines;
73 struct Curl_chunker ch;
74 enum keeponval {
75 KEEPON_DONE,
76 KEEPON_CONNECT,
77 KEEPON_IGNORE
78 } keepon;
79 curl_off_t cl; /* size of content to read and ignore */
80 h1_tunnel_state tunnel_state;
81 BIT(chunked_encoding);
82 BIT(close_connection);
83};
84
85
86static bool tunnel_is_established(struct h1_tunnel_state *ts)
87{
88 return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
89}
90
91static bool tunnel_is_failed(struct h1_tunnel_state *ts)
92{
93 return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
94}
95
96static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
97 struct Curl_easy *data,
98 struct h1_tunnel_state *ts)
99{
100 (void)data;
101 (void)cf;
102 DEBUGASSERT(ts);
103 Curl_dyn_reset(&ts->rcvbuf);
104 Curl_dyn_reset(&ts->request_data);
105 ts->tunnel_state = H1_TUNNEL_INIT;
106 ts->keepon = KEEPON_CONNECT;
107 ts->cl = 0;
108 ts->close_connection = FALSE;
109 return CURLE_OK;
110}
111
112static CURLcode tunnel_init(struct Curl_cfilter *cf,
113 struct Curl_easy *data,
114 struct h1_tunnel_state **pts)
115{
116 struct h1_tunnel_state *ts;
117
118 if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
119 failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
120 return CURLE_UNSUPPORTED_PROTOCOL;
121 }
122
123 ts = calloc(1, sizeof(*ts));
124 if(!ts)
125 return CURLE_OUT_OF_MEMORY;
126
127 infof(data, "allocate connect buffer");
128
129 Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
130 Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
131 Curl_httpchunk_init(data, &ts->ch, TRUE);
132
133 *pts = ts;
134 connkeep(cf->conn, "HTTP proxy CONNECT");
135 return tunnel_reinit(cf, data, ts);
136}
137
138static void h1_tunnel_go_state(struct Curl_cfilter *cf,
139 struct h1_tunnel_state *ts,
140 h1_tunnel_state new_state,
141 struct Curl_easy *data)
142{
143 if(ts->tunnel_state == new_state)
144 return;
145 /* entering this one */
146 switch(new_state) {
147 case H1_TUNNEL_INIT:
148 CURL_TRC_CF(data, cf, "new tunnel state 'init'");
149 tunnel_reinit(cf, data, ts);
150 break;
151
152 case H1_TUNNEL_CONNECT:
153 CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
154 ts->tunnel_state = H1_TUNNEL_CONNECT;
155 ts->keepon = KEEPON_CONNECT;
156 Curl_dyn_reset(&ts->rcvbuf);
157 break;
158
159 case H1_TUNNEL_RECEIVE:
160 CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
161 ts->tunnel_state = H1_TUNNEL_RECEIVE;
162 break;
163
164 case H1_TUNNEL_RESPONSE:
165 CURL_TRC_CF(data, cf, "new tunnel state 'response'");
166 ts->tunnel_state = H1_TUNNEL_RESPONSE;
167 break;
168
169 case H1_TUNNEL_ESTABLISHED:
170 CURL_TRC_CF(data, cf, "new tunnel state 'established'");
171 infof(data, "CONNECT phase completed");
172 data->state.authproxy.done = TRUE;
173 data->state.authproxy.multipass = FALSE;
174 FALLTHROUGH();
175 case H1_TUNNEL_FAILED:
176 if(new_state == H1_TUNNEL_FAILED)
177 CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
178 ts->tunnel_state = new_state;
179 Curl_dyn_reset(&ts->rcvbuf);
180 Curl_dyn_reset(&ts->request_data);
181 /* restore the protocol pointer */
182 data->info.httpcode = 0; /* clear it as it might've been used for the
183 proxy */
184 /* If a proxy-authorization header was used for the proxy, then we should
185 make sure that it isn't accidentally used for the document request
186 after we've connected. So let's free and clear it here. */
187 Curl_safefree(data->state.aptr.proxyuserpwd);
188#ifdef USE_HYPER
189 data->state.hconnect = FALSE;
190#endif
191 break;
192 }
193}
194
195static void tunnel_free(struct Curl_cfilter *cf,
196 struct Curl_easy *data)
197{
198 struct h1_tunnel_state *ts = cf->ctx;
199 if(ts) {
200 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
201 Curl_dyn_free(&ts->rcvbuf);
202 Curl_dyn_free(&ts->request_data);
203 Curl_httpchunk_free(data, &ts->ch);
204 free(ts);
205 cf->ctx = NULL;
206 }
207}
208
209static bool tunnel_want_send(struct h1_tunnel_state *ts)
210{
211 return (ts->tunnel_state == H1_TUNNEL_CONNECT);
212}
213
214#ifndef USE_HYPER
215static CURLcode start_CONNECT(struct Curl_cfilter *cf,
216 struct Curl_easy *data,
217 struct h1_tunnel_state *ts)
218{
219 struct httpreq *req = NULL;
220 int http_minor;
221 CURLcode result;
222
223 /* This only happens if we've looped here due to authentication
224 reasons, and we don't really use the newly cloned URL here
225 then. Just free() it. */
226 Curl_safefree(data->req.newurl);
227
228 result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
229 if(result)
230 goto out;
231
232 infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
233
234 Curl_dyn_reset(&ts->request_data);
235 ts->nsent = 0;
236 ts->headerlines = 0;
237 http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
238
239 result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
240 if(!result)
241 result = Curl_creader_set_null(data);
242
243out:
244 if(result)
245 failf(data, "Failed sending CONNECT to proxy");
246 if(req)
247 Curl_http_req_free(req);
248 return result;
249}
250
251static CURLcode send_CONNECT(struct Curl_cfilter *cf,
252 struct Curl_easy *data,
253 struct h1_tunnel_state *ts,
254 bool *done)
255{
256 char *buf = Curl_dyn_ptr(&ts->request_data);
257 size_t request_len = Curl_dyn_len(&ts->request_data);
258 size_t blen = request_len;
259 CURLcode result = CURLE_OK;
260 ssize_t nwritten;
261
262 if(blen <= ts->nsent)
263 goto out; /* we are done */
264
265 blen -= ts->nsent;
266 buf += ts->nsent;
267
268 nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
269 if(nwritten < 0) {
270 if(result == CURLE_AGAIN) {
271 result = CURLE_OK;
272 }
273 goto out;
274 }
275
276 DEBUGASSERT(blen >= (size_t)nwritten);
277 ts->nsent += (size_t)nwritten;
278 Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
279
280out:
281 if(result)
282 failf(data, "Failed sending CONNECT to proxy");
283 *done = (!result && (ts->nsent >= request_len));
284 return result;
285}
286
287static CURLcode on_resp_header(struct Curl_cfilter *cf,
288 struct Curl_easy *data,
289 struct h1_tunnel_state *ts,
290 const char *header)
291{
292 CURLcode result = CURLE_OK;
293 struct SingleRequest *k = &data->req;
294 (void)cf;
295
296 if((checkprefix("WWW-Authenticate:", header) &&
297 (401 == k->httpcode)) ||
298 (checkprefix("Proxy-authenticate:", header) &&
299 (407 == k->httpcode))) {
300
301 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
302 char *auth = Curl_copy_header_value(header);
303 if(!auth)
304 return CURLE_OUT_OF_MEMORY;
305
306 CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
307 result = Curl_http_input_auth(data, proxy, auth);
308
309 free(auth);
310
311 if(result)
312 return result;
313 }
314 else if(checkprefix("Content-Length:", header)) {
315 if(k->httpcode/100 == 2) {
316 /* A client MUST ignore any Content-Length or Transfer-Encoding
317 header fields received in a successful response to CONNECT.
318 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
319 infof(data, "Ignoring Content-Length in CONNECT %03d response",
320 k->httpcode);
321 }
322 else {
323 (void)curlx_strtoofft(header + strlen("Content-Length:"),
324 NULL, 10, &ts->cl);
325 }
326 }
327 else if(Curl_compareheader(header,
328 STRCONST("Connection:"), STRCONST("close")))
329 ts->close_connection = TRUE;
330 else if(checkprefix("Transfer-Encoding:", header)) {
331 if(k->httpcode/100 == 2) {
332 /* A client MUST ignore any Content-Length or Transfer-Encoding
333 header fields received in a successful response to CONNECT.
334 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
335 infof(data, "Ignoring Transfer-Encoding in "
336 "CONNECT %03d response", k->httpcode);
337 }
338 else if(Curl_compareheader(header,
339 STRCONST("Transfer-Encoding:"),
340 STRCONST("chunked"))) {
341 infof(data, "CONNECT responded chunked");
342 ts->chunked_encoding = TRUE;
343 /* reset our chunky engine */
344 Curl_httpchunk_reset(data, &ts->ch, TRUE);
345 }
346 }
347 else if(Curl_compareheader(header,
348 STRCONST("Proxy-Connection:"),
349 STRCONST("close")))
350 ts->close_connection = TRUE;
351 else if(!strncmp(header, "HTTP/1.", 7) &&
352 ((header[7] == '0') || (header[7] == '1')) &&
353 (header[8] == ' ') &&
354 ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
355 !ISDIGIT(header[12])) {
356 /* store the HTTP code from the proxy */
357 data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
358 (header[10] - '0') * 10 + (header[11] - '0');
359 }
360 return result;
361}
362
363static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
364 struct Curl_easy *data,
365 struct h1_tunnel_state *ts,
366 bool *done)
367{
368 CURLcode result = CURLE_OK;
369 struct SingleRequest *k = &data->req;
370 char *linep;
371 size_t line_len;
372 int error, writetype;
373
374#define SELECT_OK 0
375#define SELECT_ERROR 1
376
377 error = SELECT_OK;
378 *done = FALSE;
379
380 if(!Curl_conn_data_pending(data, cf->sockindex))
381 return CURLE_OK;
382
383 while(ts->keepon) {
384 ssize_t nread;
385 char byte;
386
387 /* Read one byte at a time to avoid a race condition. Wait at most one
388 second before looping to ensure continuous pgrsUpdates. */
389 result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
390 if(result == CURLE_AGAIN)
391 /* socket buffer drained, return */
392 return CURLE_OK;
393
394 if(Curl_pgrsUpdate(data))
395 return CURLE_ABORTED_BY_CALLBACK;
396
397 if(result) {
398 ts->keepon = KEEPON_DONE;
399 break;
400 }
401
402 if(nread <= 0) {
403 if(data->set.proxyauth && data->state.authproxy.avail &&
404 data->state.aptr.proxyuserpwd) {
405 /* proxy auth was requested and there was proxy auth available,
406 then deem this as "mere" proxy disconnect */
407 ts->close_connection = TRUE;
408 infof(data, "Proxy CONNECT connection closed");
409 }
410 else {
411 error = SELECT_ERROR;
412 failf(data, "Proxy CONNECT aborted");
413 }
414 ts->keepon = KEEPON_DONE;
415 break;
416 }
417
418 if(ts->keepon == KEEPON_IGNORE) {
419 /* This means we are currently ignoring a response-body */
420
421 if(ts->cl) {
422 /* A Content-Length based body: simply count down the counter
423 and make sure to break out of the loop when we're done! */
424 ts->cl--;
425 if(ts->cl <= 0) {
426 ts->keepon = KEEPON_DONE;
427 break;
428 }
429 }
430 else if(ts->chunked_encoding) {
431 /* chunked-encoded body, so we need to do the chunked dance
432 properly to know when the end of the body is reached */
433 size_t consumed = 0;
434
435 /* now parse the chunked piece of data so that we can
436 properly tell when the stream ends */
437 result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
438 if(result)
439 return result;
440 if(Curl_httpchunk_is_done(data, &ts->ch)) {
441 /* we're done reading chunks! */
442 infof(data, "chunk reading DONE");
443 ts->keepon = KEEPON_DONE;
444 }
445 }
446 continue;
447 }
448
449 if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
450 failf(data, "CONNECT response too large");
451 return CURLE_RECV_ERROR;
452 }
453
454 /* if this is not the end of a header line then continue */
455 if(byte != 0x0a)
456 continue;
457
458 ts->headerlines++;
459 linep = Curl_dyn_ptr(&ts->rcvbuf);
460 line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
461
462 /* output debug if that is requested */
463 Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
464
465 /* send the header to the callback */
466 writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
467 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
468 result = Curl_client_write(data, writetype, linep, line_len);
469 if(result)
470 return result;
471
472 result = Curl_bump_headersize(data, line_len, TRUE);
473 if(result)
474 return result;
475
476 /* Newlines are CRLF, so the CR is ignored as the line isn't
477 really terminated until the LF comes. Treat a following CR
478 as end-of-headers as well.*/
479
480 if(('\r' == linep[0]) ||
481 ('\n' == linep[0])) {
482 /* end of response-headers from the proxy */
483
484 if((407 == k->httpcode) && !data->state.authproblem) {
485 /* If we get a 407 response code with content length
486 when we have no auth problem, we must ignore the
487 whole response-body */
488 ts->keepon = KEEPON_IGNORE;
489
490 if(ts->cl) {
491 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
492 " bytes of response-body", ts->cl);
493 }
494 else if(ts->chunked_encoding) {
495 infof(data, "Ignore chunked response-body");
496 }
497 else {
498 /* without content-length or chunked encoding, we
499 can't keep the connection alive since the close is
500 the end signal so we bail out at once instead */
501 CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
502 ts->keepon = KEEPON_DONE;
503 }
504 }
505 else {
506 ts->keepon = KEEPON_DONE;
507 }
508
509 DEBUGASSERT(ts->keepon == KEEPON_IGNORE
510 || ts->keepon == KEEPON_DONE);
511 continue;
512 }
513
514 result = on_resp_header(cf, data, ts, linep);
515 if(result)
516 return result;
517
518 Curl_dyn_reset(&ts->rcvbuf);
519 } /* while there's buffer left and loop is requested */
520
521 if(error)
522 result = CURLE_RECV_ERROR;
523 *done = (ts->keepon == KEEPON_DONE);
524 if(!result && *done && data->info.httpproxycode/100 != 2) {
525 /* Deal with the possibly already received authenticate
526 headers. 'newurl' is set to a new URL if we must loop. */
527 result = Curl_http_auth_act(data);
528 }
529 return result;
530}
531
532#else /* USE_HYPER */
533
534static CURLcode CONNECT_host(struct Curl_cfilter *cf,
535 struct Curl_easy *data,
536 char **pauthority,
537 char **phost_header)
538{
539 const char *hostname;
540 int port;
541 bool ipv6_ip;
542 CURLcode result;
543 char *authority; /* for CONNECT, the destination host + port */
544 char *host_header = NULL; /* Host: authority */
545
546 result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
547 if(result)
548 return result;
549
550 authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
551 port);
552 if(!authority)
553 return CURLE_OUT_OF_MEMORY;
554
555 /* If user is not overriding the Host header later */
556 if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
557 host_header = aprintf("Host: %s\r\n", authority);
558 if(!host_header) {
559 free(authority);
560 return CURLE_OUT_OF_MEMORY;
561 }
562 }
563 *pauthority = authority;
564 *phost_header = host_header;
565 return CURLE_OK;
566}
567
568/* The Hyper version of CONNECT */
569static CURLcode start_CONNECT(struct Curl_cfilter *cf,
570 struct Curl_easy *data,
571 struct h1_tunnel_state *ts)
572{
573 struct connectdata *conn = cf->conn;
574 struct hyptransfer *h = &data->hyp;
575 curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
576 hyper_io *io = NULL;
577 hyper_request *req = NULL;
578 hyper_headers *headers = NULL;
579 hyper_clientconn_options *options = NULL;
580 hyper_task *handshake = NULL;
581 hyper_task *task = NULL; /* for the handshake */
582 hyper_clientconn *client = NULL;
583 hyper_task *sendtask = NULL; /* for the send */
584 char *authority = NULL; /* for CONNECT */
585 char *host_header = NULL; /* Host: */
586 CURLcode result = CURLE_OUT_OF_MEMORY;
587 (void)ts;
588
589 io = hyper_io_new();
590 if(!io) {
591 failf(data, "Couldn't create hyper IO");
592 result = CURLE_OUT_OF_MEMORY;
593 goto error;
594 }
595 /* tell Hyper how to read/write network data */
596 h->io_ctx.data = data;
597 h->io_ctx.sockindex = cf->sockindex;
598 hyper_io_set_userdata(io, &h->io_ctx);
599 hyper_io_set_read(io, Curl_hyper_recv);
600 hyper_io_set_write(io, Curl_hyper_send);
601 conn->sockfd = tunnelsocket;
602
603 data->state.hconnect = TRUE;
604
605 /* create an executor to poll futures */
606 if(!h->exec) {
607 h->exec = hyper_executor_new();
608 if(!h->exec) {
609 failf(data, "Couldn't create hyper executor");
610 result = CURLE_OUT_OF_MEMORY;
611 goto error;
612 }
613 }
614
615 options = hyper_clientconn_options_new();
616 if(!options) {
617 failf(data, "Couldn't create hyper client options");
618 result = CURLE_OUT_OF_MEMORY;
619 goto error;
620 }
621 hyper_clientconn_options_set_preserve_header_case(options, 1);
622 hyper_clientconn_options_set_preserve_header_order(options, 1);
623
624 hyper_clientconn_options_exec(options, h->exec);
625
626 /* "Both the `io` and the `options` are consumed in this function
627 call" */
628 handshake = hyper_clientconn_handshake(io, options);
629 if(!handshake) {
630 failf(data, "Couldn't create hyper client handshake");
631 result = CURLE_OUT_OF_MEMORY;
632 goto error;
633 }
634 io = NULL;
635 options = NULL;
636
637 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
638 failf(data, "Couldn't hyper_executor_push the handshake");
639 result = CURLE_OUT_OF_MEMORY;
640 goto error;
641 }
642 handshake = NULL; /* ownership passed on */
643
644 task = hyper_executor_poll(h->exec);
645 if(!task) {
646 failf(data, "Couldn't hyper_executor_poll the handshake");
647 result = CURLE_OUT_OF_MEMORY;
648 goto error;
649 }
650
651 client = hyper_task_value(task);
652 hyper_task_free(task);
653
654 req = hyper_request_new();
655 if(!req) {
656 failf(data, "Couldn't hyper_request_new");
657 result = CURLE_OUT_OF_MEMORY;
658 goto error;
659 }
660 if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
661 strlen("CONNECT"))) {
662 failf(data, "error setting method");
663 result = CURLE_OUT_OF_MEMORY;
664 goto error;
665 }
666
667 /* This only happens if we've looped here due to authentication
668 reasons, and we don't really use the newly cloned URL here
669 then. Just free() it. */
670 Curl_safefree(data->req.newurl);
671
672 result = CONNECT_host(cf, data, &authority, &host_header);
673 if(result)
674 goto error;
675
676 infof(data, "Establish HTTP proxy tunnel to %s", authority);
677
678 if(hyper_request_set_uri(req, (uint8_t *)authority,
679 strlen(authority))) {
680 failf(data, "error setting path");
681 result = CURLE_OUT_OF_MEMORY;
682 goto error;
683 }
684 if(data->set.verbose) {
685 char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority);
686 if(!se) {
687 result = CURLE_OUT_OF_MEMORY;
688 goto error;
689 }
690 Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
691 free(se);
692 }
693 /* Setup the proxy-authorization header, if any */
694 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
695 authority, TRUE);
696 if(result)
697 goto error;
698 Curl_safefree(authority);
699
700 /* default is 1.1 */
701 if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
702 (HYPERE_OK != hyper_request_set_version(req,
703 HYPER_HTTP_VERSION_1_0))) {
704 failf(data, "error setting HTTP version");
705 result = CURLE_OUT_OF_MEMORY;
706 goto error;
707 }
708
709 headers = hyper_request_headers(req);
710 if(!headers) {
711 failf(data, "hyper_request_headers");
712 result = CURLE_OUT_OF_MEMORY;
713 goto error;
714 }
715 if(host_header) {
716 result = Curl_hyper_header(data, headers, host_header);
717 if(result)
718 goto error;
719 Curl_safefree(host_header);
720 }
721
722 if(data->state.aptr.proxyuserpwd) {
723 result = Curl_hyper_header(data, headers,
724 data->state.aptr.proxyuserpwd);
725 if(result)
726 goto error;
727 }
728
729 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
730 data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
731 struct dynbuf ua;
732 Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
733 result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
734 data->set.str[STRING_USERAGENT]);
735 if(result)
736 goto error;
737 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
738 if(result)
739 goto error;
740 Curl_dyn_free(&ua);
741 }
742
743 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
744 result = Curl_hyper_header(data, headers,
745 "Proxy-Connection: Keep-Alive");
746 if(result)
747 goto error;
748 }
749
750 result = Curl_add_custom_headers(data, TRUE, headers);
751 if(result)
752 goto error;
753
754 result = Curl_creader_set_null(data);
755 if(result)
756 goto error;
757
758 sendtask = hyper_clientconn_send(client, req);
759 if(!sendtask) {
760 failf(data, "hyper_clientconn_send");
761 result = CURLE_OUT_OF_MEMORY;
762 goto error;
763 }
764 req = NULL;
765
766 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
767 failf(data, "Couldn't hyper_executor_push the send");
768 result = CURLE_OUT_OF_MEMORY;
769 goto error;
770 }
771 sendtask = NULL; /* ownership passed on */
772
773 hyper_clientconn_free(client);
774 client = NULL;
775
776error:
777 free(host_header);
778 free(authority);
779 if(io)
780 hyper_io_free(io);
781 if(options)
782 hyper_clientconn_options_free(options);
783 if(handshake)
784 hyper_task_free(handshake);
785 if(client)
786 hyper_clientconn_free(client);
787 if(req)
788 hyper_request_free(req);
789
790 return result;
791}
792
793static CURLcode send_CONNECT(struct Curl_cfilter *cf,
794 struct Curl_easy *data,
795 struct h1_tunnel_state *ts,
796 bool *done)
797{
798 struct hyptransfer *h = &data->hyp;
799 struct connectdata *conn = cf->conn;
800 hyper_task *task = NULL;
801 hyper_error *hypererr = NULL;
802 CURLcode result = CURLE_OK;
803
804 (void)ts;
805 (void)conn;
806 do {
807 task = hyper_executor_poll(h->exec);
808 if(task) {
809 bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
810 if(error)
811 hypererr = hyper_task_value(task);
812 hyper_task_free(task);
813 if(error) {
814 /* this could probably use a better error code? */
815 result = CURLE_OUT_OF_MEMORY;
816 goto error;
817 }
818 }
819 } while(task);
820error:
821 *done = (result == CURLE_OK);
822 if(hypererr) {
823 uint8_t errbuf[256];
824 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
825 failf(data, "Hyper: %.*s", (int)errlen, errbuf);
826 hyper_error_free(hypererr);
827 }
828 return result;
829}
830
831static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
832 struct Curl_easy *data,
833 struct h1_tunnel_state *ts,
834 bool *done)
835{
836 struct hyptransfer *h = &data->hyp;
837 CURLcode result;
838 int didwhat;
839
840 (void)ts;
841 result = Curl_hyper_stream(data, cf->conn, &didwhat,
842 CURL_CSELECT_IN | CURL_CSELECT_OUT);
843 *done = data->req.done;
844 if(result || !*done)
845 return result;
846 if(h->exec) {
847 hyper_executor_free(h->exec);
848 h->exec = NULL;
849 }
850 if(h->read_waker) {
851 hyper_waker_free(h->read_waker);
852 h->read_waker = NULL;
853 }
854 if(h->write_waker) {
855 hyper_waker_free(h->write_waker);
856 h->write_waker = NULL;
857 }
858 return result;
859}
860
861#endif /* USE_HYPER */
862
863static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
864 struct Curl_easy *data,
865 struct h1_tunnel_state *ts)
866{
867 struct connectdata *conn = cf->conn;
868 CURLcode result;
869 bool done;
870
871 if(tunnel_is_established(ts))
872 return CURLE_OK;
873 if(tunnel_is_failed(ts))
874 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
875
876 do {
877 timediff_t check;
878
879 check = Curl_timeleft(data, NULL, TRUE);
880 if(check <= 0) {
881 failf(data, "Proxy CONNECT aborted due to timeout");
882 result = CURLE_OPERATION_TIMEDOUT;
883 goto out;
884 }
885
886 switch(ts->tunnel_state) {
887 case H1_TUNNEL_INIT:
888 /* Prepare the CONNECT request and make a first attempt to send. */
889 CURL_TRC_CF(data, cf, "CONNECT start");
890 result = start_CONNECT(cf, data, ts);
891 if(result)
892 goto out;
893 h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
894 FALLTHROUGH();
895
896 case H1_TUNNEL_CONNECT:
897 /* see that the request is completely sent */
898 CURL_TRC_CF(data, cf, "CONNECT send");
899 result = send_CONNECT(cf, data, ts, &done);
900 if(result || !done)
901 goto out;
902 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
903 FALLTHROUGH();
904
905 case H1_TUNNEL_RECEIVE:
906 /* read what is there */
907 CURL_TRC_CF(data, cf, "CONNECT receive");
908 result = recv_CONNECT_resp(cf, data, ts, &done);
909 if(Curl_pgrsUpdate(data)) {
910 result = CURLE_ABORTED_BY_CALLBACK;
911 goto out;
912 }
913 /* error or not complete yet. return for more multi-multi */
914 if(result || !done)
915 goto out;
916 /* got it */
917 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
918 FALLTHROUGH();
919
920 case H1_TUNNEL_RESPONSE:
921 CURL_TRC_CF(data, cf, "CONNECT response");
922 if(data->req.newurl) {
923 /* not the "final" response, we need to do a follow up request.
924 * If the other side indicated a connection close, or if someone
925 * else told us to close this connection, do so now.
926 */
927 Curl_req_soft_reset(&data->req, data);
928 if(ts->close_connection || conn->bits.close) {
929 /* Close this filter and the sub-chain, re-connect the
930 * sub-chain and continue. Closing this filter will
931 * reset our tunnel state. To avoid recursion, we return
932 * and expect to be called again.
933 */
934 CURL_TRC_CF(data, cf, "CONNECT need to close+open");
935 infof(data, "Connect me again please");
936 Curl_conn_cf_close(cf, data);
937 connkeep(conn, "HTTP proxy CONNECT");
938 result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
939 goto out;
940 }
941 else {
942 /* staying on this connection, reset state */
943 h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
944 }
945 }
946 break;
947
948 default:
949 break;
950 }
951
952 } while(data->req.newurl);
953
954 DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
955 if(data->info.httpproxycode/100 != 2) {
956 /* a non-2xx response and we have no next url to try. */
957 Curl_safefree(data->req.newurl);
958 /* failure, close this connection to avoid reuse */
959 streamclose(conn, "proxy CONNECT failure");
960 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
961 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
962 return CURLE_RECV_ERROR;
963 }
964 /* 2xx response, SUCCESS! */
965 h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
966 infof(data, "CONNECT tunnel established, response %d",
967 data->info.httpproxycode);
968 result = CURLE_OK;
969
970out:
971 if(result)
972 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
973 return result;
974}
975
976static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
977 struct Curl_easy *data,
978 bool blocking, bool *done)
979{
980 CURLcode result;
981 struct h1_tunnel_state *ts = cf->ctx;
982
983 if(cf->connected) {
984 *done = TRUE;
985 return CURLE_OK;
986 }
987
988 CURL_TRC_CF(data, cf, "connect");
989 result = cf->next->cft->do_connect(cf->next, data, blocking, done);
990 if(result || !*done)
991 return result;
992
993 *done = FALSE;
994 if(!ts) {
995 result = tunnel_init(cf, data, &ts);
996 if(result)
997 return result;
998 cf->ctx = ts;
999 }
1000
1001 /* TODO: can we do blocking? */
1002 /* We want "seamless" operations through HTTP proxy tunnel */
1003
1004 result = H1_CONNECT(cf, data, ts);
1005 if(result)
1006 goto out;
1007 Curl_safefree(data->state.aptr.proxyuserpwd);
1008
1009out:
1010 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
1011 if(*done) {
1012 cf->connected = TRUE;
1013 /* The real request will follow the CONNECT, reset request partially */
1014 Curl_req_soft_reset(&data->req, data);
1015 Curl_client_reset(data);
1016 Curl_pgrsSetUploadCounter(data, 0);
1017 Curl_pgrsSetDownloadCounter(data, 0);
1018
1019 tunnel_free(cf, data);
1020 }
1021 return result;
1022}
1023
1024static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
1025 struct Curl_easy *data,
1026 struct easy_pollset *ps)
1027{
1028 struct h1_tunnel_state *ts = cf->ctx;
1029
1030 if(!cf->connected) {
1031 /* If we are not connected, but the filter "below" is
1032 * and not waiting on something, we are tunneling. */
1033 curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
1034 if(ts) {
1035 /* when we've sent a CONNECT to a proxy, we should rather either
1036 wait for the socket to become readable to be able to get the
1037 response headers or if we're still sending the request, wait
1038 for write. */
1039 if(tunnel_want_send(ts))
1040 Curl_pollset_set_out_only(data, ps, sock);
1041 else
1042 Curl_pollset_set_in_only(data, ps, sock);
1043 }
1044 else
1045 Curl_pollset_set_out_only(data, ps, sock);
1046 }
1047}
1048
1049static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
1050 struct Curl_easy *data)
1051{
1052 CURL_TRC_CF(data, cf, "destroy");
1053 tunnel_free(cf, data);
1054}
1055
1056static void cf_h1_proxy_close(struct Curl_cfilter *cf,
1057 struct Curl_easy *data)
1058{
1059 CURL_TRC_CF(data, cf, "close");
1060 cf->connected = FALSE;
1061 if(cf->ctx) {
1062 h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
1063 }
1064 if(cf->next)
1065 cf->next->cft->do_close(cf->next, data);
1066}
1067
1068
1069struct Curl_cftype Curl_cft_h1_proxy = {
1070 "H1-PROXY",
1071 CF_TYPE_IP_CONNECT,
1072 0,
1073 cf_h1_proxy_destroy,
1074 cf_h1_proxy_connect,
1075 cf_h1_proxy_close,
1076 Curl_cf_http_proxy_get_host,
1077 cf_h1_proxy_adjust_pollset,
1078 Curl_cf_def_data_pending,
1079 Curl_cf_def_send,
1080 Curl_cf_def_recv,
1081 Curl_cf_def_cntrl,
1082 Curl_cf_def_conn_is_alive,
1083 Curl_cf_def_conn_keep_alive,
1084 Curl_cf_def_query,
1085};
1086
1087CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
1088 struct Curl_easy *data)
1089{
1090 struct Curl_cfilter *cf;
1091 CURLcode result;
1092
1093 (void)data;
1094 result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
1095 if(!result)
1096 Curl_conn_cf_insert_after(cf_at, cf);
1097 return result;
1098}
1099
1100#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette