Skip to content

Commit 909e9b0

Browse files
authored
fix(CURLRequest): multiple header sections after redirects (#9426)
* 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
1 parent 9727f10 commit 909e9b0

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

system/HTTP/CURLRequest.php

+30
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ public function send(string $method, string $url)
385385
// Set the string we want to break our response from
386386
$breakString = "\r\n\r\n";
387387

388+
if (isset($this->config['allow_redirects']) && $this->config['allow_redirects'] !== false) {
389+
$output = $this->handleRedirectHeaders($output, $breakString);
390+
}
391+
388392
while (str_starts_with($output, 'HTTP/1.1 100 Continue')) {
389393
$output = substr($output, strpos($output, $breakString) + 4);
390394
}
@@ -713,4 +717,30 @@ protected function sendRequest(array $curlOptions = []): string
713717

714718
return $output;
715719
}
720+
721+
private function handleRedirectHeaders(string $output, string $breakString): string
722+
{
723+
// Strip out multiple redirect header sections
724+
while (preg_match('/^HTTP\/\d(?:\.\d)? 3\d\d/', $output)) {
725+
$breakStringPos = strpos($output, $breakString);
726+
$redirectHeaderSection = substr($output, 0, $breakStringPos);
727+
$redirectHeaders = explode("\n", $redirectHeaderSection);
728+
$locationHeaderFound = false;
729+
730+
foreach ($redirectHeaders as $header) {
731+
if (str_starts_with(strtolower($header), 'location:')) {
732+
$locationHeaderFound = true;
733+
break;
734+
}
735+
}
736+
737+
if ($locationHeaderFound) {
738+
$output = substr($output, $breakStringPos + 4);
739+
} else {
740+
break;
741+
}
742+
}
743+
744+
return $output;
745+
}
716746
}

tests/system/HTTP/CURLRequestTest.php

+68
Original file line numberDiff line numberDiff line change
@@ -1313,4 +1313,72 @@ public function testHTTPversionAsString(): void
13131313
$this->assertArrayHasKey(CURLOPT_HTTP_VERSION, $options);
13141314
$this->assertSame(CURL_HTTP_VERSION_2_0, $options[CURLOPT_HTTP_VERSION]);
13151315
}
1316+
1317+
public function testRemoveMultipleRedirectHeaderSections(): void
1318+
{
1319+
$testBody = 'Hello world';
1320+
1321+
$output = "HTTP/1.1 301 Moved Permanently
1322+
content-type: text/html; charset=utf-8
1323+
content-length: 211
1324+
location: http://example.com
1325+
date: Mon, 20 Jan 2025 11:46:34 GMT
1326+
server: nginx/1.21.6
1327+
vary: Origin\r\n\r\nHTTP/1.1 302 Found
1328+
content-type: text/html; charset=utf-8
1329+
content-length: 211
1330+
location: http://example.com
1331+
date: Mon, 20 Jan 2025 11:46:34 GMT
1332+
server: nginx/1.21.6
1333+
vary: Origin\r\n\r\nHTTP/1.1 399 Custom Redirect
1334+
content-type: text/html; charset=utf-8
1335+
content-length: 211
1336+
location: http://example.com
1337+
date: Mon, 20 Jan 2025 11:46:34 GMT
1338+
server: nginx/1.21.6
1339+
vary: Origin\r\n\r\nHTTP/2 308
1340+
content-type: text/html; charset=utf-8
1341+
content-length: 211
1342+
location: http://example.com
1343+
date: Mon, 20 Jan 2025 11:46:34 GMT
1344+
server: nginx/1.21.6
1345+
vary: Origin\r\n\r\nHTTP/1.1 200 OK
1346+
content-type: text/html; charset=utf-8
1347+
content-length: 211
1348+
date: Mon, 20 Jan 2025 11:46:34 GMT
1349+
server: nginx/1.21.6
1350+
vary: Origin\r\n\r\n" . $testBody;
1351+
1352+
$this->request->setOutput($output);
1353+
1354+
$response = $this->request->request('GET', 'http://example.com', [
1355+
'allow_redirects' => true,
1356+
]);
1357+
1358+
$this->assertSame(200, $response->getStatusCode());
1359+
1360+
$this->assertSame($testBody, $response->getBody());
1361+
}
1362+
1363+
public function testNotRemoveMultipleRedirectHeaderSectionsWithoutLocationHeader(): void
1364+
{
1365+
$testBody = 'Hello world';
1366+
1367+
$output = "HTTP/1.1 301 Moved Permanently
1368+
content-type: text/html; charset=utf-8
1369+
content-length: 211
1370+
date: Mon, 20 Jan 2025 11:46:34 GMT
1371+
server: nginx/1.21.6
1372+
vary: Origin\r\n\r\n" . $testBody;
1373+
1374+
$this->request->setOutput($output);
1375+
1376+
$response = $this->request->request('GET', 'http://example.com', [
1377+
'allow_redirects' => true,
1378+
]);
1379+
1380+
$this->assertSame(301, $response->getStatusCode());
1381+
1382+
$this->assertSame($testBody, $response->getBody());
1383+
}
13161384
}

user_guide_src/source/changelogs/v4.6.1.rst

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Deprecations
3030
Bugs Fixed
3131
**********
3232

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

0 commit comments

Comments
 (0)