fix(CURLRequest): multiple header sections after redirects (#9426)
Some checks failed
Deploy API Documentation / Deploy to api (push) Has been cancelled
Deploy User Guide (latest) / Deploy to gh-pages (push) Has been cancelled
AutoReview / Automatic Code Review (push) Has been cancelled
AutoReview / Check normalized composer.json (push) Has been cancelled
Coding Standards / PHP 8.1 Lint with PHP CS Fixer (push) Has been cancelled
Coding Standards / PHP 8.4 Lint with PHP CS Fixer (push) Has been cancelled
Deptrac / Architectural Inspection (push) Has been cancelled
Check File Permissions / Check File Permission (push) Has been cancelled
PHPCPD / phpcpd (push) Has been cancelled
PHPStan / PHP ${{ matrix.php-versions }} Static Analysis (push) Has been cancelled
PHPUnit / Setup PHP Version for Code Coverage (push) Has been cancelled
Psalm / Psalm Analysis (push) Has been cancelled
Rector / PHP 8.1 Analyze code (Rector) (push) Has been cancelled
Rector / PHP 8.4 Analyze code (Rector) (push) Has been cancelled
PHPUnit / Others (8.1) (push) Has been cancelled
PHPUnit / Others (8.2) (push) Has been cancelled
PHPUnit / Others (8.3) (push) Has been cancelled
PHPUnit / Others (8.4) (push) Has been cancelled
PHPUnit / DatabaseLive (MySQLi, 5.7, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (MySQLi, 8.0, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (MySQLi, 8.0, 8.2) (push) Has been cancelled
PHPUnit / DatabaseLive (MySQLi, 8.0, 8.3) (push) Has been cancelled
PHPUnit / DatabaseLive (MySQLi, 8.0, 8.4) (push) Has been cancelled
PHPUnit / DatabaseLive (OCI8, 8.0, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (OCI8, 8.0, 8.2) (push) Has been cancelled
PHPUnit / DatabaseLive (OCI8, 8.0, 8.3) (push) Has been cancelled
PHPUnit / DatabaseLive (OCI8, 8.0, 8.4) (push) Has been cancelled
PHPUnit / DatabaseLive (Postgre, 8.0, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (Postgre, 8.0, 8.2) (push) Has been cancelled
PHPUnit / DatabaseLive (Postgre, 8.0, 8.3) (push) Has been cancelled
PHPUnit / DatabaseLive (Postgre, 8.0, 8.4) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLSRV, 8.0, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLSRV, 8.0, 8.2) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLSRV, 8.0, 8.3) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLSRV, 8.0, 8.4) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLite3, 8.0, 8.1) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLite3, 8.0, 8.2) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLite3, 8.0, 8.3) (push) Has been cancelled
PHPUnit / DatabaseLive (SQLite3, 8.0, 8.4) (push) Has been cancelled
PHPUnit / SeparateProcess (8.1) (push) Has been cancelled
PHPUnit / SeparateProcess (8.2) (push) Has been cancelled
PHPUnit / SeparateProcess (8.3) (push) Has been cancelled
PHPUnit / SeparateProcess (8.4) (push) Has been cancelled
PHPUnit / CacheLive (8.1) (push) Has been cancelled
PHPUnit / CacheLive (8.2) (push) Has been cancelled
PHPUnit / CacheLive (8.3) (push) Has been cancelled
PHPUnit / CacheLive (8.4) (push) Has been cancelled
PHPUnit / Upload coverage results to Coveralls (push) Has been cancelled

* fix: strip out multiple header sections for redirect

* test: add test for stripping out multiple redirect header sections

* docs: changelog for fixing returning multiple header sections after redirects

* Added a non-standard http redirect code test

* apply cs-fix for test

* Moved redirect header loop to a separate function

* Updated changelog to be clearer
This commit is contained in:
Thomas Nguyen 2025-01-23 14:28:25 +13:00 committed by GitHub
parent 9727f104c8
commit 909e9b0a1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 0 deletions

View File

@ -385,6 +385,10 @@ class CURLRequest extends OutgoingRequest
// Set the string we want to break our response from
$breakString = "\r\n\r\n";
if (isset($this->config['allow_redirects']) && $this->config['allow_redirects'] !== false) {
$output = $this->handleRedirectHeaders($output, $breakString);
}
while (str_starts_with($output, 'HTTP/1.1 100 Continue')) {
$output = substr($output, strpos($output, $breakString) + 4);
}
@ -713,4 +717,30 @@ class CURLRequest extends OutgoingRequest
return $output;
}
private function handleRedirectHeaders(string $output, string $breakString): string
{
// Strip out multiple redirect header sections
while (preg_match('/^HTTP\/\d(?:\.\d)? 3\d\d/', $output)) {
$breakStringPos = strpos($output, $breakString);
$redirectHeaderSection = substr($output, 0, $breakStringPos);
$redirectHeaders = explode("\n", $redirectHeaderSection);
$locationHeaderFound = false;
foreach ($redirectHeaders as $header) {
if (str_starts_with(strtolower($header), 'location:')) {
$locationHeaderFound = true;
break;
}
}
if ($locationHeaderFound) {
$output = substr($output, $breakStringPos + 4);
} else {
break;
}
}
return $output;
}
}

View File

@ -1313,4 +1313,72 @@ alt-svc: h3=":443"; ma=86400' . "\x0d\x0a\x0d\x0aResponse Body";
$this->assertArrayHasKey(CURLOPT_HTTP_VERSION, $options);
$this->assertSame(CURL_HTTP_VERSION_2_0, $options[CURLOPT_HTTP_VERSION]);
}
public function testRemoveMultipleRedirectHeaderSections(): void
{
$testBody = 'Hello world';
$output = "HTTP/1.1 301 Moved Permanently
content-type: text/html; charset=utf-8
content-length: 211
location: http://example.com
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\nHTTP/1.1 302 Found
content-type: text/html; charset=utf-8
content-length: 211
location: http://example.com
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\nHTTP/1.1 399 Custom Redirect
content-type: text/html; charset=utf-8
content-length: 211
location: http://example.com
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\nHTTP/2 308
content-type: text/html; charset=utf-8
content-length: 211
location: http://example.com
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\nHTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 211
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\n" . $testBody;
$this->request->setOutput($output);
$response = $this->request->request('GET', 'http://example.com', [
'allow_redirects' => true,
]);
$this->assertSame(200, $response->getStatusCode());
$this->assertSame($testBody, $response->getBody());
}
public function testNotRemoveMultipleRedirectHeaderSectionsWithoutLocationHeader(): void
{
$testBody = 'Hello world';
$output = "HTTP/1.1 301 Moved Permanently
content-type: text/html; charset=utf-8
content-length: 211
date: Mon, 20 Jan 2025 11:46:34 GMT
server: nginx/1.21.6
vary: Origin\r\n\r\n" . $testBody;
$this->request->setOutput($output);
$response = $this->request->request('GET', 'http://example.com', [
'allow_redirects' => true,
]);
$this->assertSame(301, $response->getStatusCode());
$this->assertSame($testBody, $response->getBody());
}
}

View File

@ -30,6 +30,8 @@ Deprecations
Bugs Fixed
**********
- **CURLRequest:** Fixed an issue where multiple header sections appeared in the CURL response body during multiple redirects from the target server.
See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
for a complete list of bugs fixed.