Skip to content

Commit

Permalink
Add support for cookie parsing.
Browse files Browse the repository at this point in the history
Also add support for headers with multiple values, like Set-Cookie.
  • Loading branch information
markstory committed Dec 27, 2012
1 parent 53e5fd3 commit b0d8b37
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 9 deletions.
90 changes: 83 additions & 7 deletions lib/Cake/Network/Http/Response.php
Expand Up @@ -44,6 +44,13 @@ class Response {
*/
protected $_headers;

/**
* The array of cookies in the response.
*
* @var array
*/
protected $_cookies;

/**
* The response body
*
Expand Down Expand Up @@ -77,13 +84,57 @@ protected function _parseHeaders($headers) {
$this->_code = $matches[1];
continue;
}
if (is_int($key)) {
list($name, $value) = explode(':', $value, 2);
$name = $this->_normalizeHeader($name);
$this->_headers[trim($name)] = trim($value);
list($name, $value) = explode(':', $value, 2);
$value = trim($value);
$name = $this->_normalizeHeader($name);
if ($name === 'Set-Cookie') {
$this->_parseCookie($value);
}
if (isset($this->_headers[$name])) {
$this->_headers[$name] = (array)$this->_headers[$name];
$this->_headers[$name][] = $value;
} else {
$this->_headers[$name] = $value;
}
}
}

/**
* Parse a cookie header into data.
*
* @param string $value The cookie value to parse.
* @return void
*/
protected function _parseCookie($value) {
$nestedSemi = '";"';
if (strpos($value, $nestedSemi) !== false) {
$value = str_replace($nestedSemi, "{__cookie_replace__}", $value);
$parts = explode(';', $value);
$parts = str_replace("{__cookie_replace__}", $nestedSemi, $parts);
} else {
$parts = preg_split('/\;[ \t]*/', $value);
}

$name = false;
$cookie = [];
foreach ($parts as $i => $part) {
if (strpos($part, '=') !== false) {
list($key, $value) = explode('=', $part, 2);
} else {
$key = $part;
$value = true;
}
if ($i === 0) {
$name = $key;
$cookie['value'] = $value;
continue;
}
$key = strtolower($key);
if (!isset($cookie[$key])) {
$cookie[$key] = $value;
}
}
$this->_cookies[$name] = $cookie;
}

/**
Expand All @@ -93,7 +144,7 @@ protected function _parseHeaders($headers) {
* @return string Normalized header name.
*/
protected function _normalizeHeader($name) {
$parts = explode('-', $name);
$parts = explode('-', trim($name));
$parts = array_map('strtolower', $parts);
$parts = array_map('ucfirst', $parts);
return implode('-', $parts);
Expand Down Expand Up @@ -143,9 +194,12 @@ public function encoding() {
/**
* Read single/multiple header value(s) out.
*
* @param string $name The name of the header you want. Leave
* @param string $name The name of the header you want. Leave
* null to get all headers.
* @return null|string
* @return mixed Null when the header doesn't exist. An array
* will be returned when getting all headers or when getting
* a header that had multiple values set. Otherwise a string
* will be returned.
*/
public function header($name = null) {
if ($name === null) {
Expand All @@ -158,6 +212,28 @@ public function header($name = null) {
return $this->_headers[$name];
}

/**
* Read single/multiple cookie values out.
*
* @param string $name The name of the cookie you want. Leave
* null to get all cookies.
* @param boolean $all Get all parts of the cookie. When false only
* the value will be returned.
* @return mixed
*/
public function cookie($name = null, $all = false) {
if ($name === null) {
return $this->_cookies;
}
if (!isset($this->_cookies[$name])) {
return null;
}
if ($all) {
return $this->_cookies[$name];
}
return $this->_cookies[$name]['value'];
}

/**
* Get the response body.
*
Expand Down
40 changes: 38 additions & 2 deletions lib/Cake/Test/TestCase/Network/Http/ResponseTest.php
Expand Up @@ -128,12 +128,48 @@ public function testIsRedirect() {
$this->assertFalse($response->isRedirect());
}

/**
* Test parsing / getting cookies.
*
* @return void
*/
public function testCookie() {
$this->markTestIncomplete();
$headers = [
'HTTP/1.0 200 Ok',
'Set-Cookie: test=value',
'Set-Cookie: session=123abc',
'Set-Cookie: expiring=soon; Expires=Wed, 09-Jun-2021 10:18:14 GMT; Path=/; HttpOnly; Secure',
];
$response = new Response($headers, '');
$this->assertEquals('value', $response->cookie('test'));
$this->assertEquals('123abc', $response->cookie('session'));
$this->assertEquals('soon', $response->cookie('expiring'));

$result = $response->cookie('expiring', true);
$this->assertTrue($result['httponly']);
$this->assertTrue($result['secure']);
$this->assertEquals(
'Wed, 09-Jun-2021 10:18:14 GMT',
$result['expires']
);
$this->assertEquals('/', $result['path']);

$result = $response->header('set-cookie');
$this->assertCount(3, $result, 'Should be an array.');
}

/**
* Test statusCode()
*
* @return void
*/
public function testStatusCode() {
$this->markTestIncomplete();
$headers = [
'HTTP/1.0 404 Not Found',
'Content-Type: text/html'
];
$response = new Response($headers, '');
$this->assertEquals(404, $response->statusCode());
}

public function testEncoding() {
Expand Down

0 comments on commit b0d8b37

Please sign in to comment.