curl: --test-duphandle in debug builds runs "duphandled"

Using this option (only available in debug builds) makes curl always
call curl_easy_duphandle() on the handle before using it.

To help us catch curl_easy_duphandle() mistakes better.

Add a CI job using this.

Bonus: the previous runtests option -e is now also supported as
--test-event

Closes #15504
This commit is contained in:
Daniel Stenberg 2024-11-07 10:22:32 +01:00
parent 354f3f96a1
commit cbafcec50b
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
19 changed files with 93 additions and 10 deletions

View File

@ -248,7 +248,12 @@ jobs:
- name: event-based - name: event-based
install_packages: libssh-dev install_packages: libssh-dev
configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl
tflags: -n -e '!TLS-SRP' tflags: -n --test-event '!TLS-SRP'
- name: duphandle
install_packages: libssh-dev
configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl
tflags: -n --test-duphandle '!TLS-SRP'
- name: rustls valgrind - name: rustls valgrind
install_packages: valgrind install_packages: valgrind

View File

@ -136,7 +136,7 @@ jobs:
compiler: clang compiler: clang
configure: --enable-debug --with-openssl=$(brew --prefix openssl) configure: --enable-debug --with-openssl=$(brew --prefix openssl)
macos-version-min: '10.9' macos-version-min: '10.9'
tflags: -e tflags: --test-event
- name: 'OpenSSL libssh2 !ldap 10.15' - name: 'OpenSSL libssh2 !ldap 10.15'
compiler: clang compiler: clang
configure: --enable-debug --disable-ldap --with-openssl=$(brew --prefix openssl) configure: --enable-debug --disable-ldap --with-openssl=$(brew --prefix openssl)

View File

@ -330,6 +330,7 @@ struct GlobalConfig {
long ms_per_transfer; /* start next transfer after (at least) this long ms_per_transfer; /* start next transfer after (at least) this
many milliseconds */ many milliseconds */
#ifdef DEBUGBUILD #ifdef DEBUGBUILD
bool test_duphandle;
bool test_event_based; bool test_event_based;
#endif #endif
bool parallel; bool parallel;

View File

@ -312,7 +312,10 @@ static const struct LongShort aliases[]= {
{"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN}, {"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN},
{"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY}, {"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY},
{"telnet-option", ARG_STRG, 't', C_TELNET_OPTION}, {"telnet-option", ARG_STRG, 't', C_TELNET_OPTION},
#ifdef DEBUGBUILD
{"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE},
{"test-event", ARG_BOOL, ' ', C_TEST_EVENT}, {"test-event", ARG_BOOL, ' ', C_TEST_EVENT},
#endif
{"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE}, {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE},
{"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS}, {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},
{"time-cond", ARG_STRG, 'z', C_TIME_COND}, {"time-cond", ARG_STRG, 'z', C_TIME_COND},
@ -1653,13 +1656,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
case C_SASL_IR: /* --sasl-ir */ case C_SASL_IR: /* --sasl-ir */
config->sasl_ir = toggle; config->sasl_ir = toggle;
break; break;
case C_TEST_EVENT: /* --test-event */
#ifdef DEBUGBUILD #ifdef DEBUGBUILD
global->test_event_based = toggle; case C_TEST_DUPHANDLE: /* --test-duphandle */
#else global->test_duphandle = toggle;
warnf(global, "--test-event is ignored unless a debug build");
#endif
break; break;
case C_TEST_EVENT: /* --test-event */
global->test_event_based = toggle;
break;
#endif
case C_UNIX_SOCKET: /* --unix-socket */ case C_UNIX_SOCKET: /* --unix-socket */
config->abstract_unix_socket = FALSE; config->abstract_unix_socket = FALSE;
err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);

View File

@ -267,6 +267,7 @@ typedef enum {
C_TCP_FASTOPEN, C_TCP_FASTOPEN,
C_TCP_NODELAY, C_TCP_NODELAY,
C_TELNET_OPTION, C_TELNET_OPTION,
C_TEST_DUPHANDLE,
C_TEST_EVENT, C_TEST_EVENT,
C_TFTP_BLKSIZE, C_TFTP_BLKSIZE,
C_TFTP_NO_OPTIONS, C_TFTP_NO_OPTIONS,

View File

@ -2885,6 +2885,17 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
if(getenv("CURL_FORBID_REUSE")) if(getenv("CURL_FORBID_REUSE"))
(void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L); (void)curl_easy_setopt(per->curl, CURLOPT_FORBID_REUSE, 1L);
if(global->test_duphandle) {
CURL *dup = curl_easy_duphandle(per->curl);
curl_easy_cleanup(per->curl);
per->curl = dup;
if(!dup) {
result = CURLE_OUT_OF_MEMORY;
break;
}
/* a duplicate needs the share re-added */
(void)curl_easy_setopt(per->curl, CURLOPT_SHARE, share);
}
if(global->test_event_based) if(global->test_event_based)
result = curl_easy_perform_ev(per->curl); result = curl_easy_perform_ev(per->curl);
else else

View File

@ -522,6 +522,7 @@ the `unit/` directory (if the tool name starts with `unit`).
Brief test case description, shown when the test runs. Brief test case description, shown when the test runs.
### `<setenv>` ### `<setenv>`
variable1=contents1 variable1=contents1
variable2=contents2 variable2=contents2
variable3 variable3
@ -606,6 +607,11 @@ Pass this given data on stdin to the tool.
If `nonewline` is set, we cut off the trailing newline of this given data If `nonewline` is set, we cut off the trailing newline of this given data
before comparing with the one actually received by the client before comparing with the one actually received by the client
## `<disable>`
If `test-duphandle` is a listed item here, this is not run when
`--test-duphandle` is used.
## `<verify>` ## `<verify>`
### `<errorcode>` ### `<errorcode>`
numerical error code curl is supposed to return. Specify a list of accepted numerical error code curl is supposed to return. Specify a list of accepted

View File

@ -44,6 +44,10 @@ HSTS with trailing-dot host name in URL but none in hsts file
<command> <command>
-x http://%HOSTIP:%HTTPPORT http://this.hsts.example./%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n' -x http://%HOSTIP:%HTTPPORT http://this.hsts.example./%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n'
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -44,6 +44,9 @@ HSTS with no t-dot host name in URL but t-dot in file
<command> <command>
-x http://%HOSTIP:%HTTPPORT http://this.hsts.example/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n' -x http://%HOSTIP:%HTTPPORT http://this.hsts.example/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n'
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -43,6 +43,9 @@ HSTS and %{url_effective} after upgrade
<command> <command>
-x http://%HOSTIP:%HTTPPORT http://this.hsts.example/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n' -x http://%HOSTIP:%HTTPPORT http://this.hsts.example/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n'
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -55,6 +55,9 @@ HSTS with updated expiry in response
<command> <command>
-x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k -x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -57,6 +57,9 @@ HSTS update expiry, with parent includeSubDomains domain present
<command> <command>
-x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k -x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -57,6 +57,9 @@ HSTS update expiry, with two includeSubDomains domains present
<command> <command>
-x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k -x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -57,6 +57,9 @@ HSTS update expiry, removing includesubdomains in update
<command> <command>
-x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k -x http://%HOSTIP:%PROXYPORT http://this.hsts.example:%HTTPSPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -k
</command> </command>
<disable>
test-duphandle
</disable>
</client> </client>
<verify> <verify>

View File

@ -54,6 +54,7 @@ BEGIN {
$PROXYIN $PROXYIN
$pwd $pwd
$randseed $randseed
$run_duphandle
$run_event_based $run_event_based
$SERVERCMD $SERVERCMD
$SERVERIN $SERVERIN
@ -83,6 +84,7 @@ our $verbose; # 1 to show verbose test output
our $torture; # 1 to enable torture testing our $torture; # 1 to enable torture testing
our $proxy_address; # external HTTP proxy address our $proxy_address; # external HTTP proxy address
our $listonly; # only list the tests our $listonly; # only list the tests
our $run_duphandle; # run curl with --test-duphandle to verify handle duplication
our $run_event_based; # run curl with --test-event to test the event API our $run_event_based; # run curl with --test-event to test the event API
our $automakestyle; # use automake-like test status output format our $automakestyle; # use automake-like test status output format
our $anyway; # continue anyway, even if a test fail our $anyway; # continue anyway, even if a test fail

View File

@ -894,6 +894,18 @@ sub singletest_run {
$cmdargs .= "--test-event "; $cmdargs .= "--test-event ";
$fail_due_event_based--; $fail_due_event_based--;
} }
if($run_duphandle) {
$cmdargs .= "--test-duphandle ";
my @dis = getpart("client", "disable");
if(@dis) {
chomp $dis[0] if($dis[0]);
if($dis[0] eq "test-duphandle") {
# marked to not run with duphandle
logmsg "test $testnum: IGNORED: can't run test-duphandle\n";
return (-1, 0, 0, "", "", 0);
}
}
}
$cmdargs .= $cmd; $cmdargs .= $cmd;
if ($proxy_address) { if ($proxy_address) {
$cmdargs .= " --proxy $proxy_address "; $cmdargs .= " --proxy $proxy_address ";

View File

@ -118,7 +118,7 @@ exclusion, the second field contains a pattern and the final field contains
the reason why matching tests should be skipped. The exclusion types are the reason why matching tests should be skipped. The exclusion types are
*keyword*, *test*, and *tool*. *keyword*, *test*, and *tool*.
## `-e` ## `-e` or `--test-event`
Run the test event-based (if possible). This makes runtests invoke curl with Run the test event-based (if possible). This makes runtests invoke curl with
--test-event option. This option only works if both curl and libcurl were --test-event option. This option only works if both curl and libcurl were
@ -263,6 +263,19 @@ the allocation with that number to be set to fail at once instead of looping
through everyone, which is handy when debugging and then often in combination through everyone, which is handy when debugging and then often in combination
with *-g*. with *-g*.
## `--test-duphandle`
Passes the `--test-duphandle` option to curl when invoked. This command line
option only exists in debug builds and runs curl normally, but duplicates the
easy handle before the transfer and use the duplicate instead of the original
handle. This verifies that the duplicate works exactly as good as the original
handle.
Because of how the curl tool uses a share object to store and keep some data,
not everything is however perfectly copied in the duplicate. In particular
HSTS data is not. A specific test case can be set to avoid using
`--test-duphandle` by disabling it on a per test basis.
## `-u` ## `-u`
Error instead of warning on server unexpectedly alive. Error instead of warning on server unexpectedly alive.

View File

@ -871,7 +871,8 @@ sub checksystemfeatures {
"*\n"); "*\n");
} }
logmsg sprintf("* Env: %s%s%s%s", $valgrind?"Valgrind ":"", logmsg sprintf("* Env: %s%s%s%s%s", $valgrind?"Valgrind ":"",
$run_duphandle?"test-duphandle ":"",
$run_event_based?"event-based ":"", $run_event_based?"event-based ":"",
$bundle?"bundle ":"", $bundle?"bundle ":"",
$nghttpx_h3); $nghttpx_h3);
@ -2270,10 +2271,14 @@ while(@ARGV) {
# have the servers display protocol output # have the servers display protocol output
$debugprotocol=1; $debugprotocol=1;
} }
elsif($ARGV[0] eq "-e") { elsif(($ARGV[0] eq "-e") || ($ARGV[0] eq "--test-event")) {
# run the tests cases event based if possible # run the tests cases event based if possible
$run_event_based=1; $run_event_based=1;
} }
elsif($ARGV[0] eq "--test-duphandle") {
# run the tests with --test-duphandle
$run_duphandle=1;
}
elsif($ARGV[0] eq "-f") { elsif($ARGV[0] eq "-f") {
# force - run the test case even if listed in DISABLED # force - run the test case even if listed in DISABLED
$run_disabled=1; $run_disabled=1;

View File

@ -180,6 +180,7 @@ my %opts = (
'--include' => 6, '--include' => 6,
# for tests and debug only, can remain hidden # for tests and debug only, can remain hidden
'--test-duphandle' => 6,
'--test-event' => 6, '--test-event' => 6,
'--wdebug' => 6, '--wdebug' => 6,
); );