mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-06-26 06:29:24 +02:00
API Create HTTPMiddleware and standardise middleware for request handling
This commit is contained in:
parent
2a10c2397b
commit
bba9791146
2
main.php
2
main.php
|
@ -15,5 +15,5 @@ $request = HTTPRequest::createFromEnvironment();
|
||||||
$kernel = new AppKernel();
|
$kernel = new AppKernel();
|
||||||
$app = new HTTPApplication($kernel);
|
$app = new HTTPApplication($kernel);
|
||||||
$app->addMiddleware(new OutputMiddleware());
|
$app->addMiddleware(new OutputMiddleware());
|
||||||
$app->addMiddleware(new ErrorControlChainMiddleware($app, $request));
|
$app->addMiddleware(new ErrorControlChainMiddleware($app));
|
||||||
$app->handle($request);
|
$app->handle($request);
|
||||||
|
|
|
@ -316,7 +316,7 @@ class HTTPRequest implements ArrayAccess
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication)
|
// Initiate an empty session - doesn't initialize an actual PHP session (see HTTPApplication)
|
||||||
$session = new Session($variables['_SESSION']);
|
$session = new Session(isset($variables['_SESSION']) ? $variables['_SESSION'] : null);
|
||||||
$request->setSession($session);
|
$request->setSession($session);
|
||||||
|
|
||||||
return $request;
|
return $request;
|
||||||
|
|
|
@ -13,13 +13,4 @@ interface Application
|
||||||
* @return Kernel
|
* @return Kernel
|
||||||
*/
|
*/
|
||||||
public function getKernel();
|
public function getKernel();
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoke the application control chain
|
|
||||||
*
|
|
||||||
* @param callable $callback
|
|
||||||
* @param bool $flush
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function execute(callable $callback, $flush = false);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,12 @@ use SilverStripe\Control\HTTPResponse;
|
||||||
class HTTPApplication implements Application
|
class HTTPApplication implements Application
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var callable[]
|
* @var HTTPMiddleware[]
|
||||||
*/
|
*/
|
||||||
protected $middlewares = [];
|
protected $middlewares = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return callable[]
|
* @return HTTPMiddleware[]
|
||||||
*/
|
*/
|
||||||
public function getMiddlewares()
|
public function getMiddlewares()
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ class HTTPApplication implements Application
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable[] $middlewares
|
* @param HTTPMiddleware[] $middlewares
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setMiddlewares($middlewares)
|
public function setMiddlewares($middlewares)
|
||||||
|
@ -35,10 +35,10 @@ class HTTPApplication implements Application
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callable $middleware
|
* @param HTTPMiddleware $middleware
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function addMiddleware($middleware)
|
public function addMiddleware(HTTPMiddleware $middleware)
|
||||||
{
|
{
|
||||||
$this->middlewares[] = $middleware;
|
$this->middlewares[] = $middleware;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -47,19 +47,21 @@ class HTTPApplication implements Application
|
||||||
/**
|
/**
|
||||||
* Call middleware
|
* Call middleware
|
||||||
*
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
* @param callable $last Last config to call
|
* @param callable $last Last config to call
|
||||||
* @return HTTPResponse
|
* @return HTTPResponse
|
||||||
*/
|
*/
|
||||||
protected function callMiddleware($last)
|
protected function callMiddleware(HTTPRequest $request, $last)
|
||||||
{
|
{
|
||||||
// Reverse middlewares
|
// Reverse middlewares
|
||||||
$next = $last;
|
$next = $last;
|
||||||
|
/** @var HTTPMiddleware $middleware */
|
||||||
foreach (array_reverse($this->getMiddlewares()) as $middleware) {
|
foreach (array_reverse($this->getMiddlewares()) as $middleware) {
|
||||||
$next = function () use ($middleware, $next) {
|
$next = function ($request) use ($middleware, $next) {
|
||||||
return call_user_func($middleware, $next);
|
return $middleware->process($request, $next);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return call_user_func($next);
|
return call_user_func($next, $request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +95,7 @@ class HTTPApplication implements Application
|
||||||
$flush = $request->getVar('flush') || strpos($request->getURL(), 'dev/build') === 0;
|
$flush = $request->getVar('flush') || strpos($request->getURL(), 'dev/build') === 0;
|
||||||
|
|
||||||
// Ensure boot is invoked
|
// Ensure boot is invoked
|
||||||
return $this->execute(function () use ($request) {
|
return $this->execute($request, function (HTTPRequest $request) {
|
||||||
// Start session and execute
|
// Start session and execute
|
||||||
$request->getSession()->init();
|
$request->getSession()->init();
|
||||||
return Director::direct($request);
|
return Director::direct($request);
|
||||||
|
@ -103,17 +105,18 @@ class HTTPApplication implements Application
|
||||||
/**
|
/**
|
||||||
* Safely boot the application and execute the given main action
|
* Safely boot the application and execute the given main action
|
||||||
*
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
* @param callable $callback
|
* @param callable $callback
|
||||||
* @param bool $flush
|
* @param bool $flush
|
||||||
* @return HTTPResponse
|
* @return HTTPResponse
|
||||||
*/
|
*/
|
||||||
public function execute(callable $callback, $flush = false)
|
public function execute(HTTPRequest $request, callable $callback, $flush = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return $this->callMiddleware(function () use ($callback, $flush) {
|
return $this->callMiddleware($request, function ($request) use ($callback, $flush) {
|
||||||
// Pre-request boot
|
// Pre-request boot
|
||||||
$this->getKernel()->boot($flush);
|
$this->getKernel()->boot($flush);
|
||||||
return call_user_func($callback);
|
return call_user_func($callback, $request);
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
$this->getKernel()->shutdown();
|
$this->getKernel()->shutdown();
|
||||||
|
|
22
src/Core/HTTPMiddleware.php
Normal file
22
src/Core/HTTPMiddleware.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Request middleware
|
||||||
|
* Based on https://github.com/php-fig/fig-standards/blob/master/proposed/http-middleware/middleware.md#21-psrhttpservermiddlewaremiddlewareinterface
|
||||||
|
*/
|
||||||
|
interface HTTPMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Generate response for the given request
|
||||||
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
|
* @param callable $delegate
|
||||||
|
* @return HTTPResponse
|
||||||
|
*/
|
||||||
|
public function process(HTTPRequest $request, callable $delegate);
|
||||||
|
}
|
|
@ -7,52 +7,42 @@ use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
use SilverStripe\Core\Application;
|
use SilverStripe\Core\Application;
|
||||||
|
use SilverStripe\Core\HTTPMiddleware;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorates application bootstrapping with errorcontrolchain
|
* Decorates application bootstrapping with errorcontrolchain
|
||||||
*/
|
*/
|
||||||
class ErrorControlChainMiddleware
|
class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Application
|
* @var Application
|
||||||
*/
|
*/
|
||||||
protected $application = null;
|
protected $application = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var HTTPRequest
|
|
||||||
*/
|
|
||||||
protected $request = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build error control chain for an application
|
* Build error control chain for an application
|
||||||
*
|
*
|
||||||
* @param Application $application
|
* @param Application $application
|
||||||
* @param HTTPRequest $request
|
|
||||||
*/
|
*/
|
||||||
public function __construct(Application $application, HTTPRequest $request)
|
public function __construct(Application $application)
|
||||||
{
|
{
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
$this->request = $request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function process(HTTPRequest $request, callable $next)
|
||||||
* @param callable $next
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
|
||||||
public function __invoke(callable $next)
|
|
||||||
{
|
{
|
||||||
$result = null;
|
$result = null;
|
||||||
|
|
||||||
// Prepare tokens and execute chain
|
// Prepare tokens and execute chain
|
||||||
$reloadToken = ParameterConfirmationToken::prepare_tokens(
|
$reloadToken = ParameterConfirmationToken::prepare_tokens(
|
||||||
['isTest', 'isDev', 'flush'],
|
['isTest', 'isDev', 'flush'],
|
||||||
$this->getRequest()
|
$request
|
||||||
);
|
);
|
||||||
$chain = new ErrorControlChain();
|
$chain = new ErrorControlChain();
|
||||||
$chain
|
$chain
|
||||||
->then(function () use ($chain, $reloadToken, $next, &$result) {
|
->then(function () use ($request, $chain, $reloadToken, $next, &$result) {
|
||||||
// If no redirection is necessary then we can disable error supression
|
// If no redirection is necessary then we can disable error supression
|
||||||
if (!$reloadToken) {
|
if (!$reloadToken) {
|
||||||
$chain->setSuppression(false);
|
$chain->setSuppression(false);
|
||||||
|
@ -61,10 +51,10 @@ class ErrorControlChainMiddleware
|
||||||
try {
|
try {
|
||||||
// Check if a token is requesting a redirect
|
// Check if a token is requesting a redirect
|
||||||
if ($reloadToken) {
|
if ($reloadToken) {
|
||||||
$result = $this->safeReloadWithToken($reloadToken);
|
$result = $this->safeReloadWithToken($request, $reloadToken);
|
||||||
} else {
|
} else {
|
||||||
// If no reload necessary, process application
|
// If no reload necessary, process application
|
||||||
$result = call_user_func($next);
|
$result = call_user_func($next, $request);
|
||||||
}
|
}
|
||||||
} catch (HTTPResponse_Exception $exception) {
|
} catch (HTTPResponse_Exception $exception) {
|
||||||
$result = $exception->getResponse();
|
$result = $exception->getResponse();
|
||||||
|
@ -84,16 +74,17 @@ class ErrorControlChainMiddleware
|
||||||
* Reload application with the given token, but only if either the user is authenticated,
|
* Reload application with the given token, but only if either the user is authenticated,
|
||||||
* or authentication is impossible.
|
* or authentication is impossible.
|
||||||
*
|
*
|
||||||
|
* @param HTTPRequest $request
|
||||||
* @param ParameterConfirmationToken $reloadToken
|
* @param ParameterConfirmationToken $reloadToken
|
||||||
* @return HTTPResponse
|
* @return HTTPResponse
|
||||||
*/
|
*/
|
||||||
protected function safeReloadWithToken($reloadToken)
|
protected function safeReloadWithToken(HTTPRequest $request, $reloadToken)
|
||||||
{
|
{
|
||||||
// Safe reload requires manual boot
|
// Safe reload requires manual boot
|
||||||
$this->getApplication()->getKernel()->boot(false);
|
$this->getApplication()->getKernel()->boot(false);
|
||||||
|
|
||||||
// Ensure session is started
|
// Ensure session is started
|
||||||
$this->getRequest()->getSession()->init();
|
$request->getSession()->init();
|
||||||
|
|
||||||
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
|
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
|
||||||
if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
|
if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
|
||||||
|
@ -102,7 +93,7 @@ class ErrorControlChainMiddleware
|
||||||
|
|
||||||
// Fail and redirect the user to the login page
|
// Fail and redirect the user to the login page
|
||||||
$loginPage = Director::absoluteURL(Security::config()->get('login_url'));
|
$loginPage = Director::absoluteURL(Security::config()->get('login_url'));
|
||||||
$loginPage .= "?BackURL=" . urlencode($this->getRequest()->getURL());
|
$loginPage .= "?BackURL=" . urlencode($request->getURL());
|
||||||
$result = new HTTPResponse();
|
$result = new HTTPResponse();
|
||||||
$result->redirect($loginPage);
|
$result->redirect($loginPage);
|
||||||
return $result;
|
return $result;
|
||||||
|
@ -125,22 +116,4 @@ class ErrorControlChainMiddleware
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HTTPRequest
|
|
||||||
*/
|
|
||||||
public function getRequest()
|
|
||||||
{
|
|
||||||
return $this->request;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setRequest(HTTPRequest $request)
|
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
namespace SilverStripe\Core\Startup;
|
namespace SilverStripe\Core\Startup;
|
||||||
|
|
||||||
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
use SilverStripe\Core\HTTPMiddleware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits response to the browser
|
* Emits response to the browser
|
||||||
*/
|
*/
|
||||||
class OutputMiddleware
|
class OutputMiddleware implements HTTPMiddleware
|
||||||
{
|
{
|
||||||
protected $defaultResponse = null;
|
protected $defaultResponse = null;
|
||||||
|
|
||||||
|
@ -24,11 +26,11 @@ class OutputMiddleware
|
||||||
$this->defaultResponse = $defaultResponse;
|
$this->defaultResponse = $defaultResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(callable $next)
|
public function process(HTTPRequest $request, callable $delegate)
|
||||||
{
|
{
|
||||||
/** @var HTTPResponse $response */
|
/** @var HTTPResponse $response */
|
||||||
try {
|
try {
|
||||||
$response = call_user_func($next);
|
$response = call_user_func($delegate, $request);
|
||||||
} catch (HTTPResponse_Exception $exception) {
|
} catch (HTTPResponse_Exception $exception) {
|
||||||
$response = $exception->getResponse();
|
$response = $exception->getResponse();
|
||||||
}
|
}
|
||||||
|
|
|
@ -898,7 +898,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||||
$app = new HTTPApplication($kernel);
|
$app = new HTTPApplication($kernel);
|
||||||
|
|
||||||
// Custom application
|
// Custom application
|
||||||
$app->execute(function () use ($request) {
|
$app->execute($request, function (HTTPRequest $request) {
|
||||||
// Start session and execute
|
// Start session and execute
|
||||||
$request->getSession()->init();
|
$request->getSession()->init();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user