Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: UnionOfRAD/lithium
...
head fork: UnionOfRAD/lithium
Checking mergeability… Don't worry, you can still create the pull request.
  • 16 commits
  • 27 files changed
  • 2 commit comments
  • 5 contributors
Commits on Sep 24, 2012
@d1rk d1rk allow auth adapters to return a single value instead of arrays 2110acd
@d1rk d1rk allow dispatching of unknown methods to redis connection object 5ffd4da
@nateabele nateabele Merge pull request #655 from d1rk/auth-array-scalar
allow auth adapters to return a single value instead of arrays
28c5ee3
@nateabele nateabele Merge pull request #656 from d1rk/redis-method-dispatch
allow dispatching of unknown methods to redis connection object
06fc157
Commits on Sep 26, 2012
@fellars fellars added support for PUT to add body in Curl with tests afb3e15
@rapzo rapzo Fixed typo: Set::insert() to String::insert() in String::clean()
docblock.
7e68c17
@nateabele nateabele Merge pull request #659 from rapzo/dev
Fixed typo
9432658
Commits on Sep 28, 2012
@nateabele nateabele First pass at disambiguating request/response content type. WARNING: …
…BC break - going forward, usage of the `$type` instance property should be replaced with the `type()` method.
854171d
@gwoo gwoo WARNING: BC break. Use `type` method. to properly set Content-Type on…
… all Request/Responses.

Message bodies automatically encoded/decoded for convenience.
0121ca5
@gwoo gwoo Fixing up some more failing tests related to `type()` changes. 13a1e45
@gwoo gwoo Making Service delete return the empty body again. 62ad34d
@gwoo gwoo Adding test for encoded messages in \action\Response.
Reorganizing some tests to make it clearer what is tested.
4b33020
@gwoo gwoo Merge pull request #662 from UnionOfRAD/request-response
WARNING: BC break. Request and Response refactor usage of type()
2c0485c
Commits on Oct 01, 2012
@nateabele nateabele Ensuring proper casing for Location header in `\action\Response::_ini…
…t()`.
8306219
@nateabele nateabele Ensuring request method is not improperly overwritten in `\action\Req…
…uest`.
5e0ad63
Commits on Oct 11, 2012
@nateabele nateabele Refactoring asset handling in `Media`, implementing support for addin…
…g asset hosts through `Libraries`.
974469c
Showing with 563 additions and 205 deletions.
  1. +3 −3 action/Request.php
  2. +23 −2 action/Response.php
  3. +1 −4 data/source/Http.php
  4. +1 −1  net/Message.php
  5. +52 −25 net/http/Media.php
  6. +90 −14 net/http/Message.php
  7. +12 −32 net/http/Request.php
  8. +29 −43 net/http/Response.php
  9. +8 −8 net/http/Service.php
  10. +3 −0  net/socket/Curl.php
  11. +3 −8 security/Auth.php
  12. +21 −0 storage/cache/adapter/Redis.php
  13. +6 −6 tests/cases/action/ControllerTest.php
  14. +1 −1  tests/cases/action/DispatcherTest.php
  15. +12 −0 tests/cases/action/RequestTest.php
  16. +34 −14 tests/cases/action/ResponseTest.php
  17. +48 −15 tests/cases/net/http/MediaTest.php
  18. +17 −7 tests/cases/net/http/RequestTest.php
  19. +129 −10 tests/cases/net/http/ResponseTest.php
  20. +1 −1  tests/cases/net/http/RouterTest.php
  21. +8 −7 tests/cases/net/http/ServiceTest.php
  22. +17 −0 tests/cases/net/socket/CurlTest.php
  23. +22 −0 tests/cases/security/AuthTest.php
  24. +12 −0 tests/cases/storage/cache/adapter/RedisTest.php
  25. +1 −1  tests/mocks/net/http/MockSocket.php
  26. +7 −1 tests/mocks/security/auth/adapter/MockAuthAdapter.php
  27. +2 −2 util/String.php
View
6 action/Request.php
@@ -182,7 +182,7 @@ protected function _init() {
if (!empty($this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$this->_env['REQUEST_METHOD'] = $this->_env['HTTP_X_HTTP_METHOD_OVERRIDE'];
}
- $type = $this->type($this->_env['CONTENT_TYPE']);
+ $type = $this->type($this->_config['type'] ?: $this->env('CONTENT_TYPE'));
$this->method = $method = strtoupper($this->_env['REQUEST_METHOD']);
if (!$this->data && ($method == 'POST' || $method == 'PUT')) {
@@ -461,8 +461,8 @@ public function is($flag) {
* on the content type of the request.
*/
public function type($type = null) {
- if ($type === null) {
- $type = $this->type ?: $this->env('CONTENT_TYPE');
+ if (!$type && !empty($this->params['type'])) {
+ $type = $this->params['type'];
}
return parent::type($type);
}
View
25 action/Response.php
@@ -34,7 +34,13 @@ class Response extends \lithium\net\http\Response {
protected $_autoConfig = array('classes' => 'merge');
public function __construct(array $config = array()) {
- $defaults = array('buffer' => 8192, 'location' => null, 'status' => 0, 'request' => null);
+ $defaults = array(
+ 'buffer' => 8192,
+ 'location' => null,
+ 'status' => 0,
+ 'request' => null,
+ 'decode' => false
+ );
parent::__construct($config + $defaults);
}
@@ -47,7 +53,7 @@ protected function _init() {
if ($config['location']) {
$classes = $this->_classes;
$location = $classes['router']::match($config['location'], $config['request']);
- $this->headers('location', $location);
+ $this->headers('Location', $location);
}
}
@@ -82,6 +88,21 @@ public function cache($expires) {
}
/**
+ * Sets/Gets the content type. If `'type'` is null, the method will attempt to determine the
+ * type from the params, then from the environment setting
+ *
+ * @param string $type a full content type i.e. `'application/json'` or simple name `'json'`
+ * @return string A simple content type name, i.e. `'html'`, `'xml'`, `'json'`, etc., depending
+ * on the content type of the request.
+ */
+ public function type($type = null) {
+ if ($type === null && $this->_type === null) {
+ $type = 'html';
+ }
+ return parent::type($type);
+ }
+
+ /**
* Render a response by writing headers and output. Output is echoed in chunks because of an
* issue where `echo` time increases exponentially on long message bodies.
*
View
5 data/source/Http.php
@@ -112,17 +112,14 @@ public function __get($property) {
*/
public function __call($method, $params) {
$params += array(array(), array());
+ $string = array('method' => 'GET');
if (isset($this->_methods[$method])) {
$string = $this->_methods[$method];
- } else {
- $string = array('method' => 'GET');
}
-
if (!isset($string['path'])) {
$string['path'] = '/' . $method;
}
-
$conn =& $this->connection;
$filter = function($self, $params) use (&$conn, $string) {
list($query, $options) = $params;
View
2  net/Message.php
@@ -92,7 +92,7 @@ public function __construct(array $config = array()) {
);
$config += $defaults;
- foreach (array_filter($config) as $key => $value) {
+ foreach (array_intersect_key(array_filter($config), $defaults) as $key => $value) {
$this->{$key} = $value;
}
parent::__construct($config);
View
77 net/http/Media.php
@@ -11,6 +11,7 @@
use lithium\util\Set;
use lithium\util\String;
use lithium\core\Libraries;
+use lithium\core\Environment;
use lithium\net\http\MediaException;
/**
@@ -441,42 +442,68 @@ public static function asset($path, $type, array $options = array()) {
}
$config = Libraries::get($library);
$paths = $options['path'];
-
$config['default'] ? end($paths) : reset($paths);
$options['library'] = basename($config['path']);
if ($options['suffix'] && strpos($path, $options['suffix']) === false) {
$path .= $options['suffix'];
}
+ return $self::filterAssetPath($path, $paths, $config, compact('type') + $options);
+ });
+ }
- if ($options['check'] || $options['timestamp']) {
- $file = $self::path($path, $type, $options);
- }
+ /**
+ * Performs checks and applies transformations to asset paths, including verifying that the
+ * virtual path exists on the filesystem, appending a timestamp, prepending an asset host, or
+ * applying a user-defined filter.
+ *
+ * @see lithium\net\http\Media::asset()
+ * @param string $asset A full asset path, relative to the public web path of the application.
+ * @param mixed $path Path information for the asset type.
+ * @param array $config The configuration array of the library from which the asset is being
+ * loaded.
+ * @param array $options The array of options passed to `asset()` (see the `$options` parameter
+ * of `Media::asset()`).
+ * @return mixed Returns a modified path to a web asset, or `false`, if the path fails a check.
+ */
+ public static function filterAssetPath($asset, $path, array $config, array $options = array()) {
+ $config += array('assets' => null);
- if (strlen($path) > 0 && $path[0] === '/') {
- if ($options['base'] && strpos($path, $options['base']) !== 0) {
- $path = "{$options['base']}{$path}";
- }
- } else {
- $path = String::insert(key($paths), compact('path') + $options);
- }
+ if ($options['check'] || $options['timestamp']) {
+ $file = static::path($asset, $options['type'], $options);
+ }
+ if ($options['check'] && !is_file($file)) {
+ return false;
+ }
+ $isAbsolute = ($asset && $asset[0] === '/');
- if ($options['check'] && !is_file($file)) {
- return false;
- }
+ if ($isAbsolute && $options['base'] && strpos($asset, $options['base']) !== 0) {
+ $asset = "{$options['base']}{$asset}";
+ } elseif (!$isAbsolute) {
+ $asset = String::insert(key($path), array('path' => $asset) + $options);
+ }
- if (is_array($options['filter']) && !empty($options['filter'])) {
- $keys = array_keys($options['filter']);
- $values = array_values($options['filter']);
- $path = str_replace($keys, $values, $path);
- }
+ if (is_array($options['filter']) && !empty($options['filter'])) {
+ $filter = $options['filter'];
+ $asset = str_replace(array_keys($filter), array_values($filter), $asset);
+ }
+
+ if ($options['timestamp'] && is_file($file)) {
+ $separator = (strpos($asset, '?') !== false) ? '&' : '?';
+ $asset .= $separator . filemtime($file);
+ }
+
+ if ($host = $config['assets']) {
+ $type = $options['type'];
+ $env = Environment::get();
+ $base = isset($host[$env][$type]) ? $host[$env][$type] : null;
+ $base = (isset($host[$type]) && !$base) ? $host[$type] : $base;
- if ($options['timestamp'] && is_file($file)) {
- $separator = (strpos($path, '?') !== false) ? '&' : '?';
- $path .= $separator . filemtime($file);
+ if ($base) {
+ return "{$base}{$asset}";
}
- return $path;
- });
+ }
+ return $asset;
}
/**
@@ -551,7 +578,7 @@ public static function path($path, $type, array $options = array()) {
*
* @param object $response A Response object into which the operation will be
* rendered. The content of the render operation will be assigned to the `$body`
- * property of the object, the `'Content-type'` header will be set accordingly, and it
+ * property of the object, the `'Content-Type'` header will be set accordingly, and it
* will be returned.
* @param mixed $data The data (usually an associative array) to be rendered in the response.
* @param array $options Any options specific to the response being rendered, such as type
View
104 net/http/Message.php
@@ -40,7 +40,7 @@ class Message extends \lithium\net\Message {
*
* @var string
*/
- protected $_type = 'html';
+ protected $_type = null;
/**
* Classes used by `Request`.
@@ -74,14 +74,19 @@ public function __construct(array $config = array()) {
'username' => null,
'password' => null,
'path' => null,
+ 'query' => array(),
+ 'fragment' => null,
'protocol' => null,
'version' => '1.1',
'headers' => array(),
- 'body' => null
+ 'body' => null,
+ 'auth' => null
);
$config += $defaults;
parent::__construct($config);
-
+ foreach (array_intersect_key(array_filter($config), $defaults) as $key => $value) {
+ $this->{$key} = $value;
+ }
if (strpos($this->host, '/') !== false) {
list($this->host, $this->path) = explode('/', $this->host, 2);
}
@@ -106,7 +111,6 @@ public function headers($key = null, $value = null) {
return $this->headers;
}
}
-
foreach (($value ? array($key => $value) : (array) $key) as $header => $value) {
if (!is_string($header)) {
if (preg_match('/(.*?):(.+)/', $value, $match)) {
@@ -131,25 +135,97 @@ public function headers($key = null, $value = null) {
}
/**
- * Sets/Gets the content type
+ * Sets/gets the content type.
*
- * @param string $type a full content type i.e. `'application/json'` or simple name `'json'`
+ * @param string $type A full content type i.e. `'application/json'` or simple name `'json'`
* @return string A simple content type name, i.e. `'html'`, `'xml'`, `'json'`, etc., depending
* on the content type of the request.
*/
public function type($type = null) {
- if ($type == null && $type !== false) {
+ if ($type === false) {
+ unset($this->headers['Content-Type']);
+ $this->_type = false;
+ return;
+ }
+ $media = $this->_classes['media'];
+
+ if (!$type && $this->_type) {
return $this->_type;
}
- if (strpos($type, '/')) {
- $media = $this->_classes['media'];
+ $headers = $this->headers + array('Content-Type' => null);
+ $type = $type ?: $headers['Content-Type'];
- if (!$data = $media::type($type)) {
- return $this->_type;
- }
- $type = is_array($data) ? reset($data) : $data;
+ if (!$type) {
+ return;
+ }
+ $header = $type;
+
+ if (!$data = $media::type($type)) {
+ $this->headers('Content-Type', $type);
+ return ($this->_type = $type);
+ }
+ if (is_string($data)) {
+ $type = $data;
+ } else if (!empty($data['content'])) {
+ $header = is_array($data['content']) ? reset($data['content']) : $data['content'];
+ }
+ $this->headers('Content-Type', $header);
+ return ($this->_type = $type);
+ }
+
+ /**
+ * Add body parts.
+ *
+ * @param mixed $data
+ * @param array $options
+ * - `'buffer'`: split the body string
+ * @return array
+ */
+ public function body($data = null, $options = array()) {
+ $default = array('buffer' => null, 'encode' => false, 'decode' => false);
+ $options += $default;
+ $body = $this->body = array_filter(array_merge((array) $this->body, (array) $data));
+
+ if ($options['encode']) {
+ $body = $this->_encode($body);
+ }
+ $body = is_array($body) ? join("\r\n", $body) : $body;
+
+ if ($options['decode']) {
+ $body = $this->_decode($body);
+ }
+ return ($options['buffer']) ? str_split($body, $options['buffer']) : $body;
+ }
+
+ /**
+ * Encodes the body based on the type
+ *
+ * @see lithium\net\http\Message::type()
+ * @param mixed $body
+ * @return string
+ */
+ protected function _encode($body) {
+ $media = $this->_classes['media'];
+
+ if ($type = $media::type($this->_type)) {
+ $body = $media::encode($this->_type, $body) ?: $body;
+ }
+ return $body;
+ }
+
+ /**
+ * Decodes the body based on the type
+ *
+ * @param string $body
+ * @return mixed
+ */
+ protected function _decode($body) {
+ $media = $this->_classes['media'];
+
+ if (!$type = $media::type($this->_type)) {
+ return $body;
}
- return $this->_type = $type;
+ return $media::decode($this->_type, $body) ?: $body;
}
}
View
44 net/http/Request.php
@@ -36,7 +36,7 @@ class Request extends \lithium\net\http\Message {
*
* @var string
*/
- public $method = 'GET';
+ public $method;
/**
* Cookies.
@@ -59,6 +59,7 @@ class Request extends \lithium\net\http\Message {
* - `fragment`: null - after the hashmark #
* - `auth` - the Authorization method (Basic|Digest)
* - `method` - GET
+ * - `type`: null
* - `version`: 1.1
* - `headers`: array
* - `body`: null
@@ -77,53 +78,39 @@ public function __construct(array $config = array()) {
'body' => null,
'auth' => null,
'method' => 'GET',
+ 'type' => null,
'proxy' => null,
'ignoreErrors' => true,
'followLocation' => true
);
- $config += $defaults;
- parent::__construct($config);
+ parent::__construct($config + $defaults);
+ $this->method = $this->method ?: $this->_config['method'];
$this->headers = array(
'Host' => $this->port ? "{$this->host}:{$this->port}" : $this->host,
'Connection' => 'Close',
'User-Agent' => 'Mozilla/5.0'
);
- $this->headers($config['headers']);
- }
-
- /**
- * Encodes the body based on the type
- *
- * @see lithium\net\http\Message::type()
- * @param mixed $body
- * @return string
- */
- protected function _encode($body) {
- $media = $this->_classes['media'];
+ $this->headers($this->_config['headers']);
- if ($type = $media::type($this->_type)) {
- $body = $media::encode($this->_type, $body) ?: $body;
+ if ($type = $this->_config['type']) {
+ $this->type($type);
}
- return is_array($body) ? join("\r\n", $body) : $body;
}
/**
- * Add body parts and encodes it into formated string
+ * Add body parts and encodes it into formatted string
*
* @see lithium\net\Message::body()
+ * @see lithium\net\http\Message::_encode()
* @param mixed $data
* @param array $options
* - `'buffer'`: split the body string
* @return array
*/
public function body($data = null, $options = array()) {
- $default = array('buffer' => null);
- $options += $default;
- $this->body = array_merge((array) $this->body, (array) $data);
-
- $body = $this->_encode($this->body);
- return ($options['buffer']) ? str_split($body, $options['buffer']) : $body;
+ $defaults = array('encode' => true);
+ return parent::body($data, $options + $defaults);
}
/**
@@ -230,13 +217,6 @@ public function to($format, array $options = array()) {
$data = $auth::encode($options['username'], $options['password'], $data);
$this->headers('Authorization', $auth::header($data));
}
- if (in_array($options['method'], array('POST', 'PUT', 'PATCH'))) {
- $media = $this->_classes['media'];
- if ($type = $media::type($this->_type)) {
- $this->headers('Content-Type', $type['content'][0]);
- }
- }
-
$body = $this->body($options['body']);
$this->headers('Content-Length', strlen($body));
View
72 net/http/Response.php
@@ -21,13 +21,6 @@ class Response extends \lithium\net\http\Message {
public $status = array('code' => 200, 'message' => 'OK');
/**
- * Content Type.
- *
- * @var string
- */
- public $type = 'text/html';
-
- /**
* Character encoding.
*
* @var string
@@ -95,18 +88,8 @@ class Response extends \lithium\net\http\Message {
* @param array $config
*/
public function __construct(array $config = array()) {
- $defaults = array('message' => null);
- $config += $defaults;
- parent::__construct($config);
- }
-
- /**
- * Initialize the Response
- *
- * @return void
- */
- protected function _init() {
- parent::_init();
+ $defaults = array('message' => null, 'type' => null);
+ parent::__construct($config + $defaults);
if ($this->_config['message']) {
$this->body = $this->_parseMessage($this->_config['message']);
@@ -114,34 +97,36 @@ protected function _init() {
if (isset($this->headers['Transfer-Encoding'])) {
$this->body = $this->_httpChunkedDecode($this->body);
}
- if (isset($this->headers['Content-Type'])) {
- $pattern = '/([-\w\/\.+]+)(;\s*?charset=(.+))?/i';
- preg_match($pattern, $this->headers['Content-Type'], $match);
+ if ($type = $this->_config['type']) {
+ $this->type($type);
+ }
+ if (!isset($this->headers['Content-Type'])) {
+ return;
+ }
+ $pattern = '/([-\w\/\.+]+)(;\s*?charset=(.+))?/i';
+ preg_match($pattern, $this->headers['Content-Type'], $match);
- if (isset($match[1])) {
- $this->type = trim($match[1]);
- $this->body = $this->_decode($this->body);
- }
- if (isset($match[3])) {
- $this->encoding = strtoupper(trim($match[3]));
- }
+ if (isset($match[1])) {
+ $this->type(trim($match[1]));
+ }
+ if (isset($match[3])) {
+ $this->encoding = strtoupper(trim($match[3]));
}
}
/**
- * Decodes the body based on the type
+ * Return body parts and decode it into formatted type.
*
- * @param string $body
- * @return mixed
+ * @see lithium\net\Message::body()
+ * @see lithium\net\http\Message::_decode()
+ * @param mixed $data
+ * @param array $options
+ * @return array
*/
- protected function _decode($body) {
- $media = $this->_classes['media'];
- if ($type = $media::type($this->_type)) {
- $body = $media::decode($this->_type, $body) ?: $body;
- }
- return $body;
+ public function body($data = null, $options = array()) {
+ $defaults = array('decode' => true);
+ return parent::body($data, $options + $defaults);
}
-
/**
* Set and get the status for the response.
*
@@ -248,11 +233,12 @@ protected function _httpChunkedDecode($body) {
* @return string
*/
public function __toString() {
- if ($this->type != 'text/html' && !isset($this->headers['Content-Type'])) {
- $this->headers['Content-Type'] = $this->type;
- }
$first = "{$this->protocol} {$this->status['code']} {$this->status['message']}";
- $response = array($first, join("\r\n", $this->headers()), "", $this->body());
+ if ($type = $this->headers('Content-Type')) {
+ $this->headers('Content-Type', "{$type};charset={$this->encoding}");
+ }
+ $body = join("\r\n", (array) $this->body);
+ $response = array($first, join("\r\n", $this->headers()), "", $body);
return join("\r\n", $response);
}
}
View
16 net/http/Service.php
@@ -40,7 +40,7 @@ class Service extends \lithium\core\Object {
/**
* Array of closures that return various pieces of information about an HTTP response.
- *
+ *
* @var array
*/
protected $_responseTypes = array();
@@ -109,8 +109,6 @@ protected function _init() {
*
* @param string $method
* @param string $params
- * @return void
- * @author gwoo
*/
public function __call($method, $params = array()) {
array_unshift($params, $method);
@@ -120,11 +118,13 @@ public function __call($method, $params = array()) {
/**
* Send HEAD request.
*
+ * @param string $path
+ * @param array $data
* @param array $options
* @return string
*/
public function head($path = null, $data = array(), array $options = array()) {
- $defaults = array('return' => 'headers');
+ $defaults = array('return' => 'headers', 'type' => false);
return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
}
@@ -137,7 +137,8 @@ public function head($path = null, $data = array(), array $options = array()) {
* @return string
*/
public function get($path = null, $data = array(), array $options = array()) {
- return $this->send(__FUNCTION__, $path, $data, $options);
+ $defaults = array('type' => false);
+ return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
}
/**
@@ -185,7 +186,8 @@ public function patch($path = null, $data = array(), array $options = array()) {
* @return string
*/
public function delete($path = null, $data = array(), array $options = array()) {
- return $this->send(__FUNCTION__, $path, $data, $options);
+ $defaults = array('type' => false);
+ return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
}
/**
@@ -243,9 +245,7 @@ protected function _request($method, $path, $data, $options) {
$request = $this->_instance('request', $options);
$request->path = str_replace('//', '/', "{$request->path}{$path}");
$request->method = $method = strtoupper($method);
-
$hasBody = in_array($method, array('POST', 'PUT', 'PATCH'));
- $hasBody ? $request->type($options['type']) : null;
$hasBody ? $request->body($data) : $request->query = $data;
return $request;
}
View
3  net/socket/Curl.php
@@ -153,6 +153,9 @@ public function write($data = null) {
if (isset($data->method) && $data->method == 'POST') {
$this->set(array(CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data->body()));
}
+ if (isset($data->method) && $data->method == 'PUT') {
+ $this->set(array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $data->body()));
+ }
}
return (boolean) curl_setopt_array($this->_resource, $this->options);
}
View
11 security/Auth.php
@@ -150,16 +150,11 @@ public static function check($name, $credentials = null, array $options = array(
}
if (($credentials) && $data = $self::adapter($name)->check($credentials, $options)) {
- if ($options['persist']) {
- foreach ($data as $key => $value) {
- if (!in_array($key, $options['persist'])) {
- unset($data[$key]);
- }
- }
- } else {
+ if ($options['persist'] && is_array($data)) {
+ $data = array_intersect_key($data, array_fill_keys($options['persist'], true));
+ } elseif (is_array($data)) {
unset($data['password']);
}
-
return ($options['writeSession']) ? $self::set($name, $data) : $data;
}
return false;
View
21 storage/cache/adapter/Redis.php
@@ -100,6 +100,27 @@ protected function _init() {
}
/**
+ * Dispatches a not-found method to the Redis connection object.
+ *
+ * That way, one can easily use a custom method on that redis adapter like that:
+ *
+ * {{{Cache::adapter('named-of-redis-config')->methodName($argument);}}}
+ *
+ * If you want to know, what methods are available, have a look at the readme of phprdis.
+ * One use-case might be to query possible keys, e.g.
+ *
+ * {{{Cache::adapter('redis')->keys('*');}}}
+ *
+ * @link https://github.com/nicolasff/phpredis GitHub: PhpRedis Extension
+ * @param string $method Name of the method to call
+ * @param array $params Parameter list to use when calling $method
+ * @return mixed Returns the result of the method call
+ */
+ public function __call($method, $params = array()) {
+ return call_user_func_array(array(&$this->connection, $method), $params);
+ }
+
+ /**
* Sets expiration time for cache keys
*
* @param string $key The key to uniquely identify the cached item
View
12 tests/cases/action/ControllerTest.php
@@ -84,7 +84,7 @@ public function testRedirectResponse() {
$result = $postsController(null, array('action' => 'delete'));
$this->assertEqual($result->body(), '');
- $headers = array('Location' => '/posts');
+ $headers = array('Location' => '/posts', 'Content-Type' => 'text/html');
$this->assertEqual($result->headers, $headers);
$postsController = new MockPostsController();
@@ -107,7 +107,7 @@ public function testRedirectResponse() {
$this->assertEqual($postsController->response->body(), null);
$this->assertEqual(
$postsController->response->headers,
- array('Location' => '/posts')
+ array('Location' => '/posts', 'Content-Type' => 'text/html')
);
}
@@ -228,7 +228,7 @@ public function testResponseStatus() {
$expected = array('code' => 404, 'message' => 'Not Found');
$result = $postsController->response->status;
$this->assertEqual($expected, $result);
- $result = json_decode($postsController->response->body(), true);
+ $result = $postsController->response->body();
$this->assertEqual($expected, $result);
}
@@ -256,7 +256,7 @@ public function testResponseTypeBasedOnRequestType() {
$result = $postsController->response->headers('Content-Type');
$this->assertEqual('application/json; charset=UTF-8', $result);
- $result = json_decode($postsController->response->body(), true);
+ $result = $postsController->response->body();
$this->assertEqual(array('data' => 'test'), $result);
}
@@ -285,7 +285,7 @@ public function testResponseTypeBasedOnRequestParamsType() {
$this->assertEqual('application/json; charset=UTF-8', $result);
$expected = array('data' => 'test');
- $result = json_decode($postsController->response->body(), true);
+ $result = $postsController->response->body();
$this->assertEqual($expected, $result);
}
@@ -346,7 +346,7 @@ public function testResponseTypeBasedOnRequestHeaderType() {
$result = $postsController->response->headers('Content-Type');
$this->assertEqual('application/json; charset=UTF-8', $result);
- $result = json_decode($postsController->response->body(), true);
+ $result = $postsController->response->body();
$this->assertEqual(array('data' => 'test'), $result);
}
View
2  tests/cases/action/DispatcherTest.php
@@ -198,7 +198,7 @@ public function testCall() {
public function testAutoHandler() {
$result = MockDispatcher::run(new Request(array('url' => '/auto')));
- $this->assertEqual(array('location: /redirect'), $result->headers());
+ $this->assertEqual(array('Location: /redirect'), $result->headers());
}
public static function process($request) {
View
12 tests/cases/action/RequestTest.php
@@ -1076,6 +1076,18 @@ public function testConvertToUrl2() {
$expected = 'https://foo.com/the/base/path/posts?some=query&parameter=values';
$this->assertEqual($expected, $request->to('url'));
}
+
+ /**
+ * Tests that the HTTP request method set by `Request` from the server information is not
+ * overwritten in a parent class.
+ */
+ public function testRequesMethodConfiguration() {
+ $request = new Request(array('env' => array('REQUEST_METHOD' => 'POST')));
+ $this->assertEqual('POST', $request->method);
+
+ $request = new Request(array('env' => array('REQUEST_METHOD' => 'PATCH')));
+ $this->assertEqual('PATCH', $request->method);
+ }
}
?>
View
48 tests/cases/action/ResponseTest.php
@@ -28,20 +28,40 @@ public function testTypeManipulation() {
$this->assertEqual(false, $this->response->type());
}
- public function testResponseRendering() {
+ public function testResponseRenderString() {
$this->response->body = 'Document body';
ob_start();
$this->response->render();
$result = ob_get_clean();
- $this->assertEqual('Document body', $result);
- $this->assertEqual(array('HTTP/1.1 200 OK'), $this->response->testHeaders);
+ $this->assertIdentical('Document body', $result);
+ $this->assertIdentical(array('HTTP/1.1 200 OK'), $this->response->testHeaders);
+ }
+
+ public function testResponseRenderJson() {
+ $this->response->type('json');
+ $this->response->body[] = '{"message": "Hello World"}';
+
+ ob_start();
+ $this->response->render();
+ $result = ob_get_clean();
+ $this->assertIdentical('{"message": "Hello World"}', $result);
+ $this->assertIdentical('HTTP/1.1 200 OK', $this->response->testHeaders[0]);
+ }
+
+ public function testResponseToString() {
+ $this->response->type(false);
+ $this->response->body = 'Document body';
ob_start();
echo $this->response;
$result = ob_get_clean();
- $this->assertEqual('Document body', $result);
- $this->assertEqual(array('HTTP/1.1 200 OK'), $this->response->testHeaders);
+ $this->assertIdentical('Document body', $result);
+ $this->assertIdentical(array('HTTP/1.1 200 OK'), $this->response->testHeaders);
+ }
+
+ public function testResponseCaching() {
+ $this->response->body = 'Document body';
$expires = strtotime('+1 hour');
$this->response->cache($expires);
@@ -54,7 +74,7 @@ public function testResponseRendering() {
'Cache-Control: max-age=' . ($expires - time()),
'Pragma: cache'
);
- $this->assertEqual($headers, $this->response->testHeaders);
+ $this->assertIdentical($headers, $this->response->testHeaders);
$expires = '+2 hours';
$this->response->cache($expires);
@@ -67,7 +87,7 @@ public function testResponseRendering() {
'Cache-Control: max-age=' . (strtotime($expires) - time()),
'Pragma: cache'
);
- $this->assertEqual($headers, $this->response->testHeaders);
+ $this->assertIdentical($headers, $this->response->testHeaders);
$this->response->body = 'Created';
$this->response->status(201);
@@ -80,12 +100,12 @@ public function testResponseRendering() {
'Cache-Control: max-age=0',
'Pragma: no-cache'
);
- $this->assertEqual($expected, $result);
+ $this->assertIdentical($expected, $result);
ob_start();
$this->response->render();
$result = ob_get_clean();
- $this->assertEqual('Created', $result);
+ $this->assertIdentical('Created', $result);
$headers = array (
'HTTP/1.1 201 Created',
@@ -97,7 +117,7 @@ public function testResponseRendering() {
),
'Pragma: no-cache'
);
- $this->assertEqual($headers, $this->response->testHeaders);
+ $this->assertIdentical($headers, $this->response->testHeaders);
}
/**
@@ -135,7 +155,7 @@ public function testHeaderTypes() {
$this->response->headers('download', 'report.csv');
ob_start();
$this->response->render();
- ob_end_clean();
+ ob_get_clean();
$headers = array(
'HTTP/1.1 200 OK',
@@ -147,7 +167,7 @@ public function testHeaderTypes() {
$this->response->headers('location', '/');
ob_start();
$this->response->render();
- ob_end_clean();
+ ob_get_clean();
$headers = array('HTTP/1.1 302 Found', 'Location: /');
$this->assertEqual($headers, $this->response->testHeaders);
@@ -159,7 +179,7 @@ public function testLocationHeaderStatus() {
$this->response->headers('location', '/');
ob_start();
$this->response->render();
- ob_end_clean();
+ ob_get_clean();
$headers = array('HTTP/1.1 301 Moved Permanently', 'Location: /');
$this->assertEqual($headers, $this->response->testHeaders);
@@ -168,7 +188,7 @@ public function testLocationHeaderStatus() {
'classes' => array('router' => __CLASS__),
'location' => array('controller' => 'foo_bar', 'action' => 'index')
));
- $this->assertEqual(array('location: /foo_bar'), $this->response->headers());
+ $this->assertEqual(array('Location: /foo_bar'), $this->response->headers());
}
public static function match($url) {
View
63 tests/cases/net/http/MediaTest.php
@@ -8,6 +8,7 @@
namespace lithium\tests\cases\net\http;
+use lithium\core\Environment;
use lithium\net\http\Media;
use lithium\action\Request;
use lithium\action\Response;
@@ -147,6 +148,37 @@ public function testAssetAbsoluteRelativePaths() {
$this->assertEqual($expected, $result);
}
+ public function testCustomAssetUrls() {
+ $env = Environment::get();
+
+ Libraries::add('cdn_js_test', array(
+ 'path' => Libraries::get(true, 'path'),
+ 'assets' => array(
+ 'js' => 'http://static.cdn.com'
+ )
+ ));
+
+ Libraries::add('cdn_env_test', array(
+ 'path' => Libraries::get(true, 'path'),
+ 'assets' => array(
+ 'js' => 'wrong',
+ $env => array('js' => 'http://static.cdn.com/myapp')
+ )
+ ));
+
+ $result = Media::asset('foo', 'js', array('library' => 'cdn_js_test'));
+ $this->assertEqual("http://static.cdn.com/lithium/js/foo.js", $result);
+
+ $result = Media::asset('foo', 'css', array('library' => 'cdn_js_test'));
+ $this->assertEqual("/lithium/css/foo.css", $result);
+
+ $result = Media::asset('foo', 'js', array('library' => 'cdn_env_test'));
+ $this->assertEqual("http://static.cdn.com/myapp/lithium/js/foo.js", $result);
+
+ Libraries::remove('cdn_env_test');
+ Libraries::remove('cdn_js_test');
+ }
+
public function testAssetPathGeneration() {
$resources = Libraries::get(true, 'resources');
$this->skipIf(!is_writable($resources), "Cannot write test app to resources directory.");
@@ -291,7 +323,7 @@ public function testRender() {
$this->assertEqual(array('Content-Type: application/json; charset=UTF-8'), $result);
$result = $response->body();
- $this->assertEqual(json_encode($data), $result);
+ $this->assertEqual($data, $result);
}
/**
@@ -327,23 +359,24 @@ public function testDecode() {
public function testCustomEncodeHandler() {
$response = new Response();
- $response->type('csv');
- Media::type('csv', 'application/csv', array('encode' => function($data) {
- ob_start();
- $out = fopen('php://output', 'w');
- foreach ($data as $record) {
- fputcsv($out, $record);
+ Media::type('csv', 'application/csv', array(
+ 'encode' => function($data) {
+ ob_start();
+ $out = fopen('php://output', 'w');
+ foreach ($data as $record) {
+ fputcsv($out, $record);
+ }
+ fclose($out);
+ return ob_get_clean();
}
- fclose($out);
- return ob_get_clean();
- }));
+ ));
$data = array(
array('John', 'Doe', '123 Main St.', 'Anytown, CA', '91724'),
array('Jane', 'Doe', '124 Main St.', 'Anytown, CA', '91724')
);
-
+ $response->type('csv');
Media::render($response, $data);
$result = $response->body;
$expected = 'John,Doe,"123 Main St.","Anytown, CA",91724' . "\n";
@@ -401,7 +434,7 @@ public function testUndhandledContent() {
$this->expectException("Unhandled media type `bad`.");
Media::render($response, array('foo' => 'bar'));
- $result = $response->body;
+ $result = $response->body();
$this->assertNull($result);
}
@@ -431,7 +464,7 @@ public function testUnregisteredContentHandler() {
public function testManualContentHandling() {
Media::type('custom', 'text/x-custom');
$response = new Response();
- $response->type = 'custom';
+ $response->type('custom');
Media::render($response, 'Hello, world!', array(
'layout' => false,
@@ -463,7 +496,7 @@ public function testRequestOptionMerging() {
$request->params['foo'] = 'bar';
$response = new Response();
- $response->type = 'custom';
+ $response->type('custom');
Media::render($response, null, compact('request') + array(
'layout' => false,
@@ -494,7 +527,7 @@ public function testRenderWithOptionsMerging() {
$request->params['controller'] = 'pages';
$response = new Response();
- $response->type = 'html';
+ $response->type('html');
$this->expectException('/Template not found/');
Media::render($response, null, compact('request'));
View
24 tests/cases/net/http/RequestTest.php
@@ -23,8 +23,7 @@ public function testConstruct() {
'host' => 'localhost',
'port' => 443,
'headers' => array('Header' => 'Value'),
- 'body' => array('Part 1'),
- 'params' => array('param' => 'value')
+ 'body' => array('Part 1')
));
$expected = 'localhost';
@@ -51,10 +50,6 @@ public function testConstruct() {
$result = $request->path;
$this->assertEqual($expected, $result);
- $expected = array('param' => 'value');
- $result = $request->params;
- $this->assertEqual($expected, $result);
-
$expected = array(
'Host: localhost:443',
'Connection: Close',
@@ -278,7 +273,7 @@ public function testQueryStringWithArrayValuesCustomFormat() {
public function testDigest() {
$request = new Request(array(
- 'path' => 'http_auth',
+ 'path' => '/http_auth',
'auth' => array(
'realm' => 'app',
'qop' => 'auth',
@@ -326,6 +321,21 @@ public function testQueryParamsConstructed() {
$result = $request->queryString(array('param3' => 3));
$this->assertEqual($expected, $result);
}
+
+ public function testKeepDefinedContentTypeHeaderOnPost() {
+ $request = new Request(array(
+ 'method' => 'POST',
+ 'headers' => array('Content-Type' => 'text/x-test')
+ ));
+ $expected = 'Content-Type: text/x-test';
+ $result = $request->headers();
+ $message = "Expected value `{$expected}` not found in result.";
+ $this->assertTrue(in_array($expected, $result), $message);
+
+ $expected = '#Content-Type: text/x-test#';
+ $result = $request->to('string');
+ $this->assertPattern($expected, $result);
+ }
}
?>
View
139 tests/cases/net/http/ResponseTest.php
@@ -51,33 +51,33 @@ public function testParsingContentTypeWithEncoding() {
$response = new Response(array('headers' => array(
'Content-Type' => 'text/xml;charset=UTF-8'
)));
- $this->assertEqual('text/xml', $response->type);
+ $this->assertEqual('xml', $response->type());
$this->assertEqual('UTF-8', $response->encoding);
$response = new Response(array('headers' => array(
'Content-Type' => 'application/soap+xml; charset=iso-8859-1'
)));
- $this->assertEqual('application/soap+xml', $response->type);
+ $this->assertEqual('xml', $response->type());
$this->assertEqual('ISO-8859-1', $response->encoding);
// Content type WITHOUT space between type and charset
$response = new Response(array('headers' => array(
'Content-Type' => 'application/json;charset=iso-8859-1'
)));
- $this->assertEqual('application/json', $response->type);
+ $this->assertEqual('json', $response->type());
$this->assertEqual('ISO-8859-1', $response->encoding);
// Content type WITH ONE space between type and charset
$response = new Response(array('headers' => array(
'Content-Type' => 'application/json; charset=iso-8859-1'
)));
- $this->assertEqual('application/json', $response->type);
+ $this->assertEqual('json', $response->type());
$this->assertEqual('ISO-8859-1', $response->encoding);
$response = new Response(array('headers' => array(
'Content-Type' => 'application/json; charset=iso-8859-1'
)));
- $this->assertEqual('application/json', $response->type);
+ $this->assertEqual('json', $response->type());
$this->assertEqual('ISO-8859-1', $response->encoding);
}
@@ -85,7 +85,7 @@ public function testParsingContentTypeWithoutEncoding() {
$response = new Response(array('headers' => array(
'Content-Type' => 'application/json'
)));
- $this->assertEqual('application/json', $response->type);
+ $this->assertEqual('json', $response->type());
$this->assertEqual('UTF-8', $response->encoding); //default
}
@@ -93,7 +93,7 @@ public function testParsingContentTypeWithVersionNumber() {
$response = new Response(array('headers' => array(
'Content-Type' => 'application/x-amz-json-1.0'
)));
- $this->assertEqual('application/x-amz-json-1.0', $response->type);
+ $this->assertEqual('application/x-amz-json-1.0', $response->type());
}
public function testConstructionWithBody() {
@@ -109,14 +109,14 @@ public function testParseMessage() {
'HTTP/1.1 404 Not Found',
'Header: Value',
'Connection: close',
- 'Content-Type: application/json;charset=iso-8859-1',
+ 'Content-Type: text/plain;charset=ISO-8859-1',
'',
'Test!'
));
$response = new Response(compact('message'));
$this->assertEqual($message, (string) $response);
- $this->assertEqual('application/json', $response->type);
+ $this->assertEqual('text', $response->type());
$this->assertEqual('ISO-8859-1', $response->encoding);
$this->assertEqual('404', $response->status['code']);
$this->assertEqual('Not Found', $response->status['message']);
@@ -128,6 +128,51 @@ public function testParseMessage() {
$this->assertEqual($expected, (string) $response);
}
+ public function testParseMessageWithContentTypeHeaderSetsType() {
+ $response = new Response(array(
+ 'message' => join("\r\n", array(
+ 'HTTP/1.1 200 OK',
+ 'Content-Type: text/x-test-a',
+ '',
+ 'foo!'
+ ))
+ ));
+ $this->assertEqual('text/x-test-a', $response->headers('Content-Type'));
+ }
+
+ public function testContentTypeHeaderAndTypePropertyAreSynchronized() {
+ $response = new Response(array(
+ 'message' => "Content-type: text/x-test-a\r\n\r\nfoo"
+ ));
+ $this->assertEqual($response->type(), $response->headers('Content-Type'));
+
+ $response = new Response(array(
+ 'headers' => array('Content-Type' => 'text/x-test-a')
+ ));
+ $this->assertEqual($response->type(), $response->headers('Content-Type'));
+
+ $response = new Response(array(
+ 'type' => 'text/x-test-a'
+ ));
+ $this->assertEqual($response->type(), $response->headers('Content-Type'));
+ }
+
+ public function testParseMessageHeadersMerging() {
+ $response = new Response(array(
+ 'message' => "Content-type: text/x-test-a\r\nX-Test-A: foo\r\n\r\nfoo",
+ 'headers' => array(
+ 'Content-Type' => 'text/x-test-b',
+ 'X-Test-B' => 'bar'
+ )
+ ));
+ $expected = array(
+ 'Content-Type: text/x-test-b',
+ 'X-Test-B: bar',
+ 'X-Test-A: foo'
+ );
+ $this->assertEqual($expected, $response->headers());
+ }
+
public function testEmptyResponse() {
$response = new Response(array('message' => "\n"));
$result = trim((string) $response);
@@ -161,6 +206,61 @@ function testToString() {
$this->assertEqual($expected, (string) $response);
}
+ public function testToStringDoesNotAddContentTypeHeaderOnTextHtml() {
+ $response = new Response();
+
+ $expected = "HTTP/1.1 200 OK\r\n\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+
+ /* Decide what to do with this */
+ return "Is this test correct?";
+
+ $response = new Response();
+ $response->type('text/html');
+
+ $expected = "HTTP/1.1 200 OK\r\n\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+
+ $response = new Response();
+ $response->type('text/plain');
+
+ $expected = "HTTP/1.1 200 OK\r\nContent-Type: text/plain;charset=UTF-8\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+ }
+
+ public function testToStringTypeAlwaysUsesContentTypeHeader() {
+ $response = new Response();
+ $response->headers('Content-Type', 'text/html');
+
+ $expected = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=UTF-8\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+
+ $response = new Response();
+ $response->headers('Content-Type', 'text/plain');
+
+ $expected = "HTTP/1.1 200 OK\r\nContent-Type: text/plain;charset=UTF-8\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+ }
+
+ public function testToStringPrefersHeadersContentTypeOverType() {
+
+ /* Decide what to do with this */
+ return "Is this test correct?";
+
+ $response = new Response();
+ $response->headers('Content-Type', 'text/plain');
+ $response->type('text/html');
+
+ $expected = "HTTP/1.1 200 OK\r\nContent-Type: text/plain;charset=UTF-8\r\n\r\n";
+ $result = (string) $response;
+ $this->assertEqual($expected, $result);
+ }
+
function testTransferEncodingChunkedDecode() {
$headers = join("\r\n", array(
'HTTP/1.1 200 OK',
@@ -221,11 +321,30 @@ function testTransferEncodingChunkedDecode() {
$this->assertEqual($expected, $result);
}
+ public function testTypePriority() {
+
+ /* Decide what to do with this */
+ return "Is this test correct?";
+
+ $response = new Response(array(
+ 'message' => "Content-type: text/x-test-a\r\n\r\nfoo",
+ 'type' => 'text/x-test-b',
+ 'headers' => array('Content-Type' => 'text/x-test-c')
+ ));
+ $this->assertEqual('text/x-test-c', $response->type());
+
+ $response = new Response(array(
+ 'message' => "Content-type: text/x-test-a\r\n\r\nfoo",
+ 'type' => 'text/x-test-b'
+ ));
+ $this->assertEqual('text/x-test-b', $response->type());
+ }
+
public function testTypeHeader() {
$response = new Response(array('type' => 'application/json'));
$result = (string) $response;
$this->assertPattern('/^HTTP\/1\.1 200 OK/', $result);
- $this->assertPattern('/Content-Type: application\/json\s+$/ms', $result);
+ $this->assertPattern('/Content-Type: application\/json(.*)$/ms', $result);
}
/**
View
2  tests/cases/net/http/RouterTest.php
@@ -541,7 +541,7 @@ public function testRouteHandler() {
$result = Router::process(new Request(array('url' => '/users/login')));
$this->assertTrue($result instanceof Response);
- $headers = array('location' => '/login');
+ $headers = array('Location' => '/login');
$this->assertEqual($headers, $result->headers);
}
View
15 tests/cases/net/http/ServiceTest.php
@@ -84,7 +84,7 @@ public function testHead() {
$this->assertEqual('HTTP/1.1', $http->last->response->protocol);
$this->assertEqual('200', $http->last->response->status['code']);
$this->assertEqual('OK', $http->last->response->status['message']);
- $this->assertEqual('text/html', $http->last->response->type);
+ $this->assertEqual(null, $http->last->response->type());
$this->assertEqual('UTF-8', $http->last->response->encoding);
$this->assertEqual('', $http->last->response->body());
}
@@ -109,7 +109,7 @@ public function testGet() {
$this->assertEqual('HTTP/1.1', $http->last->response->protocol);
$this->assertEqual('200', $http->last->response->status['code']);
$this->assertEqual('OK', $http->last->response->status['message']);
- $this->assertEqual('text/html', $http->last->response->type);
+ $this->assertEqual(null, $http->last->response->type());
$this->assertEqual('UTF-8', $http->last->response->encoding);
}
@@ -119,7 +119,7 @@ public function testGetPath() {
$this->assertEqual('HTTP/1.1', $http->last->response->protocol);
$this->assertEqual('200', $http->last->response->status['code']);
$this->assertEqual('OK', $http->last->response->status['message']);
- $this->assertEqual('text/html', $http->last->response->type);
+ $this->assertEqual(null, $http->last->response->type());
$this->assertEqual('UTF-8', $http->last->response->encoding);
}
@@ -143,7 +143,7 @@ public function testPost() {
'Host: localhost:80',
'Connection: Close',
'User-Agent: Mozilla/5.0',
- 'Content-Type: application/x-www-form-urlencoded',
+ 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length: 11',
'', 'status=cool'
));
@@ -171,7 +171,7 @@ public function testPut() {
'Host: localhost:80',
'Connection: Close',
'User-Agent: Mozilla/5.0',
- 'Content-Type: application/x-www-form-urlencoded',
+ 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8',
'Content-Length: 11',
'', 'status=cool'
));
@@ -205,7 +205,8 @@ public function testDelete() {
public function testJsonPost() {
$http = new Service($this->_testConfig);
- $http->post('update.xml', array('status' => array('cool', 'awesome')), array('type' => 'json'));
+ $data = array('status' => array('cool', 'awesome'));
+ $http->post('update.xml', $data, array('type' => 'json'));
$expected = join("\r\n", array(
'POST /update.xml HTTP/1.1',
'Host: localhost:80'