2023-01-17 22:58:49 +08:00
|
|
|
/***************************************************************************
|
|
|
|
* _ _ ____ _
|
|
|
|
* 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 "curlcheck.h"
|
|
|
|
|
2023-02-17 17:48:34 +08:00
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN6_H
|
|
|
|
#include <netinet/in6.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 <setjmp.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
2023-01-17 22:58:49 +08:00
|
|
|
#include "urldata.h"
|
|
|
|
#include "connect.h"
|
|
|
|
#include "cfilters.h"
|
2023-08-18 17:08:52 +08:00
|
|
|
#include "multiif.h"
|
2024-07-15 17:56:51 +08:00
|
|
|
#include "select.h"
|
2023-08-03 23:32:25 +08:00
|
|
|
#include "curl_trc.h"
|
2023-01-17 22:58:49 +08:00
|
|
|
|
2023-02-17 17:48:34 +08:00
|
|
|
|
2023-01-17 22:58:49 +08:00
|
|
|
static CURL *easy;
|
|
|
|
|
|
|
|
static CURLcode unit_setup(void)
|
|
|
|
{
|
|
|
|
CURLcode res = CURLE_OK;
|
|
|
|
|
|
|
|
global_init(CURL_GLOBAL_ALL);
|
|
|
|
easy = curl_easy_init();
|
|
|
|
if(!easy) {
|
|
|
|
curl_global_cleanup();
|
|
|
|
return CURLE_OUT_OF_MEMORY;
|
|
|
|
}
|
2024-07-15 17:56:51 +08:00
|
|
|
curl_global_trace("all");
|
2023-01-17 22:58:49 +08:00
|
|
|
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void unit_stop(void)
|
|
|
|
{
|
|
|
|
curl_easy_cleanup(easy);
|
|
|
|
curl_global_cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct test_case {
|
|
|
|
int id;
|
|
|
|
const char *url;
|
|
|
|
const char *resolve_info;
|
2023-02-15 17:31:52 +08:00
|
|
|
unsigned char ip_version;
|
2023-01-17 22:58:49 +08:00
|
|
|
timediff_t connect_timeout_ms;
|
|
|
|
timediff_t he_timeout_ms;
|
|
|
|
timediff_t cf4_fail_delay_ms;
|
|
|
|
timediff_t cf6_fail_delay_ms;
|
|
|
|
|
|
|
|
int exp_cf4_creations;
|
|
|
|
int exp_cf6_creations;
|
|
|
|
timediff_t min_duration_ms;
|
|
|
|
timediff_t max_duration_ms;
|
|
|
|
CURLcode exp_result;
|
|
|
|
const char *pref_family;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ai_family_stats {
|
|
|
|
const char *family;
|
|
|
|
int creations;
|
|
|
|
timediff_t first_created;
|
|
|
|
timediff_t last_created;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct test_result {
|
|
|
|
CURLcode result;
|
|
|
|
struct curltime started;
|
|
|
|
struct curltime ended;
|
|
|
|
struct ai_family_stats cf4;
|
|
|
|
struct ai_family_stats cf6;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct test_case *current_tc;
|
|
|
|
static struct test_result *current_tr;
|
|
|
|
|
|
|
|
struct cf_test_ctx {
|
|
|
|
int ai_family;
|
connections: introduce http/3 happy eyeballs
New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing.
- filter is installed when `--http3` in the tool is used (or
the equivalent CURLOPT_ done in the library)
- starts a QUIC/HTTP/3 connect right away. Should that not
succeed after 100ms (subject to change), a parallel attempt
is started for HTTP/2 and HTTP/1.1 via TCP
- both attempts are subject to IPv6/IPv4 eyeballing, same
as happens for other connections
- tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT
- use a `soft` timeout at half the value. When the soft timeout
expires, the HTTPS-CONNECT filter checks if the QUIC filter
has received any data from the server. If not, it will start
the HTTP/2 attempt.
HTTP/3(ngtcp2) improvements.
- setting call_data in all cfilter calls similar to http/2 and vtls filters
for use in callback where no stream data is available.
- returning CURLE_PARTIAL_FILE for prematurely terminated transfers
- enabling pytest test_05 for h3
- shifting functionality to "connect" UDP sockets from ngtcp2
implementation into the udp socket cfilter. Because unconnected
UDP sockets are weird. For example they error when adding to a
pollset.
HTTP/3(quiche) improvements.
- fixed upload bug in quiche implementation, now passes 251 and pytest
- error codes on stream RESET
- improved debug logs
- handling of DRAIN during connect
- limiting pending event queue
HTTP/2 cfilter improvements.
- use LOG_CF macros for dynamic logging in debug build
- fix CURLcode on RST streams to be CURLE_PARTIAL_FILE
- enable pytest test_05 for h2
- fix upload pytests and improve parallel transfer performance.
GOAWAY handling for ngtcp2/quiche
- during connect, when the remote server refuses to accept new connections
and closes immediately (so the local conn goes into DRAIN phase), the
connection is torn down and a another attempt is made after a short grace
period.
This is the behaviour observed with nghttpx when we tell it to shut
down gracefully. Tested in pytest test_03_02.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
- new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementation.
Invoke:
python3 tests/tests-httpd/scorecard.py --help
for usage.
Improvements on gathering connect statistics and socket access.
- new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters
report connection statistics. This is triggered when the connection
has completely connected.
- new void Curl_pgrsTimeWas(..) method to report a timer update with
a timestamp of when it happend. This allows for updating timers
"later", e.g. a connect statistic after full connectivity has been
reached.
- in case of HTTP eyeballing, the previous changes will update
statistics only from the filter chain that "won" the eyeballing.
- new cfilter query CF_QUERY_SOCKET for retrieving the socket used
by a filter chain.
Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket()
for convenient use of this query.
- Change VTLS backend to query their sub-filters for the socket when
checks during the handshake are made.
HTTP/3 documentation on how https eyeballing works.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
Scorecard with Caddy.
- configure can be run with `--with-test-caddy=path` to specify which caddy to use for testing
- tests/tests-httpd/scorecard.py now measures download speeds with caddy
pytest improvements
- adding Makfile to clean gen dir
- adding nghttpx rundir creation on start
- checking httpd version 2.4.55 for test_05 cases where it is needed. Skipping with message if too old.
- catch exception when checking for caddy existance on system.
Closes #10349
2023-02-02 00:13:12 +08:00
|
|
|
int transport;
|
2023-01-17 22:58:49 +08:00
|
|
|
char id[16];
|
|
|
|
struct curltime started;
|
|
|
|
timediff_t fail_delay_ms;
|
|
|
|
struct ai_family_stats *stats;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void cf_test_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
|
|
|
{
|
|
|
|
struct cf_test_ctx *ctx = cf->ctx;
|
2023-08-17 20:43:14 +08:00
|
|
|
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
2023-01-17 22:58:49 +08:00
|
|
|
infof(data, "%04dms: cf[%s] destroyed",
|
2023-10-13 08:25:20 +08:00
|
|
|
(int)Curl_timediff(Curl_now(), current_tr->started), ctx->id);
|
2023-08-17 20:43:14 +08:00
|
|
|
#else
|
|
|
|
(void)data;
|
|
|
|
#endif
|
2023-01-17 22:58:49 +08:00
|
|
|
free(ctx);
|
|
|
|
cf->ctx = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CURLcode cf_test_connect(struct Curl_cfilter *cf,
|
|
|
|
struct Curl_easy *data,
|
|
|
|
bool blocking, bool *done)
|
|
|
|
{
|
|
|
|
struct cf_test_ctx *ctx = cf->ctx;
|
2023-08-18 17:08:52 +08:00
|
|
|
timediff_t duration_ms;
|
2023-01-17 22:58:49 +08:00
|
|
|
|
|
|
|
(void)data;
|
|
|
|
(void)blocking;
|
|
|
|
*done = FALSE;
|
2023-08-18 17:08:52 +08:00
|
|
|
duration_ms = Curl_timediff(Curl_now(), ctx->started);
|
|
|
|
if(duration_ms >= ctx->fail_delay_ms) {
|
2023-01-17 22:58:49 +08:00
|
|
|
infof(data, "%04dms: cf[%s] fail delay reached",
|
2023-10-13 08:25:20 +08:00
|
|
|
(int)duration_ms, ctx->id);
|
2023-01-17 22:58:49 +08:00
|
|
|
return CURLE_COULDNT_CONNECT;
|
|
|
|
}
|
2024-07-15 17:56:51 +08:00
|
|
|
if(duration_ms) {
|
2023-08-18 17:08:52 +08:00
|
|
|
infof(data, "%04dms: cf[%s] continuing", (int)duration_ms, ctx->id);
|
2024-07-15 17:56:51 +08:00
|
|
|
Curl_wait_ms(10);
|
|
|
|
}
|
2023-08-18 17:08:52 +08:00
|
|
|
Curl_expire(data, ctx->fail_delay_ms - duration_ms, EXPIRE_RUN_NOW);
|
2023-01-17 22:58:49 +08:00
|
|
|
return CURLE_OK;
|
|
|
|
}
|
|
|
|
|
2024-07-01 20:56:27 +08:00
|
|
|
static void cf_test_adjust_pollset(struct Curl_cfilter *cf,
|
|
|
|
struct Curl_easy *data,
|
|
|
|
struct easy_pollset *ps)
|
|
|
|
{
|
|
|
|
/* just for testing, give one socket with events back */
|
|
|
|
(void)cf;
|
|
|
|
Curl_pollset_set(data, ps, 1, TRUE, TRUE);
|
|
|
|
}
|
|
|
|
|
2023-01-17 22:58:49 +08:00
|
|
|
static struct Curl_cftype cft_test = {
|
|
|
|
"TEST",
|
|
|
|
CF_TYPE_IP_CONNECT,
|
2023-08-03 23:32:25 +08:00
|
|
|
CURL_LOG_LVL_NONE,
|
2023-01-17 22:58:49 +08:00
|
|
|
cf_test_destroy,
|
|
|
|
cf_test_connect,
|
|
|
|
Curl_cf_def_close,
|
2024-06-07 16:12:39 +08:00
|
|
|
Curl_cf_def_shutdown,
|
2023-01-17 22:58:49 +08:00
|
|
|
Curl_cf_def_get_host,
|
2024-07-01 20:56:27 +08:00
|
|
|
cf_test_adjust_pollset,
|
2023-01-17 22:58:49 +08:00
|
|
|
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_test_create(struct Curl_cfilter **pcf,
|
|
|
|
struct Curl_easy *data,
|
|
|
|
struct connectdata *conn,
|
connections: introduce http/3 happy eyeballs
New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing.
- filter is installed when `--http3` in the tool is used (or
the equivalent CURLOPT_ done in the library)
- starts a QUIC/HTTP/3 connect right away. Should that not
succeed after 100ms (subject to change), a parallel attempt
is started for HTTP/2 and HTTP/1.1 via TCP
- both attempts are subject to IPv6/IPv4 eyeballing, same
as happens for other connections
- tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT
- use a `soft` timeout at half the value. When the soft timeout
expires, the HTTPS-CONNECT filter checks if the QUIC filter
has received any data from the server. If not, it will start
the HTTP/2 attempt.
HTTP/3(ngtcp2) improvements.
- setting call_data in all cfilter calls similar to http/2 and vtls filters
for use in callback where no stream data is available.
- returning CURLE_PARTIAL_FILE for prematurely terminated transfers
- enabling pytest test_05 for h3
- shifting functionality to "connect" UDP sockets from ngtcp2
implementation into the udp socket cfilter. Because unconnected
UDP sockets are weird. For example they error when adding to a
pollset.
HTTP/3(quiche) improvements.
- fixed upload bug in quiche implementation, now passes 251 and pytest
- error codes on stream RESET
- improved debug logs
- handling of DRAIN during connect
- limiting pending event queue
HTTP/2 cfilter improvements.
- use LOG_CF macros for dynamic logging in debug build
- fix CURLcode on RST streams to be CURLE_PARTIAL_FILE
- enable pytest test_05 for h2
- fix upload pytests and improve parallel transfer performance.
GOAWAY handling for ngtcp2/quiche
- during connect, when the remote server refuses to accept new connections
and closes immediately (so the local conn goes into DRAIN phase), the
connection is torn down and a another attempt is made after a short grace
period.
This is the behaviour observed with nghttpx when we tell it to shut
down gracefully. Tested in pytest test_03_02.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
- new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementation.
Invoke:
python3 tests/tests-httpd/scorecard.py --help
for usage.
Improvements on gathering connect statistics and socket access.
- new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters
report connection statistics. This is triggered when the connection
has completely connected.
- new void Curl_pgrsTimeWas(..) method to report a timer update with
a timestamp of when it happend. This allows for updating timers
"later", e.g. a connect statistic after full connectivity has been
reached.
- in case of HTTP eyeballing, the previous changes will update
statistics only from the filter chain that "won" the eyeballing.
- new cfilter query CF_QUERY_SOCKET for retrieving the socket used
by a filter chain.
Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket()
for convenient use of this query.
- Change VTLS backend to query their sub-filters for the socket when
checks during the handshake are made.
HTTP/3 documentation on how https eyeballing works.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
Scorecard with Caddy.
- configure can be run with `--with-test-caddy=path` to specify which caddy to use for testing
- tests/tests-httpd/scorecard.py now measures download speeds with caddy
pytest improvements
- adding Makfile to clean gen dir
- adding nghttpx rundir creation on start
- checking httpd version 2.4.55 for test_05 cases where it is needed. Skipping with message if too old.
- catch exception when checking for caddy existance on system.
Closes #10349
2023-02-02 00:13:12 +08:00
|
|
|
const struct Curl_addrinfo *ai,
|
|
|
|
int transport)
|
2023-01-17 22:58:49 +08:00
|
|
|
{
|
|
|
|
struct cf_test_ctx *ctx = NULL;
|
|
|
|
struct Curl_cfilter *cf = NULL;
|
|
|
|
timediff_t created_at;
|
|
|
|
CURLcode result;
|
|
|
|
|
|
|
|
(void)data;
|
|
|
|
(void)conn;
|
2023-11-08 07:22:58 +08:00
|
|
|
ctx = calloc(1, sizeof(*ctx));
|
2023-01-17 22:58:49 +08:00
|
|
|
if(!ctx) {
|
|
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ctx->ai_family = ai->ai_family;
|
connections: introduce http/3 happy eyeballs
New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing.
- filter is installed when `--http3` in the tool is used (or
the equivalent CURLOPT_ done in the library)
- starts a QUIC/HTTP/3 connect right away. Should that not
succeed after 100ms (subject to change), a parallel attempt
is started for HTTP/2 and HTTP/1.1 via TCP
- both attempts are subject to IPv6/IPv4 eyeballing, same
as happens for other connections
- tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT
- use a `soft` timeout at half the value. When the soft timeout
expires, the HTTPS-CONNECT filter checks if the QUIC filter
has received any data from the server. If not, it will start
the HTTP/2 attempt.
HTTP/3(ngtcp2) improvements.
- setting call_data in all cfilter calls similar to http/2 and vtls filters
for use in callback where no stream data is available.
- returning CURLE_PARTIAL_FILE for prematurely terminated transfers
- enabling pytest test_05 for h3
- shifting functionality to "connect" UDP sockets from ngtcp2
implementation into the udp socket cfilter. Because unconnected
UDP sockets are weird. For example they error when adding to a
pollset.
HTTP/3(quiche) improvements.
- fixed upload bug in quiche implementation, now passes 251 and pytest
- error codes on stream RESET
- improved debug logs
- handling of DRAIN during connect
- limiting pending event queue
HTTP/2 cfilter improvements.
- use LOG_CF macros for dynamic logging in debug build
- fix CURLcode on RST streams to be CURLE_PARTIAL_FILE
- enable pytest test_05 for h2
- fix upload pytests and improve parallel transfer performance.
GOAWAY handling for ngtcp2/quiche
- during connect, when the remote server refuses to accept new connections
and closes immediately (so the local conn goes into DRAIN phase), the
connection is torn down and a another attempt is made after a short grace
period.
This is the behaviour observed with nghttpx when we tell it to shut
down gracefully. Tested in pytest test_03_02.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
- new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementation.
Invoke:
python3 tests/tests-httpd/scorecard.py --help
for usage.
Improvements on gathering connect statistics and socket access.
- new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters
report connection statistics. This is triggered when the connection
has completely connected.
- new void Curl_pgrsTimeWas(..) method to report a timer update with
a timestamp of when it happend. This allows for updating timers
"later", e.g. a connect statistic after full connectivity has been
reached.
- in case of HTTP eyeballing, the previous changes will update
statistics only from the filter chain that "won" the eyeballing.
- new cfilter query CF_QUERY_SOCKET for retrieving the socket used
by a filter chain.
Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket()
for convenient use of this query.
- Change VTLS backend to query their sub-filters for the socket when
checks during the handshake are made.
HTTP/3 documentation on how https eyeballing works.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
Scorecard with Caddy.
- configure can be run with `--with-test-caddy=path` to specify which caddy to use for testing
- tests/tests-httpd/scorecard.py now measures download speeds with caddy
pytest improvements
- adding Makfile to clean gen dir
- adding nghttpx rundir creation on start
- checking httpd version 2.4.55 for test_05 cases where it is needed. Skipping with message if too old.
- catch exception when checking for caddy existance on system.
Closes #10349
2023-02-02 00:13:12 +08:00
|
|
|
ctx->transport = transport;
|
2023-01-17 22:58:49 +08:00
|
|
|
ctx->started = Curl_now();
|
2024-04-11 20:01:58 +08:00
|
|
|
#ifdef USE_IPV6
|
2023-01-17 22:58:49 +08:00
|
|
|
if(ctx->ai_family == AF_INET6) {
|
|
|
|
ctx->stats = ¤t_tr->cf6;
|
|
|
|
ctx->fail_delay_ms = current_tc->cf6_fail_delay_ms;
|
|
|
|
curl_msprintf(ctx->id, "v6-%d", ctx->stats->creations);
|
|
|
|
ctx->stats->creations++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ctx->stats = ¤t_tr->cf4;
|
|
|
|
ctx->fail_delay_ms = current_tc->cf4_fail_delay_ms;
|
|
|
|
curl_msprintf(ctx->id, "v4-%d", ctx->stats->creations);
|
|
|
|
ctx->stats->creations++;
|
|
|
|
}
|
|
|
|
|
|
|
|
created_at = Curl_timediff(ctx->started, current_tr->started);
|
|
|
|
if(ctx->stats->creations == 1)
|
|
|
|
ctx->stats->first_created = created_at;
|
|
|
|
ctx->stats->last_created = created_at;
|
|
|
|
infof(data, "%04dms: cf[%s] created", (int)created_at, ctx->id);
|
|
|
|
|
|
|
|
result = Curl_cf_create(&cf, &cft_test, ctx);
|
2023-08-18 17:08:52 +08:00
|
|
|
if(result)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
Curl_expire(data, ctx->fail_delay_ms, EXPIRE_RUN_NOW);
|
2023-01-17 22:58:49 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
*pcf = (!result)? cf : NULL;
|
|
|
|
if(result) {
|
|
|
|
free(cf);
|
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_result(struct test_case *tc,
|
|
|
|
struct test_result *tr)
|
|
|
|
{
|
|
|
|
char msg[256];
|
2023-08-18 17:08:52 +08:00
|
|
|
timediff_t duration_ms;
|
|
|
|
|
|
|
|
duration_ms = Curl_timediff(tr->ended, tr->started);
|
|
|
|
fprintf(stderr, "%d: test case took %dms\n", tc->id, (int)duration_ms);
|
2023-01-17 22:58:49 +08:00
|
|
|
|
|
|
|
if(tr->result != tc->exp_result
|
|
|
|
&& CURLE_OPERATION_TIMEDOUT != tr->result) {
|
|
|
|
/* on CI we encounter the TIMEOUT result, since images get less CPU
|
|
|
|
* and events are not as sharply timed. */
|
|
|
|
curl_msprintf(msg, "%d: expected result %d but got %d",
|
2023-02-17 17:48:34 +08:00
|
|
|
tc->id, tc->exp_result, tr->result);
|
2023-01-17 22:58:49 +08:00
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
if(tr->cf4.creations != tc->exp_cf4_creations) {
|
|
|
|
curl_msprintf(msg, "%d: expected %d ipv4 creations, but got %d",
|
|
|
|
tc->id, tc->exp_cf4_creations, tr->cf4.creations);
|
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
if(tr->cf6.creations != tc->exp_cf6_creations) {
|
|
|
|
curl_msprintf(msg, "%d: expected %d ipv6 creations, but got %d",
|
|
|
|
tc->id, tc->exp_cf6_creations, tr->cf6.creations);
|
|
|
|
fail(msg);
|
|
|
|
}
|
2023-02-17 17:48:34 +08:00
|
|
|
|
2023-01-17 22:58:49 +08:00
|
|
|
duration_ms = Curl_timediff(tr->ended, tr->started);
|
|
|
|
if(duration_ms < tc->min_duration_ms) {
|
|
|
|
curl_msprintf(msg, "%d: expected min duration of %dms, but took %dms",
|
|
|
|
tc->id, (int)tc->min_duration_ms, (int)duration_ms);
|
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
if(duration_ms > tc->max_duration_ms) {
|
|
|
|
curl_msprintf(msg, "%d: expected max duration of %dms, but took %dms",
|
|
|
|
tc->id, (int)tc->max_duration_ms, (int)duration_ms);
|
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
if(tr->cf6.creations && tr->cf4.creations && tc->pref_family) {
|
|
|
|
/* did ipv4 and ipv6 both, expect the preferred family to start right arway
|
|
|
|
* with the other being delayed by the happy_eyeball_timeout */
|
|
|
|
struct ai_family_stats *stats1 = !strcmp(tc->pref_family, "v6")?
|
|
|
|
&tr->cf6 : &tr->cf4;
|
|
|
|
struct ai_family_stats *stats2 = !strcmp(tc->pref_family, "v6")?
|
|
|
|
&tr->cf4 : &tr->cf6;
|
|
|
|
|
|
|
|
if(stats1->first_created > 100) {
|
|
|
|
curl_msprintf(msg, "%d: expected ip%s to start right away, instead "
|
|
|
|
"first attempt made after %dms",
|
|
|
|
tc->id, stats1->family, (int)stats1->first_created);
|
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
if(stats2->first_created < tc->he_timeout_ms) {
|
|
|
|
curl_msprintf(msg, "%d: expected ip%s to start delayed after %dms, "
|
|
|
|
"instead first attempt made after %dms",
|
|
|
|
tc->id, stats2->family, (int)tc->he_timeout_ms,
|
|
|
|
(int)stats2->first_created);
|
|
|
|
fail(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_connect(struct test_case *tc)
|
|
|
|
{
|
|
|
|
struct test_result tr;
|
|
|
|
struct curl_slist *list = NULL;
|
|
|
|
|
|
|
|
Curl_debug_set_transport_provider(TRNSPRT_TCP, cf_test_create);
|
|
|
|
current_tc = tc;
|
|
|
|
current_tr = &tr;
|
|
|
|
|
|
|
|
list = curl_slist_append(NULL, tc->resolve_info);
|
|
|
|
fail_unless(list, "error allocating resolve list entry");
|
|
|
|
curl_easy_setopt(easy, CURLOPT_RESOLVE, list);
|
2023-02-15 17:31:52 +08:00
|
|
|
curl_easy_setopt(easy, CURLOPT_IPRESOLVE, (long)tc->ip_version);
|
2023-08-18 17:08:52 +08:00
|
|
|
curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT_MS,
|
|
|
|
(long)tc->connect_timeout_ms);
|
2023-02-06 16:52:33 +08:00
|
|
|
curl_easy_setopt(easy, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
|
|
|
|
(long)tc->he_timeout_ms);
|
2023-01-17 22:58:49 +08:00
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_URL, tc->url);
|
|
|
|
memset(&tr, 0, sizeof(tr));
|
|
|
|
tr.cf6.family = "v6";
|
|
|
|
tr.cf4.family = "v4";
|
|
|
|
|
|
|
|
tr.started = Curl_now();
|
|
|
|
tr.result = curl_easy_perform(easy);
|
|
|
|
tr.ended = Curl_now();
|
|
|
|
|
|
|
|
curl_easy_setopt(easy, CURLOPT_RESOLVE, NULL);
|
|
|
|
curl_slist_free_all(list);
|
|
|
|
list = NULL;
|
|
|
|
current_tc = NULL;
|
|
|
|
current_tr = NULL;
|
|
|
|
|
|
|
|
check_result(tc, &tr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* How these test cases work:
|
2023-06-21 03:47:33 +08:00
|
|
|
* - replace the creation of the TCP socket filter with our test filter
|
2023-01-17 22:58:49 +08:00
|
|
|
* - test filter does nothing and reports failure after configured delay
|
|
|
|
* - we feed addresses into the resolve cache to simulate different cases
|
|
|
|
* - we monitor how many instances of ipv4/v6 attempts are made and when
|
|
|
|
* - for mixed families, we expect HAPPY_EYEBALLS_TIMEOUT to trigger
|
|
|
|
*
|
|
|
|
* Max Duration checks needs to be conservative since CI jobs are not
|
|
|
|
* as sharp.
|
|
|
|
*/
|
|
|
|
#define TURL "http://test.com:123"
|
|
|
|
|
|
|
|
#define R_FAIL CURLE_COULDNT_CONNECT
|
2023-08-18 17:08:52 +08:00
|
|
|
/* timeout values accounting for low cpu resources in CI */
|
|
|
|
#define TC_TMOT 90000 /* 90 sec max test duration */
|
|
|
|
#define CNCT_TMOT 60000 /* 60sec connect timeout */
|
2023-01-17 22:58:49 +08:00
|
|
|
|
|
|
|
static struct test_case TEST_CASES[] = {
|
2023-08-18 17:08:52 +08:00
|
|
|
/* TIMEOUT_MS, FAIL_MS CREATED DURATION Result, HE_PREF */
|
|
|
|
/* CNCT HE v4 v6 v4 v6 MIN MAX */
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 1, TURL, "test.com:123:192.0.2.1", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 1, 0, 200, TC_TMOT, R_FAIL, NULL },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* 1 ipv4, fails after ~200ms, reports COULDNT_CONNECT */
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 2, TURL, "test.com:123:192.0.2.1,192.0.2.2", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 2, 0, 400, TC_TMOT, R_FAIL, NULL },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* 2 ipv4, fails after ~400ms, reports COULDNT_CONNECT */
|
2024-04-11 20:01:58 +08:00
|
|
|
#ifdef USE_IPV6
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 3, TURL, "test.com:123:::1", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 0, 1, 200, TC_TMOT, R_FAIL, NULL },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* 1 ipv6, fails after ~200ms, reports COULDNT_CONNECT */
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 4, TURL, "test.com:123:::1,::2", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 0, 2, 400, TC_TMOT, R_FAIL, NULL },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* 2 ipv6, fails after ~400ms, reports COULDNT_CONNECT */
|
|
|
|
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 5, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 1, 1, 350, TC_TMOT, R_FAIL, "v4" },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* mixed ip4+6, v4 starts, v6 kicks in on HE, fails after ~350ms */
|
2023-02-15 17:31:52 +08:00
|
|
|
{ 6, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_WHATEVER,
|
2023-08-18 17:08:52 +08:00
|
|
|
CNCT_TMOT, 150, 200, 200, 1, 1, 350, TC_TMOT, R_FAIL, "v6" },
|
2023-01-17 22:58:49 +08:00
|
|
|
/* mixed ip6+4, v6 starts, v4 never starts due to high HE, TIMEOUT */
|
2023-08-18 17:08:52 +08:00
|
|
|
{ 7, TURL, "test.com:123:192.0.2.1,::1", CURL_IPRESOLVE_V4,
|
|
|
|
CNCT_TMOT, 150, 500, 500, 1, 0, 400, TC_TMOT, R_FAIL, NULL },
|
2023-02-15 17:31:52 +08:00
|
|
|
/* mixed ip4+6, but only use v4, check it uses full connect timeout,
|
2023-05-22 05:27:43 +08:00
|
|
|
although another address of the 'wrong' family is available */
|
2023-08-18 17:08:52 +08:00
|
|
|
{ 8, TURL, "test.com:123:::1,192.0.2.1", CURL_IPRESOLVE_V6,
|
|
|
|
CNCT_TMOT, 150, 500, 500, 0, 1, 400, TC_TMOT, R_FAIL, NULL },
|
2023-02-15 17:31:52 +08:00
|
|
|
/* mixed ip4+6, but only use v6, check it uses full connect timeout,
|
2023-05-22 05:27:43 +08:00
|
|
|
although another address of the 'wrong' family is available */
|
2023-01-17 22:58:49 +08:00
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
UNITTEST_START
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for(i = 0; i < sizeof(TEST_CASES)/sizeof(TEST_CASES[0]); ++i) {
|
|
|
|
test_connect(&TEST_CASES[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNITTEST_STOP
|