VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/http_proxy.c@ 99797

Last change on this file since 99797 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

  • Property svn:eol-style set to native
File size: 40.7 KB
Line 
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#include "http_proxy.h"
28
29#if !defined(CURL_DISABLE_PROXY)
30
31#include <curl/curl.h>
32#ifdef USE_HYPER
33#include <hyper.h>
34#endif
35#include "sendf.h"
36#include "http.h"
37#include "url.h"
38#include "select.h"
39#include "progress.h"
40#include "cfilters.h"
41#include "connect.h"
42#include "curlx.h"
43#include "vtls/vtls.h"
44#include "transfer.h"
45#include "multiif.h"
46
47/* The last 3 #include files should be in this order */
48#include "curl_printf.h"
49#include "curl_memory.h"
50#include "memdebug.h"
51
52
53#if !defined(CURL_DISABLE_HTTP)
54
55typedef enum {
56 TUNNEL_INIT, /* init/default/no tunnel state */
57 TUNNEL_CONNECT, /* CONNECT request is being send */
58 TUNNEL_RECEIVE, /* CONNECT answer is being received */
59 TUNNEL_RESPONSE, /* CONNECT response received completely */
60 TUNNEL_ESTABLISHED,
61 TUNNEL_FAILED
62} tunnel_state;
63
64/* struct for HTTP CONNECT tunneling */
65struct tunnel_state {
66 int sockindex;
67 const char *hostname;
68 int remote_port;
69 struct HTTP CONNECT;
70 struct dynbuf rcvbuf;
71 struct dynbuf req;
72 size_t nsend;
73 size_t headerlines;
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 tunnel_state tunnel_state;
81 BIT(chunked_encoding);
82 BIT(close_connection);
83};
84
85
86static bool tunnel_is_established(struct tunnel_state *ts)
87{
88 return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
89}
90
91static bool tunnel_is_failed(struct tunnel_state *ts)
92{
93 return ts && (ts->tunnel_state == TUNNEL_FAILED);
94}
95
96static CURLcode tunnel_reinit(struct tunnel_state *ts,
97 struct connectdata *conn,
98 struct Curl_easy *data)
99{
100 (void)data;
101 DEBUGASSERT(ts);
102 Curl_dyn_reset(&ts->rcvbuf);
103 Curl_dyn_reset(&ts->req);
104 ts->tunnel_state = TUNNEL_INIT;
105 ts->keepon = KEEPON_CONNECT;
106 ts->cl = 0;
107 ts->close_connection = FALSE;
108
109 if(conn->bits.conn_to_host)
110 ts->hostname = conn->conn_to_host.name;
111 else if(ts->sockindex == SECONDARYSOCKET)
112 ts->hostname = conn->secondaryhostname;
113 else
114 ts->hostname = conn->host.name;
115
116 if(ts->sockindex == SECONDARYSOCKET)
117 ts->remote_port = conn->secondary_port;
118 else if(conn->bits.conn_to_port)
119 ts->remote_port = conn->conn_to_port;
120 else
121 ts->remote_port = conn->remote_port;
122
123 return CURLE_OK;
124}
125
126static CURLcode tunnel_init(struct tunnel_state **pts,
127 struct Curl_easy *data,
128 struct connectdata *conn,
129 int sockindex)
130{
131 struct tunnel_state *ts;
132 CURLcode result;
133
134 if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
135 failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
136 return CURLE_UNSUPPORTED_PROTOCOL;
137 }
138
139 /* we might need the upload buffer for streaming a partial request */
140 result = Curl_get_upload_buffer(data);
141 if(result)
142 return result;
143
144 ts = calloc(1, sizeof(*ts));
145 if(!ts)
146 return CURLE_OUT_OF_MEMORY;
147
148 ts->sockindex = sockindex;
149 infof(data, "allocate connect buffer");
150
151 Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
152 Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
153
154 *pts = ts;
155 connkeep(conn, "HTTP proxy CONNECT");
156 return tunnel_reinit(ts, conn, data);
157}
158
159static void tunnel_go_state(struct Curl_cfilter *cf,
160 struct tunnel_state *ts,
161 tunnel_state new_state,
162 struct Curl_easy *data)
163{
164 if(ts->tunnel_state == new_state)
165 return;
166 /* leaving this one */
167 switch(ts->tunnel_state) {
168 case TUNNEL_CONNECT:
169 data->req.ignorebody = FALSE;
170 break;
171 default:
172 break;
173 }
174 /* entering this one */
175 switch(new_state) {
176 case TUNNEL_INIT:
177 DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
178 tunnel_reinit(ts, cf->conn, data);
179 break;
180
181 case TUNNEL_CONNECT:
182 DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
183 ts->tunnel_state = TUNNEL_CONNECT;
184 ts->keepon = KEEPON_CONNECT;
185 Curl_dyn_reset(&ts->rcvbuf);
186 break;
187
188 case TUNNEL_RECEIVE:
189 DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
190 ts->tunnel_state = TUNNEL_RECEIVE;
191 break;
192
193 case TUNNEL_RESPONSE:
194 DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
195 ts->tunnel_state = TUNNEL_RESPONSE;
196 break;
197
198 case TUNNEL_ESTABLISHED:
199 DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
200 infof(data, "CONNECT phase completed");
201 data->state.authproxy.done = TRUE;
202 data->state.authproxy.multipass = FALSE;
203 /* FALLTHROUGH */
204 case TUNNEL_FAILED:
205 DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
206 ts->tunnel_state = new_state;
207 Curl_dyn_reset(&ts->rcvbuf);
208 Curl_dyn_reset(&ts->req);
209 /* restore the protocol pointer */
210 data->info.httpcode = 0; /* clear it as it might've been used for the
211 proxy */
212 /* If a proxy-authorization header was used for the proxy, then we should
213 make sure that it isn't accidentally used for the document request
214 after we've connected. So let's free and clear it here. */
215 Curl_safefree(data->state.aptr.proxyuserpwd);
216 data->state.aptr.proxyuserpwd = NULL;
217#ifdef USE_HYPER
218 data->state.hconnect = FALSE;
219#endif
220 break;
221 }
222}
223
224static void tunnel_free(struct Curl_cfilter *cf,
225 struct Curl_easy *data)
226{
227 struct tunnel_state *ts = cf->ctx;
228 if(ts) {
229 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
230 Curl_dyn_free(&ts->rcvbuf);
231 Curl_dyn_free(&ts->req);
232 free(ts);
233 cf->ctx = NULL;
234 }
235}
236
237static CURLcode CONNECT_host(struct Curl_easy *data,
238 struct connectdata *conn,
239 const char *hostname,
240 int remote_port,
241 char **connecthostp,
242 char **hostp)
243{
244 char *hostheader; /* for CONNECT */
245 char *host = NULL; /* Host: */
246 bool ipv6_ip = conn->bits.ipv6_ip;
247
248 /* the hostname may be different */
249 if(hostname != conn->host.name)
250 ipv6_ip = (strchr(hostname, ':') != NULL);
251 hostheader = /* host:port with IPv6 support */
252 aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
253 remote_port);
254 if(!hostheader)
255 return CURLE_OUT_OF_MEMORY;
256
257 if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
258 host = aprintf("Host: %s\r\n", hostheader);
259 if(!host) {
260 free(hostheader);
261 return CURLE_OUT_OF_MEMORY;
262 }
263 }
264 *connecthostp = hostheader;
265 *hostp = host;
266 return CURLE_OK;
267}
268
269#ifndef USE_HYPER
270static CURLcode start_CONNECT(struct Curl_cfilter *cf,
271 struct Curl_easy *data,
272 struct tunnel_state *ts)
273{
274 struct connectdata *conn = cf->conn;
275 char *hostheader = NULL;
276 char *host = NULL;
277 const char *httpv;
278 CURLcode result;
279
280 infof(data, "Establish HTTP proxy tunnel to %s:%d",
281 ts->hostname, ts->remote_port);
282
283 /* This only happens if we've looped here due to authentication
284 reasons, and we don't really use the newly cloned URL here
285 then. Just free() it. */
286 Curl_safefree(data->req.newurl);
287
288 result = CONNECT_host(data, conn,
289 ts->hostname, ts->remote_port,
290 &hostheader, &host);
291 if(result)
292 goto out;
293
294 /* Setup the proxy-authorization header, if any */
295 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
296 hostheader, TRUE);
297 if(result)
298 goto out;
299
300 httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
301
302 result =
303 Curl_dyn_addf(&ts->req,
304 "CONNECT %s HTTP/%s\r\n"
305 "%s" /* Host: */
306 "%s", /* Proxy-Authorization */
307 hostheader,
308 httpv,
309 host?host:"",
310 data->state.aptr.proxyuserpwd?
311 data->state.aptr.proxyuserpwd:"");
312 if(result)
313 goto out;
314
315 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent"))
316 && data->set.str[STRING_USERAGENT])
317 result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n",
318 data->set.str[STRING_USERAGENT]);
319 if(result)
320 goto out;
321
322 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection")))
323 result = Curl_dyn_addn(&ts->req,
324 STRCONST("Proxy-Connection: Keep-Alive\r\n"));
325 if(result)
326 goto out;
327
328 result = Curl_add_custom_headers(data, TRUE, &ts->req);
329 if(result)
330 goto out;
331
332 /* CRLF terminate the request */
333 result = Curl_dyn_addn(&ts->req, STRCONST("\r\n"));
334 if(result)
335 goto out;
336
337 /* Send the connect request to the proxy */
338 result = Curl_buffer_send(&ts->req, data, &ts->CONNECT,
339 &data->info.request_size, 0,
340 ts->sockindex);
341 ts->headerlines = 0;
342
343out:
344 if(result)
345 failf(data, "Failed sending CONNECT to proxy");
346 free(host);
347 free(hostheader);
348 return result;
349}
350
351static CURLcode send_CONNECT(struct Curl_easy *data,
352 struct connectdata *conn,
353 struct tunnel_state *ts,
354 bool *done)
355{
356 struct SingleRequest *k = &data->req;
357 struct HTTP *http = &ts->CONNECT;
358 CURLcode result = CURLE_OK;
359
360 if(http->sending != HTTPSEND_REQUEST)
361 goto out;
362
363 if(!ts->nsend) {
364 size_t fillcount;
365 k->upload_fromhere = data->state.ulbuf;
366 result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
367 &fillcount);
368 if(result)
369 goto out;
370 ts->nsend = fillcount;
371 }
372 if(ts->nsend) {
373 ssize_t bytes_written;
374 /* write to socket (send away data) */
375 result = Curl_write(data,
376 conn->writesockfd, /* socket to send to */
377 k->upload_fromhere, /* buffer pointer */
378 ts->nsend, /* buffer size */
379 &bytes_written); /* actually sent */
380 if(result)
381 goto out;
382 /* send to debug callback! */
383 Curl_debug(data, CURLINFO_HEADER_OUT,
384 k->upload_fromhere, bytes_written);
385
386 ts->nsend -= bytes_written;
387 k->upload_fromhere += bytes_written;
388 }
389 if(!ts->nsend)
390 http->sending = HTTPSEND_NADA;
391
392out:
393 if(result)
394 failf(data, "Failed sending CONNECT to proxy");
395 *done = (http->sending != HTTPSEND_REQUEST);
396 return result;
397}
398
399static CURLcode on_resp_header(struct Curl_cfilter *cf,
400 struct Curl_easy *data,
401 struct tunnel_state *ts,
402 const char *header)
403{
404 CURLcode result = CURLE_OK;
405 struct SingleRequest *k = &data->req;
406 (void)cf;
407
408 if((checkprefix("WWW-Authenticate:", header) &&
409 (401 == k->httpcode)) ||
410 (checkprefix("Proxy-authenticate:", header) &&
411 (407 == k->httpcode))) {
412
413 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
414 char *auth = Curl_copy_header_value(header);
415 if(!auth)
416 return CURLE_OUT_OF_MEMORY;
417
418 DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header));
419 result = Curl_http_input_auth(data, proxy, auth);
420
421 free(auth);
422
423 if(result)
424 return result;
425 }
426 else if(checkprefix("Content-Length:", header)) {
427 if(k->httpcode/100 == 2) {
428 /* A client MUST ignore any Content-Length or Transfer-Encoding
429 header fields received in a successful response to CONNECT.
430 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
431 infof(data, "Ignoring Content-Length in CONNECT %03d response",
432 k->httpcode);
433 }
434 else {
435 (void)curlx_strtoofft(header + strlen("Content-Length:"),
436 NULL, 10, &ts->cl);
437 }
438 }
439 else if(Curl_compareheader(header,
440 STRCONST("Connection:"), STRCONST("close")))
441 ts->close_connection = TRUE;
442 else if(checkprefix("Transfer-Encoding:", header)) {
443 if(k->httpcode/100 == 2) {
444 /* A client MUST ignore any Content-Length or Transfer-Encoding
445 header fields received in a successful response to CONNECT.
446 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
447 infof(data, "Ignoring Transfer-Encoding in "
448 "CONNECT %03d response", k->httpcode);
449 }
450 else if(Curl_compareheader(header,
451 STRCONST("Transfer-Encoding:"),
452 STRCONST("chunked"))) {
453 infof(data, "CONNECT responded chunked");
454 ts->chunked_encoding = TRUE;
455 /* init our chunky engine */
456 Curl_httpchunk_init(data);
457 }
458 }
459 else if(Curl_compareheader(header,
460 STRCONST("Proxy-Connection:"),
461 STRCONST("close")))
462 ts->close_connection = TRUE;
463 else if(!strncmp(header, "HTTP/1.", 7) &&
464 ((header[7] == '0') || (header[7] == '1')) &&
465 (header[8] == ' ') &&
466 ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
467 !ISDIGIT(header[12])) {
468 /* store the HTTP code from the proxy */
469 data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
470 (header[10] - '0') * 10 + (header[11] - '0');
471 }
472 return result;
473}
474
475static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
476 struct Curl_easy *data,
477 struct tunnel_state *ts,
478 bool *done)
479{
480 CURLcode result = CURLE_OK;
481 struct SingleRequest *k = &data->req;
482 curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
483 char *linep;
484 size_t perline;
485 int error;
486
487#define SELECT_OK 0
488#define SELECT_ERROR 1
489
490 error = SELECT_OK;
491 *done = FALSE;
492
493 if(!Curl_conn_data_pending(data, ts->sockindex))
494 return CURLE_OK;
495
496 while(ts->keepon) {
497 ssize_t gotbytes;
498 char byte;
499
500 /* Read one byte at a time to avoid a race condition. Wait at most one
501 second before looping to ensure continuous pgrsUpdates. */
502 result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
503 if(result == CURLE_AGAIN)
504 /* socket buffer drained, return */
505 return CURLE_OK;
506
507 if(Curl_pgrsUpdate(data))
508 return CURLE_ABORTED_BY_CALLBACK;
509
510 if(result) {
511 ts->keepon = KEEPON_DONE;
512 break;
513 }
514
515 if(gotbytes <= 0) {
516 if(data->set.proxyauth && data->state.authproxy.avail &&
517 data->state.aptr.proxyuserpwd) {
518 /* proxy auth was requested and there was proxy auth available,
519 then deem this as "mere" proxy disconnect */
520 ts->close_connection = TRUE;
521 infof(data, "Proxy CONNECT connection closed");
522 }
523 else {
524 error = SELECT_ERROR;
525 failf(data, "Proxy CONNECT aborted");
526 }
527 ts->keepon = KEEPON_DONE;
528 break;
529 }
530
531 if(ts->keepon == KEEPON_IGNORE) {
532 /* This means we are currently ignoring a response-body */
533
534 if(ts->cl) {
535 /* A Content-Length based body: simply count down the counter
536 and make sure to break out of the loop when we're done! */
537 ts->cl--;
538 if(ts->cl <= 0) {
539 ts->keepon = KEEPON_DONE;
540 break;
541 }
542 }
543 else {
544 /* chunked-encoded body, so we need to do the chunked dance
545 properly to know when the end of the body is reached */
546 CHUNKcode r;
547 CURLcode extra;
548 ssize_t tookcareof = 0;
549
550 /* now parse the chunked piece of data so that we can
551 properly tell when the stream ends */
552 r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
553 if(r == CHUNKE_STOP) {
554 /* we're done reading chunks! */
555 infof(data, "chunk reading DONE");
556 ts->keepon = KEEPON_DONE;
557 }
558 }
559 continue;
560 }
561
562 if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
563 failf(data, "CONNECT response too large");
564 return CURLE_RECV_ERROR;
565 }
566
567 /* if this is not the end of a header line then continue */
568 if(byte != 0x0a)
569 continue;
570
571 ts->headerlines++;
572 linep = Curl_dyn_ptr(&ts->rcvbuf);
573 perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
574
575 /* output debug if that is requested */
576 Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
577
578 if(!data->set.suppress_connect_headers) {
579 /* send the header to the callback */
580 int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
581 (data->set.include_header ? CLIENTWRITE_BODY : 0) |
582 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
583
584 result = Curl_client_write(data, writetype, linep, perline);
585 if(result)
586 return result;
587 }
588
589 data->info.header_size += (long)perline;
590
591 /* Newlines are CRLF, so the CR is ignored as the line isn't
592 really terminated until the LF comes. Treat a following CR
593 as end-of-headers as well.*/
594
595 if(('\r' == linep[0]) ||
596 ('\n' == linep[0])) {
597 /* end of response-headers from the proxy */
598
599 if((407 == k->httpcode) && !data->state.authproblem) {
600 /* If we get a 407 response code with content length
601 when we have no auth problem, we must ignore the
602 whole response-body */
603 ts->keepon = KEEPON_IGNORE;
604
605 if(ts->cl) {
606 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
607 " bytes of response-body", ts->cl);
608 }
609 else if(ts->chunked_encoding) {
610 CHUNKcode r;
611 CURLcode extra;
612
613 infof(data, "Ignore chunked response-body");
614
615 /* We set ignorebody true here since the chunked decoder
616 function will acknowledge that. Pay attention so that this is
617 cleared again when this function returns! */
618 k->ignorebody = TRUE;
619
620 if(linep[1] == '\n')
621 /* this can only be a LF if the letter at index 0 was a CR */
622 linep++;
623
624 /* now parse the chunked piece of data so that we can properly
625 tell when the stream ends */
626 r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
627 &extra);
628 if(r == CHUNKE_STOP) {
629 /* we're done reading chunks! */
630 infof(data, "chunk reading DONE");
631 ts->keepon = KEEPON_DONE;
632 }
633 }
634 else {
635 /* without content-length or chunked encoding, we
636 can't keep the connection alive since the close is
637 the end signal so we bail out at once instead */
638 DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked"));
639 ts->keepon = KEEPON_DONE;
640 }
641 }
642 else {
643 ts->keepon = KEEPON_DONE;
644 }
645
646 DEBUGASSERT(ts->keepon == KEEPON_IGNORE
647 || ts->keepon == KEEPON_DONE);
648 continue;
649 }
650
651 result = on_resp_header(cf, data, ts, linep);
652 if(result)
653 return result;
654
655 Curl_dyn_reset(&ts->rcvbuf);
656 } /* while there's buffer left and loop is requested */
657
658 if(error)
659 result = CURLE_RECV_ERROR;
660 *done = (ts->keepon == KEEPON_DONE);
661 if(!result && *done && data->info.httpproxycode/100 != 2) {
662 /* Deal with the possibly already received authenticate
663 headers. 'newurl' is set to a new URL if we must loop. */
664 result = Curl_http_auth_act(data);
665 }
666 return result;
667}
668
669#else /* USE_HYPER */
670/* The Hyper version of CONNECT */
671static CURLcode start_CONNECT(struct Curl_cfilter *cf,
672 struct Curl_easy *data,
673 struct tunnel_state *ts)
674{
675 struct connectdata *conn = cf->conn;
676 struct hyptransfer *h = &data->hyp;
677 curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
678 hyper_io *io = NULL;
679 hyper_request *req = NULL;
680 hyper_headers *headers = NULL;
681 hyper_clientconn_options *options = NULL;
682 hyper_task *handshake = NULL;
683 hyper_task *task = NULL; /* for the handshake */
684 hyper_clientconn *client = NULL;
685 hyper_task *sendtask = NULL; /* for the send */
686 char *hostheader = NULL; /* for CONNECT */
687 char *host = NULL; /* Host: */
688 CURLcode result = CURLE_OUT_OF_MEMORY;
689
690 io = hyper_io_new();
691 if(!io) {
692 failf(data, "Couldn't create hyper IO");
693 result = CURLE_OUT_OF_MEMORY;
694 goto error;
695 }
696 /* tell Hyper how to read/write network data */
697 hyper_io_set_userdata(io, data);
698 hyper_io_set_read(io, Curl_hyper_recv);
699 hyper_io_set_write(io, Curl_hyper_send);
700 conn->sockfd = tunnelsocket;
701
702 data->state.hconnect = TRUE;
703
704 /* create an executor to poll futures */
705 if(!h->exec) {
706 h->exec = hyper_executor_new();
707 if(!h->exec) {
708 failf(data, "Couldn't create hyper executor");
709 result = CURLE_OUT_OF_MEMORY;
710 goto error;
711 }
712 }
713
714 options = hyper_clientconn_options_new();
715 hyper_clientconn_options_set_preserve_header_case(options, 1);
716 hyper_clientconn_options_set_preserve_header_order(options, 1);
717
718 if(!options) {
719 failf(data, "Couldn't create hyper client options");
720 result = CURLE_OUT_OF_MEMORY;
721 goto error;
722 }
723
724 hyper_clientconn_options_exec(options, h->exec);
725
726 /* "Both the `io` and the `options` are consumed in this function
727 call" */
728 handshake = hyper_clientconn_handshake(io, options);
729 if(!handshake) {
730 failf(data, "Couldn't create hyper client handshake");
731 result = CURLE_OUT_OF_MEMORY;
732 goto error;
733 }
734 io = NULL;
735 options = NULL;
736
737 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
738 failf(data, "Couldn't hyper_executor_push the handshake");
739 result = CURLE_OUT_OF_MEMORY;
740 goto error;
741 }
742 handshake = NULL; /* ownership passed on */
743
744 task = hyper_executor_poll(h->exec);
745 if(!task) {
746 failf(data, "Couldn't hyper_executor_poll the handshake");
747 result = CURLE_OUT_OF_MEMORY;
748 goto error;
749 }
750
751 client = hyper_task_value(task);
752 hyper_task_free(task);
753 req = hyper_request_new();
754 if(!req) {
755 failf(data, "Couldn't hyper_request_new");
756 result = CURLE_OUT_OF_MEMORY;
757 goto error;
758 }
759 if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
760 strlen("CONNECT"))) {
761 failf(data, "error setting method");
762 result = CURLE_OUT_OF_MEMORY;
763 goto error;
764 }
765
766 infof(data, "Establish HTTP proxy tunnel to %s:%d",
767 ts->hostname, ts->remote_port);
768
769 /* This only happens if we've looped here due to authentication
770 reasons, and we don't really use the newly cloned URL here
771 then. Just free() it. */
772 Curl_safefree(data->req.newurl);
773
774 result = CONNECT_host(data, conn, ts->hostname, ts->remote_port,
775 &hostheader, &host);
776 if(result)
777 goto error;
778
779 if(hyper_request_set_uri(req, (uint8_t *)hostheader,
780 strlen(hostheader))) {
781 failf(data, "error setting path");
782 result = CURLE_OUT_OF_MEMORY;
783 goto error;
784 }
785 if(data->set.verbose) {
786 char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
787 if(!se) {
788 result = CURLE_OUT_OF_MEMORY;
789 goto error;
790 }
791 Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
792 free(se);
793 }
794 /* Setup the proxy-authorization header, if any */
795 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
796 hostheader, TRUE);
797 if(result)
798 goto error;
799 Curl_safefree(hostheader);
800
801 /* default is 1.1 */
802 if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
803 (HYPERE_OK != hyper_request_set_version(req,
804 HYPER_HTTP_VERSION_1_0))) {
805 failf(data, "error setting HTTP version");
806 result = CURLE_OUT_OF_MEMORY;
807 goto error;
808 }
809
810 headers = hyper_request_headers(req);
811 if(!headers) {
812 failf(data, "hyper_request_headers");
813 result = CURLE_OUT_OF_MEMORY;
814 goto error;
815 }
816 if(host) {
817 result = Curl_hyper_header(data, headers, host);
818 if(result)
819 goto error;
820 Curl_safefree(host);
821 }
822
823 if(data->state.aptr.proxyuserpwd) {
824 result = Curl_hyper_header(data, headers,
825 data->state.aptr.proxyuserpwd);
826 if(result)
827 goto error;
828 }
829
830 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
831 data->set.str[STRING_USERAGENT]) {
832 struct dynbuf ua;
833 Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
834 result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
835 data->set.str[STRING_USERAGENT]);
836 if(result)
837 goto error;
838 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
839 if(result)
840 goto error;
841 Curl_dyn_free(&ua);
842 }
843
844 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
845 result = Curl_hyper_header(data, headers,
846 "Proxy-Connection: Keep-Alive");
847 if(result)
848 goto error;
849 }
850
851 result = Curl_add_custom_headers(data, TRUE, headers);
852 if(result)
853 goto error;
854
855 sendtask = hyper_clientconn_send(client, req);
856 if(!sendtask) {
857 failf(data, "hyper_clientconn_send");
858 result = CURLE_OUT_OF_MEMORY;
859 goto error;
860 }
861
862 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
863 failf(data, "Couldn't hyper_executor_push the send");
864 result = CURLE_OUT_OF_MEMORY;
865 goto error;
866 }
867
868error:
869 free(host);
870 free(hostheader);
871 if(io)
872 hyper_io_free(io);
873 if(options)
874 hyper_clientconn_options_free(options);
875 if(handshake)
876 hyper_task_free(handshake);
877 if(client)
878 hyper_clientconn_free(client);
879 return result;
880}
881
882static CURLcode send_CONNECT(struct Curl_easy *data,
883 struct connectdata *conn,
884 struct tunnel_state *ts,
885 bool *done)
886{
887 struct hyptransfer *h = &data->hyp;
888 hyper_task *task = NULL;
889 hyper_error *hypererr = NULL;
890 CURLcode result = CURLE_OK;
891
892 (void)ts;
893 (void)conn;
894 do {
895 task = hyper_executor_poll(h->exec);
896 if(task) {
897 bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
898 if(error)
899 hypererr = hyper_task_value(task);
900 hyper_task_free(task);
901 if(error) {
902 /* this could probably use a better error code? */
903 result = CURLE_OUT_OF_MEMORY;
904 goto error;
905 }
906 }
907 } while(task);
908error:
909 *done = (result == CURLE_OK);
910 if(hypererr) {
911 uint8_t errbuf[256];
912 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
913 failf(data, "Hyper: %.*s", (int)errlen, errbuf);
914 hyper_error_free(hypererr);
915 }
916 return result;
917}
918
919static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
920 struct Curl_easy *data,
921 struct tunnel_state *ts,
922 bool *done)
923{
924 struct hyptransfer *h = &data->hyp;
925 CURLcode result;
926 int didwhat;
927
928 (void)ts;
929 *done = FALSE;
930 result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
931 CURL_CSELECT_IN | CURL_CSELECT_OUT);
932 if(result || !*done)
933 return result;
934 if(h->exec) {
935 hyper_executor_free(h->exec);
936 h->exec = NULL;
937 }
938 if(h->read_waker) {
939 hyper_waker_free(h->read_waker);
940 h->read_waker = NULL;
941 }
942 if(h->write_waker) {
943 hyper_waker_free(h->write_waker);
944 h->write_waker = NULL;
945 }
946 return result;
947}
948
949#endif /* USE_HYPER */
950
951static CURLcode CONNECT(struct Curl_cfilter *cf,
952 struct Curl_easy *data,
953 struct tunnel_state *ts)
954{
955 struct connectdata *conn = cf->conn;
956 CURLcode result;
957 bool done;
958
959 if(tunnel_is_established(ts))
960 return CURLE_OK;
961 if(tunnel_is_failed(ts))
962 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
963
964 do {
965 timediff_t check;
966
967 check = Curl_timeleft(data, NULL, TRUE);
968 if(check <= 0) {
969 failf(data, "Proxy CONNECT aborted due to timeout");
970 result = CURLE_OPERATION_TIMEDOUT;
971 goto out;
972 }
973
974 switch(ts->tunnel_state) {
975 case TUNNEL_INIT:
976 /* Prepare the CONNECT request and make a first attempt to send. */
977 DEBUGF(LOG_CF(data, cf, "CONNECT start"));
978 result = start_CONNECT(cf, data, ts);
979 if(result)
980 goto out;
981 tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
982 /* FALLTHROUGH */
983
984 case TUNNEL_CONNECT:
985 /* see that the request is completely sent */
986 DEBUGF(LOG_CF(data, cf, "CONNECT send"));
987 result = send_CONNECT(data, cf->conn, ts, &done);
988 if(result || !done)
989 goto out;
990 tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
991 /* FALLTHROUGH */
992
993 case TUNNEL_RECEIVE:
994 /* read what is there */
995 DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
996 result = recv_CONNECT_resp(cf, data, ts, &done);
997 if(Curl_pgrsUpdate(data)) {
998 result = CURLE_ABORTED_BY_CALLBACK;
999 goto out;
1000 }
1001 /* error or not complete yet. return for more multi-multi */
1002 if(result || !done)
1003 goto out;
1004 /* got it */
1005 tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
1006 /* FALLTHROUGH */
1007
1008 case TUNNEL_RESPONSE:
1009 DEBUGF(LOG_CF(data, cf, "CONNECT response"));
1010 if(data->req.newurl) {
1011 /* not the "final" response, we need to do a follow up request.
1012 * If the other side indicated a connection close, or if someone
1013 * else told us to close this connection, do so now.
1014 */
1015 if(ts->close_connection || conn->bits.close) {
1016 /* Close this filter and the sub-chain, re-connect the
1017 * sub-chain and continue. Closing this filter will
1018 * reset our tunnel state. To avoid recursion, we return
1019 * and expect to be called again.
1020 */
1021 DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open"));
1022 infof(data, "Connect me again please");
1023 Curl_conn_cf_close(cf, data);
1024 connkeep(conn, "HTTP proxy CONNECT");
1025 result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
1026 goto out;
1027 }
1028 else {
1029 /* staying on this connection, reset state */
1030 tunnel_go_state(cf, ts, TUNNEL_INIT, data);
1031 }
1032 }
1033 break;
1034
1035 default:
1036 break;
1037 }
1038
1039 } while(data->req.newurl);
1040
1041 DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
1042 if(data->info.httpproxycode/100 != 2) {
1043 /* a non-2xx response and we have no next url to try. */
1044 free(data->req.newurl);
1045 data->req.newurl = NULL;
1046 /* failure, close this connection to avoid re-use */
1047 streamclose(conn, "proxy CONNECT failure");
1048 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1049 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
1050 return CURLE_RECV_ERROR;
1051 }
1052 /* 2xx response, SUCCESS! */
1053 tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
1054 infof(data, "CONNECT tunnel established, response %d",
1055 data->info.httpproxycode);
1056 result = CURLE_OK;
1057
1058out:
1059 if(result)
1060 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1061 return result;
1062}
1063
1064static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
1065 struct Curl_easy *data,
1066 bool blocking, bool *done)
1067{
1068 CURLcode result;
1069 struct tunnel_state *ts = cf->ctx;
1070
1071 if(cf->connected) {
1072 *done = TRUE;
1073 return CURLE_OK;
1074 }
1075
1076 DEBUGF(LOG_CF(data, cf, "connect"));
1077 result = cf->next->cft->connect(cf->next, data, blocking, done);
1078 if(result || !*done)
1079 return result;
1080
1081 DEBUGF(LOG_CF(data, cf, "subchain is connected"));
1082 /* TODO: can we do blocking? */
1083 /* We want "seamless" operations through HTTP proxy tunnel */
1084
1085 /* for the secondary socket (FTP), use the "connect to host"
1086 * but ignore the "connect to port" (use the secondary port)
1087 */
1088 *done = FALSE;
1089 if(!ts) {
1090 result = tunnel_init(&ts, data, cf->conn, cf->sockindex);
1091 if(result)
1092 return result;
1093 cf->ctx = ts;
1094 }
1095
1096 result = CONNECT(cf, data, ts);
1097 if(result)
1098 goto out;
1099 Curl_safefree(data->state.aptr.proxyuserpwd);
1100
1101out:
1102 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
1103 if (*done) {
1104 cf->connected = TRUE;
1105 tunnel_free(cf, data);
1106 }
1107 return result;
1108}
1109
1110static void http_proxy_cf_get_host(struct Curl_cfilter *cf,
1111 struct Curl_easy *data,
1112 const char **phost,
1113 const char **pdisplay_host,
1114 int *pport)
1115{
1116 (void)data;
1117 if(!cf->connected) {
1118 *phost = cf->conn->http_proxy.host.name;
1119 *pdisplay_host = cf->conn->http_proxy.host.dispname;
1120 *pport = (int)cf->conn->http_proxy.port;
1121 }
1122 else {
1123 cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
1124 }
1125}
1126
1127static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
1128 struct Curl_easy *data,
1129 curl_socket_t *socks)
1130{
1131 struct tunnel_state *ts = cf->ctx;
1132 int fds;
1133
1134 fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1135 if(!fds && cf->next->connected && !cf->connected) {
1136 /* If we are not connected, but the filter "below" is
1137 * and not waiting on something, we are tunneling. */
1138 socks[0] = Curl_conn_cf_get_socket(cf, data);
1139 if(ts) {
1140 /* when we've sent a CONNECT to a proxy, we should rather either
1141 wait for the socket to become readable to be able to get the
1142 response headers or if we're still sending the request, wait
1143 for write. */
1144 if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
1145 return GETSOCK_WRITESOCK(0);
1146 }
1147 return GETSOCK_READSOCK(0);
1148 }
1149 return GETSOCK_WRITESOCK(0);
1150 }
1151 return fds;
1152}
1153
1154static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
1155 struct Curl_easy *data)
1156{
1157 DEBUGF(LOG_CF(data, cf, "destroy"));
1158 tunnel_free(cf, data);
1159}
1160
1161static void http_proxy_cf_close(struct Curl_cfilter *cf,
1162 struct Curl_easy *data)
1163{
1164 DEBUGASSERT(cf->next);
1165 DEBUGF(LOG_CF(data, cf, "close"));
1166 cf->connected = FALSE;
1167 cf->next->cft->close(cf->next, data);
1168 if(cf->ctx) {
1169 tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
1170 }
1171}
1172
1173
1174struct Curl_cftype Curl_cft_http_proxy = {
1175 "HTTP-PROXY",
1176 CF_TYPE_IP_CONNECT,
1177 0,
1178 http_proxy_cf_destroy,
1179 http_proxy_cf_connect,
1180 http_proxy_cf_close,
1181 http_proxy_cf_get_host,
1182 http_proxy_cf_get_select_socks,
1183 Curl_cf_def_data_pending,
1184 Curl_cf_def_send,
1185 Curl_cf_def_recv,
1186 Curl_cf_def_cntrl,
1187 Curl_cf_def_conn_is_alive,
1188 Curl_cf_def_conn_keep_alive,
1189 Curl_cf_def_query,
1190};
1191
1192CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
1193 struct connectdata *conn,
1194 int sockindex)
1195{
1196 struct Curl_cfilter *cf;
1197 CURLcode result;
1198
1199 result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
1200 if(!result)
1201 Curl_conn_cf_add(data, conn, sockindex, cf);
1202 return result;
1203}
1204
1205CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
1206 struct Curl_easy *data)
1207{
1208 struct Curl_cfilter *cf;
1209 CURLcode result;
1210
1211 (void)data;
1212 result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
1213 if(!result)
1214 Curl_conn_cf_insert_after(cf_at, cf);
1215 return result;
1216}
1217
1218#endif /* ! CURL_DISABLE_HTTP */
1219
1220
1221typedef enum {
1222 HAPROXY_INIT, /* init/default/no tunnel state */
1223 HAPROXY_SEND, /* data_out being sent */
1224 HAPROXY_DONE /* all work done */
1225} haproxy_state;
1226
1227struct cf_haproxy_ctx {
1228 int state;
1229 struct dynbuf data_out;
1230};
1231
1232static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
1233{
1234 DEBUGASSERT(ctx);
1235 ctx->state = HAPROXY_INIT;
1236 Curl_dyn_reset(&ctx->data_out);
1237}
1238
1239static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
1240{
1241 if(ctx) {
1242 Curl_dyn_free(&ctx->data_out);
1243 free(ctx);
1244 }
1245}
1246
1247static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
1248 struct Curl_easy *data)
1249{
1250 struct cf_haproxy_ctx *ctx = cf->ctx;
1251 CURLcode result;
1252 const char *tcp_version;
1253
1254 DEBUGASSERT(ctx);
1255 DEBUGASSERT(ctx->state == HAPROXY_INIT);
1256#ifdef USE_UNIX_SOCKETS
1257 if(cf->conn->unix_domain_socket)
1258 /* the buffer is large enough to hold this! */
1259 result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
1260 else {
1261#endif /* USE_UNIX_SOCKETS */
1262 /* Emit the correct prefix for IPv6 */
1263 tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
1264
1265 result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
1266 tcp_version,
1267 data->info.conn_local_ip,
1268 data->info.conn_primary_ip,
1269 data->info.conn_local_port,
1270 data->info.conn_primary_port);
1271
1272#ifdef USE_UNIX_SOCKETS
1273 }
1274#endif /* USE_UNIX_SOCKETS */
1275 return result;
1276}
1277
1278static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
1279 struct Curl_easy *data,
1280 bool blocking, bool *done)
1281{
1282 struct cf_haproxy_ctx *ctx = cf->ctx;
1283 CURLcode result;
1284 size_t len;
1285
1286 DEBUGASSERT(ctx);
1287 if(cf->connected) {
1288 *done = TRUE;
1289 return CURLE_OK;
1290 }
1291
1292 result = cf->next->cft->connect(cf->next, data, blocking, done);
1293 if(result || !*done)
1294 return result;
1295
1296 switch(ctx->state) {
1297 case HAPROXY_INIT:
1298 result = cf_haproxy_date_out_set(cf, data);
1299 if(result)
1300 goto out;
1301 ctx->state = HAPROXY_SEND;
1302 /* FALLTHROUGH */
1303 case HAPROXY_SEND:
1304 len = Curl_dyn_len(&ctx->data_out);
1305 if(len > 0) {
1306 ssize_t written = Curl_conn_send(data, cf->sockindex,
1307 Curl_dyn_ptr(&ctx->data_out),
1308 len, &result);
1309 if(written < 0)
1310 goto out;
1311 Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
1312 if(Curl_dyn_len(&ctx->data_out) > 0) {
1313 result = CURLE_OK;
1314 goto out;
1315 }
1316 }
1317 ctx->state = HAPROXY_DONE;
1318 /* FALLTHROUGH */
1319 default:
1320 Curl_dyn_free(&ctx->data_out);
1321 break;
1322 }
1323
1324out:
1325 *done = (!result) && (ctx->state == HAPROXY_DONE);
1326 cf->connected = *done;
1327 return result;
1328}
1329
1330static void cf_haproxy_destroy(struct Curl_cfilter *cf,
1331 struct Curl_easy *data)
1332{
1333 (void)data;
1334 DEBUGF(LOG_CF(data, cf, "destroy"));
1335 cf_haproxy_ctx_free(cf->ctx);
1336}
1337
1338static void cf_haproxy_close(struct Curl_cfilter *cf,
1339 struct Curl_easy *data)
1340{
1341 DEBUGF(LOG_CF(data, cf, "close"));
1342 cf->connected = FALSE;
1343 cf_haproxy_ctx_reset(cf->ctx);
1344 if(cf->next)
1345 cf->next->cft->close(cf->next, data);
1346}
1347
1348static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
1349 struct Curl_easy *data,
1350 curl_socket_t *socks)
1351{
1352 int fds;
1353
1354 fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1355 if(!fds && cf->next->connected && !cf->connected) {
1356 /* If we are not connected, but the filter "below" is
1357 * and not waiting on something, we are sending. */
1358 socks[0] = Curl_conn_cf_get_socket(cf, data);
1359 return GETSOCK_WRITESOCK(0);
1360 }
1361 return fds;
1362}
1363
1364
1365struct Curl_cftype Curl_cft_haproxy = {
1366 "HAPROXY",
1367 0,
1368 0,
1369 cf_haproxy_destroy,
1370 cf_haproxy_connect,
1371 cf_haproxy_close,
1372 Curl_cf_def_get_host,
1373 cf_haproxy_get_select_socks,
1374 Curl_cf_def_data_pending,
1375 Curl_cf_def_send,
1376 Curl_cf_def_recv,
1377 Curl_cf_def_cntrl,
1378 Curl_cf_def_conn_is_alive,
1379 Curl_cf_def_conn_keep_alive,
1380 Curl_cf_def_query,
1381};
1382
1383static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
1384 struct Curl_easy *data)
1385{
1386 struct Curl_cfilter *cf = NULL;
1387 struct cf_haproxy_ctx *ctx;
1388 CURLcode result;
1389
1390 (void)data;
1391 ctx = calloc(sizeof(*ctx), 1);
1392 if(!ctx) {
1393 result = CURLE_OUT_OF_MEMORY;
1394 goto out;
1395 }
1396 ctx->state = HAPROXY_INIT;
1397 Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
1398
1399 result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
1400 if(result)
1401 goto out;
1402 ctx = NULL;
1403
1404out:
1405 cf_haproxy_ctx_free(ctx);
1406 *pcf = result? NULL : cf;
1407 return result;
1408}
1409
1410CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
1411 struct connectdata *conn,
1412 int sockindex)
1413{
1414 struct Curl_cfilter *cf;
1415 CURLcode result;
1416
1417 result = cf_haproxy_create(&cf, data);
1418 if(result)
1419 goto out;
1420 Curl_conn_cf_add(data, conn, sockindex, cf);
1421
1422out:
1423 return result;
1424}
1425
1426CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
1427 struct Curl_easy *data)
1428{
1429 struct Curl_cfilter *cf;
1430 CURLcode result;
1431
1432 result = cf_haproxy_create(&cf, data);
1433 if(result)
1434 goto out;
1435 Curl_conn_cf_insert_after(cf_at, cf);
1436
1437out:
1438 return result;
1439}
1440
1441#endif /* !CURL_DISABLE_PROXY */
Note: See TracBrowser for help on using the repository browser.

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