Commit 04813907 authored by Lukas Reschke's avatar Lukas Reschke
Browse files

Add functions to modify cookies to response class

Currently there is no AppFramework way to modify cookies, which makes it unusable for quite some use-cases or results in untestable code.

This PR adds some basic functionalities to add and invalidate cookies.

Usage:
```php
$response = new TemplateResponse(...);
$response->addCookie('foo', 'bar');
$response->invalidateCookie('foo');
$response->addCookie('bar', 'foo', new \DateTime('2015-01-01 00:00'));
```

Existing cookies can be accessed with the AppFramework using `$this->request->getCookie($name)`.
parent e306b588
......@@ -53,7 +53,7 @@ class App {
// initialize the dispatcher and run all the middleware before the controller
$dispatcher = $container['Dispatcher'];
list($httpHeaders, $responseHeaders, $output) =
list($httpHeaders, $responseHeaders, $responseCookies, $output) =
$dispatcher->dispatch($controller, $methodName);
if(!is_null($httpHeaders)) {
......@@ -64,6 +64,14 @@ class App {
header($name . ': ' . $value);
}
foreach($responseCookies as $name => $value) {
$expireDate = null;
if($value['expireDate'] instanceof \DateTime) {
$expireDate = $value['expireDate']->getTimestamp();
}
setcookie($name, $value['value'], $expireDate, \OC::$WEBROOT, null, \OC::$server->getConfig()->getSystemValue('forcessl', false), true);
}
if(!is_null($output)) {
header('Content-Length: ' . strlen($output));
print($output);
......
......@@ -48,7 +48,7 @@ class Dispatcher {
* @param Http $protocol the http protocol with contains all status headers
* @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
* runs the middleware
* @param ControllerMethodReflector the reflector that is used to inject
* @param ControllerMethodReflector $reflector the reflector that is used to inject
* the arguments for the controller
* @param IRequest $request the incoming request
*/
......@@ -71,6 +71,7 @@ class Dispatcher {
* @return array $array[0] contains a string with the http main header,
* $array[1] contains headers in the form: $key => value, $array[2] contains
* the response output
* @throws \Exception
*/
public function dispatch(Controller $controller, $methodName) {
$out = array(null, array(), null);
......@@ -102,13 +103,14 @@ class Dispatcher {
// get the output which should be printed and run the after output
// middleware to modify the response
$output = $response->render();
$out[2] = $this->middlewareDispatcher->beforeOutput(
$out[3] = $this->middlewareDispatcher->beforeOutput(
$controller, $methodName, $output);
// depending on the cache object the headers need to be changed
$out[0] = $this->protocol->getStatusHeader($response->getStatus(),
$response->getLastModified(), $response->getETag());
$out[1] = $response->getHeaders();
$out[1] = array_merge($response->getHeaders());
$out[2] = $response->getCookies();
return $out;
}
......
......@@ -45,9 +45,16 @@ class Response {
);
/**
* Cookies that will be need to be constructed as header
* @var array
*/
private $cookies = array();
/**
* HTTP status code - defaults to STATUS OK
* @var string
* @var int
*/
private $status = Http::STATUS_OK;
......@@ -70,6 +77,7 @@ class Response {
* Caches the response
* @param int $cacheSeconds the amount of seconds that should be cached
* if 0 then caching will be disabled
* @return $this
*/
public function cacheFor($cacheSeconds) {
......@@ -83,13 +91,68 @@ class Response {
return $this;
}
/**
* Adds a new cookie to the response
* @param string $name The name of the cookie
* @param string $value The value of the cookie
* @param \DateTime|null $expireDate Date on that the cookie should expire, if set
* to null cookie will be considered as session
* cookie.
* @return $this
*/
public function addCookie($name, $value, \DateTime $expireDate = null) {
$this->cookies[$name] = array('value' => $value, 'expireDate' => $expireDate);
return $this;
}
/**
* Set the specified cookies
* @param array $cookies array('foo' => array('value' => 'bar', 'expire' => null))
* @return $this
*/
public function setCookies(array $cookies) {
$this->cookies = $cookies;
return $this;
}
/**
* Invalidates the specified cookie
* @param string $name
* @return $this
*/
public function invalidateCookie($name) {
$this->addCookie($name, 'expired', new \DateTime('1971-01-01 00:00'));
return $this;
}
/**
* Invalidates the specified cookies
* @param array $cookieNames array('foo', 'bar')
* @return $this
*/
public function invalidateCookies(array $cookieNames) {
foreach($cookieNames as $cookieName) {
$this->invalidateCookie($cookieName);
}
return $this;
}
/**
* Returns the cookies
* @return array
*/
public function getCookies() {
return $this->cookies;
}
/**
* Adds a new header to the response that will be called before the render
* function
* @param string $name The name of the HTTP header
* @param string $value The value, null will delete it
* @return Response Reference to this object
* @return $this
*/
public function addHeader($name, $value) {
$name = trim($name); // always remove leading and trailing whitespace
......@@ -108,10 +171,10 @@ class Response {
/**
* Set the headers
* @param array key value header pairs
* @return Response Reference to this object
* @param array $headers value header pairs
* @return $this
*/
public function setHeaders($headers) {
public function setHeaders(array $headers) {
$this->headers = $headers;
return $this;
......
......@@ -63,7 +63,7 @@ class AppTest extends \Test\TestCase {
public function testControllerNameAndMethodAreBeingPassed(){
$return = array(null, array(), null);
$return = array(null, array(), array(), null);
$this->dispatcher->expects($this->once())
->method('dispatch')
->with($this->equalTo($this->controller),
......
......@@ -227,7 +227,7 @@ class DispatcherTest extends \Test\TestCase {
$this->assertEquals($httpHeaders, $response[0]);
$this->assertEquals($responseHeaders, $response[1]);
$this->assertEquals($out, $response[2]);
$this->assertEquals($out, $response[3]);
}
......@@ -246,7 +246,7 @@ class DispatcherTest extends \Test\TestCase {
$this->assertEquals($httpHeaders, $response[0]);
$this->assertEquals($responseHeaders, $response[1]);
$this->assertEquals($out, $response[2]);
$this->assertEquals($out, $response[3]);
}
......@@ -301,7 +301,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('[3,true,4,1]', $response[2]);
$this->assertEquals('[3,true,4,1]', $response[3]);
}
......@@ -324,7 +324,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('[3,true,4,7]', $response[2]);
$this->assertEquals('[3,true,4,7]', $response[3]);
}
......@@ -350,7 +350,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]);
$this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
}
......@@ -375,7 +375,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'execDataResponse');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]);
$this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
}
......@@ -401,7 +401,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,false,4,1]}', $response[2]);
$this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
}
......@@ -429,7 +429,7 @@ class DispatcherTest extends \Test\TestCase {
$this->dispatcherPassthrough();
$response = $this->dispatcher->dispatch($controller, 'exec');
$this->assertEquals('{"text":[3,true,4,1]}', $response[2]);
$this->assertEquals('{"text":[3,true,4,1]}', $response[3]);
}
......
......@@ -76,6 +76,92 @@ class ResponseTest extends \Test\TestCase {
}
public function testAddCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo', new \DateTime('1970-01-01'));
$expectedResponse = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null,
),
'bar' => array(
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01')
)
);
$this->assertEquals($expectedResponse, $this->childResponse->getCookies());
}
function testSetCookies() {
$expected = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null,
),
'bar' => array(
'value' => 'foo',
'expireDate' => new \DateTime('1970-01-01')
)
);
$this->childResponse->setCookies($expected);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
function testInvalidateCookie() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->invalidateCookie('foo');
$expected = array(
'foo' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
function testInvalidateCookies() {
$this->childResponse->addCookie('foo', 'bar');
$this->childResponse->addCookie('bar', 'foo');
$expected = array(
'foo' => array(
'value' => 'bar',
'expireDate' => null
),
'bar' => array(
'value' => 'foo',
'expireDate' => null
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
$this->childResponse->invalidateCookies(array('foo', 'bar'));
$expected = array(
'foo' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
),
'bar' => array(
'value' => 'expired',
'expireDate' => new \DateTime('1971-01-01')
)
);
$cookies = $this->childResponse->getCookies();
$this->assertEquals($expected, $cookies);
}
public function testRenderReturnNullByDefault(){
$this->assertEquals(null, $this->childResponse->render());
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment