feat: add configurable HLS CORS allowlists
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Plugin\AzuraCastOnDemandHls\Cors;
|
||||
|
||||
interface CorsConfigurationProvider
|
||||
{
|
||||
/** @return list<string> */
|
||||
public function getAllowedOrigins(int $stationId): array;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Plugin\AzuraCastOnDemandHls\Cors;
|
||||
|
||||
/** Builds response headers without ever combining multiple origins into one header. */
|
||||
final class CorsPolicy
|
||||
{
|
||||
/** @param list<string> $allowedOrigins */
|
||||
public function __construct(private array $allowedOrigins)
|
||||
{
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function headersFor(?string $requestOrigin, bool $preflight = false): array
|
||||
{
|
||||
if (null === $requestOrigin || '' === trim($requestOrigin)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$origin = Origin::normalize($requestOrigin);
|
||||
} catch (\InvalidArgumentException) {
|
||||
return ['Vary' => 'Origin'];
|
||||
}
|
||||
|
||||
if (!in_array($origin, $this->allowedOrigins, true)) {
|
||||
return ['Vary' => 'Origin'];
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Access-Control-Allow-Origin' => $origin,
|
||||
'Vary' => 'Origin',
|
||||
];
|
||||
|
||||
if ($preflight) {
|
||||
$headers += [
|
||||
'Access-Control-Allow-Methods' => 'GET, HEAD, OPTIONS',
|
||||
'Access-Control-Allow-Headers' => 'Range',
|
||||
'Access-Control-Max-Age' => '600',
|
||||
];
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Plugin\AzuraCastOnDemandHls\Cors;
|
||||
|
||||
final class NginxRules
|
||||
{
|
||||
/** @param list<string> $origins */
|
||||
public static function forOrigins(array $origins): string
|
||||
{
|
||||
$rules = ' set $ondemand_hls_cors_origin "";' . "\n";
|
||||
$rules .= ' set $ondemand_hls_cors_vary "";' . "\n";
|
||||
$rules .= ' if ($http_origin != "") { set $ondemand_hls_cors_vary "Origin"; }' . "\n";
|
||||
foreach ($origins as $origin) {
|
||||
$rules .= ' if ($http_origin = "' . Origin::normalize($origin)
|
||||
. '") { set $ondemand_hls_cors_origin $http_origin; }' . "\n";
|
||||
}
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Plugin\AzuraCastOnDemandHls\Cors;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/** Validates and canonicalizes web origins used by the HLS CORS allowlist. */
|
||||
final class Origin
|
||||
{
|
||||
public static function normalize(string $origin): string
|
||||
{
|
||||
$origin = trim($origin);
|
||||
if ('' === $origin || 'null' === strtolower($origin)) {
|
||||
throw new InvalidArgumentException('A CORS origin must be an absolute HTTP or HTTPS origin.');
|
||||
}
|
||||
|
||||
$parts = parse_url($origin);
|
||||
if (false === $parts
|
||||
|| !isset($parts['scheme'], $parts['host'])
|
||||
|| isset($parts['user'], $parts['pass'], $parts['query'], $parts['fragment'])
|
||||
|| (isset($parts['path']) && '/' !== $parts['path'])
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid CORS origin: %s', $origin));
|
||||
}
|
||||
|
||||
$scheme = strtolower($parts['scheme']);
|
||||
$host = strtolower($parts['host']);
|
||||
if (!in_array($scheme, ['http', 'https'], true)
|
||||
|| '' === $host
|
||||
|| !preg_match('/^[a-z0-9.-]+$/D', $host)
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid CORS origin: %s', $origin));
|
||||
}
|
||||
|
||||
$port = $parts['port'] ?? null;
|
||||
if (null !== $port && ($port < 1 || $port > 65535)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid CORS origin: %s', $origin));
|
||||
}
|
||||
|
||||
if (('https' === $scheme && 443 === $port) || ('http' === $scheme && 80 === $port)) {
|
||||
$port = null;
|
||||
}
|
||||
|
||||
return $scheme . '://' . $host . (null === $port ? '' : ':' . $port);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user