mirror of
https://github.com/curl/curl.git
synced 2026-08-24 15:23:36 +03:00
connect: fix strategy testing for attempts, timeouts and happy-eyeball
- add test2600 as a unit test that triggers various connect conditions and monitors behaviour, available in a debug build only. - this exposed edge cases in connect.c that have been fixed Closes #10312
This commit is contained in:
parent
5651a36d1a
commit
d55de24dce
7 changed files with 452 additions and 32 deletions
|
|
@ -352,7 +352,8 @@ struct eyeballer {
|
|||
struct Curl_cfilter *cf; /* current sub-cfilter connecting */
|
||||
struct eyeballer *primary; /* eyeballer this one is backup for */
|
||||
timediff_t delay_ms; /* delay until start */
|
||||
timediff_t timeoutms; /* timeout for all tries */
|
||||
struct curltime started; /* start of current attempt */
|
||||
timediff_t timeoutms; /* timeout for current attempt */
|
||||
expire_id timeout_id; /* ID for Curl_expire() */
|
||||
CURLcode result;
|
||||
int error;
|
||||
|
|
@ -463,7 +464,7 @@ static void baller_initiate(struct Curl_cfilter *cf,
|
|||
wcf->sockindex = cf->sockindex;
|
||||
}
|
||||
|
||||
if(cf->conn->num_addr > 1) {
|
||||
if(baller->addr && baller->addr->ai_next) {
|
||||
Curl_expire(data, baller->timeoutms, baller->timeout_id);
|
||||
}
|
||||
|
||||
|
|
@ -485,13 +486,17 @@ out:
|
|||
*/
|
||||
static CURLcode baller_start(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct eyeballer *baller)
|
||||
struct eyeballer *baller,
|
||||
timediff_t timeoutms)
|
||||
{
|
||||
baller->error = 0;
|
||||
baller->connected = FALSE;
|
||||
baller->has_started = TRUE;
|
||||
|
||||
while(baller->addr) {
|
||||
baller->started = Curl_now();
|
||||
baller->timeoutms = (baller->addr->ai_next == NULL) ?
|
||||
timeoutms : timeoutms / 2;
|
||||
baller_initiate(cf, data, baller);
|
||||
if(!baller->result)
|
||||
break;
|
||||
|
|
@ -508,11 +513,12 @@ static CURLcode baller_start(struct Curl_cfilter *cf,
|
|||
more address exists or error */
|
||||
static CURLcode baller_start_next(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct eyeballer *baller)
|
||||
struct eyeballer *baller,
|
||||
timediff_t timeoutms)
|
||||
{
|
||||
if(cf->sockindex == FIRSTSOCKET) {
|
||||
baller_next_addr(baller);
|
||||
baller_start(cf, data, baller);
|
||||
baller_start(cf, data, baller, timeoutms);
|
||||
}
|
||||
else {
|
||||
baller->error = 0;
|
||||
|
|
@ -530,8 +536,7 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
|
|||
struct curltime *now,
|
||||
bool *connected)
|
||||
{
|
||||
struct cf_he_ctx *ctx = cf->ctx;
|
||||
|
||||
(void)cf;
|
||||
*connected = baller->connected;
|
||||
if(!baller->result && !*connected) {
|
||||
/* evaluate again */
|
||||
|
|
@ -542,14 +547,13 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
|
|||
baller->connected = TRUE;
|
||||
baller->is_done = TRUE;
|
||||
}
|
||||
else if(Curl_timediff(*now, ctx->started) >= baller->timeoutms) {
|
||||
else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
|
||||
infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T
|
||||
"ms, move on!", baller->name, baller->timeoutms);
|
||||
#if defined(ETIMEDOUT)
|
||||
baller->error = ETIMEDOUT;
|
||||
#endif
|
||||
baller->result = CURLE_OPERATION_TIMEDOUT;
|
||||
baller->is_done = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -566,7 +570,6 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
|
|||
struct cf_he_ctx *ctx = cf->ctx;
|
||||
struct connectdata *conn = cf->conn;
|
||||
CURLcode result;
|
||||
timediff_t allow;
|
||||
struct curltime now;
|
||||
size_t i;
|
||||
int ongoing, not_started;
|
||||
|
|
@ -613,9 +616,7 @@ evaluate:
|
|||
data->state.os_errno = baller->error;
|
||||
SET_SOCKERRNO(baller->error);
|
||||
}
|
||||
allow = Curl_timeleft(data, &now, TRUE);
|
||||
baller->timeoutms = baller->addr->ai_next == NULL ? allow : allow / 2;
|
||||
baller_start_next(cf, data, baller);
|
||||
baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
|
||||
if(baller->is_done) {
|
||||
DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
|
||||
}
|
||||
|
|
@ -632,9 +633,9 @@ evaluate:
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Nothing connected, have we timed out completely yet? */
|
||||
allow = Curl_timeleft(data, &now, TRUE);
|
||||
if(allow < 0) {
|
||||
/* Nothing connected, check the time before we might
|
||||
* start new ballers or return ok. */
|
||||
if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
|
||||
failf(data, "Connection timeout after %ld ms",
|
||||
Curl_timediff(now, data->progress.t_startsingle));
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
|
|
@ -653,7 +654,7 @@ evaluate:
|
|||
* its start delay_ms have expired */
|
||||
if((baller->primary && baller->primary->is_done) ||
|
||||
Curl_timediff(now, ctx->started) >= baller->delay_ms) {
|
||||
baller_start(cf, data, baller);
|
||||
baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
|
||||
if(baller->is_done) {
|
||||
DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
|
||||
}
|
||||
|
|
@ -740,7 +741,6 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
|
|||
}
|
||||
|
||||
ctx->started = Curl_now();
|
||||
conn->num_addr = Curl_num_addresses(remotehost->addr);
|
||||
|
||||
/* remotehost->addr is the list of addresses from the resolver, each
|
||||
* with an address family. The list has at least one entry, possibly
|
||||
|
|
@ -1010,23 +1010,51 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
struct transport_provider {
|
||||
int transport;
|
||||
cf_ip_connect_create *cf_create;
|
||||
};
|
||||
|
||||
static
|
||||
#ifndef DEBUGBUILD
|
||||
const
|
||||
#endif
|
||||
struct transport_provider transport_providers[] = {
|
||||
{ TRNSPRT_TCP, Curl_cf_tcp_create },
|
||||
#ifdef ENABLE_QUIC
|
||||
{ TRNSPRT_QUIC, Curl_cf_quic_create },
|
||||
#endif
|
||||
{ TRNSPRT_UDP, Curl_cf_udp_create },
|
||||
{ TRNSPRT_UNIX, Curl_cf_unix_create },
|
||||
};
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
|
||||
#endif
|
||||
|
||||
static cf_ip_connect_create *get_cf_create(int transport)
|
||||
{
|
||||
switch(transport) {
|
||||
case TRNSPRT_TCP:
|
||||
return Curl_cf_tcp_create;
|
||||
case TRNSPRT_UDP:
|
||||
return Curl_cf_udp_create;
|
||||
case TRNSPRT_UNIX:
|
||||
return Curl_cf_unix_create;
|
||||
#ifdef ENABLE_QUIC
|
||||
case TRNSPRT_QUIC:
|
||||
return Curl_cf_quic_create;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
size_t i;
|
||||
for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
|
||||
if(transport == transport_providers[i].transport)
|
||||
return transport_providers[i].cf_create;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
void Curl_debug_set_transport_provider(int transport,
|
||||
cf_ip_connect_create *cf_create)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
|
||||
if(transport == transport_providers[i].transport) {
|
||||
transport_providers[i].cf_create = cf_create;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* DEBUGBUILD */
|
||||
|
||||
static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
|
|
|
|||
|
|
@ -135,4 +135,9 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
|
|||
extern struct Curl_cftype Curl_cft_happy_eyeballs;
|
||||
extern struct Curl_cftype Curl_cft_setup;
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
void Curl_debug_set_transport_provider(int transport,
|
||||
cf_ip_connect_create *cf_create);
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_CONNECT_H */
|
||||
|
|
|
|||
|
|
@ -1038,7 +1038,6 @@ struct connectdata {
|
|||
int socks5_gssapi_enctype;
|
||||
#endif
|
||||
/* The field below gets set in connect.c:connecthost() */
|
||||
int num_addr; /* number of addresses to try to connect to */
|
||||
int port; /* which port to use locally - to connect to */
|
||||
int remote_port; /* the remote port, not the proxy port! */
|
||||
int conn_to_port; /* the remote port to connect to. valid only if
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue