mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Added support to serve redirects with status 301 and Cache-Control
This commit is contained in:
parent
186168b26c
commit
68db52679b
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_STATUS_CODE;
|
||||||
use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
|
use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -15,6 +16,8 @@ return [
|
|||||||
'anonymize_remote_addr' => true,
|
'anonymize_remote_addr' => true,
|
||||||
'visits_webhooks' => [],
|
'visits_webhooks' => [],
|
||||||
'default_short_codes_length' => DEFAULT_SHORT_CODES_LENGTH,
|
'default_short_codes_length' => DEFAULT_SHORT_CODES_LENGTH,
|
||||||
|
'redirect_status_code' => DEFAULT_REDIRECT_STATUS_CODE,
|
||||||
|
'redirect_cache_lifetime' => 30,
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -4,11 +4,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||||
use Laminas\ServiceManager\ServiceManager;
|
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
return (function () {
|
return (function () {
|
||||||
/** @var ContainerInterface|ServiceManager $container */
|
/** @var ContainerInterface $container */
|
||||||
$container = include __DIR__ . '/container.php';
|
$container = include __DIR__ . '/container.php';
|
||||||
$em = $container->get(EntityManager::class);
|
$em = $container->get(EntityManager::class);
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ return [
|
|||||||
Service\ShortUrl\ShortUrlResolver::class,
|
Service\ShortUrl\ShortUrlResolver::class,
|
||||||
Service\VisitsTracker::class,
|
Service\VisitsTracker::class,
|
||||||
Options\AppOptions::class,
|
Options\AppOptions::class,
|
||||||
|
Options\UrlShortenerOptions::class,
|
||||||
'Logger_Shlink',
|
'Logger_Shlink',
|
||||||
],
|
],
|
||||||
Action\PixelAction::class => [
|
Action\PixelAction::class => [
|
||||||
|
@ -6,12 +6,15 @@ namespace Shlinkio\Shlink\Core;
|
|||||||
|
|
||||||
use Cake\Chronos\Chronos;
|
use Cake\Chronos\Chronos;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
use PUGX\Shortid\Factory as ShortIdFactory;
|
use PUGX\Shortid\Factory as ShortIdFactory;
|
||||||
|
|
||||||
use function sprintf;
|
use function sprintf;
|
||||||
|
|
||||||
const DEFAULT_SHORT_CODES_LENGTH = 5;
|
const DEFAULT_SHORT_CODES_LENGTH = 5;
|
||||||
const MIN_SHORT_CODES_LENGTH = 4;
|
const MIN_SHORT_CODES_LENGTH = 4;
|
||||||
|
const DEFAULT_REDIRECT_STATUS_CODE = StatusCodeInterface::STATUS_FOUND;
|
||||||
|
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||||
|
|
||||||
function generateRandomShortCode(int $length): string
|
function generateRandomShortCode(int $length): string
|
||||||
|
@ -4,18 +4,40 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Shlinkio\Shlink\Core\Action;
|
namespace Shlinkio\Shlink\Core\Action;
|
||||||
|
|
||||||
|
use Fig\Http\Message\StatusCodeInterface;
|
||||||
use Laminas\Diactoros\Response\RedirectResponse;
|
use Laminas\Diactoros\Response\RedirectResponse;
|
||||||
use Psr\Http\Message\ResponseInterface as Response;
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
use Psr\Http\Message\ServerRequestInterface;
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
use Psr\Http\Server\RequestHandlerInterface;
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Options;
|
||||||
|
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Service\VisitsTrackerInterface;
|
||||||
|
use function sprintf;
|
||||||
|
|
||||||
class RedirectAction extends AbstractTrackingAction
|
class RedirectAction extends AbstractTrackingAction implements StatusCodeInterface
|
||||||
{
|
{
|
||||||
|
private Options\UrlShortenerOptions $urlShortenerOptions;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ShortUrlResolverInterface $urlResolver,
|
||||||
|
VisitsTrackerInterface $visitTracker,
|
||||||
|
Options\AppOptions $appOptions,
|
||||||
|
Options\UrlShortenerOptions $urlShortenerOptions,
|
||||||
|
?LoggerInterface $logger = null
|
||||||
|
) {
|
||||||
|
parent::__construct($urlResolver, $visitTracker, $appOptions, $logger);
|
||||||
|
$this->urlShortenerOptions = $urlShortenerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
protected function createSuccessResp(string $longUrl): Response
|
protected function createSuccessResp(string $longUrl): Response
|
||||||
{
|
{
|
||||||
// Return a redirect response to the long URL.
|
$statusCode = $this->urlShortenerOptions->redirectStatusCode();
|
||||||
// Use a temporary redirect to make sure browsers always hit the server for analytics purposes
|
$headers = $statusCode === self::STATUS_FOUND ? [] : [
|
||||||
return new RedirectResponse($longUrl);
|
'Cache-Control' => sprintf('private,max-age=%s', $this->urlShortenerOptions->redirectCacheLifetime()),
|
||||||
|
];
|
||||||
|
|
||||||
|
return new RedirectResponse($longUrl, $statusCode, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createErrorResp(ServerRequestInterface $request, RequestHandlerInterface $handler): Response
|
protected function createErrorResp(ServerRequestInterface $request, RequestHandlerInterface $handler): Response
|
||||||
|
@ -6,20 +6,50 @@ namespace Shlinkio\Shlink\Core\Options;
|
|||||||
|
|
||||||
use Laminas\Stdlib\AbstractOptions;
|
use Laminas\Stdlib\AbstractOptions;
|
||||||
|
|
||||||
|
use function Functional\contains;
|
||||||
|
|
||||||
|
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_STATUS_CODE;
|
||||||
|
|
||||||
class UrlShortenerOptions extends AbstractOptions
|
class UrlShortenerOptions extends AbstractOptions
|
||||||
{
|
{
|
||||||
protected $__strictMode__ = false; // phpcs:ignore
|
protected $__strictMode__ = false; // phpcs:ignore
|
||||||
|
|
||||||
private bool $validateUrl = true;
|
private bool $validateUrl = true;
|
||||||
|
private int $redirectStatusCode = DEFAULT_REDIRECT_STATUS_CODE;
|
||||||
|
private int $redirectCacheLifetime = 30;
|
||||||
|
|
||||||
public function isUrlValidationEnabled(): bool
|
public function isUrlValidationEnabled(): bool
|
||||||
{
|
{
|
||||||
return $this->validateUrl;
|
return $this->validateUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setValidateUrl(bool $validateUrl): self
|
protected function setValidateUrl(bool $validateUrl): void
|
||||||
{
|
{
|
||||||
$this->validateUrl = $validateUrl;
|
$this->validateUrl = $validateUrl;
|
||||||
return $this;
|
}
|
||||||
|
|
||||||
|
public function redirectStatusCode(): int
|
||||||
|
{
|
||||||
|
return $this->redirectStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setRedirectStatusCode(int $redirectStatusCode): void
|
||||||
|
{
|
||||||
|
$this->redirectStatusCode = $this->normalizeRedirectStatusCode($redirectStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeRedirectStatusCode(int $statusCode): int
|
||||||
|
{
|
||||||
|
return contains([301, 302], $statusCode) ? $statusCode : 302;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirectCacheLifetime(): int
|
||||||
|
{
|
||||||
|
return $this->redirectCacheLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setRedirectCacheLifetime(int $redirectCacheLifetime): void
|
||||||
|
{
|
||||||
|
$this->redirectCacheLifetime = $redirectCacheLifetime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user