Skip to content

Commit

Permalink
fix(CURLRequest): multiple header sections after redirects (#9426)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ducng99 authored Jan 23, 2025
1 parent 9727f10 commit 909e9b0
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 0 deletions.
30 changes: 30 additions & 0 deletions system/HTTP/CURLRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ public function send(string $method, string $url)
// 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);
}
Expand Down Expand Up @@ -713,4 +717,30 @@ protected function sendRequest(array $curlOptions = []): string

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;
}
}
68 changes: 68 additions & 0 deletions tests/system/HTTP/CURLRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1313,4 +1313,72 @@ public function testHTTPversionAsString(): void
$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());
}
}
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.6.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

0 comments on commit 909e9b0

Please sign in to comment.