Added support for redirect status code 307 and 308

This commit is contained in:
Alejandro Celaya 2023-01-07 11:27:15 +01:00
parent 85464f0fbb
commit 390bc59d99
6 changed files with 36 additions and 14 deletions

View File

@ -16,7 +16,7 @@ return [
],
'redirects' => [
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE->value),
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
DEFAULT_REDIRECT_CACHE_LIFETIME,
),

View File

@ -4,12 +4,12 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Fig\Http\Message\StatusCodeInterface;
use Shlinkio\Shlink\Core\Util\RedirectStatus;
const DEFAULT_DELETE_SHORT_URL_THRESHOLD = 15;
const DEFAULT_SHORT_CODES_LENGTH = 5;
const MIN_SHORT_CODES_LENGTH = 4;
const DEFAULT_REDIRECT_STATUS_CODE = StatusCodeInterface::STATUS_FOUND;
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag

View File

@ -4,23 +4,22 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Options;
use function Functional\contains;
use Fig\Http\Message\StatusCodeInterface;
use Shlinkio\Shlink\Core\Util\RedirectStatus;
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
final class RedirectOptions
{
public readonly int $redirectStatusCode;
public readonly RedirectStatus $redirectStatusCode;
public readonly int $redirectCacheLifetime;
public function __construct(
int $redirectStatusCode = DEFAULT_REDIRECT_STATUS_CODE,
int $redirectStatusCode = StatusCodeInterface::STATUS_FOUND,
int $redirectCacheLifetime = DEFAULT_REDIRECT_CACHE_LIFETIME,
) {
$this->redirectStatusCode = contains([301, 302], $redirectStatusCode)
? $redirectStatusCode
: DEFAULT_REDIRECT_STATUS_CODE;
$this->redirectStatusCode = RedirectStatus::tryFrom($redirectStatusCode) ?? DEFAULT_REDIRECT_STATUS_CODE;
$this->redirectCacheLifetime = $redirectCacheLifetime > 0
? $redirectCacheLifetime
: DEFAULT_REDIRECT_CACHE_LIFETIME;

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\Util;
use Fig\Http\Message\StatusCodeInterface;
use Laminas\Diactoros\Response\RedirectResponse;
use Psr\Http\Message\ResponseInterface;
use Shlinkio\Shlink\Core\Options\RedirectOptions;
@ -13,17 +12,17 @@ use function sprintf;
class RedirectResponseHelper implements RedirectResponseHelperInterface
{
public function __construct(private RedirectOptions $options)
public function __construct(private readonly RedirectOptions $options)
{
}
public function buildRedirectResponse(string $location): ResponseInterface
{
$statusCode = $this->options->redirectStatusCode;
$headers = $statusCode === StatusCodeInterface::STATUS_FOUND ? [] : [
$headers = ! $statusCode->allowsCache() ? [] : [
'Cache-Control' => sprintf('private,max-age=%s', $this->options->redirectCacheLifetime),
];
return new RedirectResponse($location, $statusCode, $headers);
return new RedirectResponse($location, $statusCode->value, $headers);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Shlinkio\Shlink\Core\Util;
use Fig\Http\Message\StatusCodeInterface;
use function Functional\contains;
enum RedirectStatus: int
{
case STATUS_301 = StatusCodeInterface::STATUS_MOVED_PERMANENTLY;
case STATUS_302 = StatusCodeInterface::STATUS_FOUND;
case STATUS_307 = StatusCodeInterface::STATUS_TEMPORARY_REDIRECT;
case STATUS_308 = StatusCodeInterface::STATUS_PERMANENT_REDIRECT;
public function allowsCache(): bool
{
return contains([self::STATUS_301, self::STATUS_308], $this);
}
}

View File

@ -36,11 +36,15 @@ class RedirectResponseHelperTest extends TestCase
public function provideRedirectConfigs(): iterable
{
yield 'status 302' => [302, 20, 302, null];
yield 'status over 302' => [400, 20, 302, null];
yield 'status 307' => [307, 20, 307, null];
yield 'status over 308' => [400, 20, 302, null];
yield 'status below 301' => [201, 20, 302, null];
yield 'status 301 with valid expiration' => [301, 20, 301, 'private,max-age=20'];
yield 'status 301 with zero expiration' => [301, 0, 301, 'private,max-age=30'];
yield 'status 301 with negative expiration' => [301, -20, 301, 'private,max-age=30'];
yield 'status 308 with valid expiration' => [308, 20, 308, 'private,max-age=20'];
yield 'status 308 with zero expiration' => [308, 0, 308, 'private,max-age=30'];
yield 'status 308 with negative expiration' => [308, -20, 308, 'private,max-age=30'];
}
private function helper(?RedirectOptions $options = null): RedirectResponseHelper