Commit 27c10ddce63 for php.net

commit 27c10ddce6302912a46d72a4dcd47c89f1209544
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Wed Jun 24 07:34:59 2026 -0400

    soap: do not overwrite the parsed host on a protocol-relative redirect

    When a redirect Location is a protocol-relative reference (//host/path),
    php_url_parse() already fills new_url->host, but the scheme-less redirect
    handling overwrote it with a copy of the request host without releasing
    the parsed one. That leaks a zend_string per such redirect and pins the
    redirect back to the original host instead of the one the server named.
    Inherit host and port from the request URL only when new_url->host is
    NULL, mirroring the scheme guard directly above.

    Closes GH-22434

diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 0bec42afbc3..e9fd68007d2 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -1151,8 +1151,10 @@ int make_http_soap_request(zval        *this_ptr,
 				zend_string_release_ex(http_body, 0);
 				if (new_url->scheme == NULL && new_url->path != NULL) {
 					new_url->scheme = phpurl->scheme ? zend_string_copy(phpurl->scheme) : NULL;
-					new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL;
-					new_url->port = phpurl->port;
+					if (new_url->host == NULL) {
+						new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL;
+						new_url->port = phpurl->port;
+					}
 					if (new_url->path && ZSTR_VAL(new_url->path)[0] != '/') {
 						if (phpurl->path) {
 							char *t = ZSTR_VAL(phpurl->path);
diff --git a/ext/soap/tests/bugs/protocol_relative_redirect.phpt b/ext/soap/tests/bugs/protocol_relative_redirect.phpt
new file mode 100644
index 00000000000..e8f30ca6687
--- /dev/null
+++ b/ext/soap/tests/bugs/protocol_relative_redirect.phpt
@@ -0,0 +1,49 @@
+--TEST--
+SOAP client follows a protocol-relative (//host/path) redirect Location without leaking the host
+--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()) {
+    $args[] = "-c";
+    $args[] = php_ini_loaded_file();
+}
+
+$code = <<<'PHP'
+if ($_SERVER["REQUEST_URI"] === "/redirected") {
+    header("Content-Type: text/xml; charset=utf-8");
+    echo '<?xml version="1.0" encoding="UTF-8"?>',
+        '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">',
+        '<SOAP-ENV:Body><fooResponse><result>ok</result></fooResponse></SOAP-ENV:Body>',
+        '</SOAP-ENV:Envelope>';
+} else {
+    http_response_code(302);
+    header("Location: //" . $_SERVER["HTTP_HOST"] . "/redirected");
+}
+PHP;
+
+php_cli_server_start($code, null, $args);
+
+$client = new SoapClient(null, [
+    'location' => 'http://' . PHP_CLI_SERVER_ADDRESS . '/start',
+    'uri' => 'test-uri',
+]);
+
+try {
+    $client->__soapCall("foo", []);
+    echo "redirect followed\n";
+} catch (SoapFault $e) {
+    echo "SoapFault: " . $e->getMessage() . "\n";
+}
+?>
+--EXPECT--
+redirect followed