mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
ext/curl: Add CURLOPT_DEBUGFUNCTION
option (GH-15674)
This adds support for `CURLOPT_DEBUGFUNCTION`[^1] Curl option to set a
custom callback that gets called with debug information during the
lifetime of a Curl request.
The callback gets called with the `CurlHandle` object, an integer
containing the type of the debug message, and a string containing the
debug message. The callback may get called multiple times with the
same message type during a request.
PHP already uses `CURLOPT_DEBUGFUNCTION` functionality to internally
to expose a Curl option named `CURLINFO_HEADER_OUT`.
However,`CURLINFO_HEADER_OUT` is not a "real" Curl option supported
by libcurl. Back in 2006, `CURLINFO_HEADER_OUT` was added[^2] as
a Curl option by using the debug-callback feature. Git history does
not run that back to show why `CURLINFO_HEADER_OUT` was added as a
Curl option, and why the other debug types (such as
`CURLINFO_HEADER_IN` were not added as Curl options, but this seems
to be a historical artifact when we added features without trying
to be close to libcurl options.
This approach has a few issues:
1. `CURLINFO_HEADER_OUT` is not an actual Curl option supported by
upstream libcurl.
2. All of the Curl options have `CURLOPT_` prefix, and `CURLINFO_HEADER_OUT`
is the only Curl "option" that uses the `CURLINFO` prefix. This exception
is, however, noted[^3] in docs.
3. When `CURLINFO_HEADER_OUT` is set, the `CURLOPT_VERBOSE` is also implicitly
set. This was reported[^4] to bugs.php.net, but the bug is marked as wontfix.
This commit adds support for `CURLOPT_DEBUGFUNCTION`. It extends the existing
`curl_debug` callback to store the header-in information if it encounters
a debug message with `CURLINFO_HEADER_OUT`. In all cases, if a callable
is set, it gets called.
`CURLOPT_DEBUGFUNCTION` intends to replace `CURLINFO_HEADER_OUT` Curl
option as a versatile alternative that can also be used to extract
other debug information such as SSL data, text information messages,
incoming headers, as well as headers sent out (which `CURLINFO_HEADER_OUT`
makes available).
The callables are allowed to throw exceptions, but the return values are
ignored.
`CURLOPT_DEBUGFUNCTION` requires `CURLOPT_VERBOSE` enabled, and setting
`CURLOPT_DEBUGFUNCTION` does _not_ implicitly enable `CURLOPT_VERBOSE`.
If the `CURLOPT_DEBUGFUNCTION` option is set, setting `CURLINFO_HEADER_OUT`
throws a `ValueError` exception. Setting `CURLOPT_DEBUGFUNCTION` _after_
enabling `CURLINFO_HEADER_OUT` is allowed. Technically, it is possible
for both functionality (calling user-provided callback _and_ storing
header-out data) is possible, setting `CURLINFO_HEADER_OUT` is not
allowed to encourage the use of `CURLOPT_DEBUGFUNCTION` function.
This commit also adds the rest of the `CURLINFO_` constants used as
the `type` integer value in `CURLOPT_DEBUGFUNCTION` callback.
---
[^1]: [cur.se - CURLOPT_DEBUGFUNCTION](https://curl.se/libcurl/c/CURLOPT_DEBUGFUNCTION.html)
[^2]: [`5f25d80`](5f25d80d10
)
[^3]: [curl_setopt doc mentioning `CURLINFO_` prefix is intentional](https://www.php.net/manual/en/function.curl-setopt.php#:~:text=prefix%20is%20intentional)
[^4]: [bugs.php.net - `CURLOPT_VERBOSE` does not work with `CURLINFO_HEADER_OUT`](https://bugs.php.net/bug.php?id=65348)
This commit is contained in:
parent
ea297654f4
commit
ba748e7bb5
8 changed files with 362 additions and 10 deletions
3
NEWS
3
NEWS
|
@ -7,6 +7,9 @@ PHP NEWS
|
|||
. ext/bcmath: Check for scale overflow. (SakiTakamachi)
|
||||
. [RFC] ext/bcmath: Added bcdivmod. (SakiTakamachi)
|
||||
|
||||
- Curl:
|
||||
. Added CURLOPT_DEBUGFUNCTION as a Curl option. (Ayesh Karunaratne)
|
||||
|
||||
- Debugging:
|
||||
. Fixed bug GH-15923 (GDB: Python Exception <class 'TypeError'>:
|
||||
exceptions must derive from BaseException). (nielsdos)
|
||||
|
|
14
UPGRADING
14
UPGRADING
|
@ -302,6 +302,13 @@ PHP 8.4 UPGRADE NOTES
|
|||
to allow or abort the request.
|
||||
. Added CURLOPT_SERVER_RESPONSE_TIMEOUT, which was formerly known as
|
||||
CURLOPT_FTP_RESPONSE_TIMEOUT. Both constants hold the same value.
|
||||
. Added CURLOPT_DEBUGFUNCTION support. This Curl option accepts a callable
|
||||
that gets called during the request lifetime with the CurlHandle object,
|
||||
an integer containing the debug message type, and a string containing the
|
||||
debug message. The debug message type is one of CURLINFO_TEXT, CURLINFO_HEADER_IN,
|
||||
CURLINFO_HEADER_OUT, CURLINFO_DATA_IN, CURLINFO_DATA_OUT, CURLINFO_SSL_DATA_OUT,
|
||||
CURLINFO_SSL_DATA_IN constants. Once this option is set, CURLINFO_HEADER_OUT
|
||||
must not be set because it uses the same libcurl functionality.
|
||||
|
||||
- Date:
|
||||
. Added static methods
|
||||
|
@ -1041,6 +1048,13 @@ PHP 8.4 UPGRADE NOTES
|
|||
. CURL_PREREQFUNC_OK.
|
||||
. CURL_PREREQFUNC_ABORT.
|
||||
. CURLOPT_SERVER_RESPONSE_TIMEOUT.
|
||||
. CURLOPT_DEBUGFUNCTION.
|
||||
. CURLINFO_TEXT.
|
||||
. CURLINFO_HEADER_IN.
|
||||
. CURLINFO_DATA_IN.
|
||||
. CURLINFO_DATA_OUT.
|
||||
. CURLINFO_SSL_DATA_OUT.
|
||||
. CURLINFO_SSL_DATA_IN.
|
||||
|
||||
- Intl:
|
||||
. The IntlDateFormatter class exposes now the new PATTERN constant
|
||||
|
|
|
@ -486,6 +486,48 @@ const CURLOPT_WRITEHEADER = UNKNOWN;
|
|||
*/
|
||||
const CURLOPT_XFERINFOFUNCTION = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLOPT_DEBUGFUNCTION
|
||||
*/
|
||||
const CURLOPT_DEBUGFUNCTION = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_TEXT
|
||||
*/
|
||||
const CURLINFO_TEXT = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_HEADER_IN
|
||||
*/
|
||||
const CURLINFO_HEADER_IN = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_DATA_IN
|
||||
*/
|
||||
const CURLINFO_DATA_IN = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_DATA_OUT
|
||||
*/
|
||||
const CURLINFO_DATA_OUT = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_SSL_DATA_OUT
|
||||
*/
|
||||
const CURLINFO_SSL_DATA_OUT = UNKNOWN;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @cvalue CURLINFO_SSL_DATA_IN
|
||||
*/
|
||||
const CURLINFO_SSL_DATA_IN = UNKNOWN;
|
||||
|
||||
/* */
|
||||
/**
|
||||
* @var int
|
||||
|
|
9
ext/curl/curl_arginfo.h
generated
9
ext/curl/curl_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
|||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 5aa5f230880f8373ef8ec378f7e600247332136e */
|
||||
* Stub hash: c5e16a7da3f25d061813235a262501e0d8747ccb */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_curl_close, 0, 1, IS_VOID, 0)
|
||||
ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0)
|
||||
|
@ -312,6 +312,13 @@ static void register_curl_symbols(int module_number)
|
|||
REGISTER_LONG_CONSTANT("CURLOPT_WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_WRITEHEADER", CURLOPT_WRITEHEADER, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLOPT_DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_TEXT", CURLINFO_TEXT, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_HEADER_IN", CURLINFO_HEADER_IN, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_DATA_IN", CURLINFO_DATA_IN, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_DATA_OUT", CURLINFO_DATA_OUT, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_OUT", CURLINFO_SSL_DATA_OUT, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLINFO_SSL_DATA_IN", CURLINFO_SSL_DATA_IN, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLE_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLE_BAD_CALLING_ORDER", CURLE_BAD_CALLING_ORDER, CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("CURLE_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING, CONST_PERSISTENT);
|
||||
|
|
|
@ -68,6 +68,7 @@ typedef struct {
|
|||
zend_fcall_info_cache progress;
|
||||
zend_fcall_info_cache xferinfo;
|
||||
zend_fcall_info_cache fnmatch;
|
||||
zend_fcall_info_cache debug;
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
zend_fcall_info_cache prereq;
|
||||
#endif
|
||||
|
|
|
@ -504,6 +504,10 @@ static HashTable *curl_get_gc(zend_object *object, zval **table, int *n)
|
|||
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.fnmatch);
|
||||
}
|
||||
|
||||
if (ZEND_FCC_INITIALIZED(curl->handlers.debug)) {
|
||||
zend_get_gc_buffer_add_fcc(gc_buffer, &curl->handlers.debug);
|
||||
}
|
||||
|
||||
#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);
|
||||
|
@ -915,18 +919,46 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
static int curl_debug(CURL *cp, curl_infotype type, char *buf, size_t buf_len, void *ctx) /* {{{ */
|
||||
static int curl_debug(CURL *handle, curl_infotype type, char *data, size_t size, void *clientp) /* {{{ */
|
||||
{
|
||||
php_curl *ch = (php_curl *)ctx;
|
||||
php_curl *ch = (php_curl *)clientp;
|
||||
|
||||
if (type == CURLINFO_HEADER_OUT) {
|
||||
if (ch->header.str) {
|
||||
zend_string_release_ex(ch->header.str, 0);
|
||||
}
|
||||
ch->header.str = zend_string_init(buf, buf_len, 0);
|
||||
}
|
||||
#if PHP_CURL_DEBUG
|
||||
fprintf(stderr, "curl_debug() called\n");
|
||||
fprintf(stderr, "type = %d, data = %s\n", type, data);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
// Implicitly store the headers for compatibility with CURLINFO_HEADER_OUT
|
||||
// used as a Curl option. Previously, setting CURLINFO_HEADER_OUT set curl_debug
|
||||
// as the CURLOPT_DEBUGFUNCTION and stored the debug data when type is set to
|
||||
// CURLINFO_HEADER_OUT. For backward compatibility, we now store the headers
|
||||
// but also call the user-callback function if available.
|
||||
if (type == CURLINFO_HEADER_OUT) {
|
||||
if (ch->header.str) {
|
||||
zend_string_release_ex(ch->header.str, 0);
|
||||
}
|
||||
ch->header.str = zend_string_init(data, size, 0);
|
||||
}
|
||||
|
||||
if (!ZEND_FCC_INITIALIZED(ch->handlers.debug)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
zval args[3];
|
||||
|
||||
GC_ADDREF(&ch->std);
|
||||
ZVAL_OBJ(&args[0], &ch->std);
|
||||
ZVAL_LONG(&args[1], type);
|
||||
ZVAL_STRINGL(&args[2], data, size);
|
||||
|
||||
ch->in_callback = true;
|
||||
zend_call_known_fcc(&ch->handlers.debug, NULL, /* param_count */ 3, args, /* named_params */ NULL);
|
||||
ch->in_callback = false;
|
||||
|
||||
zval_ptr_dtor(&args[0]);
|
||||
zval_ptr_dtor(&args[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -1096,6 +1128,7 @@ 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;
|
||||
ch->handlers.debug = empty_fcall_info_cache;
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
ch->handlers.prereq = empty_fcall_info_cache;
|
||||
#endif
|
||||
|
@ -1269,6 +1302,7 @@ 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);
|
||||
php_curl_copy_fcc_with_option(ch, CURLOPT_DEBUGDATA, &ch->handlers.debug, &source->handlers.debug);
|
||||
#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
|
||||
|
@ -1632,6 +1666,8 @@ 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);
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_DEBUG, handlers.debug, curl_debug);
|
||||
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
HANDLE_CURL_OPTION_CALLABLE(ch, CURLOPT_PREREQ, handlers.prereq, curl_prereqfunction);
|
||||
#endif
|
||||
|
@ -2216,6 +2252,11 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue
|
|||
}
|
||||
|
||||
case CURLINFO_HEADER_OUT:
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {
|
||||
zend_value_error("CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (zend_is_true(zvalue)) {
|
||||
curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug);
|
||||
curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *)ch);
|
||||
|
@ -2795,6 +2836,9 @@ static void curl_free_obj(zend_object *object)
|
|||
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
|
||||
zend_fcc_dtor(&ch->handlers.fnmatch);
|
||||
}
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {
|
||||
zend_fcc_dtor(&ch->handlers.debug);
|
||||
}
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
|
||||
zend_fcc_dtor(&ch->handlers.prereq);
|
||||
|
@ -2878,6 +2922,10 @@ static void _php_curl_reset_handlers(php_curl *ch)
|
|||
if (ZEND_FCC_INITIALIZED(ch->handlers.fnmatch)) {
|
||||
zend_fcc_dtor(&ch->handlers.fnmatch);
|
||||
}
|
||||
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.debug)) {
|
||||
zend_fcc_dtor(&ch->handlers.debug);
|
||||
}
|
||||
#if LIBCURL_VERSION_NUM >= 0x075000 /* Available since 7.80.0 */
|
||||
if (ZEND_FCC_INITIALIZED(ch->handlers.prereq)) {
|
||||
zend_fcc_dtor(&ch->handlers.prereq);
|
||||
|
|
|
@ -18,6 +18,7 @@ const IGNORED_CURL_CONSTANTS = [
|
|||
'CURLOPT_PROGRESSDATA',
|
||||
'CURLOPT_XFERINFODATA',
|
||||
'CURLOPT_PREREQDATA',
|
||||
'CURLOPT_DEBUGDATA',
|
||||
];
|
||||
|
||||
const IGNORED_PHP_CONSTANTS = [
|
||||
|
|
236
ext/curl/tests/curl_setopt_CURLOPT_DEBUGFUNCTION.phpt
Normal file
236
ext/curl/tests/curl_setopt_CURLOPT_DEBUGFUNCTION.phpt
Normal file
|
@ -0,0 +1,236 @@
|
|||
--TEST--
|
||||
Curl option CURLOPT_DEBUGFUNCTION
|
||||
--EXTENSIONS--
|
||||
curl
|
||||
--FILE--
|
||||
<?php
|
||||
include 'server.inc';
|
||||
|
||||
$allowedTypes = [
|
||||
CURLINFO_TEXT,
|
||||
CURLINFO_HEADER_IN,
|
||||
CURLINFO_HEADER_OUT,
|
||||
CURLINFO_DATA_IN,
|
||||
CURLINFO_DATA_OUT,
|
||||
CURLINFO_SSL_DATA_OUT,
|
||||
CURLINFO_SSL_DATA_IN,
|
||||
];
|
||||
|
||||
var_dump(CURLOPT_DEBUGFUNCTION);
|
||||
|
||||
$host = curl_cli_server_start();
|
||||
|
||||
echo "\n===Testing with regular CURLOPT_VERBOSE with verbose=false===\n";
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "{$host}/get.inc?test=file");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, 0);
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, function() {
|
||||
echo 'This should not be called';
|
||||
}));
|
||||
curl_exec($ch);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, 1);
|
||||
|
||||
echo "\n===Testing with regular CURLOPT_VERBOSE on stderr===\n";
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "{$host}/get.inc?test=file");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, 1);
|
||||
$stderr = fopen('php://temp', 'wb+');
|
||||
curl_setopt($ch, CURLOPT_STDERR, $stderr);
|
||||
$curlVerboseOutput = curl_exec($ch);
|
||||
rewind($stderr);
|
||||
$receivedOutput = stream_get_contents($stderr);
|
||||
fclose($stderr);
|
||||
var_dump(str_contains($receivedOutput, 'Host'));
|
||||
|
||||
echo "\n===Testing with CURLOPT_DEBUGFUNCTION happy path===\n";
|
||||
$stderr = fopen('php://temp', 'wb+');
|
||||
curl_setopt($ch, CURLOPT_STDERR, $stderr);
|
||||
|
||||
$debugOutput = [];
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, function(CurlHandle $curlHandle, int $type, string $data) use ($allowedTypes, &$debugOutput) {
|
||||
if (!in_array($type, $allowedTypes)) {
|
||||
throw new Exception('Unexpected type value: '. $type);
|
||||
}
|
||||
$debugOutput[$type][] = $data;
|
||||
}));
|
||||
|
||||
$result = curl_exec($ch);
|
||||
rewind($stderr);
|
||||
$receivedOutputWithDebugFunction = stream_get_contents($stderr);
|
||||
fclose($stderr);
|
||||
echo "Received stderr empty:\n";
|
||||
var_dump($result);
|
||||
var_dump($receivedOutputWithDebugFunction);
|
||||
|
||||
// Header-out should be an exact match
|
||||
var_dump(str_contains($receivedOutput, $debugOutput[CURLINFO_HEADER_OUT][0]));
|
||||
|
||||
// Header-in fields should match, except for the "Date" header that is dynamic.
|
||||
foreach ($debugOutput[CURLINFO_HEADER_IN] as $headerReceived) {
|
||||
if (str_starts_with($headerReceived, 'Date')) {
|
||||
continue;
|
||||
}
|
||||
if (!str_contains($receivedOutput, $headerReceived)) {
|
||||
throw new \Exception('DEBUGFUNCTION header field does not match the previous verbose debug message');
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n===Test attempting to set CURLINFO_HEADER_OUT while CURLOPT_DEBUGFUNCTION in effect throws===\n";
|
||||
$ch = curl_init();
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, null));
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, 'strlen'));
|
||||
try {
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
}
|
||||
catch (\ValueError $e) {
|
||||
var_dump($e->getMessage());
|
||||
}
|
||||
$chCopy = curl_copy_handle($ch);
|
||||
try {
|
||||
var_dump(curl_setopt($chCopy, CURLINFO_HEADER_OUT, true));
|
||||
}
|
||||
catch (\ValueError $e) {
|
||||
var_dump($e->getMessage());
|
||||
}
|
||||
var_dump(curl_setopt($chCopy, CURLOPT_DEBUGFUNCTION, null));
|
||||
var_dump(curl_setopt($chCopy, CURLINFO_HEADER_OUT, true));
|
||||
|
||||
echo "\n===Test attempting to set CURLOPT_DEBUGFUNCTION while CURLINFO_HEADER_OUT does not throw===\n";
|
||||
$ch = curl_init();
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, null));
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, 'strlen'));
|
||||
$chCopy = curl_copy_handle($ch);
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, 'strlen'));
|
||||
var_dump(curl_setopt($chCopy, CURLOPT_DEBUGFUNCTION, null));
|
||||
var_dump(curl_setopt($chCopy, CURLINFO_HEADER_OUT, 1));
|
||||
|
||||
echo "\n===Test CURLOPT_DEBUGFUNCTION=null works===\n";
|
||||
$ch = curl_init("{$host}/get.inc?test=file");
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, null));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
var_dump(curl_exec($ch));
|
||||
|
||||
echo "\n===Test CURLINFO_HEADER_OUT works while CURLOPT_DEBUGFUNCTION in effect===\n";
|
||||
$ch = curl_init("{$host}/get.inc?test=file");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
var_dump(curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
var_dump(curl_setopt($ch, CURLINFO_HEADER_OUT, true));
|
||||
var_dump(curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
var_dump(curl_setopt($ch, CURLOPT_DEBUGFUNCTION, static function() {}));
|
||||
var_dump(curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
var_dump($result = curl_exec($ch));
|
||||
var_dump(curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
|
||||
echo "\n===Test CURLOPT_DEBUGFUNCTION can throw within callback===\n";
|
||||
$ch = curl_init("{$host}/get.inc?test=file");
|
||||
curl_setopt($ch, CURLOPT_DEBUGFUNCTION, static function() {
|
||||
throw new \RuntimeException('This should get caught after verbose=true');
|
||||
});
|
||||
var_dump(curl_exec($ch));
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||
try {
|
||||
var_dump($result = curl_exec($ch));
|
||||
}
|
||||
catch (\RuntimeException $e) {
|
||||
var_dump($e->getMessage());
|
||||
}
|
||||
var_dump(curl_getinfo($ch, CURLINFO_HEADER_OUT));
|
||||
|
||||
echo "\n===Test CURLOPT_DEBUGFUNCTION on shared handles work===\n";
|
||||
$ch = curl_init("{$host}/get.inc?test=file");
|
||||
$called = false;
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_VERBOSE => true,
|
||||
CURLOPT_DEBUGFUNCTION => static function() use (&$called) {
|
||||
$called = true;
|
||||
},
|
||||
]);
|
||||
var_dump($called);
|
||||
curl_exec($ch);
|
||||
var_dump($called);
|
||||
$called = false;
|
||||
$ch2 = curl_copy_handle($ch);
|
||||
curl_exec($ch2);
|
||||
var_dump($called);
|
||||
var_dump(curl_getinfo($ch2, CURLINFO_HEADER_OUT));
|
||||
|
||||
echo "\nDone";
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(20094)
|
||||
|
||||
===Testing with regular CURLOPT_VERBOSE with verbose=false===
|
||||
bool(true)
|
||||
|
||||
===Testing with regular CURLOPT_VERBOSE on stderr===
|
||||
bool(true)
|
||||
|
||||
===Testing with CURLOPT_DEBUGFUNCTION happy path===
|
||||
bool(true)
|
||||
Received stderr empty:
|
||||
string(0) ""
|
||||
string(0) ""
|
||||
bool(true)
|
||||
|
||||
===Test attempting to set CURLINFO_HEADER_OUT while CURLOPT_DEBUGFUNCTION in effect throws===
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(87) "CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"
|
||||
string(87) "CURLINFO_HEADER_OUT option must not be set when the CURLOPT_DEBUGFUNCTION option is set"
|
||||
bool(true)
|
||||
bool(true)
|
||||
|
||||
===Test attempting to set CURLOPT_DEBUGFUNCTION while CURLINFO_HEADER_OUT does not throw===
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(true)
|
||||
|
||||
===Test CURLOPT_DEBUGFUNCTION=null works===
|
||||
bool(true)
|
||||
string(0) ""
|
||||
|
||||
===Test CURLINFO_HEADER_OUT works while CURLOPT_DEBUGFUNCTION in effect===
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(false)
|
||||
string(0) ""
|
||||
string(%d) "GET /get.inc?test=file HTTP/%s
|
||||
Host: %s:%d
|
||||
Accept: */*
|
||||
|
||||
"
|
||||
|
||||
===Test CURLOPT_DEBUGFUNCTION can throw within callback===
|
||||
bool(true)
|
||||
string(41) "This should get caught after verbose=true"
|
||||
string(%d) "GET /get.inc?test=file HTTP/%s
|
||||
Host: %s:%d
|
||||
Accept: */*
|
||||
|
||||
"
|
||||
|
||||
===Test CURLOPT_DEBUGFUNCTION on shared handles work===
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(71) "GET /get.inc?test=file HTTP/%s
|
||||
Host: %s:%d
|
||||
Accept: */*
|
||||
|
||||
"
|
||||
|
||||
Done
|
Loading…
Add table
Add a link
Reference in a new issue