summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Dymond <max.dymond@microsoft.com>2021-07-22 15:32:30 +0100
committerDaniel Stenberg <daniel@haxx.se>2021-09-27 17:16:43 +0200
commita517378de58358a85b7cfe9efecb56051268f629 (patch)
tree8b8b43685911d3cbc6450b8d7255f5a10d2b8795
parent06981ba7f620364eba253f5afcd7ebfaefcc8694 (diff)
downloadcurl-a517378de58358a85b7cfe9efecb56051268f629.tar.gz
CURLOPT_PREREQFUNCTION: add new callback
Triggered before a request is made but after a connection is set up Changes: - callback: Update docs and callback for pre-request callback - Add documentation for CURLOPT_PREREQDATA and CURLOPT_PREREQFUNCTION, - Add redirect test and callback failure test - Note that the function may be called multiple times on a redirection - Disable new 2086 test due to Windows weirdness Closes #7477
-rw-r--r--docs/libcurl/curl_easy_setopt.34
-rw-r--r--docs/libcurl/opts/CURLOPT_PREREQDATA.361
-rw-r--r--docs/libcurl/opts/CURLOPT_PREREQFUNCTION.3104
-rw-r--r--docs/libcurl/opts/Makefile.inc2
-rw-r--r--docs/libcurl/symbols-in-versions4
-rw-r--r--include/curl/curl.h21
-rw-r--r--lib/easyoptions.c2
-rw-r--r--lib/multi.c22
-rw-r--r--lib/setopt.c6
-rw-r--r--lib/urldata.h2
-rw-r--r--tests/data/DISABLED2
-rw-r--r--tests/data/Makefile.inc2
-rw-r--r--tests/data/test208251
-rw-r--r--tests/data/test208345
-rw-r--r--tests/data/test208454
-rw-r--r--tests/data/test208564
-rw-r--r--tests/data/test208652
-rw-r--r--tests/libtest/.gitignore1
-rw-r--r--tests/libtest/Makefile.inc8
-rw-r--r--tests/libtest/libprereq.c98
-rwxr-xr-xtests/libtest/mk-lib1521.pl1
21 files changed, 602 insertions, 4 deletions
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index 592692b94..b1abf43e3 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -144,6 +144,10 @@ Suppress proxy CONNECT response headers from user callbacks. See \fICURLOPT_SUPP
Callback to be called before a new resolve request is started. See \fICURLOPT_RESOLVER_START_FUNCTION(3)\fP
.IP CURLOPT_RESOLVER_START_DATA
Data pointer to pass to resolver start callback. See \fICURLOPT_RESOLVER_START_DATA(3)\fP
+.IP CURLOPT_PREREQFUNCTION
+Callback to be called after a connection is established but before a request is made on that connection. See \fICURLOPT_PREREQFUNCTION(3)\fP
+.IP CURLOPT_PREREQDATA
+Data pointer to pass to the CURLOPT_PREREQFUNCTION callback. See \fICURLOPT_PREREQDATA(3)\fP
.SH ERROR OPTIONS
.IP CURLOPT_ERRORBUFFER
Error message buffer. See \fICURLOPT_ERRORBUFFER(3)\fP
diff --git a/docs/libcurl/opts/CURLOPT_PREREQDATA.3 b/docs/libcurl/opts/CURLOPT_PREREQDATA.3
new file mode 100644
index 000000000..07497a83b
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_PREREQDATA.3
@@ -0,0 +1,61 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 2021, Max Dymond, <max.dymond@microsoft.com>, 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.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_PREREQDATA 3 "2 Aug 2021" "libcurl 7.80.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_PREREQDATA \- custom pointer passed to the pre-request callback
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PREREQDATA, void *pointer);
+.SH DESCRIPTION
+Pass a \fIpointer\fP that will be untouched by libcurl and passed as the first
+argument in the pre-request callback set with
+\fICURLOPT_PREREQFUNCTION(3)\fP.
+.SH DEFAULT
+NULL
+.SH PROTOCOLS
+All
+.SH EXAMPLE
+.nf
+static int prereq_callback(void *clientp,
+ char *conn_primary_ip,
+ char *conn_local_ip,
+ int conn_primary_port,
+ int conn_local_port)
+{
+ printf("Connection made to %s:%s\n", conn_primary_ip, conn_primary_port);
+ return CURL_PREREQFUNC_OK;
+}
+
+{
+ struct data prereq_data;
+ curl_easy_setopt(CURL *handle, CURLOPT_PREREQFUNCTION, prereq_callback);
+ curl_easy_setopt(CURL *handle, CURLOPT_PREREQDATA, &prereq_data);
+}
+.fi
+.SH AVAILABILITY
+Added in 7.80.0
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_PREREQFUNCTION "(3) "
diff --git a/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.3 b/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.3
new file mode 100644
index 000000000..dcc782f9d
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.3
@@ -0,0 +1,104 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 2021, Max Dymond, <max.dymond@microsoft.com>, 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.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_PREREQFUNCTION 3 "2 Aug 2021" "libcurl 7.80.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_PREREQFUNCTION \- user callback called when a connection has been
+established, but before a request has been made.
+.SH SYNOPSIS
+.nf
+#include <curl/curl.h>
+
+/* These are the return codes for the pre-request callback. */
+#define CURL_PREREQFUNC_OK 0
+#define CURL_PREREQFUNC_ABORT 1 /* fail the entire transfer */
+
+int prereq_callback(void *clientp,
+ char *conn_primary_ip,
+ char *conn_local_ip,
+ int conn_primary_port,
+ int conn_local_port);
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PREREQFUNCTION, prereq_callback);
+.SH DESCRIPTION
+Pass a pointer to your callback function, which should match the prototype
+shown above.
+
+This function gets called by libcurl after a connection has been established
+or a connection has been reused (including any SSL handshaking), but before any
+request is actually made on the connection. For example, for HTTP, this
+callback is called once a connection has been established to the server, but
+before a GET/HEAD/POST/etc request has been sent.
+
+This function may be called multiple times if redirections are enabled and are
+being followed (see \fICURLOPT_FOLLOWLOCATION(3)\fP).
+
+This function is passed the following information:
+.IP conn_primary_ip
+A nul-terminated pointer to a C string containing the primary IP of the remote
+server established with this connection. For FTP, this is the IP for the control
+connection. IPv6 addresses are represented without surrounding brackets.
+.IP conn_local_ip
+A nul-terminated pointer to a C string containing the originating IP for this
+connection. IPv6 addresses are represented without surrounding brackets.
+.IP conn_primary_port
+The primary port number on the remote server established with this connection.
+For FTP, this is the port for the control connection. This can be a TCP or a
+UDP port number dependending on the protocol.
+.IP conn_local_port
+The originating port number for this connection. This can be a TCP or a UDP port
+number dependending on the protocol.
+.RE
+
+\fIclientp\fP is the pointer you set with \fICURLOPT_PREREQDATA(3)\fP.
+
+The callback function must return \fICURL_PREREQFUNC_OK\fP on success, or
+\fICURL_PREREQFUNC_ABORT\fP to cause the transfer to fail.
+
+.SH DEFAULT
+By default, this is NULL and unused.
+.SH PROTOCOLS
+ALL
+.SH EXAMPLE
+.nf
+static int prereq_callback(void *clientp,
+ char *conn_primary_ip,
+ char *conn_local_ip,
+ int conn_primary_port,
+ int conn_local_port)
+{
+ printf("Connection made to %s:%s\n", conn_primary_ip, conn_primary_port);
+ return CURL_PREREQFUNC_OK;
+}
+
+{
+ struct data prereq_data;
+ curl_easy_setopt(CURL *handle, CURLOPT_PREREQFUNCTION, prereq_callback);
+ curl_easy_setopt(CURL *handle, CURLOPT_PREREQDATA, &prereq_data);
+}
+.fi
+.SH AVAILABILITY
+Added in 7.80.0
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_PREREQDATA "(3) "
diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc
index 4ef5ddf43..7bac21024 100644
--- a/docs/libcurl/opts/Makefile.inc
+++ b/docs/libcurl/opts/Makefile.inc
@@ -254,6 +254,8 @@ man_MANS = \
CURLOPT_POSTQUOTE.3 \
CURLOPT_POSTREDIR.3 \
CURLOPT_PREQUOTE.3 \
+ CURLOPT_PREREQDATA.3 \
+ CURLOPT_PREREQFUNCTION.3 \
CURLOPT_PRE_PROXY.3 \
CURLOPT_PRIVATE.3 \
CURLOPT_PROGRESSDATA.3 \
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 8be22d504..1dbcdbd3e 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -531,6 +531,8 @@ CURLOPT_POSTFIELDSIZE_LARGE 7.11.1
CURLOPT_POSTQUOTE 7.1
CURLOPT_POSTREDIR 7.19.1
CURLOPT_PREQUOTE 7.9.5
+CURLOPT_PREREQDATA 7.80.0
+CURLOPT_PREREQFUNCTION 7.80.0
CURLOPT_PRE_PROXY 7.52.0
CURLOPT_PRIVATE 7.10.3
CURLOPT_PROGRESSDATA 7.1
@@ -964,6 +966,8 @@ CURL_POLL_INOUT 7.14.0
CURL_POLL_NONE 7.14.0
CURL_POLL_OUT 7.14.0
CURL_POLL_REMOVE 7.14.0
+CURL_PREREQFUNC_ABORT 7.79.0
+CURL_PREREQFUNC_OK 7.79.0
CURL_PROGRESSFUNC_CONTINUE 7.68.0
CURL_PROGRESS_BAR 7.1.1 - 7.4.1
CURL_PROGRESS_STATS 7.1.1 - 7.4.1
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 6eb0fcb82..d0862afaf 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -470,6 +470,20 @@ typedef int (*curl_debug_callback)
size_t size, /* size of the data pointed to */
void *userptr); /* whatever the user please */
+/* This is the CURLOPT_PREREQFUNCTION callback prototype. */
+typedef int (*curl_prereq_callback)(void *clientp,
+ char *conn_primary_ip,
+ char *conn_local_ip,
+ int conn_primary_port,
+ int conn_local_port);
+
+/* Return code for when the pre-request callback has terminated without
+ any errors */
+#define CURL_PREREQFUNC_OK 0
+/* Return code for when the pre-request callback wants to abort the
+ request */
+#define CURL_PREREQFUNC_ABORT 1
+
/* All possible error codes from all sorts of curl functions. Future versions
may return other values, stay prepared.
@@ -2105,6 +2119,13 @@ typedef enum {
/* used by scp/sftp to verify the host's public key */
CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311),
+ /* Function that will be called immediately before the initial request
+ is made on a connection (after any protocol negotiation step). */
+ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312),
+
+ /* Data passed to the CURLOPT_PREREQFUNCTION callback */
+ CURLOPT(CURLOPT_PREREQDATA, CURLOPTTYPE_CBPOINT, 313),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
diff --git a/lib/easyoptions.c b/lib/easyoptions.c
index b1c0704d5..717b081a3 100644
--- a/lib/easyoptions.c
+++ b/lib/easyoptions.c
@@ -356,6 +356,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (311 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (313 + 1));
}
#endif
diff --git a/lib/multi.c b/lib/multi.c
index 68c1a64d5..f31b25262 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -2028,6 +2028,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
break;
case MSTATE_DO:
+ if(data->set.fprereq) {
+ int prereq_rc;
+
+ /* call the prerequest callback function */
+ Curl_set_in_callback(data, true);
+ prereq_rc = data->set.fprereq(data->set.prereq_userp,
+ data->info.conn_primary_ip,
+ data->info.conn_local_ip,
+ data->info.conn_primary_port,
+ data->info.conn_local_port);
+ Curl_set_in_callback(data, false);
+ if(prereq_rc != CURL_PREREQFUNC_OK) {
+ failf(data, "operation aborted by pre-request callback");
+ /* failure in pre-request callback - don't do any other processing */
+ result = CURLE_ABORTED_BY_CALLBACK;
+ Curl_posttransfer(data);
+ multi_done(data, result, FALSE);
+ stream_error = TRUE;
+ break;
+ }
+ }
+
if(data->set.connect_only) {
/* keep connection open for application to use the socket */
connkeep(data->conn, "CONNECT_ONLY");
diff --git a/lib/setopt.c b/lib/setopt.c
index c62a62fb5..8e19389ae 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -3013,6 +3013,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return result;
break;
#endif
+ case CURLOPT_PREREQFUNCTION:
+ data->set.fprereq = va_arg(param, curl_prereq_callback);
+ break;
+ case CURLOPT_PREREQDATA:
+ data->set.prereq_userp = va_arg(param, void *);
+ break;
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION;
diff --git a/lib/urldata.h b/lib/urldata.h
index 2d1e873a5..47cb9e282 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1652,6 +1652,8 @@ struct UserDefined {
curl_closesocket_callback fclosesocket; /* function for closing the
socket */
void *closesocket_client;
+ curl_prereq_callback fprereq; /* pre-initial request callback */
+ void *prereq_userp; /* pre-initial request user data */
void *seek_client; /* pointer to pass to the seek callback */
/* the 3 curl_conv_callback functions below are used on non-ASCII hosts */
diff --git a/tests/data/DISABLED b/tests/data/DISABLED
index 6807fe23d..aa0fee396 100644
--- a/tests/data/DISABLED
+++ b/tests/data/DISABLED
@@ -29,6 +29,8 @@
# test 1801 causes problems on Mac OS X and github
# https://github.com/curl/curl/issues/380
1801
+# test 2086 causes issues on Windows only
+2086
#
#
# Tests that are disabled here for Hyper are SUPPOSED to work but
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index d906ca338..41249fdad 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -229,7 +229,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 \
test2064 test2065 test2066 test2067 test2068 test2069 test2070 \
test2071 test2072 test2073 test2074 test2075 test2076 test2077 \
test2078 \
-test2080 test2081 \
+test2080 test2081 test2082 test2083 test2084 test2085 test2086 \
\
test2100 \
\
diff --git a/tests/data/test2082 b/tests/data/test2082
new file mode 100644
index 000000000..4c37772d5
--- /dev/null
+++ b/tests/data/test2082
@@ -0,0 +1,51 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+
+<name>
+Pre-request callback for HTTP
+</name>
+<tool>
+libprereq
+</tool>
+
+<command>
+%HOSTIP:%HTTPPORT/%TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stripfile>
+s/^Local port = \d+/Local port = stripped/
+</stripfile>
+<stdout>
+Connected to %HOSTIP
+Connected from %CLIENTIP
+Remote port = %HTTPPORT
+Local port = stripped
+Returning = 0
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test2083 b/tests/data/test2083
new file mode 100644
index 000000000..9256151a8
--- /dev/null
+++ b/tests/data/test2083
@@ -0,0 +1,45 @@
+<testcase>
+<info>
+<keywords>
+FTP
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+ftp
+</server>
+
+<name>
+Pre-request callback for FTP
+</name>
+<tool>
+libprereq
+</tool>
+
+<command>
+ftp://%HOSTIP:%FTPPORT/test-%TESTNUMBER/
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stripfile>
+s/^Local port = \d+/Local port = stripped/
+</stripfile>
+<stdout>
+Connected to %HOSTIP
+Connected from %CLIENTIP
+Remote port = %FTPPORT
+Local port = stripped
+Returning = 0
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test2084 b/tests/data/test2084
new file mode 100644
index 000000000..ced7086c4
--- /dev/null
+++ b/tests/data/test2084
@@ -0,0 +1,54 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+
+<name>
+Pre-request callback for HTTP with callback terminating transfer
+</name>
+<tool>
+libprereq
+</tool>
+
+<command>
+%HOSTIP:%HTTPPORT/%TESTNUMBER#err
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+42
+</errorcode>
+<stripfile>
+s/^Local port = \d+/Local port = stripped/
+</stripfile>
+<stdout>
+Connected to %HOSTIP
+Connected from %CLIENTIP
+Remote port = %HTTPPORT
+Local port = stripped
+Returning = 1
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test2085 b/tests/data/test2085
new file mode 100644
index 000000000..665a756c0
--- /dev/null
+++ b/tests/data/test2085
@@ -0,0 +1,64 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+followlocation
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 302 OK swsclose
+Location: data2.html/%TESTNUMBER0002
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Connection: close
+
+</data>
+<data2 nocheck="yes">
+HTTP/1.1 200 OK swsclose
+Location: this should be ignored
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Connection: close
+
+body
+</data2>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+
+<name>
+Pre-request callback for HTTP with location following
+</name>
+<tool>
+libprereq
+</tool>
+
+<command>
+%HOSTIP:%HTTPPORT/%TESTNUMBER#redir
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stripfile>
+s/^Local port = \d+/Local port = stripped/
+</stripfile>
+<stdout>
+Connected to %HOSTIP
+Connected from %CLIENTIP
+Remote port = %HTTPPORT
+Local port = stripped
+Returning = 0
+Connected to %HOSTIP
+Connected from %CLIENTIP
+Remote port = %HTTPPORT
+Local port = stripped
+Returning = 0
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test2086 b/tests/data/test2086
new file mode 100644
index 000000000..ce60aa831
--- /dev/null
+++ b/tests/data/test2086
@@ -0,0 +1,52 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+IPv6
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http-ipv6
+</server>
+
+<name>
+Pre-request callback for HTTP IPv6
+</name>
+<tool>
+libprereq
+</tool>
+
+<command>
+%HOST6IP:%HTTP6PORT/%TESTNUMBER#ipv6
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stripfile>
+s/^Local port = \d+/Local port = stripped/
+</stripfile>
+<stdout>
+Connected to %HOST6IP
+Connected from %CLIENT6IP
+Remote port = %HTTP6PORT
+Local port = stripped
+Returning = 0
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/libtest/.gitignore b/tests/libtest/.gitignore
index 62d9176d5..f27ba8128 100644
--- a/tests/libtest/.gitignore
+++ b/tests/libtest/.gitignore
@@ -5,3 +5,4 @@ lib[56][0-9][0-9]
lib1521.c
libauthretry
libntlmconnect
+libprereq
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index c2d0a6e8e..0f70ceb4b 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -36,7 +36,7 @@ SUPPORTFILES = first.c test.h
# These are all libcurl test programs
noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
- chkdecimalpoint \
+ chkdecimalpoint libprereq \
lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 lib508 lib509 \
lib510 lib511 lib512 lib513 lib514 lib515 lib516 lib517 lib518 lib519 \
lib520 lib521 lib523 lib524 lib525 lib526 lib527 lib529 lib532 \
@@ -81,6 +81,10 @@ libntlmconnect_CPPFLAGS = $(AM_CPPFLAGS)
libauthretry_SOURCES = libauthretry.c $(SUPPORTFILES)
libauthretry_CPPFLAGS = $(AM_CPPFLAGS)
+libprereq_SOURCES = libprereq.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+libprereq_LDADD = $(TESTUTIL_LIBS)
+libprereq_CPPFLAGS = $(AM_CPPFLAGS)
+
lib500_SOURCES = lib500.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE) $(MULTIBYTE)
lib500_LDADD = $(TESTUTIL_LIBS)
lib500_CPPFLAGS = $(AM_CPPFLAGS)
@@ -494,7 +498,7 @@ lib1520_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1520
nodist_lib1521_SOURCES = lib1521.c $(SUPPORTFILES)
lib1521_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)
-lib1522_SOURCES = lib1522.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE)
+lib1522_SOURCES = lib1522.c $(SUPPORTFILES) $(TESTUTIL) $(TSTTRACE)
lib1522_LDADD = $(TESTUTIL_LIBS)
lib1522_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/libtest/libprereq.c b/tests/libtest/libprereq.c
new file mode 100644
index 000000000..11eb18c06
--- /dev/null
+++ b/tests/libtest/libprereq.c
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2021, Max Dymond, <max.dymond@microsoft.com>
+ *
+ * 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.
+ *
+ ***************************************************************************/
+#include "test.h"
+
+typedef struct prcs {
+ int prereq_retcode;
+ int ipv6;
+} PRCS;
+
+static int prereq_callback(void *clientp,
+ char *conn_primary_ip,
+ char *conn_local_ip,
+ int conn_primary_port,
+ int conn_local_port)
+{
+ PRCS *prereq_cb = (PRCS *)clientp;
+
+ if(prereq_cb->ipv6) {
+ printf("Connected to [%s]\n", conn_primary_ip);
+ printf("Connected from [%s]\n", conn_local_ip);
+ }
+ else {
+ printf("Connected to %s\n", conn_primary_ip);
+ printf("Connected from %s\n", conn_local_ip);
+ }
+
+ printf("Remote port = %d\n", conn_primary_port);
+ printf("Local port = %d\n", conn_local_port);
+ printf("Returning = %d\n", prereq_cb->prereq_retcode);
+ return prereq_cb->prereq_retcode;
+}
+
+int test(char *URL)
+{
+ PRCS prereq_cb;
+ CURLcode ret = CURLE_OK;
+ CURL *curl = NULL;
+
+ prereq_cb.prereq_retcode = CURL_PREREQFUNC_OK;
+ prereq_cb.ipv6 = 0;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+
+ if(curl) {
+ if(strstr(URL, "#ipv6")) {
+ /* The IP addresses should be surrounded by brackets! */
+ prereq_cb.ipv6 = 1;
+ }
+ if(strstr(URL, "#err")) {
+ /* Set the callback to exit with failure */
+ prereq_cb.prereq_retcode = CURL_PREREQFUNC_ABORT;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, URL);
+ curl_easy_setopt(curl, CURLOPT_PREREQFUNCTION, prereq_callback);
+ curl_easy_setopt(curl, CURLOPT_PREREQDATA, &prereq_cb);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, stderr);
+
+ if(strstr(URL, "#redir")) {
+ /* Enable follow-location */
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+ }
+
+ ret = curl_easy_perform(curl);
+ if(ret) {
+ fprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n",
+ __FILE__, __LINE__, ret, curl_easy_strerror(ret));
+ goto test_cleanup;
+ }
+ }
+
+test_cleanup:
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ return ret;
+}
+
diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl
index 0a4ff3dcc..48b5bbbf2 100755
--- a/tests/libtest/mk-lib1521.pl
+++ b/tests/libtest/mk-lib1521.pl
@@ -136,6 +136,7 @@ static curl_xferinfo_callback xferinfocb;
static curl_hstsread_callback hstsreadcb;
static curl_hstswrite_callback hstswritecb;
static curl_resolver_start_callback resolver_start_cb;
+static curl_prereq_callback prereqcb;
int test(char *URL)
{