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
install_packages: libssh-dev
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
install_packages: valgrind

View File

@ -136,7 +136,7 @@ jobs:
compiler: clang
configure: --enable-debug --with-openssl=$(brew --prefix openssl)
macos-version-min: '10.9'
tflags: -e
tflags: --test-event
- name: 'OpenSSL libssh2 !ldap 10.15'
compiler: clang
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
many milliseconds */
#ifdef DEBUGBUILD
bool test_duphandle;
bool test_event_based;
#endif
bool parallel;

View File

@ -312,7 +312,10 @@ static const struct LongShort aliases[]= {
{"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN},
{"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY},
{"telnet-option", ARG_STRG, 't', C_TELNET_OPTION},
#ifdef DEBUGBUILD
{"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE},
{"test-event", ARG_BOOL, ' ', C_TEST_EVENT},
#endif
{"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE},
{"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},
{"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 */
config->sasl_ir = toggle;
break;
case C_TEST_EVENT: /* --test-event */
#ifdef DEBUGBUILD
global->test_event_based = toggle;
#else
warnf(global, "--test-event is ignored unless a debug build");
#endif
case C_TEST_DUPHANDLE: /* --test-duphandle */
global->test_duphandle = toggle;
break;
case C_TEST_EVENT: /* --test-event */
global->test_event_based = toggle;
break;
#endif
case C_UNIX_SOCKET: /* --unix-socket */
config->abstract_unix_socket = FALSE;
err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK);

View File

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

View File

@ -2885,6 +2885,17 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
if(getenv("CURL_FORBID_REUSE"))
(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)
result = curl_easy_perform_ev(per->curl);
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.
### `<setenv>`
variable1=contents1
variable2=contents2
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
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>`
### `<errorcode>`
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>
-x http://%HOSTIP:%HTTPPORT http://this.hsts.example./%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER -w '%{url_effective}\n'
</command>
<disable>
test-duphandle
</disable>
</client>
<verify>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -54,6 +54,7 @@ BEGIN {
$PROXYIN
$pwd
$randseed
$run_duphandle
$run_event_based
$SERVERCMD
$SERVERIN
@ -83,6 +84,7 @@ our $verbose; # 1 to show verbose test output
our $torture; # 1 to enable torture testing
our $proxy_address; # external HTTP proxy address
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 $automakestyle; # use automake-like test status output format
our $anyway; # continue anyway, even if a test fail

View File

@ -894,6 +894,18 @@ sub singletest_run {
$cmdargs .= "--test-event ";
$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;
if ($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
*keyword*, *test*, and *tool*.
## `-e`
## `-e` or `--test-event`
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
@ -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
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`
Error instead of warning on server unexpectedly alive.

View File

@ -871,7 +871,8 @@ sub checksystemfeatures {
"*\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 ":"",
$bundle?"bundle ":"",
$nghttpx_h3);
@ -2270,10 +2271,14 @@ while(@ARGV) {
# have the servers display protocol output
$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_event_based=1;
}
elsif($ARGV[0] eq "--test-duphandle") {
# run the tests with --test-duphandle
$run_duphandle=1;
}
elsif($ARGV[0] eq "-f") {
# force - run the test case even if listed in DISABLED
$run_disabled=1;

View File

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