Skip to content
Permalink
Browse files

Implement the middleware runner.

This runner class implements the `$next` callable that is used by
middleware objects to signal that the next middleware object should take
control of the request/response.
  • Loading branch information...
markstory committed Apr 5, 2016
1 parent 2f37224 commit b89a84565a92fd67ea11406581ea2d8439d6e16b
Showing with 224 additions and 2 deletions.
  1. +3 −2 composer.json
  2. +69 −0 src/Http/Runner.php
  3. +152 −0 tests/TestCase/Http/RunnerTest.php
@@ -23,10 +23,11 @@
"ext-mbstring": "*",
"cakephp/chronos": "*",
"aura/intl": "1.1.*",
"psr/log": "1.0"
"psr/log": "1.0",
"zendframework/zend-diactoros": "~1.0"
},
"suggest": {
"ext-openssl": "To use Security::encrypt() or have secure CSRF token generation."
"ext-openssl": "To use Security::encrypt() or have secure CSRF token generation."
},
"require-dev": {
"phpunit/phpunit": "*",
@@ -0,0 +1,69 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Http;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Executes the middleware stack and provides the `next` callable
* that allows the stack to be iterated.
*/
class Runner
{
/**
* The current index in the middleware stack.
*
* @var int
*/
protected $index;
/**
* The middleware stack being run.
*
* @var MiddlewareStack
*/
protected $middleware;
/**
* @param \Cake\Http\MiddlewareStack $middleware The middleware stack
* @param \Psr\Http\Message\ServerRequestInterface $request The Server Request
* @param \Psr\Http\Message\ResponseInterface $response The response
* @return \Psr\Http\Message\ResponseInterface A response object
*/
public function run($middleware, ServerRequestInterface $request, ResponseInterface $response)
{
$this->middleware = $middleware;
$this->index = 0;
return $this->__invoke($request, $response);
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request The server request
* @param \Psr\Http\Message\ResponseInterface $response The response object
* @return \Psr\Http\Message\ResponseInterface An updated response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
{
$next = $this->middleware->get($this->index);
if ($next) {
$this->index++;
return $next($request, $response, $this);
}
// End of the stack
return $response;
}
}
@@ -0,0 +1,152 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Test\TestCase;
use Cake\Http\MiddlewareStack;
use Cake\Http\Runner;
use Cake\TestSuite\TestCase;
use RuntimeException;
/**
* Test case for runner.
*/
class RunnerTest extends TestCase
{
/**
* setup
*
* @return void
*/
public function setUp()
{
parent::setUp();
$this->stack = new MiddlewareStack();
$this->ok = function ($req, $res, $next) {
return $next($req, $res);
};
$this->pass = function ($req, $res, $next) {
return $next($req, $res);
};
$this->noNext = function ($req, $res, $next) {
};
$this->fail = function ($req, $res, $next) {
throw new RuntimeException('A bad thing');
};
}
/**
* Test running a single middleware object.
*
* @return void
*/
public function testRunSingle()
{
$this->stack->push($this->ok);
$req = $this->getMock('Psr\Http\Message\ServerRequestInterface');
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
$runner = new Runner();
$result = $runner->run($this->stack, $req, $res);
$this->assertSame($res, $result);
}
/**
* Test replacing a response in a middleware.
*
* @return void
*/
public function testRunResponseReplace()
{
$one = function ($req, $res, $next) {
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
return $next($req, $res);
};
$this->stack->push($one);
$runner = new Runner();
$req = $this->getMock('Psr\Http\Message\ServerRequestInterface');
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
$result = $runner->run($this->stack, $req, $res);
$this->assertNotSame($res, $result, 'Response was not replaced');
$this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $result);
}
/**
* Test that middleware is run in sequence
*
* @return void
*/
public function testRunSequencing()
{
$log = [];
$one = function ($req, $res, $next) use (&$log) {
$log[] = 'one';
return $next($req, $res);
};
$two = function ($req, $res, $next) use (&$log) {
$log[] = 'two';
return $next($req, $res);
};
$three = function ($req, $res, $next) use (&$log) {
$log[] = 'three';
return $next($req, $res);
};
$this->stack->push($one)->push($two)->push($three);
$runner = new Runner();
$req = $this->getMock('Psr\Http\Message\ServerRequestInterface');
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
$result = $runner->run($this->stack, $req, $res);
$this->assertSame($res, $result, 'Response is not correct');
$expected = ['one', 'two', 'three'];
$this->assertEquals($expected, $log);
}
/**
* Test that exceptions bubble up.
*
* @expectedException RuntimeException
* @expectedExceptionMessage A bad thing
*/
public function testRunExceptionInMiddleware()
{
$this->stack->push($this->ok)->push($this->fail);
$req = $this->getMock('Psr\Http\Message\ServerRequestInterface');
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
$runner = new Runner();
$runner->run($this->stack, $req, $res);
}
/**
* Test that 'bad' middleware returns null.
*
* @return void
*/
public function testRunNextNotCalled()
{
$this->stack->push($this->noNext);
$req = $this->getMock('Psr\Http\Message\ServerRequestInterface');
$res = $this->getMock('Psr\Http\Message\ResponseInterface');
$runner = new Runner();
$result = $runner->run($this->stack, $req, $res);
$this->assertNull($result);
}
}

0 comments on commit b89a845

Please sign in to comment.
You can’t perform that action at this time.