Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a3b7cc2

Browse files
AyeshGirgias
andauthored
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>
1 parent 555b603 commit a3b7cc2

File tree

6 files changed

+289
-2
lines changed

6 files changed

+289
-2
lines changed

‎ext/curl/curl.stub.php‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3497,6 +3497,21 @@
34973497
* @cvalue CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256
34983498
*/
34993499
const CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 = UNKNOWN;
3500+
/**
3501+
* @var int
3502+
* @cvalue CURLOPT_PREREQFUNCTION
3503+
*/
3504+
const CURLOPT_PREREQFUNCTION = UNKNOWN;
3505+
/**
3506+
* @var int
3507+
* @cvalue CURL_PREREQFUNC_OK
3508+
*/
3509+
const CURL_PREREQFUNC_OK = UNKNOWN;
3510+
/**
3511+
* @var int
3512+
* @cvalue CURL_PREREQFUNC_ABORT
3513+
*/
3514+
const CURL_PREREQFUNC_ABORT = UNKNOWN;
35003515
#endif
35013516

35023517
#if LIBCURL_VERSION_NUM >= 0x075100 /* Available since 7.81.0 */

‎ext/curl/curl_arginfo.h‎

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ext/curl/curl_private.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ typedef struct {
6868
zend_fcall_info_cache progress;
6969
zend_fcall_info_cache xferinfo;
7070
zend_fcall_info_cache fnmatch;
71+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
72+
zend_fcall_info_cache prereq;
73+
#endif
7174
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
7275
zend_fcall_info_cache sshhostkey;
7376
#endif

‎ext/curl/interface.c‎

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
504504
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);
505505
}
506506

507+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
508+
if (ZEND_FCC_INITIALIZED(curl->handlers.prereq)) {
509+
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.prereq);
510+
}
511+
#endif
507512
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
508513
if (ZEND_FCC_INITIALIZED(curl->handlers.sshhostkey)) {
509514
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,
709714
}
710715
/* }}} */
711716

717+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
718+
static int curl_prereqfunction(void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port)
719+
{
720+
php_curl *ch = (php_curl *)clientp;
721+
int rval = CURL_PREREQFUNC_OK;
722+
723+
// when CURLOPT_PREREQFUNCTION is set to null, curl_prereqfunction still
724+
// gets called. Return CURL_PREREQFUNC_OK immediately in this case to avoid
725+
// zend_call_known_fcc() with an uninitialized FCC.
726+
if (!ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
727+
return rval;
728+
}
729+
730+
#if PHP_CURL_DEBUG
731+
fprintf(stderr, "curl_prereqfunction() called\n");
732+
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);
733+
#endif
734+
735+
zval args[5];
736+
zval retval;
737+
738+
GC_ADDREF(&ch->std);
739+
ZVAL_OBJ(&args[0], &ch->std);
740+
ZVAL_STRING(&args[1], conn_primary_ip);
741+
ZVAL_STRING(&args[2], conn_local_ip);
742+
ZVAL_LONG(&args[3], conn_primary_port);
743+
ZVAL_LONG(&args[4], conn_local_port);
744+
745+
ch->in_callback = true;
746+
zend_call_known_fcc(&ch->handlers.prereq, &retval, /* param_count */ 5, args, /* named_params */ NULL);
747+
ch->in_callback = false;
748+
749+
if (!Z_ISUNDEF(retval)) {
750+
_php_curl_verify_handlers(ch, /* reporterror */ true);
751+
if (Z_TYPE(retval) == IS_LONG) {
752+
zend_long retval_long = Z_LVAL(retval);
753+
if (retval_long == CURL_PREREQFUNC_OK || retval_long == CURL_PREREQFUNC_ABORT) {
754+
rval = retval_long;
755+
} else {
756+
zend_value_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT");
757+
}
758+
} else {
759+
zend_type_error("The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT");
760+
}
761+
}
762+
763+
zval_ptr_dtor(&args[0]);
764+
zval_ptr_dtor(&args[1]);
765+
zval_ptr_dtor(&args[2]);
766+
767+
return rval;
768+
}
769+
#endif
770+
712771
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
713772
static int curl_ssh_hostkeyfunction(void *clientp, int keytype, const char *key, size_t keylen)
714773
{
@@ -1037,6 +1096,9 @@ void init_curl_handle(php_curl *ch)
10371096
ch->handlers.progress = empty_fcall_info_cache;
10381097
ch->handlers.xferinfo = empty_fcall_info_cache;
10391098
ch->handlers.fnmatch = empty_fcall_info_cache;
1099+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
1100+
ch->handlers.prereq = empty_fcall_info_cache;
1101+
#endif
10401102
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
10411103
ch->handlers.sshhostkey = empty_fcall_info_cache;
10421104
#endif
@@ -1210,6 +1272,9 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source)
12101272
php_curl_copy_fcc_with_option(ch, CURLOPT_PROGRESSDATA, &ch->handlers.progress, &source->handlers.progress);
12111273
php_curl_copy_fcc_with_option(ch, CURLOPT_XFERINFODATA, &ch->handlers.xferinfo, &source->handlers.xferinfo);
12121274
php_curl_copy_fcc_with_option(ch, CURLOPT_FNMATCH_DATA, &ch->handlers.fnmatch, &source->handlers.fnmatch);
1275+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
1276+
php_curl_copy_fcc_with_option(ch, CURLOPT_PREREQDATA, &ch->handlers.prereq, &source->handlers.prereq);
1277+
#endif
12131278
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
12141279
php_curl_copy_fcc_with_option(ch, CURLOPT_SSH_HOSTKEYDATA, &ch->handlers.sshhostkey, &source->handlers.sshhostkey);
12151280
#endif
@@ -1570,6 +1635,9 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
15701635
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PROGRESS, handlers.progress, curl_progress);
15711636
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_XFERINFO, handlers.xferinfo, curl_xferinfo);
15721637
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_FNMATCH_, handlers.fnmatch, curl_fnmatch);
1638+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
1639+
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, curl_prereqfunction);
1640+
#endif
15731641
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
15741642
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_SSH_HOSTKEY, handlers.sshhostkey, curl_ssh_hostkeyfunction);
15751643
#endif
@@ -2736,6 +2804,11 @@ static void curl_free_obj(zend_object *object)
27362804
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
27372805
zend_fcc_dtor(&ch->handlers.fnmatch);
27382806
}
2807+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
2808+
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
2809+
zend_fcc_dtor(&ch->handlers.prereq);
2810+
}
2811+
#endif
27392812
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
27402813
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
27412814
zend_fcc_dtor(&ch->handlers.sshhostkey);
@@ -2814,7 +2887,11 @@ static void _php_curl_reset_handlers(php_curl *ch)
28142887
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
28152888
zend_fcc_dtor(&ch->handlers.fnmatch);
28162889
}
2817-
2890+
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
2891+
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
2892+
zend_fcc_dtor(&ch->handlers.prereq);
2893+
}
2894+
#endif
28182895
#if LIBCURL_VERSION_NUM >= 0x075400 /* Available since 7.84.0 */
28192896
if (ZEND_FCC_INITIALIZED(ch->handlers.sshhostkey)) {
28202897
zend_fcc_dtor(&ch->handlers.sshhostkey);

‎ext/curl/sync-constants.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
const IGNORED_CURL_CONSTANTS = [
1818
'CURLOPT_PROGRESSDATA',
1919
'CURLOPT_XFERINFODATA',
20+
'CURLOPT_PREREQDATA',
2021
];
2122

2223
const IGNORED_PHP_CONSTANTS = [
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
--TEST--
2+
Curl option CURLOPT_PREREQFUNCTION
3+
--EXTENSIONS--
4+
curl
5+
filter
6+
--SKIPIF--
7+
<?php
8+
$curl_version = curl_version();
9+
if ($curl_version['version_number'] < 0x075000) die("skip: test works only with curl >= 7.80.0");
10+
?>
11+
--FILE--
12+
<?php
13+
include 'server.inc';
14+
15+
$host = curl_cli_server_start();
16+
$port = (int) (explode(':', $host))[1];
17+
18+
$ch = curl_init();
19+
curl_setopt($ch, CURLOPT_URL, "{$host}/get.inc?test=file");
20+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
21+
22+
$result = curl_exec($ch);
23+
24+
var_dump(CURLOPT_PREREQFUNCTION);
25+
var_dump(CURL_PREREQFUNC_OK);
26+
var_dump(CURL_PREREQFUNC_ABORT);
27+
28+
$returnValue = CURL_PREREQFUNC_ABORT;
29+
30+
echo "\nTesting with CURL_PREREQFUNC_ABORT\n";
31+
$callback = function() use ($port, &$returnValue) {
32+
var_dump('callback');
33+
var_dump(func_num_args());
34+
$args = func_get_args();
35+
var_dump(get_class($args[0]));
36+
var_dump(filter_var($args[1], FILTER_VALIDATE_IP) !== false);
37+
var_dump(filter_var($args[2], FILTER_VALIDATE_IP) !== false);
38+
var_dump($port === $args[3]);
39+
var_dump(is_int($args[4]));
40+
41+
return $returnValue;
42+
};
43+
44+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, $callback);
45+
46+
$result = curl_exec($ch);
47+
48+
var_dump($result);
49+
var_dump(curl_error($ch));
50+
var_dump(curl_errno($ch));
51+
52+
$returnValue = CURL_PREREQFUNC_OK;
53+
54+
echo "\nTesting with CURL_PREREQFUNC_OK\n";
55+
$result = curl_exec($ch);
56+
57+
var_dump($result);
58+
var_dump(curl_error($ch));
59+
var_dump(curl_errno($ch));
60+
61+
echo "\nTesting with curl_copy_handle\n";
62+
$ch2 = curl_copy_handle($ch);
63+
$result = curl_exec($ch2);
64+
var_dump($result);
65+
var_dump(curl_error($ch2));
66+
var_dump(curl_errno($ch2));
67+
68+
echo "\nTesting with no return type\n";
69+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
70+
// returns nothing
71+
});
72+
try {
73+
curl_exec($ch);
74+
} catch (\TypeError $e) {
75+
echo $e->getMessage() . \PHP_EOL;
76+
}
77+
78+
echo "\nTesting with invalid type\n";
79+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
80+
return 'this should be an integer';
81+
});
82+
try {
83+
curl_exec($ch);
84+
} catch (\TypeError $e) {
85+
echo $e->getMessage() . \PHP_EOL;
86+
}
87+
88+
echo "\nTesting with invalid value\n";
89+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, function() use ($port) {
90+
return 42;
91+
});
92+
try {
93+
curl_exec($ch);
94+
} catch (\ValueError $e) {
95+
echo $e->getMessage() . \PHP_EOL;
96+
}
97+
98+
echo "\nTesting with invalid option value\n";
99+
try {
100+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, 42);
101+
} catch (\TypeError $e) {
102+
echo $e->getMessage() . \PHP_EOL;
103+
}
104+
105+
echo "\nTesting with invalid option callback\n";
106+
try {
107+
curl_setopt($ch, CURLOPT_PREREQFUNCTION, 'function_does_not_exist');
108+
} catch (\TypeError $e) {
109+
echo $e->getMessage() . \PHP_EOL;
110+
}
111+
112+
echo "\nTesting with null as the callback\n";
113+
var_dump(curl_setopt($ch, CURLOPT_PREREQFUNCTION, null));
114+
var_dump(curl_exec($ch));
115+
var_dump(curl_error($ch));
116+
var_dump(curl_errno($ch));
117+
118+
echo "\nDone";
119+
?>
120+
--EXPECT--
121+
int(20312)
122+
int(0)
123+
int(1)
124+
125+
Testing with CURL_PREREQFUNC_ABORT
126+
string(8) "callback"
127+
int(5)
128+
string(10) "CurlHandle"
129+
bool(true)
130+
bool(true)
131+
bool(true)
132+
bool(true)
133+
bool(false)
134+
string(41) "operation aborted by pre-request callback"
135+
int(42)
136+
137+
Testing with CURL_PREREQFUNC_OK
138+
string(8) "callback"
139+
int(5)
140+
string(10) "CurlHandle"
141+
bool(true)
142+
bool(true)
143+
bool(true)
144+
bool(true)
145+
string(0) ""
146+
string(0) ""
147+
int(0)
148+
149+
Testing with curl_copy_handle
150+
string(8) "callback"
151+
int(5)
152+
string(10) "CurlHandle"
153+
bool(true)
154+
bool(true)
155+
bool(true)
156+
bool(true)
157+
string(0) ""
158+
string(0) ""
159+
int(0)
160+
161+
Testing with no return type
162+
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
163+
164+
Testing with invalid type
165+
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
166+
167+
Testing with invalid value
168+
The CURLOPT_PREREQFUNCTION callback must return either CURL_PREREQFUNC_OK or CURL_PREREQFUNC_ABORT
169+
170+
Testing with invalid option value
171+
curl_setopt(): Argument #3 ($value) must be a valid callback for option CURLOPT_PREREQFUNCTION, no array or string given
172+
173+
Testing with invalid option callback
174+
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
175+
176+
Testing with null as the callback
177+
bool(true)
178+
string(0) ""
179+
string(0) ""
180+
int(0)
181+
182+
Done

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /