mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
ext/curl: Add CURLOPT_PREREQFUNCTION (#13255)
Curl >= 7.80.0 supports declaring a function for `CURLOPT_PREREQFUNCTION` option that gets called after Curl establishes a connection (including the TLS handshake for HTTPS connections), but before the actual request is made. The callable must return either `CURL_PREREQFUNC_OK` or `CURL_PREREQFUNC_ABORT` to allow or abort the request. This adds support for it to PHP with required ifdef. - libc: https://curl.se/libcurl/c/CURLOPT_PREREQFUNCTION.html Co-authored-by: Gina Peter Bnayard <girgias@php.net>
This commit is contained in:
committed by
GitHub
parent
555b603d23
commit
a3b7cc2217
@@ -3497,6 +3497,21 @@ const CURLOPT_MAXLIFETIME_CONN = UNKNOWN;
|
||||
* @cvalue CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256
|
||||
*/
|
||||
const CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 = UNKNOWN;
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLOPT_PREREQFUNCTION
|
||||
*/
|
||||
const CURLOPT_PREREQFUNCTION = UNKNOWN;
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURL_PREREQFUNC_OK
|
||||
*/
|
||||
const CURL_PREREQFUNC_OK = UNKNOWN;
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURL_PREREQFUNC_ABORT
|
||||
*/
|
||||
const CURL_PREREQFUNC_ABORT = UNKNOWN;
|
||||
#endif
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */
|
||||
|
||||
11
ext/curl/curl_arginfo.h
generated
11
ext/curl/curl_arginfo.h
generated
@@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: ddfcdd8a0bf0ee6c338ec1689c6de5d7fd87303d */
|
||||
* Stub hash: 3a5bd4e561f08f0dbd26383132a771acc8192fff */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0)
|
||||
ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
|
||||
@@ -1061,6 +1061,15 @@ static void register_curl_symbols(int module_number)
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CONST_PERSISTENT);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CONST_PERSISTENT);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
REGISTER_LONG_CONSTANT("CURL_PREREQFUNC_OK", CURL_PREREQFUNC_OK, CONST_PERSISTENT);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
REGISTER_LONG_CONSTANT("CURL_PREREQFUNC_ABORT", CURL_PREREQFUNC_ABORT, CONST_PERSISTENT);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CONST_PERSISTENT);
|
||||
#endif
|
||||
|
||||
@@ -68,6 +68,9 @@ typedef struct {
|
||||
zend_fcall_info_cache progress;
|
||||
zend_fcall_info_cache xferinfo;
|
||||
zend_fcall_info_cache fnmatch;
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
zend_fcall_info_cache prereq;
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
zend_fcall_info_cache sshhostkey;
|
||||
#endif
|
||||
|
||||
@@ -504,6 +504,11 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
|
||||
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);
|
||||
}
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) {
|
||||
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq);
|
||||
}
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) {
|
||||
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.sshhostkey);
|
||||
@@ -709,6 +714,60 @@ static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
static int curl_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port)
|
||||
{
|
||||
php_curl *ch = (php_curl *)clientp;
|
||||
int rval = CURL_PREREQFUNC_OK;
|
||||
|
||||
// when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still
|
||||
// gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid
|
||||
// zend_call_known_fcc() with an uninitialized FCC.
|
||||
if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
|
||||
return rval;
|
||||
}
|
||||
|
||||
#if PHP_CURL_DEBUG
|
||||
fprintf(stderr, "curl_prereqfunction() called\n");
|
||||
fprintf(stderr, "conn_primary_ip = %s, conn_local_ip = %s, conn_primary_port = %d, conn_local_port = %d\n", conn_primary_ip, conn_local_ip, conn_primary_port, conn_local_port);
|
||||
#endif
|
||||
|
||||
zval args[5];
|
||||
zval retval;
|
||||
|
||||
GC_ADDREF(&ch->std);
|
||||
ZVAL_OBJ(&args[0], &ch->std);
|
||||
ZVAL_STRING(&args[1], conn_primary_ip);
|
||||
ZVAL_STRING(&args[2], conn_local_ip);
|
||||
ZVAL_LONG(&args[3], conn_primary_port);
|
||||
ZVAL_LONG(&args[4], conn_local_port);
|
||||
|
||||
ch->in_callback = true;
|
||||
zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL);
|
||||
ch->in_callback = false;
|
||||
|
||||
if (!Z_ISUNDEF(retval)) {
|
||||
_php_curl_verify_handlers(ch, /* reporterror */ true);
|
||||
if (Z_TYPE(retval) == IS_LONG) {
|
||||
zend_long retval_long = Z_LVAL(retval);
|
||||
if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) {
|
||||
rval = retval_long;
|
||||
} else {
|
||||
zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT");
|
||||
}
|
||||
} else {
|
||||
zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT");
|
||||
}
|
||||
}
|
||||
|
||||
zval_ptr_dtor(&args[0]);
|
||||
zval_ptr_dtor(&args[1]);
|
||||
zval_ptr_dtor(&args[2]);
|
||||
|
||||
return rval;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen)
|
||||
{
|
||||
@@ -1037,6 +1096,9 @@ void init_curl_handle(php_curl *ch)
|
||||
ch->handlers.progress = empty_fcall_info_cache;
|
||||
ch->handlers.xferinfo = empty_fcall_info_cache;
|
||||
ch->handlers.fnmatch = empty_fcall_info_cache;
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
ch->handlers.prereq = empty_fcall_info_cache;
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
ch->handlers.sshhostkey = empty_fcall_info_cache;
|
||||
#endif
|
||||
@@ -1210,6 +1272,9 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo);
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch);
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey);
|
||||
#endif
|
||||
@@ -1570,6 +1635,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress);
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo);
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch);
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, curl_prereqfunction);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction);
|
||||
#endif
|
||||
@@ -2736,6 +2804,11 @@ static void curl_free_obj(zend_object *object)
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
|
||||
zend_fcc_dtor(&ch->handlers.fnmatch);
|
||||
}
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
|
||||
zend_fcc_dtor(&ch->handlers.prereq);
|
||||
}
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
|
||||
zend_fcc_dtor(&ch->handlers.sshhostkey);
|
||||
@@ -2814,7 +2887,11 @@ static void _php_curl_reset_handlers(php_curl *ch)
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
|
||||
zend_fcc_dtor(&ch->handlers.fnmatch);
|
||||
}
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
|
||||
zend_fcc_dtor(&ch->handlers.prereq);
|
||||
}
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
|
||||
zend_fcc_dtor(&ch->handlers.sshhostkey);
|
||||
|
||||
@@ -17,6 +17,7 @@ const MIN_SUPPORTED_CURL_VERSION = '7.61.0';
|
||||
const IGNORED_CURL_CONSTANTS = [
|
||||
'CURLOPT_PROGRESSDATA',
|
||||
'CURLOPT_XFERINFODATA',
|
||||
'CURLOPT_PREREQDATA',
|
||||
];
|
||||
|
||||
const IGNORED_PHP_CONSTANTS = [
|
||||
|
||||
182
ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt
Normal file
182
ext/curl/tests/curl_setopt_CURLOPT_PREREQFUNCTION.phpt
Normal file
@@ -0,0 +1,182 @@
|
||||
--TEST--
|
||||
Curl option CURLOPT_PREREQFUNCTION
|
||||
--EXTENSIONS--
|
||||
curl
|
||||
filter
|
||||
--SKIPIF--
|
||||
<?php
|
||||
$curl_version = curl_version();
|
||||
if ($curl_version['version_number'] < 0x075000) die("skip: test works only with curl >= 7.80.0");
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
include 'server.inc';
|
||||
|
||||
$host = curl_cli_server_start();
|
||||
$port = (int) (explode(':', $host))[1];
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "{$host}/get.inc?test=file");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
|
||||
var_dump(CURLOPT_PREREQFUNCTION);
|
||||
var_dump(CURL_PREREQFUNC_OK);
|
||||
var_dump(CURL_PREREQFUNC_ABORT);
|
||||
|
||||
$returnValue = CURL_PREREQFUNC_ABORT;
|
||||
|
||||
echo "\nTesting with CURL_PREREQFUNC_ABORT\n";
|
||||
$callback = function() use ($port, &$returnValue) {
|
||||
var_dump('callback');
|
||||
var_dump(func_num_args());
|
||||
$args = func_get_args();
|
||||
var_dump(get_class($args[0]));
|
||||
var_dump(filter_var($args[1], FILTER_VALIDATE_IP) !== false);
|
||||
var_dump(filter_var($args[2], FILTER_VALIDATE_IP) !== false);
|
||||
var_dump($port === $args[3]);
|
||||
var_dump(is_int($args[4]));
|
||||
|
||||
return $returnValue;
|
||||
};
|
||||
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, $callback);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
|
||||
var_dump($result);
|
||||
var_dump(curl_error($ch));
|
||||
var_dump(curl_errno($ch));
|
||||
|
||||
$returnValue = CURL_PREREQFUNC_OK;
|
||||
|
||||
echo "\nTesting with CURL_PREREQFUNC_OK\n";
|
||||
$result = curl_exec($ch);
|
||||
|
||||
var_dump($result);
|
||||
var_dump(curl_error($ch));
|
||||
var_dump(curl_errno($ch));
|
||||
|
||||
echo "\nTesting with curl_copy_handle\n";
|
||||
$ch2 = curl_copy_handle($ch);
|
||||
$result = curl_exec($ch2);
|
||||
var_dump($result);
|
||||
var_dump(curl_error($ch2));
|
||||
var_dump(curl_errno($ch2));
|
||||
|
||||
echo "\nTesting with no return type\n";
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
|
||||
// returns nothing
|
||||
});
|
||||
try {
|
||||
curl_exec($ch);
|
||||
} catch (\TypeError $e) {
|
||||
echo $e->getMessage() . \PHP_EOL;
|
||||
}
|
||||
|
||||
echo "\nTesting with invalid type\n";
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
|
||||
return 'this should be an integer';
|
||||
});
|
||||
try {
|
||||
curl_exec($ch);
|
||||
} catch (\TypeError $e) {
|
||||
echo $e->getMessage() . \PHP_EOL;
|
||||
}
|
||||
|
||||
echo "\nTesting with invalid value\n";
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
|
||||
return 42;
|
||||
});
|
||||
try {
|
||||
curl_exec($ch);
|
||||
} catch (\ValueError $e) {
|
||||
echo $e->getMessage() . \PHP_EOL;
|
||||
}
|
||||
|
||||
echo "\nTesting with invalid option value\n";
|
||||
try {
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, 42);
|
||||
} catch (\TypeError $e) {
|
||||
echo $e->getMessage() . \PHP_EOL;
|
||||
}
|
||||
|
||||
echo "\nTesting with invalid option callback\n";
|
||||
try {
|
||||
curl_setopt($ch, CURLOPT_PREREQFUNCTION, 'function_does_not_exist');
|
||||
} catch (\TypeError $e) {
|
||||
echo $e->getMessage() . \PHP_EOL;
|
||||
}
|
||||
|
||||
echo "\nTesting with null as the callback\n";
|
||||
var_dump(curl_setopt($ch, CURLOPT_PREREQFUNCTION, null));
|
||||
var_dump(curl_exec($ch));
|
||||
var_dump(curl_error($ch));
|
||||
var_dump(curl_errno($ch));
|
||||
|
||||
echo "\nDone";
|
||||
?>
|
||||
--EXPECT--
|
||||
int(20312)
|
||||
int(0)
|
||||
int(1)
|
||||
|
||||
Testing with CURL_PREREQFUNC_ABORT
|
||||
string(8) "callback"
|
||||
int(5)
|
||||
string(10) "CurlHandle"
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(false)
|
||||
string(41) "operation aborted by pre-request callback"
|
||||
int(42)
|
||||
|
||||
Testing with CURL_PREREQFUNC_OK
|
||||
string(8) "callback"
|
||||
int(5)
|
||||
string(10) "CurlHandle"
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(0) ""
|
||||
string(0) ""
|
||||
int(0)
|
||||
|
||||
Testing with curl_copy_handle
|
||||
string(8) "callback"
|
||||
int(5)
|
||||
string(10) "CurlHandle"
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(0) ""
|
||||
string(0) ""
|
||||
int(0)
|
||||
|
||||
Testing with no return type
|
||||
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
|
||||
|
||||
Testing with invalid type
|
||||
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
|
||||
|
||||
Testing with invalid value
|
||||
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
|
||||
|
||||
Testing with invalid option value
|
||||
curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_PREREQFUNCTION, no array or string given
|
||||
|
||||
Testing with invalid option callback
|
||||
curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_PREREQFUNCTION, function "function_does_not_exist" not found or invalid function name
|
||||
|
||||
Testing with null as the callback
|
||||
bool(true)
|
||||
string(0) ""
|
||||
string(0) ""
|
||||
int(0)
|
||||
|
||||
Done
|
||||
Reference in New Issue
Block a user