Skip to content

Commit

Permalink
Merge pull request #36 from hugochinchilla/cors-handler
Browse files Browse the repository at this point in the history
Handle CORS requests
  • Loading branch information
strider2038 authored May 4, 2020
2 parents 8f36ed6 + 0fb3d89 commit 647f3c0
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ Mock server options can be set via environment variables.
* _Default value_: `disabled`
* _Possible values_: `disabled`, `url_md5`, `url_and_timestamp_md5`

#### SWAGGER_MOCK_CORS_ENABLE

* When enabled, CORS request will automatically be handled
* _Default value_: `False`
* _Possible values_: `True` or `False`

### Specification cache

To speed up server response time you can use caching mechanism for OpenAPI. There are several caching strategies. Specific strategy can be set by environment variable `SWAGGER_MOCK_CACHE_STRATEGY`.
Expand Down
8 changes: 8 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ parameters:
env(SWAGGER_MOCK_CACHE_STRATEGY): 'disabled'
env(SWAGGER_MOCK_CACHE_DIRECTORY): '/dev/shm/openapi-cache'
env(SWAGGER_MOCK_CACHE_TTL): 0
env(SWAGGER_MOCK_CORS_ENABLE): False

locale: 'en'
specification_url: '%env(SWAGGER_MOCK_SPECIFICATION_URL)%'
cache_strategy: '%env(SWAGGER_MOCK_CACHE_STRATEGY)%'
cache_directory: '%env(SWAGGER_MOCK_CACHE_DIRECTORY)%'
cache_ttl: '%env(SWAGGER_MOCK_CACHE_TTL)%'
log_level: '%env(SWAGGER_MOCK_LOG_LEVEL)%'
cors_enable: '%env(SWAGGER_MOCK_CORS_ENABLE)%'

type_parser_map:
object: 'App\OpenAPI\Parsing\Type\Composite\ObjectTypeParser'
Expand Down Expand Up @@ -63,6 +65,12 @@ services:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 48 }

App\EventListener\CorsResponseListener:
arguments:
- '%cors_enable%'
tags:
- { name: kernel.event_listener, event: kernel.response, priority: 46 }

App\Mock\EndpointRepository:
arguments:
- '@specification_loader'
Expand Down
Empty file added public/index.php
Empty file.
52 changes: 52 additions & 0 deletions src/EventListener/CorsResponseListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace App\EventListener;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class CorsResponseListener
{
/** @var bool */
private $cors_enable;

public function __construct(bool $cors_enable)
{
$this->cors_enable = $cors_enable;
}

public function onKernelResponse(ResponseEvent $event): void
{
if (!$this->cors_enable) {
return;
}

$request = $event->getRequest();

if (!$request->headers->has('Origin')) {
return;
}

$response = $event->getResponse();

$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));

$response->headers->set(
'Access-Control-Allow-Methods',
$request->headers->get('Access-Control-Request-Method', 'GET,POST,PUT,DELETE')
);

if ($requestHeaders = $request->headers->get('Access-Control-Request-Headers')) {
$response->headers->set('Access-Control-Allow-Headers', $requestHeaders);
}

if ($request->isMethod('OPTIONS')) {
if ($response->getStatusCode() === Response::HTTP_NOT_FOUND) {
$response->setContent('');
$response->setStatusCode(Response::HTTP_NO_CONTENT);
}
}
}
}
93 changes: 93 additions & 0 deletions tests/Unit/EventListener/CorsResponseListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\EventListener;

use App\EventListener\CorsResponseListener;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class CorsResponseListenerTest extends TestCase
{
/** @var ResponseEvent */
private $event;

protected function setUp(): void
{
$this->event = \Phake::mock(ResponseEvent::class);
}

/** @test */
public function onUnhandledOptionsRequest_ifFeatureEnabled_handlesCors(): void
{
$listener = new CorsResponseListener(true);
$request = new Request();
$request->setMethod('OPTIONS');
$request->headers->set('Origin', 'http://example.tld');
$response = new Response();
$response->setStatusCode(Response::HTTP_NOT_FOUND);
\Phake::when($this->event)
->getRequest()
->thenReturn($request);
\Phake::when($this->event)
->getResponse()
->thenReturn($response);

$listener->onKernelResponse($this->event);

$this->assertEquals(Response::HTTP_NO_CONTENT, $response->getStatusCode());
$this->assertEquals('http://example.tld', $response->headers->get('Access-Control-Allow-Origin'));
$this->assertEquals('GET,POST,PUT,DELETE', $response->headers->get('Access-Control-Allow-Methods'));
$this->assertEquals('', $response->getContent());
}

/** @test */
public function onCorsRequest_ifFeatureEnabled_addsCorsHeaders(): void
{
$listener = new CorsResponseListener(true);
$request = new Request();
$request->setMethod('GET');
$request->headers->set('Origin', 'http://example.tld');
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->setContent('a content');
\Phake::when($this->event)
->getRequest()
->thenReturn($request);
\Phake::when($this->event)
->getResponse()
->thenReturn($response);

$listener->onKernelResponse($this->event);

$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$this->assertEquals('a content', $response->getContent());
$this->assertEquals('http://example.tld', $response->headers->get('Access-Control-Allow-Origin'));
$this->assertEquals('GET,POST,PUT,DELETE', $response->headers->get('Access-Control-Allow-Methods'));
}

/** @test */
public function onCorsRequest_ifFeatureDisabled_doesNothing(): void
{
$listener = new CorsResponseListener(false);
$request = new Request();
$request->headers->set('Origin', 'http://example.tld');
$response = new Response();
$response->setStatusCode(Response::HTTP_NOT_FOUND);
\Phake::when($this->event)
->getRequest()
->thenReturn($request);
\Phake::when($this->event)
->getResponse()
->thenReturn($response);

$listener->onKernelResponse($this->event);

$this->assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode());
$this->assertEquals('', $response->headers->get('Access-Control-Allow-Origin'));
$this->assertEquals('', $response->headers->get('Access-Control-Allow-Methods'));
}
}

0 comments on commit 647f3c0

Please sign in to comment.