Skip to content

Commit 1da96ab

Browse files
authored
fix: redirect is checked on native handler (#181)
closes #180
1 parent 1848a3a commit 1da96ab

4 files changed

Lines changed: 69 additions & 6 deletions

File tree

src/RequestResolver.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class RequestResolver {
2424
private array $headerList;
2525
/** @var array<string|null> */
2626
private array $integrityList;
27+
/** @var array<int|null> */
28+
private array $maxRedirectsList;
2729
/** @var array<object|null> */
2830
private array $signalList;
2931

@@ -38,6 +40,7 @@ public function __construct(
3840
$this->responseList = [];
3941
$this->headerList = [];
4042
$this->integrityList = [];
43+
$this->maxRedirectsList = [];
4144
$this->signalList = [];
4245
}
4346

@@ -92,6 +95,10 @@ public function add(
9295
array_push($this->curlMultiList, $curlMulti);
9396
array_push($this->deferredList, $deferred);
9497
array_push($this->integrityList, $integrity);
98+
array_push(
99+
$this->maxRedirectsList,
100+
$curlOptArray[CURLOPT_MAXREDIRS] ?? null
101+
);
95102
array_push($this->responseList, $bodyResponse);
96103
array_push($this->headerList, "");
97104
array_push($this->signalList, $signal);
@@ -198,7 +205,7 @@ private function writeHeader(
198205
}
199206

200207
if(str_starts_with(strtolower($headerLine), "location: ")) {
201-
if($ch->getInfo(CURLOPT_MAXREDIRS) === 0) {
208+
if($this->maxRedirectsList[$i] === 0) {
202209
throw new FetchException("Redirect is disallowed");
203210
}
204211
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
namespace GT\Fetch\Test\Helper;
3+
4+
class NativeHandleTestCurl extends TestCurl {
5+
public function getHandle() {
6+
return $this->ch;
7+
}
8+
}

test/phpunit/Helper/ResponseSimulator.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
namespace GT\Fetch\Test\Helper;
33

4-
use GT\Curl\Curl;
4+
use GT\Curl\CurlInterface;
55

66
class ResponseSimulator {
77
const RANDOM_BODY_WORDS = ["pursuit","forest","gravel","timber","wonder","eject","slogan","monkey","construct","earthquake","respect","publish","forward","circle","summer","define","highlight","refuse","salon","theater","lily","earwax","variant","account","resource"];
@@ -25,6 +25,7 @@ static public function setBodyCallback(callable $callback) {
2525

2626
static public function start() {
2727
self::$started = true;
28+
self::$redirectAdded = false;
2829
self::$headerBuffer = self::generateHeaders();
2930
self::$bodyBuffer = self::generateBody();
3031
}
@@ -73,11 +74,11 @@ static public function hasStarted():bool {
7374
return self::$started;
7475
}
7576

76-
static public function sendChunk(Curl $ch):int {
77+
static public function sendChunk(CurlInterface $ch):int {
7778
// TODO: If the URL is test://should-redirect, send the redirect header here
7879
$url = $ch->getInfo(CURLINFO_EFFECTIVE_URL);
7980
if($url === "test://should-redirect") {
80-
$locationRedirect = "Location: /redirected";
81+
$locationRedirect = "Location: /redirected\r\n";
8182
if(!self::$redirectAdded) {
8283
array_splice(self::$headerBuffer, 1, 0, $locationRedirect);
8384
self::$redirectAdded = true;
@@ -88,7 +89,7 @@ static public function sendChunk(Curl $ch):int {
8889

8990
call_user_func(
9091
self::$headerCallback,
91-
$ch,
92+
$ch->getHandle(),
9293
$data
9394
);
9495

@@ -100,13 +101,14 @@ static public function sendChunk(Curl $ch):int {
100101

101102
call_user_func(
102103
self::$bodyCallback,
103-
$ch,
104+
$ch->getHandle(),
104105
$data
105106
);
106107

107108
return 1;
108109
}
109110
else {
111+
self::$started = false;
110112
return 0;
111113
}
112114
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
namespace GT\Fetch\Test;
3+
4+
use GT\Fetch\FetchException;
5+
use GT\Fetch\RequestResolver;
6+
use GT\Fetch\Test\Helper\NativeHandleTestCurl;
7+
use GT\Fetch\Test\Helper\TestCurlMulti;
8+
use Gt\Async\Loop;
9+
use Gt\Http\Response;
10+
use PHPUnit\Framework\TestCase;
11+
use ReflectionMethod;
12+
use ReflectionProperty;
13+
14+
class RequestResolverTest extends TestCase {
15+
public function testWriteHeaderRejectsRedirectsForNativeCurlHandle():void {
16+
$sut = new RequestResolver(
17+
new Loop(),
18+
NativeHandleTestCurl::class,
19+
TestCurlMulti::class,
20+
);
21+
$curl = new NativeHandleTestCurl("test://should-redirect");
22+
$response = new Response();
23+
$response->startDeferredResponse($curl);
24+
25+
$this->setProperty($sut, "curlList", [$curl]);
26+
$this->setProperty($sut, "responseList", [$response]);
27+
$this->setProperty($sut, "headerList", [""]);
28+
$this->setProperty($sut, "maxRedirectsList", [0]);
29+
$this->setProperty($sut, "signalList", [null]);
30+
31+
$writeHeader = new ReflectionMethod($sut, "writeHeader");
32+
33+
self::expectException(FetchException::class);
34+
self::expectExceptionMessage("Redirect is disallowed");
35+
$writeHeader->invoke($sut, $curl->getHandle(), "Location: /redirected\r\n");
36+
}
37+
38+
private function setProperty(
39+
RequestResolver $requestResolver,
40+
string $property,
41+
array $value,
42+
):void {
43+
$reflectionProperty = new ReflectionProperty($requestResolver, $property);
44+
$reflectionProperty->setValue($requestResolver, $value);
45+
}
46+
}

0 commit comments

Comments
 (0)