Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

CorsService.php

Namespace

Asm89\Stack

File

vendor/asm89/stack-cors/src/CorsService.php

View source
<?php


/*
 * This file is part of asm89/stack-cors.
 *
 * (c) Alexander <iam.asm89@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Asm89\Stack;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class CorsService {
    private $options;
    public function __construct(array $options = []) {
        $this->options = $this->normalizeOptions($options);
    }
    private function normalizeOptions(array $options = []) : array {
        $options += [
            'allowedOrigins' => [],
            'allowedOriginsPatterns' => [],
            'supportsCredentials' => false,
            'allowedHeaders' => [],
            'exposedHeaders' => [],
            'allowedMethods' => [],
            'maxAge' => 0,
        ];
        // normalize array('*') to true
        if (in_array('*', $options['allowedOrigins'])) {
            $options['allowedOrigins'] = true;
        }
        if (in_array('*', $options['allowedHeaders'])) {
            $options['allowedHeaders'] = true;
        }
        else {
            $options['allowedHeaders'] = array_map('strtolower', $options['allowedHeaders']);
        }
        if (in_array('*', $options['allowedMethods'])) {
            $options['allowedMethods'] = true;
        }
        else {
            $options['allowedMethods'] = array_map('strtoupper', $options['allowedMethods']);
        }
        return $options;
    }
    
    /**
     * @deprecated use isOriginAllowed
     */
    public function isActualRequestAllowed(Request $request) : bool {
        return $this->isOriginAllowed($request);
    }
    public function isCorsRequest(Request $request) : bool {
        return $request->headers
            ->has('Origin');
    }
    public function isPreflightRequest(Request $request) : bool {
        return $request->getMethod() === 'OPTIONS' && $request->headers
            ->has('Access-Control-Request-Method');
    }
    public function handlePreflightRequest(Request $request) : Response {
        $response = new Response();
        $response->setStatusCode(204);
        return $this->addPreflightRequestHeaders($response, $request);
    }
    public function addPreflightRequestHeaders(Response $response, Request $request) : Response {
        $this->configureAllowedOrigin($response, $request);
        if ($response->headers
            ->has('Access-Control-Allow-Origin')) {
            $this->configureAllowCredentials($response, $request);
            $this->configureAllowedMethods($response, $request);
            $this->configureAllowedHeaders($response, $request);
            $this->configureMaxAge($response, $request);
        }
        return $response;
    }
    public function isOriginAllowed(Request $request) : bool {
        if ($this->options['allowedOrigins'] === true) {
            return true;
        }
        if (!$request->headers
            ->has('Origin')) {
            return false;
        }
        $origin = $request->headers
            ->get('Origin');
        if (in_array($origin, $this->options['allowedOrigins'])) {
            return true;
        }
        foreach ($this->options['allowedOriginsPatterns'] as $pattern) {
            if (preg_match($pattern, $origin)) {
                return true;
            }
        }
        return false;
    }
    public function addActualRequestHeaders(Response $response, Request $request) : Response {
        $this->configureAllowedOrigin($response, $request);
        if ($response->headers
            ->has('Access-Control-Allow-Origin')) {
            $this->configureAllowCredentials($response, $request);
            $this->configureExposedHeaders($response, $request);
        }
        return $response;
    }
    private function configureAllowedOrigin(Response $response, Request $request) {
        if ($this->options['allowedOrigins'] === true && !$this->options['supportsCredentials']) {
            // Safe+cacheable, allow everything
            $response->headers
                ->set('Access-Control-Allow-Origin', '*');
        }
        elseif ($this->isSingleOriginAllowed()) {
            // Single origins can be safely set
            $response->headers
                ->set('Access-Control-Allow-Origin', array_values($this->options['allowedOrigins'])[0]);
        }
        else {
            // For dynamic headers, set the requested Origin header when set and allowed
            if ($this->isCorsRequest($request) && $this->isOriginAllowed($request)) {
                $response->headers
                    ->set('Access-Control-Allow-Origin', $request->headers
                    ->get('Origin'));
            }
            $this->varyHeader($response, 'Origin');
        }
    }
    private function isSingleOriginAllowed() : bool {
        if ($this->options['allowedOrigins'] === true || !empty($this->options['allowedOriginsPatterns'])) {
            return false;
        }
        return count($this->options['allowedOrigins']) === 1;
    }
    private function configureAllowedMethods(Response $response, Request $request) {
        if ($this->options['allowedMethods'] === true) {
            $allowMethods = strtoupper($request->headers
                ->get('Access-Control-Request-Method'));
            $this->varyHeader($response, 'Access-Control-Request-Method');
        }
        else {
            $allowMethods = implode(', ', $this->options['allowedMethods']);
        }
        $response->headers
            ->set('Access-Control-Allow-Methods', $allowMethods);
    }
    private function configureAllowedHeaders(Response $response, Request $request) {
        if ($this->options['allowedHeaders'] === true) {
            $allowHeaders = $request->headers
                ->get('Access-Control-Request-Headers');
            $this->varyHeader($response, 'Access-Control-Request-Headers');
        }
        else {
            $allowHeaders = implode(', ', $this->options['allowedHeaders']);
        }
        $response->headers
            ->set('Access-Control-Allow-Headers', $allowHeaders);
    }
    private function configureAllowCredentials(Response $response, Request $request) {
        if ($this->options['supportsCredentials']) {
            $response->headers
                ->set('Access-Control-Allow-Credentials', 'true');
        }
    }
    private function configureExposedHeaders(Response $response, Request $request) {
        if ($this->options['exposedHeaders']) {
            $response->headers
                ->set('Access-Control-Expose-Headers', implode(', ', $this->options['exposedHeaders']));
        }
    }
    private function configureMaxAge(Response $response, Request $request) {
        if ($this->options['maxAge'] !== null) {
            $response->headers
                ->set('Access-Control-Max-Age', (int) $this->options['maxAge']);
        }
    }
    public function varyHeader(Response $response, $header) : Response {
        if (!$response->headers
            ->has('Vary')) {
            $response->headers
                ->set('Vary', $header);
        }
        elseif (!in_array($header, explode(', ', $response->headers
            ->get('Vary')))) {
            $response->headers
                ->set('Vary', $response->headers
                ->get('Vary') . ', ' . $header);
        }
        return $response;
    }
    private function isSameHost(Request $request) : bool {
        return $request->headers
            ->get('Origin') === $request->getSchemeAndHttpHost();
    }

}

Classes

Title Deprecated Summary
CorsService

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal