Skip to content

Commit 520fce5

Browse files
committed
Merge branch 'PHP-8.3'
* PHP-8.3: Fix bug #73182: PHP SOAPClient does not support stream context HTTP headers in array form
2 parents 15abb6e + 2a95e3f commit 520fce5

File tree

3 files changed

+136
-58
lines changed

3 files changed

+136
-58
lines changed

ext/soap/php_http.c

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,68 @@ int basic_authentication(zval* this_ptr, smart_str* soap_headers)
7878
return 0;
7979
}
8080

81+
static void http_context_add_header(const char *s,
82+
bool has_authorization,
83+
bool has_proxy_authorization,
84+
bool has_cookies,
85+
smart_str *soap_headers)
86+
{
87+
const char *p;
88+
int name_len;
89+
90+
while (*s) {
91+
/* skip leading newlines and spaces */
92+
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
93+
s++;
94+
}
95+
/* extract header name */
96+
p = s;
97+
name_len = -1;
98+
while (*p) {
99+
if (*p == ':') {
100+
if (name_len < 0) name_len = p - s;
101+
break;
102+
} else if (*p == ' ' || *p == '\t') {
103+
if (name_len < 0) name_len = p - s;
104+
} else if (*p == '\r' || *p == '\n') {
105+
break;
106+
}
107+
p++;
108+
}
109+
if (*p == ':') {
110+
/* extract header value */
111+
while (*p && *p != '\r' && *p != '\n') {
112+
p++;
113+
}
114+
/* skip some predefined headers */
115+
if ((name_len != sizeof("host")-1 ||
116+
strncasecmp(s, "host", sizeof("host")-1) != 0) &&
117+
(name_len != sizeof("connection")-1 ||
118+
strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
119+
(name_len != sizeof("user-agent")-1 ||
120+
strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
121+
(name_len != sizeof("content-length")-1 ||
122+
strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
123+
(name_len != sizeof("content-type")-1 ||
124+
strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
125+
(!has_cookies ||
126+
name_len != sizeof("cookie")-1 ||
127+
strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
128+
(!has_authorization ||
129+
name_len != sizeof("authorization")-1 ||
130+
strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
131+
(!has_proxy_authorization ||
132+
name_len != sizeof("proxy-authorization")-1 ||
133+
strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
134+
/* add header */
135+
smart_str_appendl(soap_headers, s, p-s);
136+
smart_str_append_const(soap_headers, "\r\n");
137+
}
138+
}
139+
s = (*p) ? (p + 1) : p;
140+
}
141+
}
142+
81143
/* Additional HTTP headers */
82144
void http_context_headers(php_stream_context* context,
83145
bool has_authorization,
@@ -86,64 +148,16 @@ void http_context_headers(php_stream_context* context,
86148
smart_str* soap_headers)
87149
{
88150
zval *tmp;
89-
90-
if (context &&
91-
(tmp = php_stream_context_get_option(context, "http", "header")) != NULL &&
92-
Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
93-
char *s = Z_STRVAL_P(tmp);
94-
char *p;
95-
int name_len;
96-
97-
while (*s) {
98-
/* skip leading newlines and spaces */
99-
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') {
100-
s++;
101-
}
102-
/* extract header name */
103-
p = s;
104-
name_len = -1;
105-
while (*p) {
106-
if (*p == ':') {
107-
if (name_len < 0) name_len = p - s;
108-
break;
109-
} else if (*p == ' ' || *p == '\t') {
110-
if (name_len < 0) name_len = p - s;
111-
} else if (*p == '\r' || *p == '\n') {
112-
break;
151+
if (context && (tmp = php_stream_context_get_option(context, "http", "header")) != NULL) {
152+
if (Z_TYPE_P(tmp) == IS_STRING && Z_STRLEN_P(tmp)) {
153+
http_context_add_header(Z_STRVAL_P(tmp), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
154+
} else if (Z_TYPE_P(tmp) == IS_ARRAY) {
155+
zval *value;
156+
ZEND_HASH_FOREACH_VAL(Z_ARR_P(tmp), value) {
157+
if (Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) {
158+
http_context_add_header(Z_STRVAL_P(value), has_authorization, has_proxy_authorization, has_cookies, soap_headers);
113159
}
114-
p++;
115-
}
116-
if (*p == ':') {
117-
/* extract header value */
118-
while (*p && *p != '\r' && *p != '\n') {
119-
p++;
120-
}
121-
/* skip some predefined headers */
122-
if ((name_len != sizeof("host")-1 ||
123-
strncasecmp(s, "host", sizeof("host")-1) != 0) &&
124-
(name_len != sizeof("connection")-1 ||
125-
strncasecmp(s, "connection", sizeof("connection")-1) != 0) &&
126-
(name_len != sizeof("user-agent")-1 ||
127-
strncasecmp(s, "user-agent", sizeof("user-agent")-1) != 0) &&
128-
(name_len != sizeof("content-length")-1 ||
129-
strncasecmp(s, "content-length", sizeof("content-length")-1) != 0) &&
130-
(name_len != sizeof("content-type")-1 ||
131-
strncasecmp(s, "content-type", sizeof("content-type")-1) != 0) &&
132-
(!has_cookies ||
133-
name_len != sizeof("cookie")-1 ||
134-
strncasecmp(s, "cookie", sizeof("cookie")-1) != 0) &&
135-
(!has_authorization ||
136-
name_len != sizeof("authorization")-1 ||
137-
strncasecmp(s, "authorization", sizeof("authorization")-1) != 0) &&
138-
(!has_proxy_authorization ||
139-
name_len != sizeof("proxy-authorization")-1 ||
140-
strncasecmp(s, "proxy-authorization", sizeof("proxy-authorization")-1) != 0)) {
141-
/* add header */
142-
smart_str_appendl(soap_headers, s, p-s);
143-
smart_str_append_const(soap_headers, "\r\n");
144-
}
145-
}
146-
s = (*p) ? (p + 1) : p;
160+
} ZEND_HASH_FOREACH_END();
147161
}
148162
}
149163
}

ext/soap/php_sdl.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,9 @@ void sdl_set_uri_credentials(sdlCtx *ctx, char *uri)
259259
ctx->context = php_stream_context_from_zval(context_ptr, 1);
260260

261261
if (ctx->context &&
262-
(header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL) {
262+
(header = php_stream_context_get_option(ctx->context, "http", "header")) != NULL &&
263+
Z_TYPE_P(header) == IS_STRING) {
264+
/* TODO: should support header as an array, but this code path is untested */
263265
s = strstr(Z_STRVAL_P(header), "Authorization: Basic");
264266
if (s && (s == Z_STRVAL_P(header) || *(s-1) == '\n' || *(s-1) == '\r')) {
265267
char *rest = strstr(s, "\r\n");

ext/soap/tests/bugs/bug73182.phpt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
Bug #73182 (PHP SOAPClient does not support stream context HTTP headers in array form)
3+
--EXTENSIONS--
4+
soap
5+
--SKIPIF--
6+
<?php
7+
if (!file_exists(__DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc")) {
8+
echo "skip sapi/cli/tests/php_cli_server.inc required but not found";
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
14+
include __DIR__ . "/../../../../sapi/cli/tests/php_cli_server.inc";
15+
16+
$args = ["-d", "extension_dir=" . ini_get("extension_dir"), "-d", "extension=" . (substr(PHP_OS, 0, 3) == "WIN" ? "php_" : "") . "soap." . PHP_SHLIB_SUFFIX];
17+
if (php_ini_loaded_file()) {
18+
// Necessary such that it works from a development directory in which case extension_dir might not be the real extension dir
19+
$args[] = "-c";
20+
$args[] = php_ini_loaded_file();
21+
}
22+
$code = <<<'PHP'
23+
/* Receive */
24+
$content = trim(file_get_contents("php://input")) . PHP_EOL;
25+
PHP;
26+
27+
php_cli_server_start($code, null, $args);
28+
29+
$client = new soapclient(NULL, [
30+
'location' => 'http://' . PHP_CLI_SERVER_ADDRESS,
31+
'uri' => 'misc-uri',
32+
'trace' => true,
33+
'stream_context' => stream_context_create([
34+
'http' => [
35+
'header' => [
36+
// These 2 must be ignored because the soap http client sets them up
37+
'Connection: close',
38+
'User-Agent: bar',
39+
// The following 2 must be included
40+
'X-custom: foo',
41+
'Content-Description: Foo',
42+
' X-custom2: trim me ',
43+
],
44+
],
45+
]),
46+
]);
47+
48+
$client->__soapCall("foo", []);
49+
echo $client->__getLastRequestHeaders();
50+
51+
?>
52+
--EXPECTF--
53+
POST / HTTP/1.1
54+
Host: localhost:%d
55+
Connection: Keep-Alive
56+
User-Agent: PHP-SOAP/%s
57+
Content-Type: text/xml; charset=utf-8
58+
SOAPAction: "misc-uri#foo"
59+
Content-Length: %d
60+
X-custom: foo
61+
Content-Description: Foo
62+
X-custom2: trim me

0 commit comments

Comments
 (0)