From a7dacf360ffa299c1230394e621a3cdc796adb68 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Fri, 29 May 2026 13:06:21 +0100 Subject: [PATCH] fix: redirect is checked on native handler closes #180 --- src/RequestResolver.php | 9 +++- test/phpunit/Helper/NativeHandleTestCurl.php | 8 ++++ test/phpunit/Helper/ResponseSimulator.php | 12 ++--- test/phpunit/RequestResolverTest.php | 46 ++++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 test/phpunit/Helper/NativeHandleTestCurl.php create mode 100644 test/phpunit/RequestResolverTest.php diff --git a/src/RequestResolver.php b/src/RequestResolver.php index 81f9330..41cbdfa 100644 --- a/src/RequestResolver.php +++ b/src/RequestResolver.php @@ -24,6 +24,8 @@ class RequestResolver { private array $headerList; /** @var array */ private array $integrityList; + /** @var array */ + private array $maxRedirectsList; /** @var array */ private array $signalList; @@ -38,6 +40,7 @@ public function __construct( $this->responseList = []; $this->headerList = []; $this->integrityList = []; + $this->maxRedirectsList = []; $this->signalList = []; } @@ -92,6 +95,10 @@ public function add( array_push($this->curlMultiList, $curlMulti); array_push($this->deferredList, $deferred); array_push($this->integrityList, $integrity); + array_push( + $this->maxRedirectsList, + $curlOptArray[CURLOPT_MAXREDIRS] ?? null + ); array_push($this->responseList, $bodyResponse); array_push($this->headerList, ""); array_push($this->signalList, $signal); @@ -198,7 +205,7 @@ private function writeHeader( } if(str_starts_with(strtolower($headerLine), "location: ")) { - if($ch->getInfo(CURLOPT_MAXREDIRS) === 0) { + if($this->maxRedirectsList[$i] === 0) { throw new FetchException("Redirect is disallowed"); } } diff --git a/test/phpunit/Helper/NativeHandleTestCurl.php b/test/phpunit/Helper/NativeHandleTestCurl.php new file mode 100644 index 0000000..e9adbb1 --- /dev/null +++ b/test/phpunit/Helper/NativeHandleTestCurl.php @@ -0,0 +1,8 @@ +ch; + } +} diff --git a/test/phpunit/Helper/ResponseSimulator.php b/test/phpunit/Helper/ResponseSimulator.php index 304889e..a02719d 100644 --- a/test/phpunit/Helper/ResponseSimulator.php +++ b/test/phpunit/Helper/ResponseSimulator.php @@ -1,7 +1,7 @@ getInfo(CURLINFO_EFFECTIVE_URL); if($url === "test://should-redirect") { - $locationRedirect = "Location: /redirected"; + $locationRedirect = "Location: /redirected\r\n"; if(!self::$redirectAdded) { array_splice(self::$headerBuffer, 1, 0, $locationRedirect); self::$redirectAdded = true; @@ -88,7 +89,7 @@ static public function sendChunk(Curl $ch):int { call_user_func( self::$headerCallback, - $ch, + $ch->getHandle(), $data ); @@ -100,13 +101,14 @@ static public function sendChunk(Curl $ch):int { call_user_func( self::$bodyCallback, - $ch, + $ch->getHandle(), $data ); return 1; } else { + self::$started = false; return 0; } } diff --git a/test/phpunit/RequestResolverTest.php b/test/phpunit/RequestResolverTest.php new file mode 100644 index 0000000..80bb8c9 --- /dev/null +++ b/test/phpunit/RequestResolverTest.php @@ -0,0 +1,46 @@ +startDeferredResponse($curl); + + $this->setProperty($sut, "curlList", [$curl]); + $this->setProperty($sut, "responseList", [$response]); + $this->setProperty($sut, "headerList", [""]); + $this->setProperty($sut, "maxRedirectsList", [0]); + $this->setProperty($sut, "signalList", [null]); + + $writeHeader = new ReflectionMethod($sut, "writeHeader"); + + self::expectException(FetchException::class); + self::expectExceptionMessage("Redirect is disallowed"); + $writeHeader->invoke($sut, $curl->getHandle(), "Location: /redirected\r\n"); + } + + private function setProperty( + RequestResolver $requestResolver, + string $property, + array $value, + ):void { + $reflectionProperty = new ReflectionProperty($requestResolver, $property); + $reflectionProperty->setValue($requestResolver, $value); + } +}