-
-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from clue/redirect-handler
Refactor to move redirects to new `RedirectHandler`
- Loading branch information
Showing
10 changed files
with
206 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace FrameworkX; | ||
|
||
use Psr\Http\Message\ResponseInterface; | ||
use React\Http\Message\Response; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class RedirectHandler | ||
{ | ||
private $target; | ||
private $code; | ||
private $reason; | ||
|
||
/** @var HtmlHandler */ | ||
private $html; | ||
|
||
public function __construct(string $target, int $redirectStatusCode = 302) | ||
{ | ||
if ($redirectStatusCode < 300 || $redirectStatusCode === 304 || $redirectStatusCode >= 400) { | ||
throw new \InvalidArgumentException('Invalid redirect status code given'); | ||
} | ||
|
||
$this->target = $target; | ||
$this->code = $redirectStatusCode; | ||
$this->reason = \ucwords((new Response($redirectStatusCode))->getReasonPhrase()) ?: 'Redirect'; | ||
$this->html = new HtmlHandler(); | ||
} | ||
|
||
public function __invoke(): ResponseInterface | ||
{ | ||
$url = $this->html->escape($this->target); | ||
|
||
return $this->html->statusResponse( | ||
$this->code, | ||
'Redirecting to ' . $url, | ||
$this->reason, | ||
"<p>Redirecting to <a href=\"$url\"><code>$url</code></a>...</p>\n" | ||
)->withHeader('Location', $this->target); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<?php | ||
|
||
namespace Framework\Tests; | ||
|
||
use FrameworkX\RedirectHandler; | ||
use PHPUnit\Framework\TestCase; | ||
use Psr\Http\Message\ResponseInterface; | ||
|
||
class RedirectHandlerTest extends TestCase | ||
{ | ||
public function testInvokeReturnsResponseWithRedirectToGivenLocation() | ||
{ | ||
$handler = new RedirectHandler('http://example.com/'); | ||
|
||
$response = $handler(); | ||
|
||
/** @var ResponseInterface $response */ | ||
$this->assertInstanceOf(ResponseInterface::class, $response); | ||
$this->assertEquals('text/html; charset=utf-8', $response->getHeaderLine('Content-Type')); | ||
$this->assertStringMatchesFormat("<!DOCTYPE html>\n<html>%a</html>\n", (string) $response->getBody()); | ||
$this->assertStringMatchesFormat("%a<style nonce=\"%s\">\n%a", (string) $response->getBody()); | ||
$this->assertStringMatchesFormat('style-src \'nonce-%s\'; img-src \'self\'; default-src \'none\'', $response->getHeaderLine('Content-Security-Policy')); | ||
|
||
$this->assertEquals(302, $response->getStatusCode()); | ||
$this->assertEquals('http://example.com/', $response->getHeaderLine('Location')); | ||
$this->assertStringContainsString("<title>Redirecting to http://example.com/</title>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<h1>302</h1>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<strong>Found</strong>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<p>Redirecting to <a href=\"http://example.com/\"><code>http://example.com/</code></a>...</p>\n", (string) $response->getBody()); | ||
} | ||
|
||
public function testInvokeReturnsResponseWithPermanentRedirectToGivenLocationAndCode() | ||
{ | ||
$handler = new RedirectHandler('/', 301); | ||
|
||
$response = $handler(); | ||
|
||
/** @var ResponseInterface $response */ | ||
$this->assertInstanceOf(ResponseInterface::class, $response); | ||
|
||
$this->assertEquals(301, $response->getStatusCode()); | ||
$this->assertEquals('/', $response->getHeaderLine('Location')); | ||
$this->assertStringContainsString("<title>Redirecting to /</title>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<h1>301</h1>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<strong>Moved Permanently</strong>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<p>Redirecting to <a href=\"/\"><code>/</code></a>...</p>\n", (string) $response->getBody()); | ||
} | ||
|
||
public function testInvokeReturnsResponseWithCustomRedirectStatusCodeAndGivenLocation() | ||
{ | ||
$handler = new RedirectHandler('/', 399); | ||
|
||
$response = $handler(); | ||
|
||
/** @var ResponseInterface $response */ | ||
$this->assertInstanceOf(ResponseInterface::class, $response); | ||
|
||
$this->assertEquals(399, $response->getStatusCode()); | ||
$this->assertEquals('/', $response->getHeaderLine('Location')); | ||
$this->assertStringContainsString("<title>Redirecting to /</title>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<h1>399</h1>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<strong>Redirect</strong>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<p>Redirecting to <a href=\"/\"><code>/</code></a>...</p>\n", (string) $response->getBody()); | ||
} | ||
|
||
public function testInvokeReturnsResponseWithRedirectToGivenLocationWithSpecialCharsEncoded() | ||
{ | ||
$handler = new RedirectHandler('/hello%20w%7Frld?a=1&b=2'); | ||
|
||
$response = $handler(); | ||
|
||
/** @var ResponseInterface $response */ | ||
$this->assertInstanceOf(ResponseInterface::class, $response); | ||
|
||
$this->assertEquals(302, $response->getStatusCode()); | ||
$this->assertEquals('/hello%20w%7Frld?a=1&b=2', $response->getHeaderLine('Location')); | ||
$this->assertStringContainsString("<title>Redirecting to /hello%20w%7Frld?a=1&b=2</title>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<h1>302</h1>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<strong>Found</strong>\n", (string) $response->getBody()); | ||
$this->assertStringContainsString("<p>Redirecting to <a href=\"/hello%20w%7Frld?a=1&b=2\"><code>/hello%20w%7Frld?a=1&b=2</code></a>...</p>\n", (string) $response->getBody()); | ||
} | ||
|
||
public function testConstructWithSuccessCodeThrows() | ||
{ | ||
$this->expectException(\InvalidArgumentException::class); | ||
new RedirectHandler('/', 200); | ||
} | ||
|
||
public function testConstructWithNotModifiedCodeThrows() | ||
{ | ||
$this->expectException(\InvalidArgumentException::class); | ||
new RedirectHandler('/', 304); | ||
} | ||
|
||
public function testConstructWithBadRequestCodeThrows() | ||
{ | ||
$this->expectException(\InvalidArgumentException::class); | ||
new RedirectHandler('/', 400); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters