Merge branch 'PHP-8.3'

* PHP-8.3:
  Fix bug #73182: PHP SOAPClient does not support stream context HTTP headers in array form
This commit is contained in:
Niels Dossche 2024-09-10 20:24:57 +02:00
commit 520fce5607
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
3 changed files with 136 additions and 58 deletions

View file

@ -78,6 +78,68 @@ int basic_authentication(zval* this_ptr, smart_str* soap_headers)
return 0;
}
static void http_context_add_header(const char *s,
bool has_authorization,
bool has_proxy_authorization,
bool has_cookies,
smart_str *soap_headers)
{
const char *p;
int name_len;
while (*s) {
/* skip leading newlines and spaces */
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
s++;
}
/* extract header name */
p = s;
name_len = -1;
while (*p) {
if (*p == ':') {
if (name_len < 0) name_len = p - s;
break;
} else if (*p == ' ' || *p == '\t') {
if (name_len < 0) name_len = p - s;
} else if (*p == '\r' || *p == '\n') {
break;
}
p++;
}
if (*p == ':') {
/* extract header value */
while (*p && *p != '\r' && *p != '\n') {
p++;
}
/* skip some predefined headers */
if ((name_len != sizeof("host")-1 ||
strncasecmp(s, "host", sizeof("host")-1) != 0) &&
(name_len != sizeof("connection")-1 ||
strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
(name_len != sizeof("user-agent")-1 ||
strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
(name_len != sizeof("content-length")-1 ||
strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
(name_len != sizeof("content-type")-1 ||
strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
(!has_cookies ||
name_len != sizeof("cookie")-1 ||
strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
(!has_authorization ||
name_len != sizeof("authorization")-1 ||
strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
(!has_proxy_authorization ||
name_len != sizeof("proxy-authorization")-1 ||
strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
/* add header */
smart_str_appendl(soap_headers, s, p-s);
smart_str_append_const(soap_headers, "\r\n");
}
}
s = (*p) ? (p + 1) : p;
}
}
/* Additional HTTP headers */
void http_context_headers(php_stream_context* context,
bool has_authorization,
@ -86,64 +148,16 @@ void http_context_headers(php_stream_context* context,
smart_str* soap_headers)
{
zval *tmp;
if (context &&
(tmp = php_stream_context_get_option(context, "http", "header")) != NULL &&
Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
char *s = Z_STRVAL_P(tmp);
char *p;
int name_len;
while (*s) {
/* skip leading newlines and spaces */
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
s++;
}
/* extract header name */
p = s;
name_len = -1;
while (*p) {
if (*p == ':') {
if (name_len < 0) name_len = p - s;
break;
} else if (*p == ' ' || *p == '\t') {
if (name_len < 0) name_len = p - s;
} else if (*p == '\r' || *p == '\n') {
break;
if (context && (tmp = php_stream_context_get_option(context, "http", "header")) != NULL) {
if (Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
http_context_add_header(Z_STRVAL_P(tmp), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
} else if (Z_TYPE_P(tmp) == IS_ARRAY) {
zval *value;
ZEND_HASH_FOREACH_VAL(Z_ARR_P(tmp), value) {
if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) {
http_context_add_header(Z_STRVAL_P(value), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
}
p++;
}
if (*p == ':') {
/* extract header value */
while (*p && *p != '\r' && *p != '\n') {
p++;
}
/* skip some predefined headers */
if ((name_len != sizeof("host")-1 ||
strncasecmp(s, "host", sizeof("host")-1) != 0) &&
(name_len != sizeof("connection")-1 ||
strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
(name_len != sizeof("user-agent")-1 ||
strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
(name_len != sizeof("content-length")-1 ||
strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
(name_len != sizeof("content-type")-1 ||
strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
(!has_cookies ||
name_len != sizeof("cookie")-1 ||
strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
(!has_authorization ||
name_len != sizeof("authorization")-1 ||
strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
(!has_proxy_authorization ||
name_len != sizeof("proxy-authorization")-1 ||
strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
/* add header */
smart_str_appendl(soap_headers, s, p-s);
smart_str_append_const(soap_headers, "\r\n");
}
}
s = (*p) ? (p + 1) : p;
} ZEND_HASH_FOREACH_END();
}
}
}

View file

@ -259,7 +259,9 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri)
ctx->context = php_stream_context_from_zval(context_ptr, 1);
if (ctx->context &&
(header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL) {
(header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL &&
Z_TYPE_P(header) == IS_STRING) {
/* TODO: should support header as an array, but this code path is untested */
s = strstr(Z_STRVAL_P(header), "Authorization: Basic");
if (s && (s == Z_STRVAL_P(header) || *(s-1) == '\n' || *(s-1) == '\r')) {
char *rest = strstr(s, "\r\n");

View file

@ -0,0 +1,62 @@
--TEST--
Bug #73182 (PHP SOAPClient does not support stream context HTTP headers in array form)
--EXTENSIONS--
soap
--SKIPIF--
<?php
if (!file_exists(__DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc")) {
echo "skip sapi/cli/tests/php_cli_server.inc required but not found";
}
?>
--FILE--
<?php
include __DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc";
$args = ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
if (php_ini_loaded_file()) {
// Necessary such that it works from a development directory in which case extension_dir might not be the real extension dir
$args[] = "-c";
$args[] = php_ini_loaded_file();
}
$code = <<<'PHP'
/* Receive */
$content = trim(file_get_contents("php://input")) . PHP_EOL;
PHP;
php_cli_server_start($code, null, $args);
$client = new soapclient(NULL, [
'location' => 'http://' . PHP_CLI_SERVER_ADDRESS,
'uri' => 'misc-uri',
'trace' => true,
'stream_context' => stream_context_create([
'http' => [
'header' => [
// These 2 must be ignored because the soap http client sets them up
'Connection: close',
'User-Agent: bar',
// The following 2 must be included
'X-custom: foo',
'Content-Description: Foo',
' X-custom2: trim me ',
],
],
]),
]);
$client->__soapCall("foo", []);
echo $client->__getLastRequestHeaders();
?>
--EXPECTF--
POST / HTTP/1.1
Host: localhost:%d
Connection: Keep-Alive
User-Agent: PHP-SOAP/%s
Content-Type: text/xml; charset=utf-8
SOAPAction: "misc-uri#foo"
Content-Length: %d
X-custom: foo
Content-Description: Foo
X-custom2: trim me