Created
December 10, 2014 17:19
-
-
Save vatson/4c34df06754157e4e57e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* This file is part of the NelmioCorsBundle. | |
* | |
* (c) Nelmio <[email protected]> | |
* | |
* For the full copyright and license information, please view the LICENSE | |
* file that was distributed with this source code. | |
*/ | |
namespace Nelmio\CorsBundle\EventListener; | |
use Symfony\Component\HttpKernel\HttpKernelInterface; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; | |
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Nelmio\CorsBundle\Options\ResolverInterface; | |
/** | |
* Adds CORS headers and handles pre-flight requests | |
* | |
* @author Jordi Boggiano <[email protected]> | |
*/ | |
class CorsListener | |
{ | |
/** | |
* Simple headers as defined in the spec should always be accepted | |
*/ | |
protected static $simpleHeaders = array( | |
'accept', | |
'accept-language', | |
'content-language', | |
'origin', | |
); | |
protected $dispatcher; | |
protected $options; | |
/** @var ResolverInterface */ | |
protected $configurationResolver; | |
public function __construct(EventDispatcherInterface $dispatcher, ResolverInterface $configurationResolver) | |
{ | |
$this->dispatcher = $dispatcher; | |
$this->configurationResolver = $configurationResolver; | |
} | |
public function onKernelRequest(GetResponseEvent $event) | |
{ | |
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { | |
return; | |
} | |
$request = $event->getRequest(); | |
// skip if not a CORS request | |
if (!$request->headers->has('Origin') || $request->headers->get('Origin') == $request->getSchemeAndHttpHost()) { | |
return; | |
} | |
$options = $this->configurationResolver->getOptions($request); | |
if (!$options) { | |
return; | |
} | |
// perform preflight checks | |
if ('OPTIONS' === $request->getMethod()) { | |
$event->setResponse($this->getPreflightResponse($request, $options)); | |
return; | |
} | |
$this->dispatcher->addListener('kernel.response', array($this, 'onKernelResponse')); | |
$this->options = $options; | |
} | |
public function onKernelResponse(FilterResponseEvent $event) | |
{ | |
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { | |
return; | |
} | |
$response = $event->getResponse(); | |
$request = $event->getRequest(); | |
// add CORS response headers | |
//Only provide the Access-Control-Allow-Origin response header matching the client's request | |
//if the client's Origin has been white-listed | |
$options = $this->configurationResolver->getOptions($request); | |
if ( $this->checkOrigin($request, $options) ) { | |
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin')); | |
} | |
if ($this->options['allow_credentials']) { | |
$response->headers->set('Access-Control-Allow-Credentials', 'true'); | |
} | |
if ($this->options['expose_headers']) { | |
$response->headers->set('Access-Control-Expose-Headers', strtolower(implode(', ', $this->options['expose_headers']))); | |
} | |
} | |
protected function getPreflightResponse(Request $request, array $options) | |
{ | |
$response = new Response(); | |
if ($options['allow_credentials']) { | |
$response->headers->set('Access-Control-Allow-Credentials', 'true'); | |
} | |
if ($options['allow_methods']) { | |
$response->headers->set('Access-Control-Allow-Methods', implode(', ', $options['allow_methods'])); | |
} | |
if ($options['allow_headers']) { | |
$headers = $options['allow_headers'] === true | |
? $request->headers->get('Access-Control-Request-Headers') | |
: implode(', ', $options['allow_headers']); | |
$response->headers->set('Access-Control-Allow-Headers', $headers); | |
} | |
if ($options['max_age']) { | |
$response->headers->set('Access-Control-Max-Age', $options['max_age']); | |
} | |
if (!$this->checkOrigin($request, $options)) { | |
$response->headers->set('Access-Control-Allow-Origin', 'null'); | |
return $response; | |
} | |
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin')); | |
// check request method | |
if (!in_array(strtoupper($request->headers->get('Access-Control-Request-Method')), $options['allow_methods'], true)) { | |
$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 ($options['allow_headers'] !== true && $headers) { | |
$headers = trim(strtolower($headers)); | |
foreach (preg_split('{, *}', $headers) as $header) { | |
if (in_array($header, self::$simpleHeaders, true)) { | |
continue; | |
} | |
if (!in_array($header, $options['allow_headers'], true)) { | |
$response->setStatusCode(400); | |
$response->setContent('Unauthorized header '.$header); | |
break; | |
} | |
} | |
} | |
return $response; | |
} | |
protected function checkOrigin(Request $request, array $options) | |
{ | |
// check origin | |
$origin = $request->headers->get('Origin'); | |
if ($options['allow_origin'] === true || in_array($origin, $options['allow_origin'])) { | |
return true; | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment