Issue #2303673 by dawehner, damiankloip, effulgentsia, Fabianx: Implement stackphp; cleanup handlePageCache() and preHandle()

8.0.x
Dries 2014-09-03 22:58:45 -04:00
parent f4d528ca8b
commit 5ef912e965
34 changed files with 1699 additions and 185 deletions

View File

@ -26,7 +26,8 @@
"phpunit/phpunit": "4.1.*",
"phpunit/phpunit-mock-objects": "dev-master#e60bb929c50ae4237aaf680a4f6773f4ee17f0a2",
"zendframework/zend-feed": "2.2.*",
"mikey179/vfsStream": "1.*"
"mikey179/vfsStream": "1.*",
"stack/builder": "1.0.*"
},
"autoload": {
"psr-4": {

52
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "6a7a4ba69644c6cf110d03b1a5346e3e",
"hash": "8afb97667c2791fec2fb1fc43853da24",
"packages": [
{
"name": "doctrine/annotations",
@ -1413,6 +1413,56 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2014-03-07 15:35:33"
},
{
"name": "stack/builder",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/stackphp/builder.git",
"reference": "b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stackphp/builder/zipball/b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7",
"reference": "b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/http-foundation": "~2.1",
"symfony/http-kernel": "~2.1"
},
"require-dev": {
"silex/silex": "~1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-0": {
"Stack": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
}
],
"description": "Builder for stack middlewares based on HttpKernelInterface.",
"keywords": [
"stack"
],
"time": "2014-01-28 19:42:24"
},
{
"name": "symfony-cmf/routing",
"version": "1.2.0",

View File

@ -370,8 +370,30 @@ services:
class: Drupal\Core\Controller\TitleResolver
arguments: ['@controller_resolver', '@string_translation']
http_kernel:
class: Symfony\Component\HttpKernel\HttpKernel
factory_method: resolve
factory_service: http_kernel_factory
arguments: ['@http_kernel.basic']
http_kernel_factory:
class: Stack\Builder
http_kernel.basic:
class: Symfony\Component\HttpKernel\HttpKernel
arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack']
http_middleware.reverse_proxy:
class: Drupal\Core\StackMiddleware\ReverseProxyMiddleware
arguments: ['@settings']
tags:
- { name: http_middleware, priority: 300 }
http_middleware.page_cache:
class: Drupal\Core\StackMiddleware\PageCache
arguments: ['@kernel']
tags:
- { name: http_middleware, priority: 200 }
http_middleware.kernel_pre_handle:
class: Drupal\Core\StackMiddleware\KernelPreHandle
arguments: ['@kernel']
tags:
- { name: http_middleware, priority: 100 }
language_manager:
class: Drupal\Core\Language\LanguageManager
arguments: ['@language.default']
@ -560,11 +582,6 @@ services:
tags:
- { name: event_subscriber }
arguments: ['@resolver_manager.entity']
reverse_proxy_subscriber:
class: Drupal\Core\EventSubscriber\ReverseProxySubscriber
tags:
- { name: event_subscriber }
arguments: ['@settings']
ajax_subscriber:
class: Drupal\Core\EventSubscriber\AjaxSubscriber
tags:

View File

@ -183,7 +183,7 @@ class AuthenticationManager implements AuthenticationProviderInterface, Authenti
$active_providers = ($route && $route->getOption('_auth')) ? $route->getOption('_auth') : array($this->defaultProviderId());
// Get the sorted list of active providers for the given route.
$providers = array_intersect($active_providers, array_keys($this->providers));
$providers = array_intersect($active_providers, array_keys($this->getSortedProviders()));
foreach ($providers as $provider_id) {
if ($this->providers[$provider_id]->handleException($event) == TRUE) {

View File

@ -10,6 +10,7 @@ namespace Drupal\Core;
use Drupal\Core\Cache\CacheContextsPass;
use Drupal\Core\Cache\ListCacheBinsPass;
use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass;
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
@ -50,6 +51,8 @@ class CoreServiceProvider implements ServiceProviderInterface {
$container->addCompilerPass(new BackendCompilerPass());
$container->addCompilerPass(new StackedKernelPass());
// Collect tagged handler services as method calls on consumer services.
$container->addCompilerPass(new TaggedHandlersPass());

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Core\DependencyInjection\Compiler\StackedKernelPass.
*/
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Provides a compiler pass for stacked HTTP kernels.
*
* @see \Stack\Builder
*/
class StackedKernelPass implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('http_kernel_factory')) {
return;
}
$http_kernel_factory = $container->getDefinition('http_kernel_factory');
$middleware_priorities = array();
$middleware_arguments = array();
foreach ($container->findTaggedServiceIds('http_middleware') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$middleware_priorities[$id] = $priority;
$definition = $container->getDefinition($id);
$middleware_arguments[$id] = $definition->getArguments();
array_unshift($middleware_arguments[$id], $definition->getClass());
}
array_multisort($middleware_priorities, SORT_DESC, $middleware_arguments, SORT_DESC);
foreach ($middleware_arguments as $id => $push_arguments) {
$http_kernel_factory->addMethodCall('push', $push_arguments);
}
}
}

View File

@ -418,12 +418,9 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
}
/**
* Helper method that does request related initialization.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* {@inheritdoc}
*/
protected function preHandle(Request $request) {
public function preHandle(Request $request) {
// Load all enabled modules.
$this->container->get('module_handler')->loadAll();
@ -568,7 +565,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$this->boot();
$this->preHandle($request);
return $this->getHttpKernel()->handle($request, $type, $catch);
}

View File

@ -108,4 +108,12 @@ interface DrupalKernelInterface extends HttpKernelInterface {
*/
public function prepareLegacyRequest(Request $request);
/**
* Helper method that does request related initialization.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
*/
public function preHandle(Request $request);
}

View File

@ -1,63 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\EventSubscriber\ReverseProxySubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Reverse proxy subscriber for controller requests.
*/
class ReverseProxySubscriber implements EventSubscriberInterface {
/**
* A settings object.
*
* @var \Drupal\Core\Site\Settings
*/
protected $settings;
/**
* Construct the ReverseProxySubscriber.
*
* @param \Drupal\Core\Site\Settings $settings
* The read-only settings object of this request.
*/
public function __construct(Settings $settings) {
$this->settings = $settings;
}
/**
* Passes reverse proxy settings to current request.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The Event to process.
*/
public function onKernelRequestReverseProxyCheck(GetResponseEvent $event) {
$request = $event->getRequest();
if ($this->settings->get('reverse_proxy', 0)) {
$reverse_proxy_header = $this->settings->get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR');
$request::setTrustedHeaderName($request::HEADER_CLIENT_IP, $reverse_proxy_header);
$reverse_proxy_addresses = $this->settings->get('reverse_proxy_addresses', array());
$request::setTrustedProxies($reverse_proxy_addresses);
}
}
/**
* Registers the methods in this class that should be listeners.
*
* @return array
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onKernelRequestReverseProxyCheck', 10);
return $events;
}
}

View File

@ -22,7 +22,6 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
@ -127,10 +126,10 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
* The theme manager.
* @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
* The CSRF token generator.
* @param \Symfony\Component\HttpKernel\HttpKernel $http_kernel
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The HTTP kernel.
*/
public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernelInterface $http_kernel = NULL) {
$this->formValidator = $form_validator;
$this->formSubmitter = $form_submitter;
$this->formCache = $form_cache;

View File

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\Core\StackMiddleware\KernelBoot.
*/
namespace Drupal\Core\StackMiddleware;
use Drupal\Core\DrupalKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Prepares the environment after page caching ran.
*/
class KernelPreHandle implements HttpKernelInterface {
/**
* The wrapped HTTP kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* The main Drupal kernel.
*
* @var \Drupal\Core\DrupalKernelInterface
*/
protected $drupalKernel;
/**
* Constructs a new KernelPreHandle instance.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The wrapped HTTP kernel.
*
* @param \Drupal\Core\DrupalKernelInterface $drupal_kernel
* The main Drupal kernel.
*/
public function __construct(HttpKernelInterface $http_kernel, DrupalKernelInterface $drupal_kernel) {
$this->httpKernel = $http_kernel;
$this->drupalKernel = $drupal_kernel;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$this->drupalKernel->preHandle($request);
return $this->httpKernel->handle($request, $type, $catch);
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* @file
* Contains \Drupal\Core\StackMiddleware\PageCache.
*/
namespace Drupal\Core\StackMiddleware;
use Drupal\Core\DrupalKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Executes the page caching before the main kernel takes over the request.
*/
class PageCache implements HttpKernelInterface {
/**
* The wrapped HTTP kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* The main Drupal kernel.
*
* @var \Drupal\Core\DrupalKernelInterface
*/
protected $drupalKernel;
/**
* Constructs a ReverseProxyMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The decorated kernel.
* @param \Drupal\Core\DrupalKernelInterface $drupal_kernel
* The main Drupal kernel.
*/
public function __construct(HttpKernelInterface $http_kernel, DrupalKernelInterface $drupal_kernel) {
$this->httpKernel = $http_kernel;
$this->drupalKernel = $drupal_kernel;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$this->drupalKernel->handlePageCache($request);
return $this->httpKernel->handle($request, $type, $catch);
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* @file
* Contains \Drupal\Core\StackMiddleware\ReverseProxyMiddleware
*/
namespace Drupal\Core\StackMiddleware;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
*
*/
class ReverseProxyMiddleware implements HttpKernelInterface {
/**
* The decorated kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* The site settings.
*
* @var \Drupal\Core\Site\Settings
*/
protected $settings;
/**
* Constructs a ReverseProxyMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The decorated kernel.
* @param \Drupal\Core\Site\Settings $settings
* The site settings.
*/
public function __construct(HttpKernelInterface $http_kernel, Settings $settings) {
$this->httpKernel = $http_kernel;
$this->settings = $settings;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
// Initialize proxy settings.
if ($this->settings->get('reverse_proxy', FALSE)) {
$reverse_proxy_header = $this->settings->get('reverse_proxy_header', 'X_FORWARDED_FOR');
$request::setTrustedHeaderName($request::HEADER_CLIENT_IP, $reverse_proxy_header);
$proxies = $this->settings->get('reverse_proxy_addresses', array());
if (count($proxies) > 0) {
$request::setTrustedProxies($proxies);
}
}
return $this->httpKernel->handle($request, $type, $catch);
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\HttpKernel\StackKernelIntegrationTest.
*/
namespace Drupal\system\Tests\HttpKernel;
use Drupal\simpletest\KernelTestBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests the stacked kernel functionality.
*
* @group Routing.
*/
class StackKernelIntegrationTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('httpkernel_test', 'system');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'router');
}
/**
* Tests a request.
*/
public function testRequest() {
$request = new Request();
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
$http_kernel = \Drupal::service('http_kernel');
$http_kernel->handle($request);
$this->assertEqual($request->attributes->get('_hello'), 'world');
$this->assertEqual($request->attributes->get('_previous_optional_argument'), 'test_argument');
}
}

View File

@ -0,0 +1,6 @@
name: 'HttpKernel test'
type: module
description: 'Support module for httpkernel tests.'
package: Testing
version: VERSION
core: 8.x

View File

@ -0,0 +1,10 @@
services:
httpkernel_test.test_middleware:
class: Drupal\httpkernel_test\HttpKernel\TestMiddleware
tags:
- { name: http_middleware }
httpkernel_test.test_middleware2:
class: Drupal\httpkernel_test\HttpKernel\TestMiddleware
arguments: ['test_argument']
tags:
- { name: http_middleware, priority: 20 }

View File

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\httpkernel_test\HttpKernel\TestMiddleware.
*/
namespace Drupal\httpkernel_test\HttpKernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Provides a test middleware.
*/
class TestMiddleware implements HttpKernelInterface {
/**
* The decorated kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $kernel;
/**
* An optional argument.
*
* @var mixed
*/
protected $optionalArgument;
/**
* Constructs a new TestMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
* The decorated kernel.
* @param mixed $optional_argument
* (optional) An optional argument.
*/
public function __construct(HttpKernelInterface $kernel, $optional_argument = NULL) {
$this->kernel = $kernel;
$this->optionalArgument = $optional_argument;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$request->attributes->set('_hello', 'world');
if ($request->attributes->has('_optional_argument')) {
$request->attributes->set('_previous_optional_argument', $request->attributes->get('_optional_argument'));
}
elseif (isset($this->optionalArgument)) {
$request->attributes->set('_optional_argument', $this->optionalArgument);
}
return $this->kernel->handle($request, $type, $catch);
}
}

View File

@ -1,103 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\EventSubscriber\ReverseProxySubscriberUnitTest.
*/
namespace Drupal\Tests\Core\EventSubscriber;
use Drupal\Core\EventSubscriber\ReverseProxySubscriber;
use Drupal\Core\Site\Settings;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* Unit test the reverse proxy event subscriber.
*
* @group EventSubscriber
*/
class ReverseProxySubscriberUnitTest extends UnitTestCase {
/**
* Tests that subscriber does not act when reverse proxy is not set.
*/
public function testNoProxy() {
$settings = new Settings(array());
$this->assertEquals(0, $settings->get('reverse_proxy'));
$subscriber = new ReverseProxySubscriber($settings);
// Mock a request object.
$request = $this->getMock('Symfony\Component\HttpFoundation\Request', array('setTrustedHeaderName', 'setTrustedProxies'));
// setTrustedHeaderName() should never fire.
$request->expects($this->never())
->method('setTrustedHeaderName');
// Mock a response event.
$event = $this->getMockedEvent($request);
// Actually call the check method.
$subscriber->onKernelRequestReverseProxyCheck($event);
}
/**
* Tests that subscriber sets trusted headers when reverse proxy is set.
*/
public function testReverseProxyEnabled() {
$cases = array(
array(
'reverse_proxy_header' => 'HTTP_X_FORWARDED_FOR',
'reverse_proxy_addresses' => array(),
),
array(
'reverse_proxy_header' => 'X_FORWARDED_HOST',
'reverse_proxy_addresses' => array('127.0.0.2', '127.0.0.3'),
),
);
foreach ($cases as $case) {
// Enable reverse proxy and add test values.
$settings = new Settings(array('reverse_proxy' => 1) + $case);
$this->trustedHeadersAreSet($settings);
}
}
/**
* Tests that trusted header methods are called.
*
* \Symfony\Component\HttpFoundation\Request::setTrustedHeaderName() and
* \Symfony\Component\HttpFoundation\Request::setTrustedProxies() should
* always be called when reverse proxy settings are enabled.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings object that holds reverse proxy configuration.
*/
protected function trustedHeadersAreSet(Settings $settings) {
$subscriber = new ReverseProxySubscriber($settings);
$request = new Request();
$event = $this->getMockedEvent($request);
$subscriber->onKernelRequestReverseProxyCheck($event);
$this->assertSame($settings->get('reverse_proxy_header'), $request->getTrustedHeaderName($request::HEADER_CLIENT_IP));
$this->assertSame($settings->get('reverse_proxy_addresses'), $request->getTrustedProxies());
}
/**
* Creates a mocked event.
*
* Mocks a \Symfony\Component\HttpKernel\Event\GetResponseEvent object
* and stubs its getRequest() method to return a mocked request object.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A mocked Request object.
*
* @return \Symfony\Component\HttpKernel\Event\GetResponseEvent
* The GetResponseEvent mocked object.
*/
protected function getMockedEvent($request) {
$event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getRequest')
->will($this->returnValue($request));
return $event;
}
}

View File

@ -0,0 +1,100 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\StackMiddleware\ReverseProxyMiddlewareTest.
*/
namespace Drupal\Tests\Core\StackMiddleware;
use Drupal\Core\Site\Settings;
use Drupal\Core\StackMiddleware\ReverseProxyMiddleware;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\HttpFoundation\Request;
/**
* Unit test the reverse proxy stack middleware.
*
* @group StackMiddleware
*/
class ReverseProxyMiddlewareTest extends UnitTestCase {
/**
* @var \Symfony\Component\HttpKernel\HttpKernelInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $mockHttpKernel;
/**
* {@inheritdoc}
*/
public function setUp() {
$this->mockHttpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
}
/**
* Tests that subscriber does not act when reverse proxy is not set.
*/
public function testNoProxy() {
$settings = new Settings(array());
$this->assertEquals(0, $settings->get('reverse_proxy'));
$middleware = new ReverseProxyMiddleware($this->mockHttpKernel, $settings);
// Mock a request object.
$request = $this->getMock('Symfony\Component\HttpFoundation\Request', array('setTrustedHeaderName', 'setTrustedProxies'));
// setTrustedHeaderName() should never fire.
$request->expects($this->never())
->method('setTrustedHeaderName');
// Actually call the check method.
$middleware->handle($request);
}
/**
* Tests that subscriber sets trusted headers when reverse proxy is set.
*
* @dataProvider testReverseProxyEnabledProvider
*/
public function testReverseProxyEnabled($provided_settings) {
// Enable reverse proxy and add test values.
$settings = new Settings(array('reverse_proxy' => 1) + $provided_settings);
$this->trustedHeadersAreSet($settings);
}
/**
* Data provider for testReverseProxyEnabled.
*/
public function testReverseProxyEnabledProvider() {
return array(
array(
array(
'reverse_proxy_header' => 'HTTP_X_FORWARDED_FOR',
'reverse_proxy_addresses' => array(),
),
),
array(
array(
'reverse_proxy_header' => 'X_FORWARDED_HOST',
'reverse_proxy_addresses' => array('127.0.0.2', '127.0.0.3'),
),
),
);
}
/**
* Tests that trusted header methods are called.
*
* \Symfony\Component\HttpFoundation\Request::setTrustedHeaderName() and
* \Symfony\Component\HttpFoundation\Request::setTrustedProxies() should
* always be called when reverse proxy settings are enabled.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings object that holds reverse proxy configuration.
*/
protected function trustedHeadersAreSet(Settings $settings) {
$middleware = new ReverseProxyMiddleware($this->mockHttpKernel, $settings);
$request = new Request();
$middleware->handle($request);
$this->assertSame($settings->get('reverse_proxy_header'), $request->getTrustedHeaderName($request::HEADER_CLIENT_IP));
$this->assertSame($settings->get('reverse_proxy_addresses'), $request->getTrustedProxies());
}
}

View File

@ -26,6 +26,7 @@ return array(
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'),
'Symfony\\Cmf\\Component\\Routing' => array($vendorDir . '/symfony-cmf/routing'),
'Stack' => array($vendorDir . '/stack/builder/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log'),
'Gliph' => array($vendorDir . '/sdboyer/gliph/src'),
'EasyRdf_' => array($vendorDir . '/easyrdf/easyrdf/lib'),

View File

@ -2512,5 +2512,57 @@
"database",
"routing"
]
},
{
"name": "stack/builder",
"version": "v1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/stackphp/builder.git",
"reference": "b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stackphp/builder/zipball/b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7",
"reference": "b4af43e7b7f3f7fac919ff475b29f7c5dc7b23b7",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/http-foundation": "~2.1",
"symfony/http-kernel": "~2.1"
},
"require-dev": {
"silex/silex": "~1.0"
},
"time": "2014-01-28 19:42:24",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Stack": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
}
],
"description": "Builder for stack middlewares based on HttpKernelInterface.",
"keywords": [
"stack"
]
}
]

19
core/vendor/stack/builder/.travis.yml vendored Normal file
View File

@ -0,0 +1,19 @@
language: php
php:
- 5.3.3
- 5.3
- 5.4
- 5.5
- hhvm
before_script:
- composer self-update
- composer install --no-interaction --prefer-source
script: phpunit --coverage-text
matrix:
allow_failures:
- php: hhvm
fast_finish: true

14
core/vendor/stack/builder/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,14 @@
CHANGELOG
=========
* 1.0.2 (2014-xx-xx)
* Validate missing arguments (@bajbnet).
* 1.0.1 (2013-10-25)
* Lower PHP requirement to 5.3.
* 1.0.0 (2013-08-02)
* Initial release.

19
core/vendor/stack/builder/LICENSE vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2013 Igor Wiedler
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.

62
core/vendor/stack/builder/README.md vendored Normal file
View File

@ -0,0 +1,62 @@
# Stack/Builder
Builder for stack middlewares based on HttpKernelInterface.
Stack/Builder is a small library that helps you construct a nested
HttpKernelInterface decorator tree. It models it as a stack of middlewares.
## Example
If you want to decorate a [silex](https://github.com/fabpot/Silex) app with
session and cache middlewares, you'll have to do something like this:
use Symfony\Component\HttpKernel\HttpCache\Store;
$app = new Silex\Application();
$app->get('/', function () {
return 'Hello World!';
});
$app = new Stack\Session(
new Symfony\Component\HttpKernel\HttpCache\HttpCache(
$app,
new Store(__DIR__.'/cache')
)
);
This can get quite annoying indeed. Stack/Builder simplifies that:
$stack = (new Stack\Builder())
->push('Stack\Session')
->push('Symfony\Component\HttpKernel\HttpCache\HttpCache', new Store(__DIR__.'/cache'));
$app = $stack->resolve($app);
As you can see, by arranging the layers as a stack, they become a lot easier
to work with.
In the front controller, you need to serve the request:
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
$response = $app->handle($request)->send();
$app->terminate($request, $response);
Stack/Builder also supports pushing a `callable` on to the stack, for situations
where instantiating middlewares might be more complicated. The `callable` should
accept a `HttpKernelInterface` as the first argument and should also return a
`HttpKernelInterface`. The example above could be rewritten as:
$stack = (new Stack\Builder())
->push('Stack\Session')
->push(function ($app) {
$cache = new HttpCache($app, new Store(__DIR__.'/cache'));
return $cache;
});
## Inspiration
* [Rack::Builder](http://rack.rubyforge.org/doc/Rack/Builder.html)
* [HttpKernel middlewares](https://igor.io/2013/02/02/http-kernel-middlewares.html)

26
core/vendor/stack/builder/composer.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"name": "stack/builder",
"description": "Builder for stack middlewares based on HttpKernelInterface.",
"keywords": ["stack"],
"license": "MIT",
"authors": [
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
}
],
"require": {
"php": ">=5.3.0",
"symfony/http-foundation": "~2.1",
"symfony/http-kernel": "~2.1"
},
"require-dev": {
"silex/silex": "~1.0"
},
"autoload": {
"psr-0": { "Stack": "src" }
},
"extra": {
"branch-alias": { "dev-master": "1.0-dev" }
}
}

488
core/vendor/stack/builder/composer.lock generated vendored Normal file
View File

@ -0,0 +1,488 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
],
"hash": "78698481679eca710495d5aca028baaa",
"packages": [
{
"name": "psr/log",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
"reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-0": {
"Psr\\Log\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"keywords": [
"log",
"psr",
"psr-3"
],
"time": "2012-12-21 11:40:51"
},
{
"name": "symfony/debug",
"version": "v2.4.1",
"target-dir": "Symfony/Component/Debug",
"source": {
"type": "git",
"url": "https://github.com/symfony/Debug.git",
"reference": "74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Debug/zipball/74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f",
"reference": "74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/http-foundation": "~2.1",
"symfony/http-kernel": "~2.1"
},
"suggest": {
"symfony/http-foundation": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Debug\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Debug Component",
"homepage": "http://symfony.com",
"time": "2014-01-01 09:02:49"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.4.1",
"target-dir": "Symfony/Component/EventDispatcher",
"source": {
"type": "git",
"url": "https://github.com/symfony/EventDispatcher.git",
"reference": "e3ba42f6a70554ed05749e61b829550f6ac33601"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e3ba42f6a70554ed05749e61b829550f6ac33601",
"reference": "e3ba42f6a70554ed05749e61b829550f6ac33601",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"symfony/dependency-injection": "~2.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher Component",
"homepage": "http://symfony.com",
"time": "2013-12-28 08:12:03"
},
{
"name": "symfony/http-foundation",
"version": "v2.4.1",
"target-dir": "Symfony/Component/HttpFoundation",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpFoundation.git",
"reference": "6c6b8a7bcd7e2cc920cd6acace563fdbf121d844"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/6c6b8a7bcd7e2cc920cd6acace563fdbf121d844",
"reference": "6c6b8a7bcd7e2cc920cd6acace563fdbf121d844",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpFoundation\\": ""
},
"classmap": [
"Symfony/Component/HttpFoundation/Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony HttpFoundation Component",
"homepage": "http://symfony.com",
"time": "2014-01-05 02:10:50"
},
{
"name": "symfony/http-kernel",
"version": "v2.4.1",
"target-dir": "Symfony/Component/HttpKernel",
"source": {
"type": "git",
"url": "https://github.com/symfony/HttpKernel.git",
"reference": "0605eedeb52c4d3a3144128d8336395a57be60d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/HttpKernel/zipball/0605eedeb52c4d3a3144128d8336395a57be60d4",
"reference": "0605eedeb52c4d3a3144128d8336395a57be60d4",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"psr/log": "~1.0",
"symfony/debug": "~2.3",
"symfony/event-dispatcher": "~2.1",
"symfony/http-foundation": "~2.4"
},
"require-dev": {
"symfony/browser-kit": "~2.2",
"symfony/class-loader": "~2.1",
"symfony/config": "~2.0",
"symfony/console": "~2.2",
"symfony/dependency-injection": "~2.0",
"symfony/finder": "~2.0",
"symfony/process": "~2.0",
"symfony/routing": "~2.2",
"symfony/stopwatch": "~2.2",
"symfony/templating": "~2.2"
},
"suggest": {
"symfony/browser-kit": "",
"symfony/class-loader": "",
"symfony/config": "",
"symfony/console": "",
"symfony/dependency-injection": "",
"symfony/finder": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\HttpKernel\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony HttpKernel Component",
"homepage": "http://symfony.com",
"time": "2014-01-05 02:12:11"
}
],
"packages-dev": [
{
"name": "pimple/pimple",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Pimple.git",
"reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Pimple/zipball/471c7d7c52ad6594e17b8ec33efdd1be592b5d83",
"reference": "471c7d7c52ad6594e17b8ec33efdd1be592b5d83",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple is a simple Dependency Injection Container for PHP 5.3",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2013-09-19 04:53:08"
},
{
"name": "silex/silex",
"version": "v1.1.2",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Silex.git",
"reference": "47cc7d6545450ef8a91f50c04e8c7b3b04fceae9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Silex/zipball/47cc7d6545450ef8a91f50c04e8c7b3b04fceae9",
"reference": "47cc7d6545450ef8a91f50c04e8c7b3b04fceae9",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"pimple/pimple": "~1.0",
"symfony/event-dispatcher": ">=2.3,<2.5-dev",
"symfony/http-foundation": ">=2.3,<2.5-dev",
"symfony/http-kernel": ">=2.3,<2.5-dev",
"symfony/routing": ">=2.3,<2.5-dev"
},
"require-dev": {
"doctrine/dbal": ">=2.2.0,<2.4.0-dev",
"monolog/monolog": "~1.4,>=1.4.1",
"phpunit/phpunit": "~3.7",
"swiftmailer/swiftmailer": "5.*",
"symfony/browser-kit": ">=2.3,<2.5-dev",
"symfony/config": ">=2.3,<2.5-dev",
"symfony/css-selector": ">=2.3,<2.5-dev",
"symfony/debug": ">=2.3,<2.5-dev",
"symfony/dom-crawler": ">=2.3,<2.5-dev",
"symfony/finder": ">=2.3,<2.5-dev",
"symfony/form": ">=2.3,<2.5-dev",
"symfony/locale": ">=2.3,<2.5-dev",
"symfony/monolog-bridge": ">=2.3,<2.5-dev",
"symfony/options-resolver": ">=2.3,<2.5-dev",
"symfony/process": ">=2.3,<2.5-dev",
"symfony/security": ">=2.3,<2.5-dev",
"symfony/serializer": ">=2.3,<2.5-dev",
"symfony/translation": ">=2.3,<2.5-dev",
"symfony/twig-bridge": ">=2.3,<2.5-dev",
"symfony/validator": ">=2.3,<2.5-dev",
"twig/twig": ">=1.8.0,<2.0-dev"
},
"suggest": {
"symfony/browser-kit": ">=2.3,<2.5-dev",
"symfony/css-selector": ">=2.3,<2.5-dev",
"symfony/dom-crawler": ">=2.3,<2.5-dev",
"symfony/form": ">=2.3,<2.5-dev"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-0": {
"Silex": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch",
"homepage": "http://wiedler.ch/igor/"
}
],
"description": "The PHP micro-framework based on the Symfony2 Components",
"homepage": "http://silex.sensiolabs.org",
"keywords": [
"microframework"
],
"time": "2013-10-30 08:53:26"
},
{
"name": "symfony/routing",
"version": "v2.4.1",
"target-dir": "Symfony/Component/Routing",
"source": {
"type": "git",
"url": "https://github.com/symfony/Routing.git",
"reference": "4abfb500aab8be458c9e3a227ea56b190584f78a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Routing/zipball/4abfb500aab8be458c9e3a227ea56b190584f78a",
"reference": "4abfb500aab8be458c9e3a227ea56b190584f78a",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"doctrine/annotations": "~1.0",
"psr/log": "~1.0",
"symfony/config": "~2.2",
"symfony/expression-language": "~2.4",
"symfony/yaml": "~2.0"
},
"suggest": {
"doctrine/annotations": "For using the annotation loader",
"symfony/config": "For using the all-in-one router or any loader",
"symfony/expression-language": "For using expression matching",
"symfony/yaml": "For using the YAML loader"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Routing\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Routing Component",
"homepage": "http://symfony.com",
"keywords": [
"router",
"routing",
"uri",
"url"
],
"time": "2014-01-05 02:10:50"
}
],
"aliases": [
],
"minimum-stability": "stable",
"stability-flags": [
],
"platform": {
"php": ">=5.3.0"
},
"platform-dev": [
]
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="unit">
<directory>./tests/unit/</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="integration">
<directory>./tests/integration/</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="functional">
<directory>./tests/functional/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,63 @@
<?php
namespace Stack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class Builder
{
private $specs;
public function __construct()
{
$this->specs = new \SplStack();
}
public function unshift(/*$kernelClass, $args...*/)
{
if (func_num_args() === 0) {
throw new \InvalidArgumentException("Missing argument(s) when calling unshift");
}
$spec = func_get_args();
$this->specs->unshift($spec);
return $this;
}
public function push(/*$kernelClass, $args...*/)
{
if (func_num_args() === 0) {
throw new \InvalidArgumentException("Missing argument(s) when calling push");
}
$spec = func_get_args();
$this->specs->push($spec);
return $this;
}
public function resolve(HttpKernelInterface $app)
{
$middlewares = array($app);
foreach ($this->specs as $spec) {
$args = $spec;
$firstArg = array_shift($args);
if (is_callable($firstArg)) {
$app = $firstArg($app);
} else {
$kernelClass = $firstArg;
array_unshift($args, $app);
$reflection = new \ReflectionClass($kernelClass);
$app = $reflection->newInstanceArgs($args);
}
array_unshift($middlewares, $app);
}
return new StackedHttpKernel($app, $middlewares);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Stack;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class StackedHttpKernel implements HttpKernelInterface, TerminableInterface
{
private $app;
private $middlewares = array();
public function __construct(HttpKernelInterface $app, array $middlewares)
{
$this->app = $app;
$this->middlewares = $middlewares;
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
return $this->app->handle($request, $type, $catch);
}
public function terminate(Request $request, Response $response)
{
foreach ($this->middlewares as $kernel) {
if ($kernel instanceof TerminableInterface) {
$kernel->terminate($request, $response);
}
}
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace functional;
use Silex\Application;
use Stack\Builder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class SilexApplicationTest extends \PHPUnit_Framework_TestCase
{
public function testWithAppendMiddlewares()
{
$app = new Application();
$app->get('/foo', function () {
return 'bar';
});
$finished = false;
$app->finish(function () use (&$finished) {
$finished = true;
});
$stack = new Builder();
$stack
->push('functional\Append', '.A')
->push('functional\Append', '.B');
$app = $stack->resolve($app);
$request = Request::create('/foo');
$response = $app->handle($request);
$app->terminate($request, $response);
$this->assertSame('bar.B.A', $response->getContent());
$this->assertTrue($finished);
}
}
class Append implements HttpKernelInterface
{
private $app;
private $appendix;
public function __construct(HttpKernelInterface $app, $appendix)
{
$this->app = $app;
$this->appendix = $appendix;
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$response = clone $this->app->handle($request, $type, $catch);
$response->setContent($response->getContent().$this->appendix);
return $response;
}
}

View File

@ -0,0 +1,215 @@
<?php
namespace Stack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
/** @covers Stack\Builder */
class BuilderTest extends \PHPUnit_Framework_TestCase
{
/** @test */
public function withoutMiddlewaresItShouldReturnOriginalResponse()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertInstanceOf('Stack\StackedHttpKernel', $resolved);
$this->assertSame('ok', $response->getContent());
}
/** @test */
public function resolvedKernelShouldDelegateTerminateCalls()
{
$app = $this->getTerminableMock();
$stack = new Builder();
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = new Response('ok');
$resolved->handle($request);
$resolved->terminate($request, $response);
}
/** @test */
public function pushShouldReturnSelf()
{
$stack = new Builder();
$this->assertSame($stack, $stack->push('Stack\AppendA'));
}
/** @test */
public function pushShouldThrowOnInvalidInput()
{
$this->setExpectedException('InvalidArgumentException', 'Missing argument(s) when calling push');
$stack = new Builder();
$stack->push();
}
/** @test */
public function unshiftShouldReturnSelf()
{
$stack = new Builder();
$this->assertSame($stack, $stack->unshift('Stack\AppendA'));
}
/** @test */
public function unshiftShouldThrowOnInvalidInput()
{
$this->setExpectedException('InvalidArgumentException', 'Missing argument(s) when calling unshift');
$stack = new Builder();
$stack->unshift();
}
/** @test */
public function appendMiddlewareShouldAppendToBody()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$stack->push('Stack\AppendA');
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertSame('ok.A', $response->getContent());
}
/** @test */
public function unshiftMiddlewareShouldPutMiddlewareBeforePushed()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$stack->push('Stack\Append', '2.');
$stack->unshift('Stack\Append', '1.');
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertSame('ok2.1.', $response->getContent());
}
/** @test */
public function stackedMiddlewaresShouldWrapInReverseOrder()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$stack->push('Stack\AppendA');
$stack->push('Stack\AppendB');
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertSame('ok.B.A', $response->getContent());
}
/** @test */
public function resolveShouldPassPushArgumentsToMiddlewareConstructor()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$stack->push('Stack\Append', '.foo');
$stack->push('Stack\Append', '.bar');
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertSame('ok.bar.foo', $response->getContent());
}
/** @test */
public function resolveShouldCallSpecFactories()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$stack = new Builder();
$stack->push(function ($app) { return new Append($app, '.foo'); });
$stack->push(function ($app) { return new Append($app, '.bar'); });
$resolved = $stack->resolve($app);
$request = Request::create('/');
$response = $resolved->handle($request);
$this->assertSame('ok.bar.foo', $response->getContent());
}
private function getHttpKernelMock(Response $response)
{
$app = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$app->expects($this->any())
->method('handle')
->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request'))
->will($this->returnValue($response));
return $app;
}
private function getTerminableMock()
{
$app = $this->getMock('Stack\TerminableHttpKernel');
$app->expects($this->once())
->method('terminate')
->with(
$this->isInstanceOf('Symfony\Component\HttpFoundation\Request'),
$this->isInstanceOf('Symfony\Component\HttpFoundation\Response')
);
return $app;
}
}
abstract class TerminableHttpKernel implements HttpKernelInterface, TerminableInterface
{
}
class Append implements HttpKernelInterface
{
private $app;
private $appendix;
public function __construct(HttpKernelInterface $app, $appendix)
{
$this->app = $app;
$this->appendix = $appendix;
}
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$response = clone $this->app->handle($request, $type, $catch);
$response->setContent($response->getContent().$this->appendix);
return $response;
}
}
class AppendA extends Append
{
public function __construct(HttpKernelInterface $app)
{
parent::__construct($app, '.A');
}
}
class AppendB extends Append
{
public function __construct(HttpKernelInterface $app)
{
parent::__construct($app, '.B');
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Stack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
class StackedHttpKernelTest extends \PHPUnit_Framework_TestCase
{
/** @test */
public function handleShouldDelegateToApp()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$kernel = new StackedHttpKernel($app, array($app));
$request = Request::create('/');
$response = $kernel->handle($request);
$this->assertSame('ok', $response->getContent());
}
/** @test */
public function handleShouldStillDelegateToAppWithMiddlewares()
{
$app = $this->getHttpKernelMock(new Response('ok'));
$foo = $this->getHttpKernelMock(new Response('foo'));
$bar = $this->getHttpKernelMock(new Response('bar'));
$kernel = new StackedHttpKernel($app, array($app, $foo, $bar));
$request = Request::create('/');
$response = $kernel->handle($request);
$this->assertSame('ok', $response->getContent());
}
/** @test */
public function terminateShouldDelegateToMiddlewares()
{
$app = $this->getTerminableMock(new Response('ok'));
$foo = $this->getTerminableMock();
$bar = $this->getTerminableMock();
$kernel = new StackedHttpKernel($app, array($app, $foo, $bar));
$request = Request::create('/');
$response = $kernel->handle($request);
$kernel->terminate($request, $response);
}
private function getHttpKernelMock(Response $response)
{
$app = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
$app->expects($this->any())
->method('handle')
->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request'))
->will($this->returnValue($response));
return $app;
}
private function getTerminableMock(Response $response = null)
{
$app = $this->getMock('Stack\TerminableHttpKernel');
if ($response) {
$app->expects($this->any())
->method('handle')
->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request'))
->will($this->returnValue($response));
}
$app->expects($this->once())
->method('terminate')
->with(
$this->isInstanceOf('Symfony\Component\HttpFoundation\Request'),
$this->isInstanceOf('Symfony\Component\HttpFoundation\Response')
);
return $app;
}
}

View File

@ -19,8 +19,7 @@ try {
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$response = $kernel
->handlePageCache($request)
->handle($request)
->handle($request)
// Handle the response object.
->prepare($request)->send();
$kernel->terminate($request, $response);