From 5ef912e9659131fd31d1522e3216d37323a0c047 Mon Sep 17 00:00:00 2001 From: Dries <dries@buytaert.net> Date: Wed, 3 Sep 2014 22:58:45 -0400 Subject: [PATCH] Issue #2303673 by dawehner, damiankloip, effulgentsia, Fabianx: Implement stackphp; cleanup handlePageCache() and preHandle() --- composer.json | 3 +- composer.lock | 52 +- core/core.services.yml | 27 +- .../Authentication/AuthenticationManager.php | 2 +- core/lib/Drupal/Core/CoreServiceProvider.php | 3 + .../Compiler/StackedKernelPass.php | 45 ++ core/lib/Drupal/Core/DrupalKernel.php | 8 +- .../lib/Drupal/Core/DrupalKernelInterface.php | 8 + .../ReverseProxySubscriber.php | 63 --- core/lib/Drupal/Core/Form/FormBuilder.php | 5 +- .../Core/StackMiddleware/KernelPreHandle.php | 57 ++ .../Drupal/Core/StackMiddleware/PageCache.php | 55 ++ .../ReverseProxyMiddleware.php | 61 +++ .../HttpKernel/StackKernelIntegrationTest.php | 50 ++ .../httpkernel_test/httpkernel_test.info.yml | 6 + .../httpkernel_test.services.yml | 10 + .../src/HttpKernel/TestMiddleware.php | 60 +++ .../ReverseProxySubscriberUnitTest.php | 103 ---- .../ReverseProxyMiddlewareTest.php | 100 ++++ core/vendor/composer/autoload_namespaces.php | 1 + core/vendor/composer/installed.json | 52 ++ core/vendor/stack/builder/.travis.yml | 19 + core/vendor/stack/builder/CHANGELOG.md | 14 + core/vendor/stack/builder/LICENSE | 19 + core/vendor/stack/builder/README.md | 62 +++ core/vendor/stack/builder/composer.json | 26 + core/vendor/stack/builder/composer.lock | 488 ++++++++++++++++++ core/vendor/stack/builder/phpunit.xml.dist | 30 ++ .../stack/builder/src/Stack/Builder.php | 63 +++ .../builder/src/Stack/StackedHttpKernel.php | 34 ++ .../tests/functional/SilexApplicationTest.php | 60 +++ .../builder/tests/unit/Stack/BuilderTest.php | 215 ++++++++ .../unit/Stack/StackedHttpKernelTest.php | 80 +++ index.php | 3 +- 34 files changed, 1699 insertions(+), 185 deletions(-) create mode 100644 core/lib/Drupal/Core/DependencyInjection/Compiler/StackedKernelPass.php delete mode 100644 core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php create mode 100644 core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php create mode 100644 core/lib/Drupal/Core/StackMiddleware/PageCache.php create mode 100644 core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php create mode 100644 core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php create mode 100644 core/modules/system/tests/modules/httpkernel_test/httpkernel_test.info.yml create mode 100644 core/modules/system/tests/modules/httpkernel_test/httpkernel_test.services.yml create mode 100644 core/modules/system/tests/modules/httpkernel_test/src/HttpKernel/TestMiddleware.php delete mode 100644 core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php create mode 100644 core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php create mode 100644 core/vendor/stack/builder/.travis.yml create mode 100644 core/vendor/stack/builder/CHANGELOG.md create mode 100644 core/vendor/stack/builder/LICENSE create mode 100644 core/vendor/stack/builder/README.md create mode 100644 core/vendor/stack/builder/composer.json create mode 100644 core/vendor/stack/builder/composer.lock create mode 100644 core/vendor/stack/builder/phpunit.xml.dist create mode 100644 core/vendor/stack/builder/src/Stack/Builder.php create mode 100644 core/vendor/stack/builder/src/Stack/StackedHttpKernel.php create mode 100644 core/vendor/stack/builder/tests/functional/SilexApplicationTest.php create mode 100644 core/vendor/stack/builder/tests/unit/Stack/BuilderTest.php create mode 100644 core/vendor/stack/builder/tests/unit/Stack/StackedHttpKernelTest.php diff --git a/composer.json b/composer.json index 7e647d5453b7..ac5d6faea175 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/composer.lock b/composer.lock index 082fd938a067..981d888e3947 100644 --- a/composer.lock +++ b/composer.lock @@ -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", diff --git a/core/core.services.yml b/core/core.services.yml index 72bf10e04312..c43da8c81437 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -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: diff --git a/core/lib/Drupal/Core/Authentication/AuthenticationManager.php b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php index 9580fa9cc475..7c357c9d16d5 100644 --- a/core/lib/Drupal/Core/Authentication/AuthenticationManager.php +++ b/core/lib/Drupal/Core/Authentication/AuthenticationManager.php @@ -183,7 +183,7 @@ public function handleException(GetResponseForExceptionEvent $event) { $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) { diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 8662d640e403..8393903fb650 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -10,6 +10,7 @@ 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 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new BackendCompilerPass()); + $container->addCompilerPass(new StackedKernelPass()); + // Collect tagged handler services as method calls on consumer services. $container->addCompilerPass(new TaggedHandlersPass()); diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/StackedKernelPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/StackedKernelPass.php new file mode 100644 index 000000000000..bf8efc0745b7 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/StackedKernelPass.php @@ -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); + } + } + +} diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 7a303a20a5b7..5377c36c8747 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -418,12 +418,9 @@ public function getContainer() { } /** - * 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 @@ public function terminate(Request $request, Response $response) { */ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) { $this->boot(); - $this->preHandle($request); return $this->getHttpKernel()->handle($request, $type, $catch); } diff --git a/core/lib/Drupal/Core/DrupalKernelInterface.php b/core/lib/Drupal/Core/DrupalKernelInterface.php index 08d007c49135..ca7af2bd8b38 100644 --- a/core/lib/Drupal/Core/DrupalKernelInterface.php +++ b/core/lib/Drupal/Core/DrupalKernelInterface.php @@ -108,4 +108,12 @@ public function handlePageCache(Request $request); */ 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); + } diff --git a/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php deleted file mode 100644 index 788076abba98..000000000000 --- a/core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php +++ /dev/null @@ -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; - } -} diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index b1e226014213..3417549bb284 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -22,7 +22,6 @@ 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; diff --git a/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php b/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php new file mode 100644 index 000000000000..1ccdea11f78f --- /dev/null +++ b/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php @@ -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); + } + +} + diff --git a/core/lib/Drupal/Core/StackMiddleware/PageCache.php b/core/lib/Drupal/Core/StackMiddleware/PageCache.php new file mode 100644 index 000000000000..ba59e936f48e --- /dev/null +++ b/core/lib/Drupal/Core/StackMiddleware/PageCache.php @@ -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); + } + +} diff --git a/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php b/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php new file mode 100644 index 000000000000..344364d088fa --- /dev/null +++ b/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php @@ -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); + } + +} diff --git a/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php b/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php new file mode 100644 index 000000000000..ee37f7e9b7a9 --- /dev/null +++ b/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php @@ -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'); + } + +} + diff --git a/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.info.yml b/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.info.yml new file mode 100644 index 000000000000..5f1375f2825a --- /dev/null +++ b/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.info.yml @@ -0,0 +1,6 @@ +name: 'HttpKernel test' +type: module +description: 'Support module for httpkernel tests.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.services.yml b/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.services.yml new file mode 100644 index 000000000000..c4784de32b6a --- /dev/null +++ b/core/modules/system/tests/modules/httpkernel_test/httpkernel_test.services.yml @@ -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 } diff --git a/core/modules/system/tests/modules/httpkernel_test/src/HttpKernel/TestMiddleware.php b/core/modules/system/tests/modules/httpkernel_test/src/HttpKernel/TestMiddleware.php new file mode 100644 index 000000000000..93e1a89ca189 --- /dev/null +++ b/core/modules/system/tests/modules/httpkernel_test/src/HttpKernel/TestMiddleware.php @@ -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); + } + +} diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php deleted file mode 100644 index 2b197882e449..000000000000 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php +++ /dev/null @@ -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; - } -} diff --git a/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php b/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php new file mode 100644 index 000000000000..231f833e8a76 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php @@ -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()); + } +} diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index f49ab2c3c06c..5bb8438c1b11 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -26,6 +26,7 @@ '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'), diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index 97c0fa48a225..5b56c2add953 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -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" + ] } ] diff --git a/core/vendor/stack/builder/.travis.yml b/core/vendor/stack/builder/.travis.yml new file mode 100644 index 000000000000..1d50df4d17bb --- /dev/null +++ b/core/vendor/stack/builder/.travis.yml @@ -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 diff --git a/core/vendor/stack/builder/CHANGELOG.md b/core/vendor/stack/builder/CHANGELOG.md new file mode 100644 index 000000000000..f6f35d6d71c6 --- /dev/null +++ b/core/vendor/stack/builder/CHANGELOG.md @@ -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. diff --git a/core/vendor/stack/builder/LICENSE b/core/vendor/stack/builder/LICENSE new file mode 100644 index 000000000000..ceac72c5c27d --- /dev/null +++ b/core/vendor/stack/builder/LICENSE @@ -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. diff --git a/core/vendor/stack/builder/README.md b/core/vendor/stack/builder/README.md new file mode 100644 index 000000000000..812406c24584 --- /dev/null +++ b/core/vendor/stack/builder/README.md @@ -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) diff --git a/core/vendor/stack/builder/composer.json b/core/vendor/stack/builder/composer.json new file mode 100644 index 000000000000..e9f164cccdab --- /dev/null +++ b/core/vendor/stack/builder/composer.json @@ -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" } + } +} diff --git a/core/vendor/stack/builder/composer.lock b/core/vendor/stack/builder/composer.lock new file mode 100644 index 000000000000..26c6a994a725 --- /dev/null +++ b/core/vendor/stack/builder/composer.lock @@ -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": [ + + ] +} diff --git a/core/vendor/stack/builder/phpunit.xml.dist b/core/vendor/stack/builder/phpunit.xml.dist new file mode 100644 index 000000000000..a2437ecdde94 --- /dev/null +++ b/core/vendor/stack/builder/phpunit.xml.dist @@ -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> diff --git a/core/vendor/stack/builder/src/Stack/Builder.php b/core/vendor/stack/builder/src/Stack/Builder.php new file mode 100644 index 000000000000..c7ee05a196d4 --- /dev/null +++ b/core/vendor/stack/builder/src/Stack/Builder.php @@ -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); + } +} diff --git a/core/vendor/stack/builder/src/Stack/StackedHttpKernel.php b/core/vendor/stack/builder/src/Stack/StackedHttpKernel.php new file mode 100644 index 000000000000..2b96dd952096 --- /dev/null +++ b/core/vendor/stack/builder/src/Stack/StackedHttpKernel.php @@ -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); + } + } + } +} diff --git a/core/vendor/stack/builder/tests/functional/SilexApplicationTest.php b/core/vendor/stack/builder/tests/functional/SilexApplicationTest.php new file mode 100644 index 000000000000..5636336a8478 --- /dev/null +++ b/core/vendor/stack/builder/tests/functional/SilexApplicationTest.php @@ -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; + } +} diff --git a/core/vendor/stack/builder/tests/unit/Stack/BuilderTest.php b/core/vendor/stack/builder/tests/unit/Stack/BuilderTest.php new file mode 100644 index 000000000000..4092d01a54fd --- /dev/null +++ b/core/vendor/stack/builder/tests/unit/Stack/BuilderTest.php @@ -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'); + } +} diff --git a/core/vendor/stack/builder/tests/unit/Stack/StackedHttpKernelTest.php b/core/vendor/stack/builder/tests/unit/Stack/StackedHttpKernelTest.php new file mode 100644 index 000000000000..e2ced544afa4 --- /dev/null +++ b/core/vendor/stack/builder/tests/unit/Stack/StackedHttpKernelTest.php @@ -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; + } +} diff --git a/index.php b/index.php index 78ee18c2ecf5..406d3dcbe329 100644 --- a/index.php +++ b/index.php @@ -19,8 +19,7 @@ $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); -- GitLab