cf-setup: own source file

Move the setup filter into its own source file.

Move the main connect driving function, `Curl_conn_connect()`,
from cfilters.c to connect.c.

Closes #22022
This commit is contained in:
Stefan Eissing 2026-06-15 13:48:24 +02:00 committed by Daniel Stenberg
parent 8932063828
commit f497b25672
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
9 changed files with 718 additions and 662 deletions

View file

@ -165,6 +165,7 @@ LIB_CFILES = \
cf-haproxy.c \
cf-https-connect.c \
cf-ip-happy.c \
cf-setup.c \
cf-socket.c \
cfilters.c \
conncache.c \
@ -297,6 +298,7 @@ LIB_HFILES = \
cf-haproxy.h \
cf-https-connect.h \
cf-ip-happy.h \
cf-setup.h \
cf-socket.h \
cfilters.h \
conncache.h \

View file

@ -29,6 +29,7 @@
#include "curl_trc.h"
#include "cfilters.h"
#include "cf-dns.h"
#include "cf-setup.h"
#include "connect.h"
#include "hostip.h"
#include "httpsrr.h"

476
lib/cf-setup.c Normal file
View file

@ -0,0 +1,476 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
#include "cfilters.h"
#include "cf-haproxy.h"
#include "cf-ip-happy.h"
#include "cf-setup.h"
#include "curl_trc.h"
#include "connect.h"
#include "http_proxy.h"
#include "socks.h"
#include "vquic/cf-capsule.h"
#include "vquic/vquic.h"
#include "vtls/vtls.h"
typedef enum {
CF_SETUP_INIT,
CF_SETUP_CNNCT_EYEBALLS,
CF_SETUP_CNNCT_SOCKS,
CF_SETUP_CNNCT_HTTP_PROXY,
CF_SETUP_CNNCT_HAPROXY,
CF_SETUP_CNNCT_SSL,
CF_SETUP_DONE
} cf_setup_state;
struct cf_setup_ctx {
cf_setup_state state;
int ssl_mode;
uint8_t transport;
uint8_t retry_count;
};
#ifndef CURL_DISABLE_PROXY
static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
if(data->set.haproxyprotocol) {
if(ctx->transport == TRNSPRT_QUIC) {
failf(data, "haproxy protocol does not support QUIC");
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = Curl_cf_haproxy_insert_after(cf, data);
if(result) {
CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added HAPROXY filter");
}
ctx->state = CF_SETUP_CNNCT_HAPROXY;
}
return result;
}
static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
struct Curl_peer *second_peer;
if(cf->conn->http_proxy.peer)
second_peer = cf->conn->http_proxy.peer;
else
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
if(!second_peer)
return CURLE_FAILED_INIT;
result = Curl_cf_socks_proxy_insert_after(
cf, data, second_peer, cf->conn->ip_version,
cf->conn->socks_proxy.proxytype,
cf->conn->socks_proxy.creds);
if(result) {
CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u",
second_peer->hostname, second_peer->port);
ctx->state = CF_SETUP_CNNCT_SOCKS;
}
return result;
}
#ifndef CURL_DISABLE_HTTP
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
struct Curl_peer *peer = cf->conn->http_proxy.peer;
struct Curl_peer *tunnel_peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
#ifdef USE_SSL
if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
result = Curl_cf_ssl_proxy_insert_after(
cf, data, cf->conn->http_proxy.peer);
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy");
}
#endif /* USE_SSL */
result = Curl_cf_http_proxy_insert_after(
cf, data, peer, tunnel_peer,
ctx->transport, cf->conn->http_proxy.proxytype);
if(result) {
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
return result;
}
#endif /* !CURL_DISABLE_HTTP */
#endif /* CURL_DISABLE_PROXY */
/* Get the origin curl connects its socket to.
* Can be origin or the first proxy. */
static struct Curl_peer *conn_get_first_origin(struct connectdata *conn,
int sockindex)
{
#ifndef CURL_DISABLE_PROXY
if(conn->socks_proxy.peer)
return conn->socks_proxy.peer;
if(conn->http_proxy.peer)
return conn->http_proxy.peer;
#endif
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
}
static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
/* What is the first hop we directly connect to and what transport
* do we use for it? Only on the first hop we can do Happy Eyeballs.
* first_origin and first_peer differ on --connect-to. */
struct Curl_peer *first_origin =
conn_get_first_origin(cf->conn, cf->sockindex);
struct Curl_peer *first_peer =
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
struct Curl_peer *tunnel_peer = NULL;
uint8_t first_transport = ctx->transport;
if(!first_peer)
return CURLE_FAILED_INIT;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
first_transport =
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
first_transport,
tunnel_peer, ctx->transport);
if(result) {
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result);
return result;
}
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
else {
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
tunnel_peer ? "proxy" : "origin",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
}
}
return result;
}
static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
(void)data; /* not used in all builds */
if(ctx->state < CF_SETUP_CNNCT_SSL) {
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
!defined(CURL_DISABLE_PROXY)
/* Wanting QUIC with an HTTP tunneling filter, we now need to add
* the QUIC filter on top. Without tunneling, this has already
* happened in the Happy Eyeball filter. */
if(ctx->transport == TRNSPRT_QUIC &&
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_capsule_insert_after(cf, data);
if(result) {
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d",
(int)result);
return result;
}
result = Curl_cf_quic_insert_after(cf, origin, peer);
if(result) {
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result);
return result;
}
CURL_TRC_CF(data, cf, "added QUIC filter for origin");
}
else
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */
#ifdef USE_SSL
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
#ifndef CURL_DISABLE_PROXY
if(cf->conn->bits.origin_is_proxy) {
result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
}
else
#endif
{
/* Another FTP quirk: when adding SSL verification, to a DATA
* connection, always verify against the control's origin */
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
}
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SSL filter for origin");
}
#endif /* USE_SSL */
ctx->state = CF_SETUP_CNNCT_SSL;
}
return result;
}
static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
/* connect current sub-chain */
connect_sub_chain:
VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect"));
if(cf->next && !cf->next->connected) {
result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
result = cf_setup_add_ip_happy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#ifndef CURL_DISABLE_PROXY
result = cf_setup_add_socks(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#ifndef CURL_DISABLE_HTTP
result = cf_setup_add_http_proxy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#endif /* !CURL_DISABLE_HTTP */
result = cf_setup_add_haproxy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#endif /* !CURL_DISABLE_PROXY */
result = cf_setup_add_origin_filters(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
ctx->state = CF_SETUP_DONE;
cf->connected = TRUE;
*done = TRUE;
return CURLE_OK;
}
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result;
/* In some situations, a server/proxy may close the connection and
* we need to connect again (HTTP/1.x proxy auth, for example).
* We used to close the filters and reuse them for another attempt,
* however that complicates filter code and it is simpler to tear them
* all down and start over. */
retry:
result = cf_setup_connect_steps(cf, data, done);
if(result == CURLE_AGAIN) {
++ctx->retry_count;
if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */
return CURLE_COULDNT_CONNECT;
CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count);
Curl_conn_cf_discard_chain(&cf->next, data);
ctx->state = CF_SETUP_INIT;
goto retry;
}
return result;
}
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "destroy");
curlx_safefree(ctx);
}
struct Curl_cftype Curl_cft_setup = {
"SETUP",
CF_TYPE_SETUP,
CURL_LOG_LVL_NONE,
cf_setup_destroy,
cf_setup_connect,
Curl_cf_def_shutdown,
Curl_cf_def_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf = NULL;
struct cf_setup_ctx *ctx;
CURLcode result = CURLE_OK;
(void)data;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = CF_SETUP_INIT;
ctx->ssl_mode = ssl_mode;
ctx->transport = transport;
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
if(result)
goto out;
ctx = NULL;
out:
*pcf = result ? NULL : cf;
if(ctx) {
curlx_free(ctx);
}
return result;
}
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}

46
lib/cf-setup.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef HEADER_CURL_CF_SETUP_H
#define HEADER_CURL_CF_SETUP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
struct Curl_dns_entry;
struct ip_quadruple;
struct Curl_peer;
struct Curl_str;
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t transport,
int ssl_mode);
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode);
extern struct Curl_cftype Curl_cft_setup;
#endif /* HEADER_CURL_CF_SETUP_H */

View file

@ -423,29 +423,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_RECV_ERROR;
}
#ifdef CURLVERBOSE
static CURLcode cf_verboseconnect(struct Curl_easy *data,
struct Curl_cfilter *cf)
{
if(Curl_trc_is_verbose(data)) {
struct ip_quadruple ipquad;
bool is_ipv6;
CURLcode result;
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
if(result)
return result;
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
CURL_CONN_HOST_DISPNAME(data->conn),
ipquad.remote_ip, ipquad.remote_port,
ipquad.local_ip, ipquad.local_port);
}
return CURLE_OK;
}
#endif
static CURLcode cf_cntrl_all(struct connectdata *conn,
struct Curl_easy *data,
bool ignore_result,
@ -463,36 +440,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
return result;
}
static void cf_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn)
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn)
{
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
}
/**
* Update connection statistics
*/
static void conn_report_connect_stats(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf) {
struct curltime connected;
struct curltime appconnected;
memset(&connected, 0, sizeof(connected));
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
if(connected.tv_sec || connected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
memset(&appconnected, 0, sizeof(appconnected));
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
if(appconnected.tv_sec || appconnected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
}
}
static void conn_remove_setup_filters(struct Curl_easy *data,
int sockindex)
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
int sockindex)
{
struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex];
while(*anchor) {
@ -509,127 +464,6 @@ static void conn_remove_setup_filters(struct Curl_easy *data,
}
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
int sockindex,
bool blocking,
bool *done)
{
#define CF_CONN_NUM_POLLS_ON_STACK 5
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
struct easy_pollset ps;
struct curl_pollfds cpfds;
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
if(!CONN_SOCK_IDX_VALID(sockindex))
return CURLE_BAD_FUNCTION_ARGUMENT;
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
*done = TRUE;
return CURLE_OK;
}
cf = data->conn->cfilter[sockindex];
if(!cf) {
*done = FALSE;
return CURLE_FAILED_INIT;
}
*done = (bool)cf->connected;
if(*done)
return CURLE_OK;
Curl_pollset_init(&ps);
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
while(!*done) {
if(Curl_conn_needs_flush(data, sockindex)) {
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
result = Curl_conn_flush(data, sockindex);
if(result && (result != CURLE_AGAIN))
return result;
}
result = cf->cft->do_connect(cf, data, done);
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
blocking, (int)result, *done);
if(!result && *done) {
/* A final sanity check on connection security */
if((data->state.origin->scheme->flags & PROTOPT_SSL) &&
(sockindex == FIRSTSOCKET) &&
!Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
DEBUGASSERT(0);
failf(data, "transfer requires SSL, but not connected via SSL");
return CURLE_FAILED_INIT;
}
/* Now that the complete filter chain is connected, let all filters
* persist information at the connection. E.g. cf-socket sets the
* socket and ip related information. */
cf_cntrl_update_info(data, data->conn);
conn_report_connect_stats(cf, data);
data->conn->keepalive = *Curl_pgrs_now(data);
VERBOSE(result = cf_verboseconnect(data, cf));
VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected"));
conn_remove_setup_filters(data, sockindex);
VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to"));
goto out;
}
else if(result) {
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
(int)result);
VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect"));
conn_report_connect_stats(cf, data);
goto out;
}
if(!blocking)
goto out;
else {
/* check allowed time left */
const timediff_t timeout_ms = Curl_timeleft_ms(data);
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int rc;
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "connect timeout");
result = CURLE_OPERATION_TIMEDOUT;
goto out;
}
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
Curl_pollset_reset(&ps);
Curl_pollfds_reset(&cpfds);
/* In general, we want to send after connect, wait on that. */
if(sockfd != CURL_SOCKET_BAD)
result = Curl_pollset_set_out_only(data, &ps, sockfd);
if(!result)
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
if(result)
goto out;
result = Curl_pollfds_add_ps(&cpfds, &ps);
if(result)
goto out;
rc = Curl_poll(cpfds.pfds, cpfds.n,
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
rc);
if(rc < 0) {
result = CURLE_COULDNT_CONNECT;
goto out;
}
/* continue iterating */
}
}
out:
Curl_pollset_cleanup(&ps);
Curl_pollfds_cleanup(&cpfds);
return result;
}
bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
{
if(!CONN_SOCK_IDX_VALID(sockindex))

View file

@ -358,16 +358,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf,
#define CURL_CF_SSL_DISABLE 0
#define CURL_CF_SSL_ENABLE 1
/**
* Bring the filter chain at `sockindex` for connection `data->conn` into
* connected state. Which will set `*done` to TRUE.
* This can be called on an already connected chain with no side effects.
* When not `blocking`, calls may return without error and `*done != TRUE`,
* while the individual filters negotiated the connection.
*/
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
bool blocking, bool *done);
/**
* Check if a filter chain at `sockindex` for connection `conn` exists.
*/
@ -431,6 +421,12 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
struct connectdata *conn);
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn);
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
int sockindex);
/**
* Shutdown the connection at `sockindex` non-blocking, using timeout
* from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS.

View file

@ -23,49 +23,20 @@
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
#endif
#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#elif defined(HAVE_NETINET_TCP_H)
#include <netinet/tcp.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#include "urldata.h"
#include "curl_trc.h"
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
#include "cf-dns.h"
#include "cf-haproxy.h"
#include "cf-https-connect.h"
#include "cf-ip-happy.h"
#include "cf-socket.h"
#include "cf-setup.h"
#include "multiif.h"
#include "curlx/strparse.h"
#include "vtls/vtls.h" /* for vtls cfilters */
#include "vquic/vquic.h" /* for QUIC cfilters */
#include "vquic/cf-capsule.h"
#include "progress.h"
#include "conncache.h"
#include "multihandle.h"
#include "http_proxy.h"
#include "socks.h"
#include "select.h"
#include "curlx/strparse.h"
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
@ -270,444 +241,6 @@ void Curl_conncontrol(struct connectdata *conn,
}
}
typedef enum {
CF_SETUP_INIT,
CF_SETUP_CNNCT_EYEBALLS,
CF_SETUP_CNNCT_SOCKS,
CF_SETUP_CNNCT_HTTP_PROXY,
CF_SETUP_CNNCT_HAPROXY,
CF_SETUP_CNNCT_SSL,
CF_SETUP_DONE
} cf_setup_state;
struct cf_setup_ctx {
cf_setup_state state;
int ssl_mode;
uint8_t transport;
uint8_t retry_count;
};
#ifndef CURL_DISABLE_PROXY
static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
if(data->set.haproxyprotocol) {
if(ctx->transport == TRNSPRT_QUIC) {
failf(data, "haproxy protocol does not support QUIC");
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = Curl_cf_haproxy_insert_after(cf, data);
if(result) {
CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added HAPROXY filter");
}
ctx->state = CF_SETUP_CNNCT_HAPROXY;
}
return result;
}
static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
struct Curl_peer *second_peer;
if(cf->conn->http_proxy.peer)
second_peer = cf->conn->http_proxy.peer;
else
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
if(!second_peer)
return CURLE_FAILED_INIT;
result = Curl_cf_socks_proxy_insert_after(
cf, data, second_peer, cf->conn->ip_version,
cf->conn->socks_proxy.proxytype,
cf->conn->socks_proxy.creds);
if(result) {
CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u",
second_peer->hostname, second_peer->port);
ctx->state = CF_SETUP_CNNCT_SOCKS;
}
return result;
}
#ifndef CURL_DISABLE_HTTP
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
struct Curl_peer *peer = cf->conn->http_proxy.peer;
struct Curl_peer *tunnel_peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
#ifdef USE_SSL
if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
result = Curl_cf_ssl_proxy_insert_after(
cf, data, cf->conn->http_proxy.peer);
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy");
}
#endif /* USE_SSL */
result = Curl_cf_http_proxy_insert_after(
cf, data, peer, tunnel_peer,
ctx->transport, cf->conn->http_proxy.proxytype);
if(result) {
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
return result;
}
#endif /* !CURL_DISABLE_HTTP */
#endif /* CURL_DISABLE_PROXY */
/* Get the origin curl connects its socket to.
* Can be origin or the first proxy. */
static struct Curl_peer *conn_get_first_origin(struct connectdata *conn,
int sockindex)
{
#ifndef CURL_DISABLE_PROXY
if(conn->socks_proxy.peer)
return conn->socks_proxy.peer;
if(conn->http_proxy.peer)
return conn->http_proxy.peer;
#endif
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
}
static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
/* What is the first hop we directly connect to and what transport
* do we use for it? Only on the first hop we can do Happy Eyeballs.
* first_origin and first_peer differ on --connect-to. */
struct Curl_peer *first_origin =
conn_get_first_origin(cf->conn, cf->sockindex);
struct Curl_peer *first_peer =
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
struct Curl_peer *tunnel_peer = NULL;
uint8_t first_transport = ctx->transport;
if(!first_peer)
return CURLE_FAILED_INIT;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
first_transport =
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
first_transport,
tunnel_peer, ctx->transport);
if(result) {
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result);
return result;
}
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
else {
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
tunnel_peer ? "proxy" : "origin",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
}
}
return result;
}
static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
(void)data; /* not used in all builds */
if(ctx->state < CF_SETUP_CNNCT_SSL) {
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
!defined(CURL_DISABLE_PROXY)
/* Wanting QUIC with an HTTP tunneling filter, we now need to add
* the QUIC filter on top. Without tunneling, this has already
* happened in the Happy Eyeball filter. */
if(ctx->transport == TRNSPRT_QUIC &&
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_capsule_insert_after(cf, data);
if(result) {
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d",
(int)result);
return result;
}
result = Curl_cf_quic_insert_after(cf, origin, peer);
if(result) {
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result);
return result;
}
CURL_TRC_CF(data, cf, "added QUIC filter for origin");
}
else
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */
#ifdef USE_SSL
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
data->state.origin->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
#ifndef CURL_DISABLE_PROXY
if(cf->conn->bits.origin_is_proxy) {
result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
}
else
#endif
{
/* Another FTP quirk: when adding SSL verification, to a DATA
* connection, always verify against the control's origin */
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
}
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
(int)result);
return result;
}
CURL_TRC_CF(data, cf, "added SSL filter for origin");
}
#endif /* USE_SSL */
ctx->state = CF_SETUP_CNNCT_SSL;
}
return result;
}
static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
/* connect current sub-chain */
connect_sub_chain:
VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect"));
if(cf->next && !cf->next->connected) {
result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
result = cf_setup_add_ip_happy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#ifndef CURL_DISABLE_PROXY
result = cf_setup_add_socks(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#ifndef CURL_DISABLE_HTTP
result = cf_setup_add_http_proxy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#endif /* !CURL_DISABLE_HTTP */
result = cf_setup_add_haproxy(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
#endif /* !CURL_DISABLE_PROXY */
result = cf_setup_add_origin_filters(cf, data);
if(result)
return result;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
ctx->state = CF_SETUP_DONE;
cf->connected = TRUE;
*done = TRUE;
return CURLE_OK;
}
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result;
/* In some situations, a server/proxy may close the connection and
* we need to connect again (HTTP/1.x proxy auth, for example).
* We used to close the filters and reuse them for another attempt,
* however that complicates filter code and it is simpler to tear them
* all down and start over. */
retry:
result = cf_setup_connect_steps(cf, data, done);
if(result == CURLE_AGAIN) {
++ctx->retry_count;
if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */
return CURLE_COULDNT_CONNECT;
CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count);
Curl_conn_cf_discard_chain(&cf->next, data);
ctx->state = CF_SETUP_INIT;
goto retry;
}
return result;
}
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "destroy");
curlx_safefree(ctx);
}
struct Curl_cftype Curl_cft_setup = {
"SETUP",
CF_TYPE_SETUP,
CURL_LOG_LVL_NONE,
cf_setup_destroy,
cf_setup_connect,
Curl_cf_def_shutdown,
Curl_cf_def_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf = NULL;
struct cf_setup_ctx *ctx;
CURLcode result = CURLE_OK;
(void)data;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = CF_SETUP_INIT;
ctx->ssl_mode = ssl_mode;
ctx->transport = transport;
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
if(result)
goto out;
ctx = NULL;
out:
*pcf = result ? NULL : cf;
if(ctx) {
curlx_free(ctx);
}
return result;
}
static CURLcode cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
@ -736,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
/* Still no cfilter set, apply default. */
if(!conn->cfilter[sockindex]) {
result = cf_setup_add(data, conn, sockindex,
conn->transport_wanted, ssl_mode);
result = Curl_cf_setup_add(data, conn, sockindex,
conn->transport_wanted, ssl_mode);
if(result)
goto out;
}
@ -754,6 +287,173 @@ out:
return result;
}
#ifdef CURLVERBOSE
static CURLcode conn_connect_trace(struct Curl_easy *data,
struct Curl_cfilter *cf)
{
if(Curl_trc_is_verbose(data)) {
struct ip_quadruple ipquad;
bool is_ipv6;
CURLcode result;
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
if(result)
return result;
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
CURL_CONN_HOST_DISPNAME(data->conn),
ipquad.remote_ip, ipquad.remote_port,
ipquad.local_ip, ipquad.local_port);
}
return CURLE_OK;
}
#endif
/**
* Update connection statistics
*/
static void conn_report_connect_stats(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf) {
struct curltime connected;
struct curltime appconnected;
memset(&connected, 0, sizeof(connected));
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
if(connected.tv_sec || connected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
memset(&appconnected, 0, sizeof(appconnected));
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
if(appconnected.tv_sec || appconnected.tv_usec)
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
}
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
int sockindex,
bool blocking,
bool *done)
{
#define CF_CONN_NUM_POLLS_ON_STACK 5
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
struct easy_pollset ps;
struct curl_pollfds cpfds;
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
if(!CONN_SOCK_IDX_VALID(sockindex))
return CURLE_BAD_FUNCTION_ARGUMENT;
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
*done = TRUE;
return CURLE_OK;
}
cf = data->conn->cfilter[sockindex];
if(!cf) {
*done = FALSE;
return CURLE_FAILED_INIT;
}
*done = (bool)cf->connected;
if(*done)
return CURLE_OK;
Curl_pollset_init(&ps);
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
while(!*done) {
if(Curl_conn_needs_flush(data, sockindex)) {
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
result = Curl_conn_flush(data, sockindex);
if(result && (result != CURLE_AGAIN))
goto out;
}
result = cf->cft->do_connect(cf, data, done);
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
blocking, (int)result, *done);
if(!result && *done) {
/* A final sanity check on connection security */
if((data->state.origin->scheme->flags & PROTOPT_SSL) &&
(sockindex == FIRSTSOCKET) &&
!Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
DEBUGASSERT(0);
failf(data, "transfer requires SSL, but not connected via SSL");
result = CURLE_FAILED_INIT;
goto out;
}
/* Now that the complete filter chain is connected, let all filters
* persist information at the connection. E.g. cf-socket sets the
* socket and ip related information. */
Curl_conn_cntrl_update_info(data, data->conn);
conn_report_connect_stats(cf, data);
data->conn->keepalive = *Curl_pgrs_now(data);
VERBOSE(result = conn_connect_trace(data, cf));
VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected"));
Curl_conn_remove_setup_filters(data, sockindex);
VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to"));
goto out;
}
else if(result) {
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
(int)result);
VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect"));
conn_report_connect_stats(cf, data);
goto out;
}
if(!blocking)
goto out;
else {
/* check allowed time left */
const timediff_t timeout_ms = Curl_timeleft_ms(data);
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int rc;
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "connect timeout");
result = CURLE_OPERATION_TIMEDOUT;
goto out;
}
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
Curl_pollset_reset(&ps);
Curl_pollfds_reset(&cpfds);
/* In general, we want to send after connect, wait on that. */
if(sockfd != CURL_SOCKET_BAD)
result = Curl_pollset_set_out_only(data, &ps, sockfd);
if(!result)
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
if(result)
goto out;
result = Curl_pollfds_add_ps(&cpfds, &ps);
if(result)
goto out;
rc = Curl_poll(cpfds.pfds, cpfds.n,
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
rc);
if(rc < 0) {
result = CURLE_COULDNT_CONNECT;
goto out;
}
/* continue iterating */
}
}
out:
Curl_pollset_cleanup(&ps);
Curl_pollfds_cleanup(&cpfds);
return result;
}
void Curl_conn_set_multiplex(struct connectdata *conn)
{
if(!conn->bits.multiplex) {

View file

@ -28,8 +28,6 @@
#include "hostip.h"
#include "curlx/timeval.h"
struct Curl_dns_entry;
struct ip_quadruple;
struct Curl_peer;
struct Curl_str;
@ -105,11 +103,6 @@ void Curl_conncontrol(struct connectdata *conn,
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP)
#endif
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport,
int ssl_mode);
/**
* Setup the cfilters at `sockindex` in connection `conn`.
* If no filter chain is installed yet, inspects the configuration
@ -120,6 +113,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
int sockindex,
int ssl_mode);
/**
* Bring the filter chain at `sockindex` for connection `data->conn` into
* connected state. Which will set `*done` to TRUE.
* This can be called on an already connected chain with no side effects.
* When not `blocking`, calls may return without error and `*done != TRUE`,
* while the individual filters negotiated the connection.
*/
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
bool blocking, bool *done);
/* Set conn to allow multiplexing. */
void Curl_conn_set_multiplex(struct connectdata *conn);
@ -138,6 +141,4 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,
int sockindex);
extern struct Curl_cftype Curl_cft_setup;
#endif /* HEADER_CURL_CONNECT_H */

View file

@ -30,7 +30,7 @@
#include "cf-dns.h"
#include "cf-socket.h"
#include "connect.h"
#include "cf-setup.h"
#include "http2.h"
#include "http_proxy.h"
#include "cf-h1-proxy.h"