welcome back to dyb-tech
This commit is contained in:
+110
@@ -0,0 +1,110 @@
|
||||
Newer changelog entries can be found in the [GitHub Releases](https://github.com/nelmio/NelmioCorsBundle/releases)
|
||||
|
||||
### 2.3.0 (2023-02-15)
|
||||
|
||||
* Downgraded `CacheableResponseVaryListener`'s priority from 0 to -10 to ensure it runs after FrameworkExtraBundle listeners have set their cache headers (#179)
|
||||
* Added optional logging support if you inject a Logger into the CorsListener you can get debug info about the whole CORS decision process (#173)
|
||||
* Added support for setting `expose_headers` to a wildcard `'*'` which exposes all headers, this works as long as allow_credentials is not enabled as per the spec (#132)
|
||||
* Added `skip_same_as_origin` flag (default to true which is the old behavior) to allow opting out of skipping the CORS headers in the response if the Origin matches the application's hostname (#178)
|
||||
* Fixed ProviderMock having an invalid return type (#169)
|
||||
* Dropped support for Symfony 4.3 and 5.0 to 5.3
|
||||
|
||||
### 2.2.0 (2021-12-01)
|
||||
|
||||
* Added support for Symfony 6
|
||||
|
||||
### 2.1.1 (2021-04-20)
|
||||
|
||||
* Fixed response for unauthorized headers containing a reflected XSS (https://github.com/nelmio/NelmioCorsBundle/pull/163)
|
||||
|
||||
### 2.1.0 (2020-07-22)
|
||||
|
||||
* Added `Vary: Origin` header to cacheable responses to make sure proxies cache them correctly
|
||||
|
||||
### 2.0.1 (2019-11-15)
|
||||
|
||||
* Reverted CorsListener priority change as it was interfering with normal operations. The priority is back at 250.
|
||||
|
||||
### 2.0.0 (2019-11-12)
|
||||
|
||||
* BC Break: Downgraded CorsListener priority from 250 to 28, this should not affect anyone but could be a source in case of strange bugs
|
||||
* BC Break: Removed support for Symfony <4.3
|
||||
* BC Break: Removed support for PHP <7.1
|
||||
* Added support for Symfony 5
|
||||
* Added support for configuration via env vars
|
||||
* Changed the code to avoid mutating the EventDispatcher at runtime
|
||||
* Changed the code to avoid returning `Access-Control-Allow-Origin: null` headers to mark blocked requests
|
||||
|
||||
### 1.5.6 (2019-06-17)
|
||||
|
||||
* Fixed preflight request handler hijacking regular non-CORS OPTIONS requests.
|
||||
|
||||
### 1.5.5 (2019-02-27)
|
||||
|
||||
* Compatibility with Symfony 4.1
|
||||
* Fixed preflight responses to always include `Origin` in the `Vary` HTTP header
|
||||
|
||||
### 1.5.4 (2017-12-11)
|
||||
|
||||
* Compatibility with Symfony 4
|
||||
|
||||
### 1.5.3 (2017-04-24)
|
||||
|
||||
* Fixed regression in 1.5.2
|
||||
|
||||
### 1.5.2 (2017-04-21)
|
||||
|
||||
* Fixed bundle initialization in case paths is empty
|
||||
|
||||
### 1.5.1 (2017-01-22)
|
||||
|
||||
* Fixed `forced_allow_origin_value` to always set the header regardless of CORS, so that requests can properly be cached even if they are not always accessed via CORS
|
||||
|
||||
### 1.5.0 (2016-12-30)
|
||||
|
||||
* Added an `forced_allow_origin_value` option to force the value that is returned, in case you cache responses and can not have the allowed origin automatically set to the Origin header
|
||||
* Fixed `Access-Control-Allow-Headers` being sent even when it was empty
|
||||
* Fixed listener priority down to 250 (This **may be BREAKING** depending on what you do with your own listeners, but should be fine in most cases, just watch out).
|
||||
|
||||
### 1.4.1 (2015-12-09)
|
||||
|
||||
* Fixed requirements to allow Symfony3
|
||||
|
||||
### 1.4.0 (2015-01-13)
|
||||
|
||||
* Added an `origin_regex` option to allow defining origins based on regular expressions
|
||||
|
||||
### 1.3.3 (2014-12-10)
|
||||
|
||||
* Fixed a security regression in 1.3.2 that allowed GET requests to be executed from any domain
|
||||
|
||||
### 1.3.2 (2014-09-18)
|
||||
|
||||
* Removed 403 responses on non-OPTIONS requests that have an invalid origin header
|
||||
|
||||
### 1.3.1 (2014-07-21)
|
||||
|
||||
* Fixed path key normalization to allow dashes in paths
|
||||
* Fixed HTTP method case folding to support clients that send non-uppercased method names
|
||||
|
||||
### 1.3.0 (2014-02-06)
|
||||
|
||||
* Added support for host-based configuration of the bundle
|
||||
|
||||
### 1.2.0 (2013-10-29)
|
||||
|
||||
* Bumped symfony dependency to 2.1.0+
|
||||
* Fixed invalid trigger of the CORS check when the Origin header is present on same-host requests
|
||||
* Fixed fatal error when `allow_methods` was not configured for a given path
|
||||
|
||||
### 1.1.1 (2013-08-14)
|
||||
|
||||
* Fixed issue when `allow_origin` is set to `*` and `allow_credentials` to `true`.
|
||||
|
||||
### 1.1.0 (2013-07-29)
|
||||
|
||||
* Added ability to set a wildcard on accept_headers
|
||||
|
||||
### 1.0.0 (2013-01-07)
|
||||
|
||||
* Initial release
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Nelmio\CorsBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Compiler pass for the nelmio_cors.configuration.provider tag.
|
||||
*/
|
||||
class CorsConfigurationProviderPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
if (!$container->hasDefinition('nelmio_cors.options_resolver')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resolverDefinition = $container->getDefinition('nelmio_cors.options_resolver');
|
||||
|
||||
$optionsProvidersByPriority = [];
|
||||
foreach ($container->findTaggedServiceIds('nelmio_cors.options_provider') as $taggedServiceId => $tagAttributes) {
|
||||
foreach ($tagAttributes as $attribute) {
|
||||
$priority = isset($attribute['priority']) ? $attribute['priority'] : 0;
|
||||
$optionsProvidersByPriority[$priority][] = new Reference($taggedServiceId);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($optionsProvidersByPriority) > 0) {
|
||||
$resolverDefinition->setArguments(
|
||||
[$this->sortProviders($optionsProvidersByPriority)]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a two-dimensions array of providers, indexed by priority, into a flat array of Reference objects
|
||||
* @param array $providersByPriority
|
||||
* @return Reference[]
|
||||
*/
|
||||
protected function sortProviders(array $providersByPriority): array
|
||||
{
|
||||
ksort($providersByPriority);
|
||||
|
||||
return call_user_func_array('array_merge', $providersByPriority);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('nelmio_cors');
|
||||
|
||||
if (method_exists($treeBuilder, 'getRootNode')) {
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
} else {
|
||||
// BC for symfony/config < 4.2
|
||||
$rootNode = $treeBuilder->root('nelmio_cors');
|
||||
}
|
||||
|
||||
$rootNode
|
||||
->children()
|
||||
->arrayNode('defaults')
|
||||
->addDefaultsIfNotSet()
|
||||
->append($this->getAllowCredentials())
|
||||
->append($this->getAllowOrigin())
|
||||
->append($this->getAllowHeaders())
|
||||
->append($this->getAllowMethods())
|
||||
->append($this->getExposeHeaders())
|
||||
->append($this->getMaxAge())
|
||||
->append($this->getHosts())
|
||||
->append($this->getOriginRegex())
|
||||
->append($this->getForcedAllowOriginValue())
|
||||
->append($this->getSkipSameAsOrigin())
|
||||
->end()
|
||||
|
||||
->arrayNode('paths')
|
||||
->useAttributeAsKey('path')
|
||||
->normalizeKeys(false)
|
||||
->prototype('array')
|
||||
->append($this->getAllowCredentials())
|
||||
->append($this->getAllowOrigin())
|
||||
->append($this->getAllowHeaders())
|
||||
->append($this->getAllowMethods())
|
||||
->append($this->getExposeHeaders())
|
||||
->append($this->getMaxAge())
|
||||
->append($this->getHosts())
|
||||
->append($this->getOriginRegex())
|
||||
->append($this->getForcedAllowOriginValue())
|
||||
->append($this->getSkipSameAsOrigin())
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
|
||||
private function getSkipSameAsOrigin(): BooleanNodeDefinition
|
||||
{
|
||||
$node = new BooleanNodeDefinition('skip_same_as_origin');
|
||||
$node->defaultTrue();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getAllowCredentials(): BooleanNodeDefinition
|
||||
{
|
||||
$node = new BooleanNodeDefinition('allow_credentials');
|
||||
$node->defaultFalse();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getAllowOrigin(): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('allow_origin');
|
||||
|
||||
$node
|
||||
->beforeNormalization()
|
||||
->always(function ($v) {
|
||||
if ($v === '*') {
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->prototype('scalar')->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getAllowHeaders(): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('allow_headers');
|
||||
|
||||
$node
|
||||
->beforeNormalization()
|
||||
->always(function ($v) {
|
||||
if ($v === '*') {
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->prototype('scalar')->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getAllowMethods(): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('allow_methods');
|
||||
|
||||
$node->prototype('scalar')->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getExposeHeaders(): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('expose_headers');
|
||||
|
||||
$node
|
||||
->beforeNormalization()
|
||||
->always(function ($v) {
|
||||
if ($v === '*') {
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->prototype('scalar')->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getMaxAge(): ScalarNodeDefinition
|
||||
{
|
||||
$node = new ScalarNodeDefinition('max_age');
|
||||
|
||||
$node
|
||||
->defaultValue(0)
|
||||
->validate()
|
||||
->ifTrue(function ($v) {
|
||||
return !is_numeric($v);
|
||||
})
|
||||
->thenInvalid('max_age must be an integer (seconds)')
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getHosts(): ArrayNodeDefinition
|
||||
{
|
||||
$node = new ArrayNodeDefinition('hosts');
|
||||
|
||||
$node->prototype('scalar')->end();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getOriginRegex(): BooleanNodeDefinition
|
||||
{
|
||||
$node = new BooleanNodeDefinition('origin_regex');
|
||||
$node->defaultFalse();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getForcedAllowOriginValue(): ScalarNodeDefinition
|
||||
{
|
||||
$node = new ScalarNodeDefinition('forced_allow_origin_value');
|
||||
$node->defaultNull();
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class NelmioCorsExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$defaults = array_merge(
|
||||
[
|
||||
'allow_origin' => [],
|
||||
'allow_credentials' => false,
|
||||
'allow_headers' => [],
|
||||
'expose_headers' => [],
|
||||
'allow_methods' => [],
|
||||
'max_age' => 0,
|
||||
'hosts' => [],
|
||||
'origin_regex' => false,
|
||||
],
|
||||
$config['defaults']
|
||||
);
|
||||
|
||||
if ($defaults['allow_credentials'] && in_array('*', $defaults['expose_headers'], true)) {
|
||||
throw new \UnexpectedValueException('nelmio_cors expose_headers cannot contain a wildcard (*) when allow_credentials is enabled.');
|
||||
}
|
||||
|
||||
// normalize array('*') to true
|
||||
if (in_array('*', $defaults['allow_origin'])) {
|
||||
$defaults['allow_origin'] = true;
|
||||
}
|
||||
if (in_array('*', $defaults['allow_headers'])) {
|
||||
$defaults['allow_headers'] = true;
|
||||
} else {
|
||||
$defaults['allow_headers'] = array_map('strtolower', $defaults['allow_headers']);
|
||||
}
|
||||
$defaults['allow_methods'] = array_map('strtoupper', $defaults['allow_methods']);
|
||||
|
||||
if ($config['paths']) {
|
||||
foreach ($config['paths'] as $path => $opts) {
|
||||
$opts = array_filter($opts);
|
||||
if (isset($opts['allow_origin']) && in_array('*', $opts['allow_origin'])) {
|
||||
$opts['allow_origin'] = true;
|
||||
}
|
||||
if (isset($opts['allow_headers']) && in_array('*', $opts['allow_headers'])) {
|
||||
$opts['allow_headers'] = true;
|
||||
} elseif (isset($opts['allow_headers'])) {
|
||||
$opts['allow_headers'] = array_map('strtolower', $opts['allow_headers']);
|
||||
}
|
||||
if (isset($opts['allow_methods'])) {
|
||||
$opts['allow_methods'] = array_map('strtoupper', $opts['allow_methods']);
|
||||
}
|
||||
|
||||
$config['paths'][$path] = $opts;
|
||||
}
|
||||
}
|
||||
|
||||
$container->setParameter('nelmio_cors.map', $config['paths']);
|
||||
$container->setParameter('nelmio_cors.defaults', $defaults);
|
||||
|
||||
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.xml');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Nelmio\CorsBundle\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
|
||||
/**
|
||||
* When a response is cacheable the `Vary` header has to include `Origin`.
|
||||
*/
|
||||
final class CacheableResponseVaryListener
|
||||
{
|
||||
public function onResponse(ResponseEvent $event)
|
||||
{
|
||||
$response = $event->getResponse();
|
||||
|
||||
if (!$response->isCacheable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!\in_array('Origin', $response->getVary(), true)) {
|
||||
$response->setVary(array_merge(['Origin'], $response->getVary()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\EventListener;
|
||||
|
||||
use Nelmio\CorsBundle\Options\ResolverInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Adds CORS headers and handles pre-flight requests
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class CorsListener
|
||||
{
|
||||
const SHOULD_ALLOW_ORIGIN_ATTR = '_nelmio_cors_should_allow_origin';
|
||||
const SHOULD_FORCE_ORIGIN_ATTR = '_nelmio_cors_should_force_origin';
|
||||
|
||||
/**
|
||||
* Simple headers as defined in the spec should always be accepted
|
||||
*/
|
||||
protected static $simpleHeaders = [
|
||||
'accept',
|
||||
'accept-language',
|
||||
'content-language',
|
||||
'origin',
|
||||
];
|
||||
|
||||
/** @var ResolverInterface */
|
||||
protected $configurationResolver;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
private $logger;
|
||||
|
||||
public function __construct(ResolverInterface $configurationResolver, ?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->configurationResolver = $configurationResolver;
|
||||
|
||||
if (null === $logger) {
|
||||
$logger = new NullLogger();
|
||||
}
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event): void
|
||||
{
|
||||
if (HttpKernelInterface::MAIN_REQUEST !== $event->getRequestType()) {
|
||||
$this->logger->debug('Not a master type request, skipping CORS checks.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (!$options = $this->configurationResolver->getOptions($request)) {
|
||||
$this->logger->debug('Could not get options for request, skipping CORS checks.');
|
||||
return;
|
||||
}
|
||||
|
||||
// if the "forced_allow_origin_value" option is set, add a listener which will set or override the "Access-Control-Allow-Origin" header
|
||||
if (!empty($options['forced_allow_origin_value'])) {
|
||||
$this->logger->debug(sprintf(
|
||||
"The 'forced_allow_origin_value' option is set to '%s', adding a listener to set or override the 'Access-Control-Allow-Origin' header.",
|
||||
$options['forced_allow_origin_value']
|
||||
));
|
||||
|
||||
$request->attributes->set(self::SHOULD_FORCE_ORIGIN_ATTR, true);
|
||||
}
|
||||
|
||||
// skip if not a CORS request
|
||||
if (!$request->headers->has('Origin')) {
|
||||
$this->logger->debug("Request does not have 'Origin' header, skipping CORS.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($options['skip_same_as_origin'] && $request->headers->get('Origin') === $request->getSchemeAndHttpHost()) {
|
||||
$this->logger->debug("The 'Origin' header of the request equals the scheme and host the request was sent to, skipping CORS.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// perform preflight checks
|
||||
if ('OPTIONS' === $request->getMethod() && $request->headers->has('Access-Control-Request-Method')) {
|
||||
$this->logger->debug("Request is a preflight check, setting event response now.");
|
||||
|
||||
$event->setResponse($this->getPreflightResponse($request, $options));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->checkOrigin($request, $options)) {
|
||||
$this->logger->debug("Origin check failed.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->debug("Origin is allowed, proceed with adding CORS response headers.");
|
||||
|
||||
$request->attributes->set(self::SHOULD_ALLOW_ORIGIN_ATTR, true);
|
||||
}
|
||||
|
||||
public function onKernelResponse(ResponseEvent $event): void
|
||||
{
|
||||
if (HttpKernelInterface::MAIN_REQUEST !== $event->getRequestType()) {
|
||||
$this->logger->debug("Not a master type request, skip adding CORS response headers.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
|
||||
$shouldAllowOrigin = $request->attributes->getBoolean(self::SHOULD_ALLOW_ORIGIN_ATTR);
|
||||
$shouldForceOrigin = $request->attributes->getBoolean(self::SHOULD_FORCE_ORIGIN_ATTR);
|
||||
|
||||
if (!$shouldAllowOrigin && !$shouldForceOrigin) {
|
||||
$this->logger->debug("The origin should not be allowed and not be forced, skip adding CORS response headers.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$options = $this->configurationResolver->getOptions($request)) {
|
||||
$this->logger->debug("Could not resolve options for request, skip adding CORS response headers.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($shouldAllowOrigin) {
|
||||
$response = $event->getResponse();
|
||||
// add CORS response headers
|
||||
$origin = $request->headers->get('Origin');
|
||||
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Allow-Origin' response header to '%s'.", $origin));
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Origin', $origin);
|
||||
|
||||
if ($options['allow_credentials']) {
|
||||
$this->logger->debug("Setting 'Access-Control-Allow-Credentials' to 'true'.");
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
if ($options['expose_headers']) {
|
||||
$headers = strtolower(implode(', ', $options['expose_headers']));
|
||||
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Expose-Headers' response header to '%s'.", $headers));
|
||||
|
||||
$response->headers->set('Access-Control-Expose-Headers', $headers);
|
||||
}
|
||||
}
|
||||
|
||||
if ($shouldForceOrigin) {
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Allow-Origin' response header to '%s'.", $options['forced_allow_origin_value']));
|
||||
|
||||
$event->getResponse()->headers->set('Access-Control-Allow-Origin', $options['forced_allow_origin_value']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPreflightResponse(Request $request, array $options): Response
|
||||
{
|
||||
$response = new Response();
|
||||
$response->setVary(['Origin']);
|
||||
|
||||
if ($options['allow_credentials']) {
|
||||
$this->logger->debug("Setting 'Access-Control-Allow-Credentials' response header to 'true'.");
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
if ($options['allow_methods']) {
|
||||
$methods = implode(', ', $options['allow_methods']);
|
||||
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Allow-Methods' response header to '%s'.", $methods));
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Methods', $methods);
|
||||
}
|
||||
if ($options['allow_headers']) {
|
||||
$headers = $this->isWildcard($options, 'allow_headers')
|
||||
? $request->headers->get('Access-Control-Request-Headers')
|
||||
: implode(', ', $options['allow_headers']);
|
||||
|
||||
if ($headers) {
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Allow-Headers' response header to '%s'.", $headers));
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Headers', $headers);
|
||||
}
|
||||
}
|
||||
if ($options['max_age']) {
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Max-Age' response header to '%d'.", $options['max_age']));
|
||||
|
||||
$response->headers->set('Access-Control-Max-Age', $options['max_age']);
|
||||
}
|
||||
|
||||
if (!$this->checkOrigin($request, $options)) {
|
||||
$this->logger->debug("Removing 'Access-Control-Allow-Origin' response header.");
|
||||
|
||||
$response->headers->remove('Access-Control-Allow-Origin');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$origin = $request->headers->get('Origin');
|
||||
|
||||
$this->logger->debug(sprintf("Setting 'Access-Control-Allow-Origin' response header to '%s'", $origin));
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Origin', $origin);
|
||||
|
||||
// check request method
|
||||
$method = strtoupper($request->headers->get('Access-Control-Request-Method'));
|
||||
if (!in_array($method, $options['allow_methods'], true)) {
|
||||
$this->logger->debug(sprintf("Method '%s' is not allowed.", $method));
|
||||
|
||||
$response->setStatusCode(405);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to allow the header in the case-set as we received it by the client.
|
||||
* Firefox f.e. sends the LINK method as "Link", and we have to allow it like this or the browser will deny the
|
||||
* request.
|
||||
*/
|
||||
if (!in_array($request->headers->get('Access-Control-Request-Method'), $options['allow_methods'], true)) {
|
||||
$options['allow_methods'][] = $request->headers->get('Access-Control-Request-Method');
|
||||
$response->headers->set('Access-Control-Allow-Methods', implode(', ', $options['allow_methods']));
|
||||
}
|
||||
|
||||
// check request headers
|
||||
$headers = $request->headers->get('Access-Control-Request-Headers');
|
||||
if ($headers && !$this->isWildcard($options, 'allow_headers')) {
|
||||
$headers = strtolower(trim($headers));
|
||||
foreach (preg_split('{, *}', $headers) as $header) {
|
||||
if (in_array($header, self::$simpleHeaders, true)) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($header, $options['allow_headers'], true)) {
|
||||
$sanitizedMessage = htmlentities('Unauthorized header '.$header, ENT_QUOTES, 'UTF-8');
|
||||
$response->setStatusCode(400);
|
||||
$response->setContent($sanitizedMessage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function checkOrigin(Request $request, array $options): bool
|
||||
{
|
||||
// check origin
|
||||
$origin = $request->headers->get('Origin');
|
||||
|
||||
if ($this->isWildcard($options, 'allow_origin')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($options['origin_regex'] === true) {
|
||||
// origin regex matching
|
||||
foreach ($options['allow_origin'] as $originRegexp) {
|
||||
$this->logger->debug(sprintf("Matching origin regex '%s' to origin '%s'.", $originRegexp, $origin));
|
||||
|
||||
if (preg_match('{'.$originRegexp.'}i', $origin)) {
|
||||
$this->logger->debug(sprintf("Origin regex '%s' matches origin '%s'.", $originRegexp, $origin));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// old origin matching
|
||||
if (in_array($origin, $options['allow_origin'])) {
|
||||
$this->logger->debug(sprintf("Origin '%s' is allowed.", $origin));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->debug(sprintf("Origin '%s' is not allowed.", $origin));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isWildcard(array $options, string $option): bool
|
||||
{
|
||||
$result = $options[$option] === true || (is_array($options[$option]) && in_array('*', $options[$option]));
|
||||
|
||||
$this->logger->debug(sprintf("Option '%s' is %s a wildcard.", $option, $result ? '' : 'not'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011 Nelmio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class NelmioCorsBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container): void
|
||||
{
|
||||
parent::build($container);
|
||||
$container->addCompilerPass(new DependencyInjection\Compiler\CorsConfigurationProviderPass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\Options;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Default CORS configuration provider.
|
||||
*
|
||||
* Uses the bundle's semantic configuration.
|
||||
* Default settings are the lowest priority one, and can be relied upon.
|
||||
*/
|
||||
class ConfigProvider implements ProviderInterface
|
||||
{
|
||||
protected $paths;
|
||||
protected $defaults;
|
||||
|
||||
public function __construct(array $paths, array $defaults = [])
|
||||
{
|
||||
$this->defaults = $defaults;
|
||||
$this->paths = $paths;
|
||||
}
|
||||
|
||||
public function getOptions(Request $request): array
|
||||
{
|
||||
$uri = $request->getPathInfo() ?: '/';
|
||||
foreach ($this->paths as $pathRegexp => $options) {
|
||||
if (preg_match('{'.$pathRegexp.'}i', $uri)) {
|
||||
$options = array_merge($this->defaults, $options);
|
||||
|
||||
// skip if the host is not matching
|
||||
if (count($options['hosts']) > 0) {
|
||||
foreach ($options['hosts'] as $hostRegexp) {
|
||||
if (preg_match('{'.$hostRegexp.'}i', $request->getHost())) {
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->defaults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\Options;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* CORS configuration provider interface.
|
||||
*
|
||||
* Can override CORS options for a particular path.
|
||||
*/
|
||||
interface ProviderInterface
|
||||
{
|
||||
/**
|
||||
* Returns CORS options for $request.
|
||||
*
|
||||
* Any valid CORS option will overwrite those of the previous ones.
|
||||
* The method must at least return an empty array.
|
||||
*
|
||||
* All keys of the bundle's semantical configuration are valid:
|
||||
* - bool allow_credentials
|
||||
* - bool allow_origin
|
||||
* - bool allow_headers
|
||||
* - bool origin_regex
|
||||
* - array allow_methods
|
||||
* - array expose_headers
|
||||
* - int max_age
|
||||
*
|
||||
* @return array CORS options
|
||||
*/
|
||||
public function getOptions(Request $request): array;
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace Nelmio\CorsBundle\Options;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* CORS options resolver.
|
||||
*
|
||||
* Uses Cors providers to resolve options for an HTTP request
|
||||
*/
|
||||
class Resolver implements ResolverInterface
|
||||
{
|
||||
/**
|
||||
* CORS configuration providers, indexed by numerical priority
|
||||
* @var ProviderInterface[][]
|
||||
*/
|
||||
private $providers;
|
||||
|
||||
/**
|
||||
* @param $providers ProviderInterface[]
|
||||
*/
|
||||
public function __construct(array $providers = [])
|
||||
{
|
||||
$this->providers = $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the options for $request based on {@see $providers} data
|
||||
*
|
||||
* @return array CORS options
|
||||
*/
|
||||
public function getOptions(Request $request): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($this->providers as $provider) {
|
||||
$options[] = $provider->getOptions($request);
|
||||
}
|
||||
|
||||
return array_merge(...$options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the NelmioCorsBundle.
|
||||
*
|
||||
* (c) Nelmio <hello@nelm.io>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\CorsBundle\Options;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
interface ResolverInterface
|
||||
{
|
||||
/**
|
||||
* Returns CORS options for $path
|
||||
*
|
||||
* @internal param string $path
|
||||
*/
|
||||
public function getOptions(Request $request): array;
|
||||
}
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
# NelmioCorsBundle
|
||||
|
||||
## About
|
||||
|
||||
The NelmioCorsBundle allows you to send [Cross-Origin Resource Sharing](http://enable-cors.org/)
|
||||
headers with ACL-style per-URL configuration.
|
||||
|
||||
## Features
|
||||
|
||||
* Handles CORS preflight OPTIONS requests
|
||||
* Adds CORS headers to your responses
|
||||
* Configured at the PHP/application level. This is convenient but it also means
|
||||
that any request serving static files and not going through Symfony will not
|
||||
have the CORS headers added, so if you need to serve CORS for static files you
|
||||
probably should rather configure these headers in your web server
|
||||
|
||||
## Installation
|
||||
|
||||
Require the `nelmio/cors-bundle` package in your composer.json and update your dependencies:
|
||||
|
||||
```bash
|
||||
composer require nelmio/cors-bundle
|
||||
```
|
||||
|
||||
The bundle should be automatically enabled by [Symfony Flex][1]. If you don't use
|
||||
Flex, you'll need to enable it manually as explained [in the docs][2].
|
||||
|
||||
## Usage
|
||||
|
||||
See [the documentation][2] for usage instructions.
|
||||
|
||||
## License
|
||||
|
||||
Released under the MIT License, see LICENSE.
|
||||
|
||||
[1]: https://symfony.com/doc/current/setup/flex.html
|
||||
[2]: https://symfony.com/bundles/NelmioCorsBundle/current/index.html
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="nelmio_cors.cors_listener.class">Nelmio\CorsBundle\EventListener\CorsListener</parameter>
|
||||
<parameter key="nelmio_cors.options_resolver.class">Nelmio\CorsBundle\Options\Resolver</parameter>
|
||||
<parameter key="nelmio_cors.options_provider.config.class">Nelmio\CorsBundle\Options\ConfigProvider</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
<service id="nelmio_cors.cors_listener" class="%nelmio_cors.cors_listener.class%">
|
||||
<argument type="service" id="nelmio_cors.options_resolver" />
|
||||
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="250" />
|
||||
<tag name="kernel.event_listener" event="kernel.response" method="onKernelResponse" priority="0" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_cors.options_resolver" class="%nelmio_cors.options_resolver.class%" public="false" />
|
||||
|
||||
<service id="nelmio_cors.options_provider.config" class="%nelmio_cors.options_provider.config.class%">
|
||||
<argument>%nelmio_cors.map%</argument>
|
||||
<argument>%nelmio_cors.defaults%</argument>
|
||||
<tag name="nelmio_cors.options_provider" priority="-1" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_cors.cacheable_response_vary_listener" class="Nelmio\CorsBundle\EventListener\CacheableResponseVaryListener">
|
||||
<tag name="kernel.event_listener" event="kernel.response" method="onResponse" priority="-10" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
NelmioCorsBundle
|
||||
================
|
||||
|
||||
The NelmioCorsBundle allows you to send `Cross-Origin Resource Sharing`_
|
||||
headers with ACL-style per-URL configuration.
|
||||
|
||||
If you need it, check `this flow chart image`_ to have a global overview of
|
||||
entire CORS workflow.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Require the ``nelmio/cors-bundle`` package in your composer.json and update
|
||||
your dependencies:
|
||||
|
||||
.. code-block:: terminal
|
||||
|
||||
$ composer require nelmio/cors-bundle
|
||||
|
||||
The bundle should be automatically enabled by `Symfony Flex`_. If you don't use
|
||||
Flex, you'll need to manually enable the bundle by adding the following line in
|
||||
the ``config/bundles.php`` file of your project::
|
||||
|
||||
<?php
|
||||
// config/bundles.php
|
||||
|
||||
return [
|
||||
// ...
|
||||
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
|
||||
// ...
|
||||
];
|
||||
|
||||
If you don't have a ``config/bundles.php`` file in your project, chances are that
|
||||
you're using an older Symfony version. In this case, you should have an
|
||||
``app/AppKernel.php`` file instead. Edit such file::
|
||||
|
||||
<?php
|
||||
// app/AppKernel.php
|
||||
|
||||
// ...
|
||||
class AppKernel extends Kernel
|
||||
{
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = [
|
||||
// ...
|
||||
|
||||
new Nelmio\CorsBundle\NelmioCorsBundle(),
|
||||
];
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Symfony Flex generates a default configuration in ``config/packages/nelmio_cors.yaml``.
|
||||
|
||||
The options defined under ``defaults`` are the default values applied to all
|
||||
the ``paths`` that match, unless overridden in a specific URL configuration.
|
||||
If you want them to apply to everything, you must define a path with ``^/``.
|
||||
|
||||
This example config contains all the possible config values with their default
|
||||
values shown in the ``defaults`` key. In paths, you see that we allow CORS
|
||||
requests from any origin on ``/api/``. One custom header and some HTTP methods
|
||||
are defined as allowed as well. Preflight requests can be cached for 3600
|
||||
seconds.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
nelmio_cors:
|
||||
defaults:
|
||||
allow_credentials: false
|
||||
allow_origin: []
|
||||
allow_headers: []
|
||||
allow_methods: []
|
||||
expose_headers: []
|
||||
max_age: 0
|
||||
hosts: []
|
||||
origin_regex: false
|
||||
forced_allow_origin_value: ~
|
||||
skip_same_as_origin: true
|
||||
paths:
|
||||
'^/api/':
|
||||
allow_origin: ['*']
|
||||
allow_headers: ['X-Custom-Auth']
|
||||
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||
max_age: 3600
|
||||
'^/':
|
||||
origin_regex: true
|
||||
allow_origin: ['^http://localhost:[0-9]+']
|
||||
allow_headers: ['X-Custom-Auth']
|
||||
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||
max_age: 3600
|
||||
hosts: ['^api\.']
|
||||
|
||||
``allow_origin`` and ``allow_headers`` can be set to ``*`` to accept any value,
|
||||
the allowed methods however have to be explicitly listed. ``paths`` must
|
||||
contain at least one item.
|
||||
|
||||
``expose_headers`` can be set to ``*`` to accept any value as long as
|
||||
``allow_credentials`` is ``false`` `as per the specification`_.
|
||||
|
||||
If ``origin_regex`` is set, ``allow_origin`` must be a list of regular
|
||||
expressions matching allowed origins. Remember to use ``^`` and ``$`` to
|
||||
clearly define the boundaries of the regex.
|
||||
|
||||
By default, the ``Access-Control-Allow-Origin`` response header value is the
|
||||
``Origin`` request header value (if it matches the rules you've defined with
|
||||
``allow_origin``), so it should be fine for most of use cases. If it's not, you
|
||||
can override this behavior by setting the exact value you want using
|
||||
``forced_allow_origin_value``.
|
||||
|
||||
Be aware that even if you set ``forced_allow_origin_value`` to ``*``, if you
|
||||
also set ``allow_origin`` to ``http://example.com``, only this specific domain
|
||||
will be allowed to access your resources.
|
||||
|
||||
.. note::
|
||||
|
||||
If you allow POST methods and have `HTTP method overriding`_ enabled in the
|
||||
framework, it will enable the API users to perform ``PUT`` and ``DELETE``
|
||||
requests as well.
|
||||
|
||||
Cookbook
|
||||
--------
|
||||
|
||||
How to ignore preflight requests on New Relic?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On specific architectures with a mostly authenticated API, preflight request can
|
||||
represent a huge part of the traffic.
|
||||
|
||||
In such cases, you may not need to monitor on New Relic this traffic which is by
|
||||
the way categorized automatically as ``unknown`` by New Relic.
|
||||
|
||||
A request listener can be written to ignore preflight requests::
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
|
||||
class PreflightIgnoreOnNewRelicListener
|
||||
{
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (!extension_loaded('newrelic')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('OPTIONS' === $event->getRequest()->getMethod()) {
|
||||
newrelic_ignore_transaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Register this listener, and *voilà!*
|
||||
|
||||
.. _`Cross-Origin Resource Sharing`: http://enable-cors.org/
|
||||
.. _`this flow chart image`: http://www.html5rocks.com/static/images/cors_server_flowchart.png
|
||||
.. _`Symfony Flex`: https://symfony.com/doc/current/setup/flex.html
|
||||
.. _`as per the specification`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
|
||||
.. _`HTTP method overriding`: http://symfony.com/doc/current/reference/configuration/framework.html#http-method-override
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "nelmio/cors-bundle",
|
||||
"description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application",
|
||||
"keywords": ["cors", "crossdomain", "api"],
|
||||
"type": "symfony-bundle",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nelmio",
|
||||
"homepage": "http://nelm.io"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||
"psr/log": "^1.0 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.6",
|
||||
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Nelmio\\CorsBundle\\": "" },
|
||||
"exclude-from-classmap": ["/Tests/"]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user