Guzzle changes
Taking advantage of RequestFactory LSB New tests to make sure Guzzle returns what's expected
This commit is contained in:
parent
13009cf673
commit
ef6e777f31
@ -2,7 +2,7 @@
|
||||
<phpunit
|
||||
forceCoversAnnotation="true"
|
||||
mapTestClassNameToCoveredClassName="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
|
@ -1,223 +1,18 @@
|
||||
<?php
|
||||
namespace Ratchet\Component\WebSocket\Guzzle\Http\Message;
|
||||
use Guzzle\Common\Collection;
|
||||
use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory;
|
||||
use Guzzle\Http\EntityBody;
|
||||
use Guzzle\Http\QueryString;
|
||||
use Guzzle\Http\Url;
|
||||
use Guzzle\Http\Message\RequestFactoryInterface;
|
||||
|
||||
/**
|
||||
* Default HTTP request factory used to create the default
|
||||
* Guzzle\Http\Message\Request and Guzzle\Http\Message\EntityEnclosingRequest
|
||||
* objects.
|
||||
*/
|
||||
class RequestFactory implements RequestFactoryInterface
|
||||
{
|
||||
/**
|
||||
* @var Standard request headers
|
||||
*/
|
||||
protected static $requestHeaders = array(
|
||||
'accept', 'accept-charset', 'accept-encoding', 'accept-language',
|
||||
'authorization', 'cache-control', 'connection', 'cookie',
|
||||
'content-length', 'content-type', 'date', 'expect', 'from', 'host',
|
||||
'if-match', 'if-modified-since', 'if-none-match', 'if-range',
|
||||
'if-unmodified-since', 'max-forwards', 'pragma', 'proxy-authorization',
|
||||
'range', 'referer', 'te', 'transfer-encoding', 'upgrade', 'user-agent',
|
||||
'via', 'warning'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var RequestFactory Singleton instance of the default request factory
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* @var string Class to instantiate for GET, HEAD, and DELETE requests
|
||||
*/
|
||||
protected $requestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';
|
||||
|
||||
/**
|
||||
* @var string Class to instantiate for POST and PUT requests
|
||||
*/
|
||||
protected $entityEnclosingRequestClass = 'Guzzle\\Http\\Message\\EntityEnclosingRequest';
|
||||
|
||||
/**
|
||||
* Get a cached instance of the default request factory
|
||||
*
|
||||
* @return RequestFactory
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
class RequestFactory extends GuzzleRequestFactory {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseMessage($message)
|
||||
{
|
||||
if (!$message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$headers = new Collection();
|
||||
$scheme = $host = $body = $method = $user = $pass = $query = $port = $version = $protocol = '';
|
||||
$path = '/';
|
||||
|
||||
// Inspired by https://github.com/kriswallsmith/Buzz/blob/message-interfaces/lib/Buzz/Message/Parser/Parser.php#L16
|
||||
$lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
for ($i = 0, $c = count($lines); $i < $c; $i += 2) {
|
||||
|
||||
$line = $lines[$i];
|
||||
|
||||
// If two line breaks were encountered, then this is the body
|
||||
if (empty($line)) {
|
||||
$body = implode('', array_slice($lines, $i + 2));
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse message headers
|
||||
$matches = array();
|
||||
if (!$method && preg_match('#^(?P<method>[A-Za-z]+)\s+(?P<path>/.*)\s+(?P<protocol>\w+)/(?P<version>\d\.\d)\s*$#i', $line, $matches)) {
|
||||
$method = strtoupper($matches['method']);
|
||||
$protocol = strtoupper($matches['protocol']);
|
||||
$path = $matches['path'];
|
||||
$version = $matches['version'];
|
||||
$scheme = 'http';
|
||||
} else if (strpos($line, ':')) {
|
||||
list($key, $value) = explode(':', $line, 2);
|
||||
$key = trim($key);
|
||||
// Normalize standard HTTP headers
|
||||
if (in_array(strtolower($key), self::$requestHeaders)) {
|
||||
$key = str_replace(' ', '-', ucwords(str_replace('-', ' ', $key)));
|
||||
}
|
||||
// Headers are case insensitive
|
||||
$headers->add($key, trim($value));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the Host header
|
||||
if (isset($headers['Host'])) {
|
||||
$host = $headers['Host'];
|
||||
}
|
||||
|
||||
if (strpos($host, ':')) {
|
||||
list($host, $port) = array_map('trim', explode(':', $host));
|
||||
if ($port == 443) {
|
||||
$scheme = 'https';
|
||||
}
|
||||
} else {
|
||||
$port = '';
|
||||
}
|
||||
|
||||
// Check for basic authorization
|
||||
$auth = isset($headers['Authorization']) ? $headers['Authorization'] : '';
|
||||
|
||||
if ($auth) {
|
||||
list($type, $data) = explode(' ', $auth);
|
||||
if (strtolower($type) == 'basic') {
|
||||
$data = base64_decode($data);
|
||||
list($user, $pass) = explode(':', $data);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a query is present
|
||||
$qpos = strpos($path, '?');
|
||||
if ($qpos) {
|
||||
$query = substr($path, $qpos);
|
||||
$path = substr($path, 0, $qpos);
|
||||
}
|
||||
|
||||
return array(
|
||||
'method' => $method,
|
||||
'protocol' => $protocol,
|
||||
'protocol_version' => $version,
|
||||
'parts' => array(
|
||||
'scheme' => $scheme,
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'user' => $user,
|
||||
'pass' => $pass,
|
||||
'path' => $path,
|
||||
'query' => $query
|
||||
),
|
||||
'headers' => $headers->getAll(),
|
||||
'body' => $body
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fromMessage($message)
|
||||
{
|
||||
$parsed = $this->parseMessage($message);
|
||||
|
||||
if (!$parsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$request = $this->fromParts($parsed['method'], $parsed['parts'],
|
||||
$parsed['headers'], $parsed['body'], $parsed['protocol'],
|
||||
$parsed['protocol_version']);
|
||||
|
||||
// EntityEnclosingRequest adds an "Expect: 100-Continue" header when
|
||||
// using a raw request body for PUT or POST requests. This factory
|
||||
// method should accurately reflect the message, so here we are
|
||||
// removing the Expect header if one was not supplied in the message.
|
||||
if (!isset($parsed['headers']['Expect'])) {
|
||||
$request->removeHeader('Expect');
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fromParts($method, array $parts, $headers = null, $body = null, $protocol = 'HTTP', $protocolVersion = '1.1')
|
||||
{
|
||||
return $this->create($method, Url::buildUrl($parts, true), $headers, $body)
|
||||
->setProtocolVersion($protocolVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create($method, $url, $headers = null, $body = null)
|
||||
{
|
||||
if ($method != 'POST' && $method != 'PUT' && $method != 'PATCH') {
|
||||
$c = $this->requestClass;
|
||||
public function create($method, $url, $headers = null, $body = null) {
|
||||
$c = $this->entityEnclosingRequestClass;
|
||||
$request = new $c($method, $url, $headers);
|
||||
if ($body) {
|
||||
$request->setBody(EntityBody::factory($body));
|
||||
}
|
||||
} else {
|
||||
$c = $this->entityEnclosingRequestClass;
|
||||
$request = new $c($method, $url, $headers);
|
||||
|
||||
if ($body) {
|
||||
if ($method == 'POST' && (is_array($body) || $body instanceof Collection)) {
|
||||
$request->addPostFields($body);
|
||||
} else if (is_resource($body) || $body instanceof EntityBody) {
|
||||
$request->setBody($body, (string) $request->getHeader('Content-Type'));
|
||||
} else {
|
||||
$request->setBody((string) $body, (string) $request->getHeader('Content-Type'));
|
||||
}
|
||||
}
|
||||
|
||||
// Fix chunked transfers based on the passed headers
|
||||
if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked') {
|
||||
$request->removeHeader('Content-Length')
|
||||
->setHeader('Transfer-Encoding', 'chunked');
|
||||
}
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace Ratchet\Tests\Component\WebSocket\Guzzle\Http\Message;
|
||||
use Ratchet\Component\WebSocket\Guzzle\Http\Message\RequestFactory;
|
||||
|
||||
/**
|
||||
* @covers Ratchet\Component\WebSocket\Guzzle\Http\Message\RequestFactory
|
||||
*/
|
||||
class RequestFactoryTest extends \PHPUnit_Framework_TestCase {
|
||||
protected $factory;
|
||||
|
||||
public function setUp() {
|
||||
$this->factory = RequestFactory::getInstance();
|
||||
}
|
||||
|
||||
public function testMessageProvider() {
|
||||
return array(
|
||||
'status' => 'GET / HTTP/1.1'
|
||||
, 'headers' => array(
|
||||
'Upgrade' => 'WebSocket'
|
||||
, 'Connection' => 'Upgrade'
|
||||
, 'Host' => 'localhost:8000'
|
||||
, 'Sec-WebSocket-Key1' => '> b3lU Z0 fh f 3+83394 6 (zG4'
|
||||
, 'Sec-WebSocket-Key2' => ',3Z0X0677 dV-d [159 Z*4'
|
||||
)
|
||||
, 'body' => "123456\r\n\r\n"
|
||||
);
|
||||
}
|
||||
|
||||
public function combineMessage($status, array $headers, $body = '') {
|
||||
$message = $status . "\r\n";
|
||||
|
||||
foreach ($headers as $key => $val) {
|
||||
$message .= "{$key}: {$val}\r\n";
|
||||
}
|
||||
|
||||
$message .= "\r\n{$body}";
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function testExpectedDataFromGuzzleHeaders() {
|
||||
$parts = $this->testMessageProvider();
|
||||
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||
$object = $this->factory->fromMessage($message);
|
||||
|
||||
foreach ($parts['headers'] as $key => $val) {
|
||||
$this->assertEquals($val, $object->getHeader($key, true));
|
||||
}
|
||||
}
|
||||
|
||||
public function testExpectedDataFromNonGuzzleHeaders() {
|
||||
$parts = $this->testMessageProvider();
|
||||
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||
$object = $this->factory->fromMessage($message);
|
||||
|
||||
$this->assertNull($object->getHeader('Nope', true));
|
||||
$this->assertNull($object->getHeader('Nope'));
|
||||
}
|
||||
|
||||
public function testExpectedDataFromNonGuzzleBody() {
|
||||
$parts = $this->testMessageProvider();
|
||||
$message = $this->combineMessage($parts['status'], $parts['headers'], $parts['body']);
|
||||
$object = $this->factory->fromMessage($message);
|
||||
|
||||
$this->assertEquals($parts['body'], (string)$object->getBody());
|
||||
}
|
||||
}
|
@ -125,4 +125,12 @@ class RFC6455Test extends \PHPUnit_Framework_TestCase {
|
||||
$this->_version->handshake($request);
|
||||
}
|
||||
}
|
||||
|
||||
public function testNewMessage() {
|
||||
$this->assertInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\RFC6455\\Message', $this->_version->newMessage());
|
||||
}
|
||||
|
||||
public function testNewFrame() {
|
||||
$this->assertInstanceOf('\\Ratchet\\Component\\WebSocket\\Version\\RFC6455\\Frame', $this->_version->newFrame());
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
Loading…
Reference in New Issue
Block a user