406 changed files with 22235 additions and 22043 deletions
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
namespace Ratchet\Http; |
||||||
|
use Ratchet\ConnectionInterface; |
||||||
|
use GuzzleHttp\Psr7 as gPsr; |
||||||
|
use GuzzleHttp\Psr7\Response; |
||||||
|
|
||||||
|
trait CloseResponseTrait { |
||||||
|
/** |
||||||
|
* Close a connection with an HTTP response |
||||||
|
* @param \Ratchet\ConnectionInterface $conn |
||||||
|
* @param int $code HTTP status code |
||||||
|
* @return null |
||||||
|
*/ |
||||||
|
private function close(ConnectionInterface $conn, $code = 400, array $additional_headers = []) { |
||||||
|
$response = new Response($code, array_merge([ |
||||||
|
'X-Powered-By' => \Ratchet\VERSION |
||||||
|
], $additional_headers)); |
||||||
|
|
||||||
|
$conn->send(gPsr\str($response)); |
||||||
|
$conn->close(); |
||||||
|
} |
||||||
|
} |
@ -1,34 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\Http\Guzzle\Http\Message; |
|
||||||
use Guzzle\Http\Message\RequestFactory as GuzzleRequestFactory; |
|
||||||
use Guzzle\Http\EntityBody; |
|
||||||
|
|
||||||
class RequestFactory extends GuzzleRequestFactory { |
|
||||||
|
|
||||||
protected static $ratchetInstance; |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public static function getInstance() |
|
||||||
{ |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
if (!static::$ratchetInstance) { |
|
||||||
static::$ratchetInstance = new static(); |
|
||||||
} |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
|
|
||||||
return static::$ratchetInstance; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function create($method, $url, $headers = null, $body = '', array $options = array()) { |
|
||||||
$c = $this->entityEnclosingRequestClass; |
|
||||||
$request = new $c($method, $url, $headers); |
|
||||||
$request->setBody(EntityBody::factory($body)); |
|
||||||
|
|
||||||
return $request; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
namespace Ratchet\WebSocket; |
||||||
|
use Ratchet\RFC6455\Messaging\MessageBuffer; |
||||||
|
|
||||||
|
class ConnContext { |
||||||
|
/** |
||||||
|
* @var \Ratchet\WebSocket\WsConnection |
||||||
|
*/ |
||||||
|
public $connection; |
||||||
|
|
||||||
|
/** |
||||||
|
* @var \Ratchet\RFC6455\Messaging\MessageBuffer; |
||||||
|
*/ |
||||||
|
public $buffer; |
||||||
|
|
||||||
|
public function __construct(WsConnection $conn, MessageBuffer $buffer) { |
||||||
|
$this->connection = $conn; |
||||||
|
$this->buffer = $buffer; |
||||||
|
} |
||||||
|
} |
@ -1,31 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Encoding; |
|
||||||
|
|
||||||
class ToggleableValidator implements ValidatorInterface { |
|
||||||
/** |
|
||||||
* Toggle if checkEncoding checks the encoding or not |
|
||||||
* @var bool |
|
||||||
*/ |
|
||||||
public $on; |
|
||||||
|
|
||||||
/** |
|
||||||
* @var Validator |
|
||||||
*/ |
|
||||||
private $validator; |
|
||||||
|
|
||||||
public function __construct($on = true) { |
|
||||||
$this->validator = new Validator; |
|
||||||
$this->on = (boolean)$on; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function checkEncoding($str, $encoding) { |
|
||||||
if (!(boolean)$this->on) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return $this->validator->checkEncoding($str, $encoding); |
|
||||||
} |
|
||||||
} |
|
@ -1,93 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Encoding; |
|
||||||
|
|
||||||
/** |
|
||||||
* This class handled encoding validation |
|
||||||
*/ |
|
||||||
class Validator { |
|
||||||
const UTF8_ACCEPT = 0; |
|
||||||
const UTF8_REJECT = 1; |
|
||||||
|
|
||||||
/** |
|
||||||
* Incremental UTF-8 validator with constant memory consumption (minimal state). |
|
||||||
* |
|
||||||
* Implements the algorithm "Flexible and Economical UTF-8 Decoder" by |
|
||||||
* Bjoern Hoehrmann (http://bjoern.hoehrmann.de/utf-8/decoder/dfa/). |
|
||||||
*/ |
|
||||||
protected static $dfa = array( |
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 00..1f |
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 20..3f |
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 40..5f |
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, # 60..7f |
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, # 80..9f |
|
||||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, # a0..bf |
|
||||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, # c0..df |
|
||||||
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, # e0..ef |
|
||||||
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, # f0..ff |
|
||||||
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, # s0..s0 |
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, # s1..s2 |
|
||||||
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, # s3..s4 |
|
||||||
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, # s5..s6 |
|
||||||
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, # s7..s8 |
|
||||||
); |
|
||||||
|
|
||||||
/** |
|
||||||
* Lookup if mbstring is available |
|
||||||
* @var bool |
|
||||||
*/ |
|
||||||
private $hasMbString = false; |
|
||||||
|
|
||||||
/** |
|
||||||
* Lookup if iconv is available |
|
||||||
* @var bool |
|
||||||
*/ |
|
||||||
private $hasIconv = false; |
|
||||||
|
|
||||||
public function __construct() { |
|
||||||
$this->hasMbString = extension_loaded('mbstring'); |
|
||||||
$this->hasIconv = extension_loaded('iconv'); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string $str The value to check the encoding |
|
||||||
* @param string $against The type of encoding to check against |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function checkEncoding($str, $against) { |
|
||||||
if ('UTF-8' == $against) { |
|
||||||
return $this->isUtf8($str); |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->hasMbString) { |
|
||||||
return mb_check_encoding($str, $against); |
|
||||||
} elseif ($this->hasIconv) { |
|
||||||
return ($str == iconv($against, "{$against}//IGNORE", $str)); |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
protected function isUtf8($str) { |
|
||||||
if ($this->hasMbString) { |
|
||||||
if (false === mb_check_encoding($str, 'UTF-8')) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} elseif ($this->hasIconv) { |
|
||||||
if ($str != iconv('UTF-8', 'UTF-8//IGNORE', $str)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$state = static::UTF8_ACCEPT; |
|
||||||
|
|
||||||
for ($i = 0, $len = strlen($str); $i < $len; $i++) { |
|
||||||
$state = static::$dfa[256 + ($state << 4) + static::$dfa[ord($str[$i])]]; |
|
||||||
|
|
||||||
if (static::UTF8_REJECT === $state) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Encoding; |
|
||||||
|
|
||||||
interface ValidatorInterface { |
|
||||||
/** |
|
||||||
* Verify a string matches the encoding type |
|
||||||
* @param string $str The string to check |
|
||||||
* @param string $encoding The encoding type to check against |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
function checkEncoding($str, $encoding); |
|
||||||
} |
|
@ -0,0 +1,8 @@ |
|||||||
|
<?php |
||||||
|
namespace Ratchet\WebSocket; |
||||||
|
use Ratchet\ConnectionInterface; |
||||||
|
use Ratchet\RFC6455\Messaging\MessageInterface; |
||||||
|
|
||||||
|
interface MessageCallableInterface { |
||||||
|
public function onMessage(ConnectionInterface $conn, MessageInterface $msg); |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
<?php |
||||||
|
namespace Ratchet\WebSocket; |
||||||
|
use Ratchet\ComponentInterface; |
||||||
|
|
||||||
|
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface { |
||||||
|
} |
@ -1,120 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\ConnectionInterface; |
|
||||||
use Ratchet\MessageInterface; |
|
||||||
use Ratchet\WebSocket\Version\Hixie76\Connection; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\Message\Response; |
|
||||||
use Ratchet\WebSocket\Version\Hixie76\Frame; |
|
||||||
|
|
||||||
/** |
|
||||||
* FOR THE LOVE OF BEER, PLEASE PLEASE PLEASE DON'T allow the use of this in your application! |
|
||||||
* Hixie76 is bad for 2 (there's more) reasons: |
|
||||||
* 1) The handshake is done in HTTP, which includes a key for signing in the body... |
|
||||||
* BUT there is no Length defined in the header (as per HTTP spec) so the TCP buffer can't tell when the message is done! |
|
||||||
* 2) By nature it's insecure. Google did a test study where they were able to do a |
|
||||||
* man-in-the-middle attack on 10%-15% of the people who saw their ad who had a browser (currently only Safari) supporting the Hixie76 protocol. |
|
||||||
* This was exploited by taking advantage of proxy servers in front of the user who ignored some HTTP headers in the handshake |
|
||||||
* The Hixie76 is currently implemented by Safari |
|
||||||
* @link http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 |
|
||||||
*/ |
|
||||||
class Hixie76 implements VersionInterface { |
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function isProtocol(RequestInterface $request) { |
|
||||||
return !(null === $request->getHeader('Sec-WebSocket-Key2')); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getVersionNumber() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request |
|
||||||
* @return \Guzzle\Http\Message\Response |
|
||||||
* @throws \UnderflowException If there hasn't been enough data received |
|
||||||
*/ |
|
||||||
public function handshake(RequestInterface $request) { |
|
||||||
$body = substr($request->getBody(), 0, 8); |
|
||||||
if (8 !== strlen($body)) { |
|
||||||
throw new \UnderflowException("Not enough data received to issue challenge response"); |
|
||||||
} |
|
||||||
|
|
||||||
$challenge = $this->sign((string)$request->getHeader('Sec-WebSocket-Key1'), (string)$request->getHeader('Sec-WebSocket-Key2'), $body); |
|
||||||
|
|
||||||
$headers = array( |
|
||||||
'Upgrade' => 'WebSocket' |
|
||||||
, 'Connection' => 'Upgrade' |
|
||||||
, 'Sec-WebSocket-Origin' => (string)$request->getHeader('Origin') |
|
||||||
, 'Sec-WebSocket-Location' => 'ws://' . (string)$request->getHeader('Host') . $request->getPath() |
|
||||||
); |
|
||||||
|
|
||||||
$response = new Response(101, $headers, $challenge); |
|
||||||
$response->setStatus(101, 'WebSocket Protocol Handshake'); |
|
||||||
|
|
||||||
return $response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) { |
|
||||||
$upgraded = new Connection($conn); |
|
||||||
|
|
||||||
if (!isset($upgraded->WebSocket)) { |
|
||||||
$upgraded->WebSocket = new \StdClass; |
|
||||||
} |
|
||||||
|
|
||||||
$upgraded->WebSocket->coalescedCallback = $coalescedCallback; |
|
||||||
|
|
||||||
return $upgraded; |
|
||||||
} |
|
||||||
|
|
||||||
public function onMessage(ConnectionInterface $from, $data) { |
|
||||||
$overflow = ''; |
|
||||||
|
|
||||||
if (!isset($from->WebSocket->frame)) { |
|
||||||
$from->WebSocket->frame = $this->newFrame(); |
|
||||||
} |
|
||||||
|
|
||||||
$from->WebSocket->frame->addBuffer($data); |
|
||||||
if ($from->WebSocket->frame->isCoalesced()) { |
|
||||||
$overflow = $from->WebSocket->frame->extractOverflow(); |
|
||||||
|
|
||||||
$parsed = $from->WebSocket->frame->getPayload(); |
|
||||||
unset($from->WebSocket->frame); |
|
||||||
|
|
||||||
$from->WebSocket->coalescedCallback->onMessage($from, $parsed); |
|
||||||
|
|
||||||
unset($from->WebSocket->frame); |
|
||||||
} |
|
||||||
|
|
||||||
if (strlen($overflow) > 0) { |
|
||||||
$this->onMessage($from, $overflow); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function newFrame() { |
|
||||||
return new Frame; |
|
||||||
} |
|
||||||
|
|
||||||
public function generateKeyNumber($key) { |
|
||||||
if (0 === substr_count($key, ' ')) { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
return preg_replace('[\D]', '', $key) / substr_count($key, ' '); |
|
||||||
} |
|
||||||
|
|
||||||
protected function sign($key1, $key2, $code) { |
|
||||||
return md5( |
|
||||||
pack('N', $this->generateKeyNumber($key1)) |
|
||||||
. pack('N', $this->generateKeyNumber($key2)) |
|
||||||
. $code |
|
||||||
, true); |
|
||||||
} |
|
||||||
} |
|
@ -1,26 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version\Hixie76; |
|
||||||
use Ratchet\AbstractConnectionDecorator; |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
* @property \StdClass $WebSocket |
|
||||||
*/ |
|
||||||
class Connection extends AbstractConnectionDecorator { |
|
||||||
public function send($msg) { |
|
||||||
if (!$this->WebSocket->closing) { |
|
||||||
$this->getConnection()->send(chr(0) . $msg . chr(255)); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function close() { |
|
||||||
if (!$this->WebSocket->closing) { |
|
||||||
$this->getConnection()->send(chr(255)); |
|
||||||
$this->getConnection()->close(); |
|
||||||
|
|
||||||
$this->WebSocket->closing = true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,86 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version\Hixie76; |
|
||||||
use Ratchet\WebSocket\Version\FrameInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* This does not entirely follow the protocol to spec, but (mostly) works |
|
||||||
* Hixie76 probably should not even be supported |
|
||||||
*/ |
|
||||||
class Frame implements FrameInterface { |
|
||||||
/** |
|
||||||
* @type string |
|
||||||
*/ |
|
||||||
protected $_data = ''; |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function isCoalesced() { |
|
||||||
return (boolean)($this->_data[0] == chr(0) && substr($this->_data, -1) == chr(255)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function addBuffer($buf) { |
|
||||||
$this->_data .= (string)$buf; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function isFinal() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function isMasked() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getOpcode() { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getPayloadLength() { |
|
||||||
if (!$this->isCoalesced()) { |
|
||||||
throw new \UnderflowException('Not enough of the message has been buffered to determine the length of the payload'); |
|
||||||
} |
|
||||||
|
|
||||||
return strlen($this->_data) - 2; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getMaskingKey() { |
|
||||||
return ''; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getPayload() { |
|
||||||
if (!$this->isCoalesced()) { |
|
||||||
return new \UnderflowException('Not enough data buffered to read payload'); |
|
||||||
} |
|
||||||
|
|
||||||
return substr($this->_data, 1, strlen($this->_data) - 2); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContents() { |
|
||||||
return $this->_data; |
|
||||||
} |
|
||||||
|
|
||||||
public function extractOverflow() { |
|
||||||
return ''; |
|
||||||
} |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
class HyBi10 extends RFC6455 { |
|
||||||
public function isProtocol(RequestInterface $request) { |
|
||||||
$version = (int)(string)$request->getHeader('Sec-WebSocket-Version'); |
|
||||||
|
|
||||||
return ($version >= 6 && $version < 13); |
|
||||||
} |
|
||||||
|
|
||||||
public function getVersionNumber() { |
|
||||||
return 6; |
|
||||||
} |
|
||||||
} |
|
@ -1,273 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\ConnectionInterface; |
|
||||||
use Ratchet\MessageInterface; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Message; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Frame; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Connection; |
|
||||||
use Ratchet\WebSocket\Encoding\ValidatorInterface; |
|
||||||
use Ratchet\WebSocket\Encoding\Validator; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\Message\Response; |
|
||||||
|
|
||||||
/** |
|
||||||
* The latest version of the WebSocket protocol |
|
||||||
* @link http://tools.ietf.org/html/rfc6455 |
|
||||||
* @todo Unicode: return mb_convert_encoding(pack("N",$u), mb_internal_encoding(), 'UCS-4BE'); |
|
||||||
*/ |
|
||||||
class RFC6455 implements VersionInterface { |
|
||||||
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; |
|
||||||
|
|
||||||
/** |
|
||||||
* @var RFC6455\HandshakeVerifier |
|
||||||
*/ |
|
||||||
protected $_verifier; |
|
||||||
|
|
||||||
/** |
|
||||||
* A lookup of the valid close codes that can be sent in a frame |
|
||||||
* @var array |
|
||||||
*/ |
|
||||||
private $closeCodes = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* @var \Ratchet\WebSocket\Encoding\ValidatorInterface |
|
||||||
*/ |
|
||||||
protected $validator; |
|
||||||
|
|
||||||
public function __construct(ValidatorInterface $validator = null) { |
|
||||||
$this->_verifier = new HandshakeVerifier; |
|
||||||
$this->setCloseCodes(); |
|
||||||
|
|
||||||
if (null === $validator) { |
|
||||||
$validator = new Validator; |
|
||||||
} |
|
||||||
|
|
||||||
$this->validator = $validator; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function isProtocol(RequestInterface $request) { |
|
||||||
$version = (int)(string)$request->getHeader('Sec-WebSocket-Version'); |
|
||||||
|
|
||||||
return ($this->getVersionNumber() === $version); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getVersionNumber() { |
|
||||||
return 13; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function handshake(RequestInterface $request) { |
|
||||||
if (true !== $this->_verifier->verifyAll($request)) { |
|
||||||
return new Response(400); |
|
||||||
} |
|
||||||
|
|
||||||
return new Response(101, array( |
|
||||||
'Upgrade' => 'websocket' |
|
||||||
, 'Connection' => 'Upgrade' |
|
||||||
, 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')) |
|
||||||
)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param \Ratchet\ConnectionInterface $conn |
|
||||||
* @param \Ratchet\MessageInterface $coalescedCallback |
|
||||||
* @return \Ratchet\WebSocket\Version\RFC6455\Connection |
|
||||||
*/ |
|
||||||
public function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback) { |
|
||||||
$upgraded = new Connection($conn); |
|
||||||
|
|
||||||
if (!isset($upgraded->WebSocket)) { |
|
||||||
$upgraded->WebSocket = new \StdClass; |
|
||||||
} |
|
||||||
|
|
||||||
$upgraded->WebSocket->coalescedCallback = $coalescedCallback; |
|
||||||
|
|
||||||
return $upgraded; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param \Ratchet\WebSocket\Version\RFC6455\Connection $from |
|
||||||
* @param string $data |
|
||||||
*/ |
|
||||||
public function onMessage(ConnectionInterface $from, $data) { |
|
||||||
$overflow = ''; |
|
||||||
|
|
||||||
if (!isset($from->WebSocket->message)) { |
|
||||||
$from->WebSocket->message = $this->newMessage(); |
|
||||||
} |
|
||||||
|
|
||||||
// There is a frame fragment attached to the connection, add to it |
|
||||||
if (!isset($from->WebSocket->frame)) { |
|
||||||
$from->WebSocket->frame = $this->newFrame(); |
|
||||||
} |
|
||||||
|
|
||||||
$from->WebSocket->frame->addBuffer($data); |
|
||||||
if ($from->WebSocket->frame->isCoalesced()) { |
|
||||||
$frame = $from->WebSocket->frame; |
|
||||||
|
|
||||||
if (false !== $frame->getRsv1() || |
|
||||||
false !== $frame->getRsv2() || |
|
||||||
false !== $frame->getRsv3() |
|
||||||
) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
if (!$frame->isMasked()) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
$opcode = $frame->getOpcode(); |
|
||||||
|
|
||||||
if ($opcode > 2) { |
|
||||||
if ($frame->getPayloadLength() > 125 || !$frame->isFinal()) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
switch ($opcode) { |
|
||||||
case $frame::OP_CLOSE: |
|
||||||
$closeCode = 0; |
|
||||||
|
|
||||||
$bin = $frame->getPayload(); |
|
||||||
|
|
||||||
if (empty($bin)) { |
|
||||||
return $from->close(); |
|
||||||
} |
|
||||||
|
|
||||||
if (strlen($bin) >= 2) { |
|
||||||
list($closeCode) = array_merge(unpack('n*', substr($bin, 0, 2))); |
|
||||||
} |
|
||||||
|
|
||||||
if (!$this->isValidCloseCode($closeCode)) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
if (!$this->validator->checkEncoding(substr($bin, 2), 'UTF-8')) { |
|
||||||
return $from->close($frame::CLOSE_BAD_PAYLOAD); |
|
||||||
} |
|
||||||
|
|
||||||
$frame->unMaskPayload(); |
|
||||||
|
|
||||||
return $from->close($frame); |
|
||||||
break; |
|
||||||
case $frame::OP_PING: |
|
||||||
$from->send($this->newFrame($frame->getPayload(), true, $frame::OP_PONG)); |
|
||||||
break; |
|
||||||
case $frame::OP_PONG: |
|
||||||
break; |
|
||||||
default: |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
$overflow = $from->WebSocket->frame->extractOverflow(); |
|
||||||
|
|
||||||
unset($from->WebSocket->frame, $frame, $opcode); |
|
||||||
|
|
||||||
if (strlen($overflow) > 0) { |
|
||||||
$this->onMessage($from, $overflow); |
|
||||||
} |
|
||||||
|
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
$overflow = $from->WebSocket->frame->extractOverflow(); |
|
||||||
|
|
||||||
if ($frame::OP_CONTINUE == $frame->getOpcode() && 0 == count($from->WebSocket->message)) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
if (count($from->WebSocket->message) > 0 && $frame::OP_CONTINUE != $frame->getOpcode()) { |
|
||||||
return $from->close($frame::CLOSE_PROTOCOL); |
|
||||||
} |
|
||||||
|
|
||||||
$from->WebSocket->message->addFrame($from->WebSocket->frame); |
|
||||||
unset($from->WebSocket->frame); |
|
||||||
} |
|
||||||
|
|
||||||
if ($from->WebSocket->message->isCoalesced()) { |
|
||||||
$parsed = $from->WebSocket->message->getPayload(); |
|
||||||
unset($from->WebSocket->message); |
|
||||||
|
|
||||||
if (!$this->validator->checkEncoding($parsed, 'UTF-8')) { |
|
||||||
return $from->close(Frame::CLOSE_BAD_PAYLOAD); |
|
||||||
} |
|
||||||
|
|
||||||
$from->WebSocket->coalescedCallback->onMessage($from, $parsed); |
|
||||||
} |
|
||||||
|
|
||||||
if (strlen($overflow) > 0) { |
|
||||||
$this->onMessage($from, $overflow); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return RFC6455\Message |
|
||||||
*/ |
|
||||||
public function newMessage() { |
|
||||||
return new Message; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string|null $payload |
|
||||||
* @param bool|null $final |
|
||||||
* @param int|null $opcode |
|
||||||
* @return RFC6455\Frame |
|
||||||
*/ |
|
||||||
public function newFrame($payload = null, $final = null, $opcode = null) { |
|
||||||
return new Frame($payload, $final, $opcode); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Used when doing the handshake to encode the key, verifying client/server are speaking the same language |
|
||||||
* @param string $key |
|
||||||
* @return string |
|
||||||
* @internal |
|
||||||
*/ |
|
||||||
public function sign($key) { |
|
||||||
return base64_encode(sha1($key . static::GUID, true)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Determine if a close code is valid |
|
||||||
* @param int|string |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function isValidCloseCode($val) { |
|
||||||
if (array_key_exists($val, $this->closeCodes)) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
if ($val >= 3000 && $val <= 4999) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates a private lookup of valid, private close codes |
|
||||||
*/ |
|
||||||
protected function setCloseCodes() { |
|
||||||
$this->closeCodes[Frame::CLOSE_NORMAL] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_GOING_AWAY] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_PROTOCOL] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_BAD_DATA] = true; |
|
||||||
//$this->closeCodes[Frame::CLOSE_NO_STATUS] = true; |
|
||||||
//$this->closeCodes[Frame::CLOSE_ABNORMAL] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_BAD_PAYLOAD] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_POLICY] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_TOO_BIG] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_MAND_EXT] = true; |
|
||||||
$this->closeCodes[Frame::CLOSE_SRV_ERR] = true; |
|
||||||
//$this->closeCodes[Frame::CLOSE_TLS] = true; |
|
||||||
} |
|
||||||
} |
|
@ -1,57 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\MessageInterface; |
|
||||||
use Ratchet\ConnectionInterface; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* A standard interface for interacting with the various version of the WebSocket protocol |
|
||||||
*/ |
|
||||||
interface VersionInterface extends MessageInterface { |
|
||||||
/** |
|
||||||
* Given an HTTP header, determine if this version should handle the protocol |
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request |
|
||||||
* @return bool |
|
||||||
* @throws \UnderflowException If the protocol thinks the headers are still fragmented |
|
||||||
*/ |
|
||||||
function isProtocol(RequestInterface $request); |
|
||||||
|
|
||||||
/** |
|
||||||
* Although the version has a name associated with it the integer returned is the proper identification |
|
||||||
* @return int |
|
||||||
*/ |
|
||||||
function getVersionNumber(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Perform the handshake and return the response headers |
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request |
|
||||||
* @return \Guzzle\Http\Message\Response |
|
||||||
* @throws \UnderflowException If the message hasn't finished buffering (not yet implemented, theoretically will only happen with Hixie version) |
|
||||||
*/ |
|
||||||
function handshake(RequestInterface $request); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param \Ratchet\ConnectionInterface $conn |
|
||||||
* @param \Ratchet\MessageInterface $coalescedCallback |
|
||||||
* @return \Ratchet\ConnectionInterface |
|
||||||
*/ |
|
||||||
function upgradeConnection(ConnectionInterface $conn, MessageInterface $coalescedCallback); |
|
||||||
|
|
||||||
/** |
|
||||||
* @return MessageInterface |
|
||||||
*/ |
|
||||||
//function newMessage(); |
|
||||||
|
|
||||||
/** |
|
||||||
* @return FrameInterface |
|
||||||
*/ |
|
||||||
//function newFrame(); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string |
|
||||||
* @param bool |
|
||||||
* @return string |
|
||||||
* @todo Change to use other classes, this will be removed eventually |
|
||||||
*/ |
|
||||||
//function frame($message, $mask = true); |
|
||||||
} |
|
@ -1,90 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket; |
|
||||||
use Ratchet\WebSocket\Version\VersionInterface; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Manage the various versions of the WebSocket protocol |
|
||||||
* This accepts interfaces of versions to enable/disable |
|
||||||
*/ |
|
||||||
class VersionManager { |
|
||||||
/** |
|
||||||
* The header string to let clients know which versions are supported |
|
||||||
* @var string |
|
||||||
*/ |
|
||||||
private $versionString = ''; |
|
||||||
|
|
||||||
/** |
|
||||||
* Storage of each version enabled |
|
||||||
* @var array |
|
||||||
*/ |
|
||||||
protected $versions = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the protocol negotiator for the request, if supported |
|
||||||
* @param \Guzzle\Http\Message\RequestInterface $request |
|
||||||
* @throws \InvalidArgumentException |
|
||||||
* @return \Ratchet\WebSocket\Version\VersionInterface |
|
||||||
*/ |
|
||||||
public function getVersion(RequestInterface $request) { |
|
||||||
foreach ($this->versions as $version) { |
|
||||||
if ($version->isProtocol($request)) { |
|
||||||
return $version; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
throw new \InvalidArgumentException("Version not found"); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param \Guzzle\Http\Message\RequestInterface |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function isVersionEnabled(RequestInterface $request) { |
|
||||||
foreach ($this->versions as $version) { |
|
||||||
if ($version->isProtocol($request)) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Enable support for a specific version of the WebSocket protocol |
|
||||||
* @param \Ratchet\WebSocket\Version\VersionInterface $version |
|
||||||
* @return VersionManager |
|
||||||
*/ |
|
||||||
public function enableVersion(VersionInterface $version) { |
|
||||||
$this->versions[$version->getVersionNumber()] = $version; |
|
||||||
|
|
||||||
if (empty($this->versionString)) { |
|
||||||
$this->versionString = (string)$version->getVersionNumber(); |
|
||||||
} else { |
|
||||||
$this->versionString .= ", {$version->getVersionNumber()}"; |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Disable support for a specific WebSocket protocol version |
|
||||||
* @param int $versionId The version ID to un-support |
|
||||||
* @return VersionManager |
|
||||||
*/ |
|
||||||
public function disableVersion($versionId) { |
|
||||||
unset($this->versions[$versionId]); |
|
||||||
|
|
||||||
$this->versionString = implode(',', array_keys($this->versions)); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a string of version numbers supported (comma delimited) |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getSupportedVersionString() { |
|
||||||
return $this->versionString; |
|
||||||
} |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; |
|
||||||
|
|
||||||
$port = $argc > 1 ? $argv[1] : 8000; |
|
||||||
$impl = sprintf('React\EventLoop\%sLoop', $argc > 2 ? $argv[2] : 'StreamSelect'); |
|
||||||
|
|
||||||
$loop = new $impl; |
|
||||||
$sock = new React\Socket\Server($loop); |
|
||||||
$web = new Ratchet\WebSocket\WsServer(new Ratchet\Server\EchoServer); |
|
||||||
$app = new Ratchet\Http\HttpServer($web); |
|
||||||
$web->setEncodingChecks(false); |
|
||||||
|
|
||||||
$sock->listen($port, '0.0.0.0'); |
|
||||||
|
|
||||||
$server = new Ratchet\Server\IoServer($app, $sock, $loop); |
|
||||||
$server->run(); |
|
@ -1,53 +0,0 @@ |
|||||||
<?php |
|
||||||
use Guzzle\Http\Message\Request; |
|
||||||
|
|
||||||
class GuzzleTest extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $_request; |
|
||||||
|
|
||||||
protected $_headers = array( |
|
||||||
'Upgrade' => 'websocket' |
|
||||||
, 'Connection' => 'Upgrade' |
|
||||||
, 'Host' => 'localhost:8080' |
|
||||||
, 'Origin' => 'chrome://newtab' |
|
||||||
, 'Sec-WebSocket-Protocol' => 'one, two, three' |
|
||||||
, 'Sec-WebSocket-Key' => '9bnXNp3ae6FbFFRtPdiPXA==' |
|
||||||
, 'Sec-WebSocket-Version' => '13' |
|
||||||
); |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->_request = new Request('GET', 'http://localhost', $this->_headers); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetHeaderString() { |
|
||||||
$this->assertEquals('Upgrade', (string)$this->_request->getHeader('connection')); |
|
||||||
$this->assertEquals('9bnXNp3ae6FbFFRtPdiPXA==', (string)$this->_request->getHeader('Sec-Websocket-Key')); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetHeaderInteger() { |
|
||||||
$this->assertSame('13', (string)$this->_request->getHeader('Sec-Websocket-Version')); |
|
||||||
$this->assertSame(13, (int)(string)$this->_request->getHeader('Sec-WebSocket-Version')); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetHeaderObject() { |
|
||||||
$this->assertInstanceOf('Guzzle\Http\Message\Header', $this->_request->getHeader('Origin')); |
|
||||||
$this->assertNull($this->_request->getHeader('Non-existant-header')); |
|
||||||
} |
|
||||||
|
|
||||||
public function testHeaderObjectNormalizeValues() { |
|
||||||
$expected = 1 + substr_count($this->_headers['Sec-WebSocket-Protocol'], ','); |
|
||||||
$protocols = $this->_request->getHeader('Sec-WebSocket-Protocol')->normalize(); |
|
||||||
$count = 0; |
|
||||||
|
|
||||||
foreach ($protocols as $protocol) { |
|
||||||
$count++; |
|
||||||
} |
|
||||||
|
|
||||||
$this->assertEquals($expected, $count); |
|
||||||
$this->assertEquals($expected, count($protocols)); |
|
||||||
} |
|
||||||
|
|
||||||
public function testRequestFactoryCreateSignature() { |
|
||||||
$ref = new \ReflectionMethod('Guzzle\Http\Message\RequestFactory', 'create'); |
|
||||||
$this->assertEquals(2, $ref->getNumberOfRequiredParameters()); |
|
||||||
} |
|
||||||
} |
|
@ -1,67 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\Http\Guzzle\Http\Message; |
|
||||||
use Ratchet\Http\Guzzle\Http\Message\RequestFactory; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\Http\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()); |
|
||||||
} |
|
||||||
} |
|
@ -1,103 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\WebSocket\Version\Hixie76; |
|
||||||
use Ratchet\Http\HttpServer; |
|
||||||
use Ratchet\WebSocket\WsServer; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\Version\Hixie76 |
|
||||||
*/ |
|
||||||
class Hixie76Test extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $_crlf = "\r\n"; |
|
||||||
protected $_body = '6dW+XgKfWV0='; |
|
||||||
|
|
||||||
protected $_version; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->_version = new Hixie76; |
|
||||||
} |
|
||||||
|
|
||||||
public function testClassImplementsVersionInterface() { |
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); |
|
||||||
$this->assertThat($this->_version, $constraint); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider keyProvider |
|
||||||
*/ |
|
||||||
public function testKeySigningForHandshake($accept, $key) { |
|
||||||
$this->assertEquals($accept, $this->_version->generateKeyNumber($key)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function keyProvider() { |
|
||||||
return array( |
|
||||||
array(179922739, '17 9 G`ZD9 2 2b 7X 3 /r90') |
|
||||||
, array(906585445, '3e6b263 4 17 80') |
|
||||||
, array(0, '3e6b26341780') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function headerProvider() { |
|
||||||
$key1 = base64_decode('QTN+ICszNiA2IDJvICBWOG4gNyAgc08yODhZ'); |
|
||||||
$key2 = base64_decode('TzEyICAgeVsgIFFSNDUgM1IgLiAyOFggNC00dn4z'); |
|
||||||
|
|
||||||
$headers = "GET / HTTP/1.1"; |
|
||||||
$headers .= "Upgrade: WebSocket{$this->_crlf}"; |
|
||||||
$headers .= "Connection: Upgrade{$this->_crlf}"; |
|
||||||
$headers .= "Host: socketo.me{$this->_crlf}"; |
|
||||||
$headers .= "Origin: http://fiddle.jshell.net{$this->_crlf}"; |
|
||||||
$headers .= "Sec-WebSocket-Key1:17 Z4< F94 N3 7P41 7{$this->_crlf}"; |
|
||||||
$headers .= "Sec-WebSocket-Key2:1 23C3:,2% 1-29 4 f0{$this->_crlf}"; |
|
||||||
$headers .= "(Key3):70:00:EE:6E:33:20:90:69{$this->_crlf}"; |
|
||||||
$headers .= $this->_crlf; |
|
||||||
|
|
||||||
return $headers; |
|
||||||
} |
|
||||||
|
|
||||||
public function testNoUpgradeBeforeBody() { |
|
||||||
$headers = $this->headerProvider(); |
|
||||||
|
|
||||||
$mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |
|
||||||
$mockApp = $this->getMock('\Ratchet\MessageComponentInterface'); |
|
||||||
|
|
||||||
$server = new HttpServer(new WsServer($mockApp)); |
|
||||||
$server->onOpen($mockConn); |
|
||||||
$mockApp->expects($this->exactly(0))->method('onOpen'); |
|
||||||
$server->onMessage($mockConn, $headers); |
|
||||||
} |
|
||||||
|
|
||||||
public function testTcpFragmentedUpgrade() { |
|
||||||
$headers = $this->headerProvider(); |
|
||||||
$body = base64_decode($this->_body); |
|
||||||
|
|
||||||
$mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |
|
||||||
$mockApp = $this->getMock('\Ratchet\MessageComponentInterface'); |
|
||||||
|
|
||||||
$server = new HttpServer(new WsServer($mockApp)); |
|
||||||
$server->onOpen($mockConn); |
|
||||||
$server->onMessage($mockConn, $headers); |
|
||||||
|
|
||||||
$mockApp->expects($this->once())->method('onOpen'); |
|
||||||
$server->onMessage($mockConn, $body . $this->_crlf . $this->_crlf); |
|
||||||
} |
|
||||||
|
|
||||||
public function testTcpFragmentedBodyUpgrade() { |
|
||||||
$headers = $this->headerProvider(); |
|
||||||
$body = base64_decode($this->_body); |
|
||||||
$body1 = substr($body, 0, 4); |
|
||||||
$body2 = substr($body, 4); |
|
||||||
|
|
||||||
$mockConn = $this->getMock('\Ratchet\ConnectionInterface'); |
|
||||||
$mockApp = $this->getMock('\Ratchet\MessageComponentInterface'); |
|
||||||
|
|
||||||
$server = new HttpServer(new WsServer($mockApp)); |
|
||||||
$server->onOpen($mockConn); |
|
||||||
$server->onMessage($mockConn, $headers); |
|
||||||
|
|
||||||
$mockApp->expects($this->once())->method('onOpen'); |
|
||||||
|
|
||||||
$server->onMessage($mockConn, $body1); |
|
||||||
$server->onMessage($mockConn, $body2); |
|
||||||
$server->onMessage($mockConn, $this->_crlf . $this->_crlf); |
|
||||||
} |
|
||||||
} |
|
@ -1,67 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\WebSocket\Version\HyBi10; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Frame; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\Version\Hybi10 |
|
||||||
*/ |
|
||||||
class HyBi10Test extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $_version; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->_version = new HyBi10(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Is this useful? |
|
||||||
*/ |
|
||||||
public function testClassImplementsVersionInterface() { |
|
||||||
$constraint = $this->isInstanceOf('\\Ratchet\\WebSocket\\Version\\VersionInterface'); |
|
||||||
$this->assertThat($this->_version, $constraint); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider HandshakeProvider |
|
||||||
*/ |
|
||||||
public function testKeySigningForHandshake($key, $accept) { |
|
||||||
$this->assertEquals($accept, $this->_version->sign($key)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function HandshakeProvider() { |
|
||||||
return array( |
|
||||||
array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=') |
|
||||||
, array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider UnframeMessageProvider |
|
||||||
*/ |
|
||||||
public function testUnframeMessage($message, $framed) { |
|
||||||
// $decoded = $this->_version->unframe(base64_decode($framed)); |
|
||||||
$frame = new Frame; |
|
||||||
$frame->addBuffer(base64_decode($framed)); |
|
||||||
|
|
||||||
$this->assertEquals($message, $frame->getPayload()); |
|
||||||
} |
|
||||||
|
|
||||||
public static function UnframeMessageProvider() { |
|
||||||
return array( |
|
||||||
array('Hello World!', 'gYydAIfa1WXrtvIg0LXvbOP7') |
|
||||||
, array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg') |
|
||||||
, array('ಠ_ಠ', 'gYfnSpu5B/g75gf4Ow==') |
|
||||||
, array("The quick brown fox jumps over the lazy dog. All work and no play makes Chris a dull boy. I'm trying to get past 128 characters for a unit test here...", 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function testUnframeMatchesPreFraming() { |
|
||||||
$string = 'Hello World!'; |
|
||||||
$framed = $this->_version->newFrame($string)->getContents(); |
|
||||||
|
|
||||||
$frame = new Frame; |
|
||||||
$frame->addBuffer($framed); |
|
||||||
|
|
||||||
$this->assertEquals($string, $frame->getPayload()); |
|
||||||
} |
|
||||||
} |
|
@ -1,170 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version\RFC6455; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier |
|
||||||
*/ |
|
||||||
class HandshakeVerifierTest extends \PHPUnit_Framework_TestCase { |
|
||||||
/** |
|
||||||
* @var Ratchet\WebSocket\Version\RFC6455\HandshakeVerifier |
|
||||||
*/ |
|
||||||
protected $_v; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->_v = new HandshakeVerifier; |
|
||||||
} |
|
||||||
|
|
||||||
public static function methodProvider() { |
|
||||||
return array( |
|
||||||
array(true, 'GET') |
|
||||||
, array(true, 'get') |
|
||||||
, array(true, 'Get') |
|
||||||
, array(false, 'POST') |
|
||||||
, array(false, 'DELETE') |
|
||||||
, array(false, 'PUT') |
|
||||||
, array(false, 'PATCH') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider methodProvider |
|
||||||
*/ |
|
||||||
public function testMethodMustBeGet($result, $in) { |
|
||||||
$this->assertEquals($result, $this->_v->verifyMethod($in)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function httpVersionProvider() { |
|
||||||
return array( |
|
||||||
array(true, 1.1) |
|
||||||
, array(true, '1.1') |
|
||||||
, array(true, 1.2) |
|
||||||
, array(true, '1.2') |
|
||||||
, array(true, 2) |
|
||||||
, array(true, '2') |
|
||||||
, array(true, '2.0') |
|
||||||
, array(false, '1.0') |
|
||||||
, array(false, 1) |
|
||||||
, array(false, '0.9') |
|
||||||
, array(false, '') |
|
||||||
, array(false, 'hello') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider httpVersionProvider |
|
||||||
*/ |
|
||||||
public function testHttpVersionIsAtLeast1Point1($expected, $in) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyHTTPVersion($in)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function uRIProvider() { |
|
||||||
return array( |
|
||||||
array(true, '/chat') |
|
||||||
, array(true, '/hello/world?key=val') |
|
||||||
, array(false, '/chat#bad') |
|
||||||
, array(false, 'nope') |
|
||||||
, array(false, '/ ಠ_ಠ ') |
|
||||||
, array(false, '/✖') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider URIProvider |
|
||||||
*/ |
|
||||||
public function testRequestUri($expected, $in) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyRequestURI($in)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function hostProvider() { |
|
||||||
return array( |
|
||||||
array(true, 'server.example.com') |
|
||||||
, array(false, null) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider HostProvider |
|
||||||
*/ |
|
||||||
public function testVerifyHostIsSet($expected, $in) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyHost($in)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function upgradeProvider() { |
|
||||||
return array( |
|
||||||
array(true, 'websocket') |
|
||||||
, array(true, 'Websocket') |
|
||||||
, array(true, 'webSocket') |
|
||||||
, array(false, null) |
|
||||||
, array(false, '') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider upgradeProvider |
|
||||||
*/ |
|
||||||
public function testVerifyUpgradeIsWebSocket($expected, $val) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyUpgradeRequest($val)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function connectionProvider() { |
|
||||||
return array( |
|
||||||
array(true, 'Upgrade') |
|
||||||
, array(true, 'upgrade') |
|
||||||
, array(true, 'keep-alive, Upgrade') |
|
||||||
, array(true, 'Upgrade, keep-alive') |
|
||||||
, array(true, 'keep-alive, Upgrade, something') |
|
||||||
, array(false, '') |
|
||||||
, array(false, null) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider connectionProvider |
|
||||||
*/ |
|
||||||
public function testConnectionHeaderVerification($expected, $val) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyConnection($val)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function keyProvider() { |
|
||||||
return array( |
|
||||||
array(true, 'hkfa1L7uwN6DCo4IS3iWAw==') |
|
||||||
, array(true, '765vVoQpKSGJwPzJIMM2GA==') |
|
||||||
, array(true, 'AQIDBAUGBwgJCgsMDQ4PEC==') |
|
||||||
, array(true, 'axa2B/Yz2CdpfQAY2Q5P7w==') |
|
||||||
, array(false, 0) |
|
||||||
, array(false, 'Hello World') |
|
||||||
, array(false, '1234567890123456') |
|
||||||
, array(false, '123456789012345678901234') |
|
||||||
, array(true, base64_encode('UTF8allthngs+✓')) |
|
||||||
, array(true, 'dGhlIHNhbXBsZSBub25jZQ==') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider keyProvider |
|
||||||
*/ |
|
||||||
public function testKeyIsBase64Encoded16BitNonce($expected, $val) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyKey($val)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function versionProvider() { |
|
||||||
return array( |
|
||||||
array(true, 13) |
|
||||||
, array(true, '13') |
|
||||||
, array(false, 12) |
|
||||||
, array(false, 14) |
|
||||||
, array(false, '14') |
|
||||||
, array(false, 'hi') |
|
||||||
, array(false, '') |
|
||||||
, array(false, null) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider versionProvider |
|
||||||
*/ |
|
||||||
public function testVersionEquals13($expected, $in) { |
|
||||||
$this->assertEquals($expected, $this->_v->verifyVersion($in)); |
|
||||||
} |
|
||||||
} |
|
@ -1,151 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket\Version; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455\Frame; |
|
||||||
use Guzzle\Http\Message\RequestFactory; |
|
||||||
use Guzzle\Http\Message\EntityEnclosingRequest; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\Version\RFC6455 |
|
||||||
*/ |
|
||||||
class RFC6455Test extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $version; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->version = new RFC6455; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider handshakeProvider |
|
||||||
*/ |
|
||||||
public function testKeySigningForHandshake($key, $accept) { |
|
||||||
$this->assertEquals($accept, $this->version->sign($key)); |
|
||||||
} |
|
||||||
|
|
||||||
public static function handshakeProvider() { |
|
||||||
return array( |
|
||||||
array('x3JJHMbDL1EzLkh9GBhXDw==', 'HSmrc0sMlYUkAGmm5OPpG2HaGWk=') |
|
||||||
, array('dGhlIHNhbXBsZSBub25jZQ==', 's3pPLMBiTxaQ9kYGzzhZRbK+xOo=') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider UnframeMessageProvider |
|
||||||
*/ |
|
||||||
public function testUnframeMessage($message, $framed) { |
|
||||||
$frame = new Frame; |
|
||||||
$frame->addBuffer(base64_decode($framed)); |
|
||||||
|
|
||||||
$this->assertEquals($message, $frame->getPayload()); |
|
||||||
} |
|
||||||
|
|
||||||
public static function UnframeMessageProvider() { |
|
||||||
return array( |
|
||||||
array('Hello World!', 'gYydAIfa1WXrtvIg0LXvbOP7') |
|
||||||
, array('!@#$%^&*()-=_+[]{}\|/.,<>`~', 'gZv+h96r38f9j9vZ+IHWrvOWoayF9oX6gtfRqfKXwOeg') |
|
||||||
, array('ಠ_ಠ', 'gYfnSpu5B/g75gf4Ow==') |
|
||||||
, array("The quick brown fox jumps over the lazy dog. All work and no play makes Chris a dull boy. I'm trying to get past 128 characters for a unit test here...", 'gf4Amahb14P8M7Kj2S6+4MN7tfHHLLmjzjSvo8IuuvPbe7j1zSn398A+9+/JIa6jzDSwrYh7lu/Ee6Ds2jD34sY/9+3He6fvySL37skwsvCIGL/xwSj34og/ou/Ee7Xs0XX3o+F8uqPcKa7qxjz398d7sObce6fi2y/3sppj9+DAOqXiyy+y8dt7sezae7aj3TW+94gvsvDce7/m2j75rYY=') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function testUnframeMatchesPreFraming() { |
|
||||||
$string = 'Hello World!'; |
|
||||||
$framed = $this->version->newFrame($string)->getContents(); |
|
||||||
|
|
||||||
$frame = new Frame; |
|
||||||
$frame->addBuffer($framed); |
|
||||||
|
|
||||||
$this->assertEquals($string, $frame->getPayload()); |
|
||||||
} |
|
||||||
|
|
||||||
public static $good_rest = 'GET /chat HTTP/1.1'; |
|
||||||
|
|
||||||
public static $good_header = array( |
|
||||||
'Host' => 'server.example.com' |
|
||||||
, 'Upgrade' => 'websocket' |
|
||||||
, 'Connection' => 'Upgrade' |
|
||||||
, 'Sec-WebSocket-Key' => 'dGhlIHNhbXBsZSBub25jZQ==' |
|
||||||
, 'Origin' => 'http://example.com' |
|
||||||
, 'Sec-WebSocket-Protocol' => 'chat, superchat' |
|
||||||
, 'Sec-WebSocket-Version' => 13 |
|
||||||
); |
|
||||||
|
|
||||||
public function caseVariantProvider() { |
|
||||||
return array( |
|
||||||
array('Sec-Websocket-Version') |
|
||||||
, array('sec-websocket-version') |
|
||||||
, array('SEC-WEBSOCKET-VERSION') |
|
||||||
, array('sEC-wEBsOCKET-vERSION') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider caseVariantProvider |
|
||||||
*/ |
|
||||||
public function testIsProtocolWithCaseInsensitivity($headerName) { |
|
||||||
$header = static::$good_header; |
|
||||||
unset($header['Sec-WebSocket-Version']); |
|
||||||
$header[$headerName] = 13; |
|
||||||
|
|
||||||
$this->assertTrue($this->version->isProtocol(new EntityEnclosingRequest('get', '/', $header))); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* A helper function to try and quickly put together a valid WebSocket HTTP handshake |
|
||||||
* but optionally replace a piece to an invalid value for failure testing |
|
||||||
*/ |
|
||||||
public static function getAndSpliceHeader($key = null, $val = null) { |
|
||||||
$headers = static::$good_header; |
|
||||||
|
|
||||||
if (null !== $key && null !== $val) { |
|
||||||
$headers[$key] = $val; |
|
||||||
} |
|
||||||
|
|
||||||
$header = ''; |
|
||||||
foreach ($headers as $key => $val) { |
|
||||||
if (!empty($key)) { |
|
||||||
$header .= "{$key}: "; |
|
||||||
} |
|
||||||
|
|
||||||
$header .= "{$val}\r\n"; |
|
||||||
} |
|
||||||
$header .= "\r\n"; |
|
||||||
|
|
||||||
return $header; |
|
||||||
} |
|
||||||
|
|
||||||
public static function headerHandshakeProvider() { |
|
||||||
return array( |
|
||||||
array(false, "GET /test HTTP/1.0\r\n" . static::getAndSpliceHeader()) |
|
||||||
, array(true, static::$good_rest . "\r\n" . static::getAndSpliceHeader()) |
|
||||||
, array(false, "POST / HTTP:/1.1\r\n" . static::getAndSpliceHeader()) |
|
||||||
, array(false, static::$good_rest . "\r\n" . static::getAndSpliceHeader('Upgrade', 'useless')) |
|
||||||
, array(false, "GET /ಠ_ಠ HTTP/1.1\r\n" . static::getAndSpliceHeader()) |
|
||||||
, array(true, static::$good_rest . "\r\n" . static::getAndSpliceHeader('Connection', 'Herp, Upgrade, Derp')) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider headerHandshakeProvider |
|
||||||
*/ |
|
||||||
public function testVariousHeadersToCheckHandshakeTolerance($pass, $header) { |
|
||||||
$request = RequestFactory::getInstance()->fromMessage($header); |
|
||||||
$response = $this->version->handshake($request); |
|
||||||
|
|
||||||
$this->assertInstanceOf('\\Guzzle\\Http\\Message\\Response', $response); |
|
||||||
|
|
||||||
if ($pass) { |
|
||||||
$this->assertEquals(101, $response->getStatusCode()); |
|
||||||
} else { |
|
||||||
$this->assertGreaterThanOrEqual(400, $response->getStatusCode()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function testNewMessage() { |
|
||||||
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Message', $this->version->newMessage()); |
|
||||||
} |
|
||||||
|
|
||||||
public function testNewFrame() { |
|
||||||
$this->assertInstanceOf('\\Ratchet\\WebSocket\\Version\\RFC6455\\Frame', $this->version->newFrame()); |
|
||||||
} |
|
||||||
} |
|
@ -1,91 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket; |
|
||||||
use Ratchet\WebSocket\VersionManager; |
|
||||||
use Ratchet\WebSocket\Version\RFC6455; |
|
||||||
use Ratchet\WebSocket\Version\HyBi10; |
|
||||||
use Ratchet\WebSocket\Version\Hixie76; |
|
||||||
use Guzzle\Http\Message\EntityEnclosingRequest; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\VersionManager |
|
||||||
*/ |
|
||||||
class VersionManagerTest extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $vm; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->vm = new VersionManager; |
|
||||||
} |
|
||||||
|
|
||||||
public function testFluentInterface() { |
|
||||||
$rfc = new RFC6455; |
|
||||||
|
|
||||||
$this->assertSame($this->vm, $this->vm->enableVersion($rfc)); |
|
||||||
$this->assertSame($this->vm, $this->vm->disableVersion(13)); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetVersion() { |
|
||||||
$rfc = new RFC6455; |
|
||||||
$this->vm->enableVersion($rfc); |
|
||||||
|
|
||||||
$req = new EntityEnclosingRequest('get', '/', array( |
|
||||||
'Host' => 'socketo.me' |
|
||||||
, 'Sec-WebSocket-Version' => 13 |
|
||||||
)); |
|
||||||
|
|
||||||
$this->assertSame($rfc, $this->vm->getVersion($req)); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetNopeVersionAndDisable() { |
|
||||||
$req = new EntityEnclosingRequest('get', '/', array( |
|
||||||
'Host' => 'socketo.me' |
|
||||||
, 'Sec-WebSocket-Version' => 13 |
|
||||||
)); |
|
||||||
|
|
||||||
$this->setExpectedException('InvalidArgumentException'); |
|
||||||
|
|
||||||
$this->vm->getVersion($req); |
|
||||||
} |
|
||||||
|
|
||||||
public function testYesIsVersionEnabled() { |
|
||||||
$this->vm->enableVersion(new RFC6455); |
|
||||||
|
|
||||||
$this->assertTrue($this->vm->isVersionEnabled(new EntityEnclosingRequest('get', '/', array( |
|
||||||
'Host' => 'socketo.me' |
|
||||||
, 'Sec-WebSocket-Version' => 13 |
|
||||||
)))); |
|
||||||
} |
|
||||||
|
|
||||||
public function testNoIsVersionEnabled() { |
|
||||||
$this->assertFalse($this->vm->isVersionEnabled(new EntityEnclosingRequest('get', '/', array( |
|
||||||
'Host' => 'socketo.me' |
|
||||||
, 'Sec-WebSocket-Version' => 9000 |
|
||||||
)))); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetSupportedVersionString() { |
|
||||||
$v1 = new RFC6455; |
|
||||||
$v2 = new HyBi10; |
|
||||||
|
|
||||||
$this->vm->enableVersion($v1); |
|
||||||
$this->vm->enableVersion($v2); |
|
||||||
|
|
||||||
$string = $this->vm->getSupportedVersionString(); |
|
||||||
$values = explode(',', $string); |
|
||||||
|
|
||||||
$this->assertContains($v1->getVersionNumber(), $values); |
|
||||||
$this->assertContains($v2->getVersionNumber(), $values); |
|
||||||
} |
|
||||||
|
|
||||||
public function testGetSupportedVersionAfterRemoval() { |
|
||||||
$this->vm->enableVersion(new RFC6455); |
|
||||||
$this->vm->enableVersion(new HyBi10); |
|
||||||
$this->vm->enableVersion(new Hixie76); |
|
||||||
|
|
||||||
$this->vm->disableVersion(0); |
|
||||||
|
|
||||||
$values = explode(',', $this->vm->getSupportedVersionString()); |
|
||||||
|
|
||||||
$this->assertEquals(2, count($values)); |
|
||||||
$this->assertFalse(array_search(0, $values)); |
|
||||||
} |
|
||||||
} |
|
@ -1,51 +0,0 @@ |
|||||||
<?php |
|
||||||
namespace Ratchet\WebSocket; |
|
||||||
use Ratchet\WebSocket\WsServer; |
|
||||||
use Ratchet\Mock\Component as MockComponent; |
|
||||||
|
|
||||||
/** |
|
||||||
* @covers Ratchet\WebSocket\WsServer |
|
||||||
* @covers Ratchet\ComponentInterface |
|
||||||
* @covers Ratchet\MessageComponentInterface |
|
||||||
*/ |
|
||||||
class WsServerTest extends \PHPUnit_Framework_TestCase { |
|
||||||
protected $comp; |
|
||||||
|
|
||||||
protected $serv; |
|
||||||
|
|
||||||
public function setUp() { |
|
||||||
$this->comp = new MockComponent; |
|
||||||
$this->serv = new WsServer($this->comp); |
|
||||||
} |
|
||||||
|
|
||||||
public function testIsSubProtocolSupported() { |
|
||||||
$this->comp->protocols = array('hello', 'world'); |
|
||||||
|
|
||||||
$this->assertTrue($this->serv->isSubProtocolSupported('hello')); |
|
||||||
$this->assertFalse($this->serv->isSubProtocolSupported('nope')); |
|
||||||
} |
|
||||||
|
|
||||||
public function protocolProvider() { |
|
||||||
return array( |
|
||||||
array('hello', array('hello', 'world'), array('hello', 'world')) |
|
||||||
, array('', array('hello', 'world'), array('wamp')) |
|
||||||
, array('', array(), null) |
|
||||||
, array('wamp', array('hello', 'wamp', 'world'), array('herp', 'derp', 'wamp')) |
|
||||||
, array('wamp', array('wamp'), array('wamp')) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @dataProvider protocolProvider |
|
||||||
*/ |
|
||||||
public function testGetSubProtocolString($expected, $supported, $requested) { |
|
||||||
$this->comp->protocols = $supported; |
|
||||||
$req = (null === $requested ? $requested : new \ArrayIterator($requested)); |
|
||||||
|
|
||||||
$class = new \ReflectionClass('Ratchet\\WebSocket\\WsServer'); |
|
||||||
$method = $class->getMethod('getSubProtocolString'); |
|
||||||
$method->setAccessible(true); |
|
||||||
|
|
||||||
$this->assertSame($expected, $method->invokeArgs($this->serv, array($req))); |
|
||||||
} |
|
||||||
} |
|
@ -1,49 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher; |
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Class that holds an event dispatcher |
|
||||||
*/ |
|
||||||
class AbstractHasDispatcher implements HasDispatcherInterface |
|
||||||
{ |
|
||||||
/** @var EventDispatcherInterface */ |
|
||||||
protected $eventDispatcher; |
|
||||||
|
|
||||||
public static function getAllEvents() |
|
||||||
{ |
|
||||||
return array(); |
|
||||||
} |
|
||||||
|
|
||||||
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) |
|
||||||
{ |
|
||||||
$this->eventDispatcher = $eventDispatcher; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getEventDispatcher() |
|
||||||
{ |
|
||||||
if (!$this->eventDispatcher) { |
|
||||||
$this->eventDispatcher = new EventDispatcher(); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->eventDispatcher; |
|
||||||
} |
|
||||||
|
|
||||||
public function dispatch($eventName, array $context = array()) |
|
||||||
{ |
|
||||||
return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); |
|
||||||
} |
|
||||||
|
|
||||||
public function addSubscriber(EventSubscriberInterface $subscriber) |
|
||||||
{ |
|
||||||
$this->getEventDispatcher()->addSubscriber($subscriber); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
} |
|
@ -1,403 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\InvalidArgumentException; |
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Key value pair collection object |
|
||||||
*/ |
|
||||||
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface |
|
||||||
{ |
|
||||||
/** @var array Data associated with the object. */ |
|
||||||
protected $data; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param array $data Associative array of data to set |
|
||||||
*/ |
|
||||||
public function __construct(array $data = array()) |
|
||||||
{ |
|
||||||
$this->data = $data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a new collection from an array, validate the keys, and add default values where missing |
|
||||||
* |
|
||||||
* @param array $config Configuration values to apply. |
|
||||||
* @param array $defaults Default parameters |
|
||||||
* @param array $required Required parameter names |
|
||||||
* |
|
||||||
* @return self |
|
||||||
* @throws InvalidArgumentException if a parameter is missing |
|
||||||
*/ |
|
||||||
public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) |
|
||||||
{ |
|
||||||
$data = $config + $defaults; |
|
||||||
|
|
||||||
if ($missing = array_diff($required, array_keys($data))) { |
|
||||||
throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); |
|
||||||
} |
|
||||||
|
|
||||||
return new self($data); |
|
||||||
} |
|
||||||
|
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->data); |
|
||||||
} |
|
||||||
|
|
||||||
public function getIterator() |
|
||||||
{ |
|
||||||
return new \ArrayIterator($this->data); |
|
||||||
} |
|
||||||
|
|
||||||
public function toArray() |
|
||||||
{ |
|
||||||
return $this->data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Removes all key value pairs |
|
||||||
* |
|
||||||
* @return Collection |
|
||||||
*/ |
|
||||||
public function clear() |
|
||||||
{ |
|
||||||
$this->data = array(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all or a subset of matching key value pairs |
|
||||||
* |
|
||||||
* @param array $keys Pass an array of keys to retrieve only a subset of key value pairs |
|
||||||
* |
|
||||||
* @return array Returns an array of all matching key value pairs |
|
||||||
*/ |
|
||||||
public function getAll(array $keys = null) |
|
||||||
{ |
|
||||||
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a specific key value. |
|
||||||
* |
|
||||||
* @param string $key Key to retrieve. |
|
||||||
* |
|
||||||
* @return mixed|null Value of the key or NULL |
|
||||||
*/ |
|
||||||
public function get($key) |
|
||||||
{ |
|
||||||
return isset($this->data[$key]) ? $this->data[$key] : null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a key value pair |
|
||||||
* |
|
||||||
* @param string $key Key to set |
|
||||||
* @param mixed $value Value to set |
|
||||||
* |
|
||||||
* @return Collection Returns a reference to the object |
|
||||||
*/ |
|
||||||
public function set($key, $value) |
|
||||||
{ |
|
||||||
$this->data[$key] = $value; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add a value to a key. If a key of the same name has already been added, the key value will be converted into an |
|
||||||
* array and the new value will be pushed to the end of the array. |
|
||||||
* |
|
||||||
* @param string $key Key to add |
|
||||||
* @param mixed $value Value to add to the key |
|
||||||
* |
|
||||||
* @return Collection Returns a reference to the object. |
|
||||||
*/ |
|
||||||
public function add($key, $value) |
|
||||||
{ |
|
||||||
if (!array_key_exists($key, $this->data)) { |
|
||||||
$this->data[$key] = $value; |
|
||||||
} elseif (is_array($this->data[$key])) { |
|
||||||
$this->data[$key][] = $value; |
|
||||||
} else { |
|
||||||
$this->data[$key] = array($this->data[$key], $value); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a specific key value pair |
|
||||||
* |
|
||||||
* @param string $key A key to remove |
|
||||||
* |
|
||||||
* @return Collection |
|
||||||
*/ |
|
||||||
public function remove($key) |
|
||||||
{ |
|
||||||
unset($this->data[$key]); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all keys in the collection |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getKeys() |
|
||||||
{ |
|
||||||
return array_keys($this->data); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns whether or not the specified key is present. |
|
||||||
* |
|
||||||
* @param string $key The key for which to check the existence. |
|
||||||
* |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function hasKey($key) |
|
||||||
{ |
|
||||||
return array_key_exists($key, $this->data); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Case insensitive search the keys in the collection |
|
||||||
* |
|
||||||
* @param string $key Key to search for |
|
||||||
* |
|
||||||
* @return bool|string Returns false if not found, otherwise returns the key |
|
||||||
*/ |
|
||||||
public function keySearch($key) |
|
||||||
{ |
|
||||||
foreach (array_keys($this->data) as $k) { |
|
||||||
if (!strcasecmp($k, $key)) { |
|
||||||
return $k; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Checks if any keys contains a certain value |
|
||||||
* |
|
||||||
* @param string $value Value to search for |
|
||||||
* |
|
||||||
* @return mixed Returns the key if the value was found FALSE if the value was not found. |
|
||||||
*/ |
|
||||||
public function hasValue($value) |
|
||||||
{ |
|
||||||
return array_search($value, $this->data); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Replace the data of the object with the value of an array |
|
||||||
* |
|
||||||
* @param array $data Associative array of data |
|
||||||
* |
|
||||||
* @return Collection Returns a reference to the object |
|
||||||
*/ |
|
||||||
public function replace(array $data) |
|
||||||
{ |
|
||||||
$this->data = $data; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add and merge in a Collection or array of key value pair data. |
|
||||||
* |
|
||||||
* @param Collection|array $data Associative array of key value pair data |
|
||||||
* |
|
||||||
* @return Collection Returns a reference to the object. |
|
||||||
*/ |
|
||||||
public function merge($data) |
|
||||||
{ |
|
||||||
foreach ($data as $key => $value) { |
|
||||||
$this->add($key, $value); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Over write key value pairs in this collection with all of the data from an array or collection. |
|
||||||
* |
|
||||||
* @param array|\Traversable $data Values to override over this config |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function overwriteWith($data) |
|
||||||
{ |
|
||||||
if (is_array($data)) { |
|
||||||
$this->data = $data + $this->data; |
|
||||||
} elseif ($data instanceof Collection) { |
|
||||||
$this->data = $data->toArray() + $this->data; |
|
||||||
} else { |
|
||||||
foreach ($data as $key => $value) { |
|
||||||
$this->data[$key] = $value; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns a Collection containing all the elements of the collection after applying the callback function to each |
|
||||||
* one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a |
|
||||||
* modified value |
|
||||||
* |
|
||||||
* @param \Closure $closure Closure to apply |
|
||||||
* @param array $context Context to pass to the closure |
|
||||||
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection |
|
||||||
* |
|
||||||
* @return Collection |
|
||||||
*/ |
|
||||||
public function map(\Closure $closure, array $context = array(), $static = true) |
|
||||||
{ |
|
||||||
$collection = $static ? new static() : new self(); |
|
||||||
foreach ($this as $key => $value) { |
|
||||||
$collection->add($key, $closure($key, $value, $context)); |
|
||||||
} |
|
||||||
|
|
||||||
return $collection; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns |
|
||||||
* true, the current value from input is returned into the result Collection. The Closure must accept three |
|
||||||
* parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. |
|
||||||
* |
|
||||||
* @param \Closure $closure Closure evaluation function |
|
||||||
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection |
|
||||||
* |
|
||||||
* @return Collection |
|
||||||
*/ |
|
||||||
public function filter(\Closure $closure, $static = true) |
|
||||||
{ |
|
||||||
$collection = ($static) ? new static() : new self(); |
|
||||||
foreach ($this->data as $key => $value) { |
|
||||||
if ($closure($key, $value)) { |
|
||||||
$collection->add($key, $value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $collection; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetExists($offset) |
|
||||||
{ |
|
||||||
return isset($this->data[$offset]); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetGet($offset) |
|
||||||
{ |
|
||||||
return isset($this->data[$offset]) ? $this->data[$offset] : null; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetSet($offset, $value) |
|
||||||
{ |
|
||||||
$this->data[$offset] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetUnset($offset) |
|
||||||
{ |
|
||||||
unset($this->data[$offset]); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a value into a nested array key. Keys will be created as needed to set the value. |
|
||||||
* |
|
||||||
* @param string $path Path to set |
|
||||||
* @param mixed $value Value to set at the key |
|
||||||
* |
|
||||||
* @return self |
|
||||||
* @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value |
|
||||||
*/ |
|
||||||
public function setPath($path, $value) |
|
||||||
{ |
|
||||||
$current =& $this->data; |
|
||||||
$queue = explode('/', $path); |
|
||||||
while (null !== ($key = array_shift($queue))) { |
|
||||||
if (!is_array($current)) { |
|
||||||
throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); |
|
||||||
} elseif (!$queue) { |
|
||||||
$current[$key] = $value; |
|
||||||
} elseif (isset($current[$key])) { |
|
||||||
$current =& $current[$key]; |
|
||||||
} else { |
|
||||||
$current[$key] = array(); |
|
||||||
$current =& $current[$key]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) |
|
||||||
* Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This |
|
||||||
* can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. |
|
||||||
* |
|
||||||
* @param string $path Path to traverse and retrieve a value from |
|
||||||
* @param string $separator Character used to add depth to the search |
|
||||||
* @param mixed $data Optional data to descend into (used when wildcards are encountered) |
|
||||||
* |
|
||||||
* @return mixed|null |
|
||||||
*/ |
|
||||||
public function getPath($path, $separator = '/', $data = null) |
|
||||||
{ |
|
||||||
if ($data === null) { |
|
||||||
$data =& $this->data; |
|
||||||
} |
|
||||||
|
|
||||||
$path = is_array($path) ? $path : explode($separator, $path); |
|
||||||
while (null !== ($part = array_shift($path))) { |
|
||||||
if (!is_array($data)) { |
|
||||||
return null; |
|
||||||
} elseif (isset($data[$part])) { |
|
||||||
$data =& $data[$part]; |
|
||||||
} elseif ($part != '*') { |
|
||||||
return null; |
|
||||||
} else { |
|
||||||
// Perform a wildcard search by diverging and merging paths |
|
||||||
$result = array(); |
|
||||||
foreach ($data as $value) { |
|
||||||
if (!$path) { |
|
||||||
$result = array_merge_recursive($result, (array) $value); |
|
||||||
} elseif (null !== ($test = $this->getPath($path, $separator, $value))) { |
|
||||||
$result = array_merge_recursive($result, (array) $test); |
|
||||||
} |
|
||||||
} |
|
||||||
return $result; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Inject configuration settings into an input string |
|
||||||
* |
|
||||||
* @param string $input Input to inject |
|
||||||
* |
|
||||||
* @return string |
|
||||||
* @deprecated |
|
||||||
*/ |
|
||||||
public function inject($input) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated'); |
|
||||||
$replace = array(); |
|
||||||
foreach ($this->data as $key => $val) { |
|
||||||
$replace['{' . $key . '}'] = $val; |
|
||||||
} |
|
||||||
|
|
||||||
return strtr($input, $replace); |
|
||||||
} |
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
use Symfony\Component\EventDispatcher\Event as SymfonyEvent; |
|
||||||
|
|
||||||
/** |
|
||||||
* Default event for Guzzle notifications |
|
||||||
*/ |
|
||||||
class Event extends SymfonyEvent implements ToArrayInterface, \ArrayAccess, \IteratorAggregate |
|
||||||
{ |
|
||||||
/** @var array */ |
|
||||||
private $context; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param array $context Contextual information |
|
||||||
*/ |
|
||||||
public function __construct(array $context = array()) |
|
||||||
{ |
|
||||||
$this->context = $context; |
|
||||||
} |
|
||||||
|
|
||||||
public function getIterator() |
|
||||||
{ |
|
||||||
return new \ArrayIterator($this->context); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetGet($offset) |
|
||||||
{ |
|
||||||
return isset($this->context[$offset]) ? $this->context[$offset] : null; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetSet($offset, $value) |
|
||||||
{ |
|
||||||
$this->context[$offset] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetExists($offset) |
|
||||||
{ |
|
||||||
return isset($this->context[$offset]); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetUnset($offset) |
|
||||||
{ |
|
||||||
unset($this->context[$offset]); |
|
||||||
} |
|
||||||
|
|
||||||
public function toArray() |
|
||||||
{ |
|
||||||
return $this->context; |
|
||||||
} |
|
||||||
} |
|
@ -1,5 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
class BadMethodCallException extends \BadMethodCallException implements GuzzleException {} |
|
@ -1,108 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
/** |
|
||||||
* Collection of exceptions |
|
||||||
*/ |
|
||||||
class ExceptionCollection extends \Exception implements GuzzleException, \IteratorAggregate, \Countable |
|
||||||
{ |
|
||||||
/** @var array Array of Exceptions */ |
|
||||||
protected $exceptions = array(); |
|
||||||
|
|
||||||
/** @var string Succinct exception message not including sub-exceptions */ |
|
||||||
private $shortMessage; |
|
||||||
|
|
||||||
public function __construct($message = '', $code = 0, \Exception $previous = null) |
|
||||||
{ |
|
||||||
parent::__construct($message, $code, $previous); |
|
||||||
$this->shortMessage = $message; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set all of the exceptions |
|
||||||
* |
|
||||||
* @param array $exceptions Array of exceptions |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setExceptions(array $exceptions) |
|
||||||
{ |
|
||||||
$this->exceptions = array(); |
|
||||||
foreach ($exceptions as $exception) { |
|
||||||
$this->add($exception); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add exceptions to the collection |
|
||||||
* |
|
||||||
* @param ExceptionCollection|\Exception $e Exception to add |
|
||||||
* |
|
||||||
* @return ExceptionCollection; |
|
||||||
*/ |
|
||||||
public function add($e) |
|
||||||
{ |
|
||||||
$this->exceptions[] = $e; |
|
||||||
if ($this->message) { |
|
||||||
$this->message .= "\n"; |
|
||||||
} |
|
||||||
|
|
||||||
$this->message .= $this->getExceptionMessage($e, 0); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the total number of request exceptions |
|
||||||
* |
|
||||||
* @return int |
|
||||||
*/ |
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->exceptions); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Allows array-like iteration over the request exceptions |
|
||||||
* |
|
||||||
* @return \ArrayIterator |
|
||||||
*/ |
|
||||||
public function getIterator() |
|
||||||
{ |
|
||||||
return new \ArrayIterator($this->exceptions); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the first exception in the collection |
|
||||||
* |
|
||||||
* @return \Exception |
|
||||||
*/ |
|
||||||
public function getFirst() |
|
||||||
{ |
|
||||||
return $this->exceptions ? $this->exceptions[0] : null; |
|
||||||
} |
|
||||||
|
|
||||||
private function getExceptionMessage(\Exception $e, $depth = 0) |
|
||||||
{ |
|
||||||
static $sp = ' '; |
|
||||||
$prefix = $depth ? str_repeat($sp, $depth) : ''; |
|
||||||
$message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; |
|
||||||
|
|
||||||
if ($e instanceof self) { |
|
||||||
if ($e->shortMessage) { |
|
||||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; |
|
||||||
} |
|
||||||
foreach ($e as $ee) { |
|
||||||
$message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); |
|
||||||
} |
|
||||||
} else { |
|
||||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; |
|
||||||
$message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; |
|
||||||
} |
|
||||||
|
|
||||||
return str_replace(getcwd(), '.', $message); |
|
||||||
} |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
/** |
|
||||||
* Guzzle exception |
|
||||||
*/ |
|
||||||
interface GuzzleException {} |
|
@ -1,5 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {} |
|
@ -1,5 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
class RuntimeException extends \RuntimeException implements GuzzleException {} |
|
@ -1,5 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common\Exception; |
|
||||||
|
|
||||||
class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {} |
|
@ -1,18 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
/** |
|
||||||
* Interfaces that adds a factory method which is used to instantiate a class from an array of configuration options. |
|
||||||
*/ |
|
||||||
interface FromConfigInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Static factory method used to turn an array or collection of configuration data into an instantiated object. |
|
||||||
* |
|
||||||
* @param array|Collection $config Configuration data |
|
||||||
* |
|
||||||
* @return FromConfigInterface |
|
||||||
*/ |
|
||||||
public static function factory($config = array()); |
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Holds an event dispatcher |
|
||||||
*/ |
|
||||||
interface HasDispatcherInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Get a list of all of the events emitted from the class |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public static function getAllEvents(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the EventDispatcher of the request |
|
||||||
* |
|
||||||
* @param EventDispatcherInterface $eventDispatcher |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the EventDispatcher of the request |
|
||||||
* |
|
||||||
* @return EventDispatcherInterface |
|
||||||
*/ |
|
||||||
public function getEventDispatcher(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Helper to dispatch Guzzle events and set the event name on the event |
|
||||||
* |
|
||||||
* @param string $eventName Name of the event to dispatch |
|
||||||
* @param array $context Context of the event |
|
||||||
* |
|
||||||
* @return Event Returns the created event object |
|
||||||
*/ |
|
||||||
public function dispatch($eventName, array $context = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Add an event subscriber to the dispatcher |
|
||||||
* |
|
||||||
* @param EventSubscriberInterface $subscriber Event subscriber |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addSubscriber(EventSubscriberInterface $subscriber); |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
/** |
|
||||||
* An object that can be represented as an array |
|
||||||
*/ |
|
||||||
interface ToArrayInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Get the array representation of an object |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function toArray(); |
|
||||||
} |
|
@ -1,29 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Common; |
|
||||||
|
|
||||||
/** |
|
||||||
* Guzzle version information |
|
||||||
*/ |
|
||||||
class Version |
|
||||||
{ |
|
||||||
const VERSION = '3.9.2'; |
|
||||||
|
|
||||||
/** |
|
||||||
* @var bool Set this value to true to enable warnings for deprecated functionality use. This should be on in your |
|
||||||
* unit tests, but probably not in production. |
|
||||||
*/ |
|
||||||
public static $emitWarnings = false; |
|
||||||
|
|
||||||
/** |
|
||||||
* Emit a deprecation warning |
|
||||||
* |
|
||||||
* @param string $message Warning message |
|
||||||
*/ |
|
||||||
public static function warn($message) |
|
||||||
{ |
|
||||||
if (self::$emitWarnings) { |
|
||||||
trigger_error('Deprecation warning: ' . $message, E_USER_DEPRECATED); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
{ |
|
||||||
"name": "guzzle/common", |
|
||||||
"homepage": "http://guzzlephp.org/", |
|
||||||
"description": "Common libraries used by Guzzle", |
|
||||||
"keywords": ["common", "event", "exception", "collection"], |
|
||||||
"license": "MIT", |
|
||||||
"require": { |
|
||||||
"php": ">=5.3.2", |
|
||||||
"symfony/event-dispatcher": ">=2.1" |
|
||||||
}, |
|
||||||
"autoload": { |
|
||||||
"psr-0": { "Guzzle\\Common": "" } |
|
||||||
}, |
|
||||||
"target-dir": "Guzzle/Common", |
|
||||||
"extra": { |
|
||||||
"branch-alias": { |
|
||||||
"dev-master": "3.7-dev" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,221 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Stream\Stream; |
|
||||||
|
|
||||||
/** |
|
||||||
* Abstract decorator used to wrap entity bodies |
|
||||||
*/ |
|
||||||
class AbstractEntityBodyDecorator implements EntityBodyInterface |
|
||||||
{ |
|
||||||
/** @var EntityBodyInterface Decorated entity body */ |
|
||||||
protected $body; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param EntityBodyInterface $body Entity body to decorate |
|
||||||
*/ |
|
||||||
public function __construct(EntityBodyInterface $body) |
|
||||||
{ |
|
||||||
$this->body = $body; |
|
||||||
} |
|
||||||
|
|
||||||
public function __toString() |
|
||||||
{ |
|
||||||
return (string) $this->body; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Allow decorators to implement custom methods |
|
||||||
* |
|
||||||
* @param string $method Missing method name |
|
||||||
* @param array $args Method arguments |
|
||||||
* |
|
||||||
* @return mixed |
|
||||||
*/ |
|
||||||
public function __call($method, array $args) |
|
||||||
{ |
|
||||||
return call_user_func_array(array($this->body, $method), $args); |
|
||||||
} |
|
||||||
|
|
||||||
public function close() |
|
||||||
{ |
|
||||||
return $this->body->close(); |
|
||||||
} |
|
||||||
|
|
||||||
public function setRewindFunction($callable) |
|
||||||
{ |
|
||||||
$this->body->setRewindFunction($callable); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function rewind() |
|
||||||
{ |
|
||||||
return $this->body->rewind(); |
|
||||||
} |
|
||||||
|
|
||||||
public function compress($filter = 'zlib.deflate') |
|
||||||
{ |
|
||||||
return $this->body->compress($filter); |
|
||||||
} |
|
||||||
|
|
||||||
public function uncompress($filter = 'zlib.inflate') |
|
||||||
{ |
|
||||||
return $this->body->uncompress($filter); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentLength() |
|
||||||
{ |
|
||||||
return $this->getSize(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentType() |
|
||||||
{ |
|
||||||
return $this->body->getContentType(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentMd5($rawOutput = false, $base64Encode = false) |
|
||||||
{ |
|
||||||
$hash = Stream::getHash($this, 'md5', $rawOutput); |
|
||||||
|
|
||||||
return $hash && $base64Encode ? base64_encode($hash) : $hash; |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentEncoding() |
|
||||||
{ |
|
||||||
return $this->body->getContentEncoding(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getMetaData($key = null) |
|
||||||
{ |
|
||||||
return $this->body->getMetaData($key); |
|
||||||
} |
|
||||||
|
|
||||||
public function getStream() |
|
||||||
{ |
|
||||||
return $this->body->getStream(); |
|
||||||
} |
|
||||||
|
|
||||||
public function setStream($stream, $size = 0) |
|
||||||
{ |
|
||||||
$this->body->setStream($stream, $size); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function detachStream() |
|
||||||
{ |
|
||||||
$this->body->detachStream(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getWrapper() |
|
||||||
{ |
|
||||||
return $this->body->getWrapper(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getWrapperData() |
|
||||||
{ |
|
||||||
return $this->body->getWrapperData(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getStreamType() |
|
||||||
{ |
|
||||||
return $this->body->getStreamType(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getUri() |
|
||||||
{ |
|
||||||
return $this->body->getUri(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getSize() |
|
||||||
{ |
|
||||||
return $this->body->getSize(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isReadable() |
|
||||||
{ |
|
||||||
return $this->body->isReadable(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isRepeatable() |
|
||||||
{ |
|
||||||
return $this->isSeekable() && $this->isReadable(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isWritable() |
|
||||||
{ |
|
||||||
return $this->body->isWritable(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isConsumed() |
|
||||||
{ |
|
||||||
return $this->body->isConsumed(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Alias of isConsumed() |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function feof() |
|
||||||
{ |
|
||||||
return $this->isConsumed(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isLocal() |
|
||||||
{ |
|
||||||
return $this->body->isLocal(); |
|
||||||
} |
|
||||||
|
|
||||||
public function isSeekable() |
|
||||||
{ |
|
||||||
return $this->body->isSeekable(); |
|
||||||
} |
|
||||||
|
|
||||||
public function setSize($size) |
|
||||||
{ |
|
||||||
$this->body->setSize($size); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function seek($offset, $whence = SEEK_SET) |
|
||||||
{ |
|
||||||
return $this->body->seek($offset, $whence); |
|
||||||
} |
|
||||||
|
|
||||||
public function read($length) |
|
||||||
{ |
|
||||||
return $this->body->read($length); |
|
||||||
} |
|
||||||
|
|
||||||
public function write($string) |
|
||||||
{ |
|
||||||
return $this->body->write($string); |
|
||||||
} |
|
||||||
|
|
||||||
public function readLine($maxLength = null) |
|
||||||
{ |
|
||||||
return $this->body->readLine($maxLength); |
|
||||||
} |
|
||||||
|
|
||||||
public function ftell() |
|
||||||
{ |
|
||||||
return $this->body->ftell(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getCustomData($key) |
|
||||||
{ |
|
||||||
return $this->body->getCustomData($key); |
|
||||||
} |
|
||||||
|
|
||||||
public function setCustomData($key, $value) |
|
||||||
{ |
|
||||||
$this->body->setCustomData($key, $value); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
} |
|
@ -1,229 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
|
|
||||||
/** |
|
||||||
* EntityBody decorator that can cache previously read bytes from a sequentially read tstream |
|
||||||
*/ |
|
||||||
class CachingEntityBody extends AbstractEntityBodyDecorator |
|
||||||
{ |
|
||||||
/** @var EntityBody Remote stream used to actually pull data onto the buffer */ |
|
||||||
protected $remoteStream; |
|
||||||
|
|
||||||
/** @var int The number of bytes to skip reading due to a write on the temporary buffer */ |
|
||||||
protected $skipReadBytes = 0; |
|
||||||
|
|
||||||
/** |
|
||||||
* We will treat the buffer object as the body of the entity body |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function __construct(EntityBodyInterface $body) |
|
||||||
{ |
|
||||||
$this->remoteStream = $body; |
|
||||||
$this->body = new EntityBody(fopen('php://temp', 'r+')); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Will give the contents of the buffer followed by the exhausted remote stream. |
|
||||||
* |
|
||||||
* Warning: Loads the entire stream into memory |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function __toString() |
|
||||||
{ |
|
||||||
$pos = $this->ftell(); |
|
||||||
$this->rewind(); |
|
||||||
|
|
||||||
$str = ''; |
|
||||||
while (!$this->isConsumed()) { |
|
||||||
$str .= $this->read(16384); |
|
||||||
} |
|
||||||
|
|
||||||
$this->seek($pos); |
|
||||||
|
|
||||||
return $str; |
|
||||||
} |
|
||||||
|
|
||||||
public function getSize() |
|
||||||
{ |
|
||||||
return max($this->body->getSize(), $this->remoteStream->getSize()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
* @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream |
|
||||||
*/ |
|
||||||
public function seek($offset, $whence = SEEK_SET) |
|
||||||
{ |
|
||||||
if ($whence == SEEK_SET) { |
|
||||||
$byte = $offset; |
|
||||||
} elseif ($whence == SEEK_CUR) { |
|
||||||
$byte = $offset + $this->ftell(); |
|
||||||
} else { |
|
||||||
throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); |
|
||||||
} |
|
||||||
|
|
||||||
// You cannot skip ahead past where you've read from the remote stream |
|
||||||
if ($byte > $this->body->getSize()) { |
|
||||||
throw new RuntimeException( |
|
||||||
"Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->body->seek($byte); |
|
||||||
} |
|
||||||
|
|
||||||
public function rewind() |
|
||||||
{ |
|
||||||
return $this->seek(0); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Does not support custom rewind functions |
|
||||||
* |
|
||||||
* @throws RuntimeException |
|
||||||
*/ |
|
||||||
public function setRewindFunction($callable) |
|
||||||
{ |
|
||||||
throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); |
|
||||||
} |
|
||||||
|
|
||||||
public function read($length) |
|
||||||
{ |
|
||||||
// Perform a regular read on any previously read data from the buffer |
|
||||||
$data = $this->body->read($length); |
|
||||||
$remaining = $length - strlen($data); |
|
||||||
|
|
||||||
// More data was requested so read from the remote stream |
|
||||||
if ($remaining) { |
|
||||||
// If data was written to the buffer in a position that would have been filled from the remote stream, |
|
||||||
// then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This |
|
||||||
// mimics the behavior of other PHP stream wrappers. |
|
||||||
$remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); |
|
||||||
|
|
||||||
if ($this->skipReadBytes) { |
|
||||||
$len = strlen($remoteData); |
|
||||||
$remoteData = substr($remoteData, $this->skipReadBytes); |
|
||||||
$this->skipReadBytes = max(0, $this->skipReadBytes - $len); |
|
||||||
} |
|
||||||
|
|
||||||
$data .= $remoteData; |
|
||||||
$this->body->write($remoteData); |
|
||||||
} |
|
||||||
|
|
||||||
return $data; |
|
||||||
} |
|
||||||
|
|
||||||
public function write($string) |
|
||||||
{ |
|
||||||
// When appending to the end of the currently read stream, you'll want to skip bytes from being read from |
|
||||||
// the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. |
|
||||||
$overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); |
|
||||||
if ($overflow > 0) { |
|
||||||
$this->skipReadBytes += $overflow; |
|
||||||
} |
|
||||||
|
|
||||||
return $this->body->write($string); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
* @link http://php.net/manual/en/function.fgets.php |
|
||||||
*/ |
|
||||||
public function readLine($maxLength = null) |
|
||||||
{ |
|
||||||
$buffer = ''; |
|
||||||
$size = 0; |
|
||||||
while (!$this->isConsumed()) { |
|
||||||
$byte = $this->read(1); |
|
||||||
$buffer .= $byte; |
|
||||||
// Break when a new line is found or the max length - 1 is reached |
|
||||||
if ($byte == PHP_EOL || ++$size == $maxLength - 1) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $buffer; |
|
||||||
} |
|
||||||
|
|
||||||
public function isConsumed() |
|
||||||
{ |
|
||||||
return $this->body->isConsumed() && $this->remoteStream->isConsumed(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Close both the remote stream and buffer stream |
|
||||||
*/ |
|
||||||
public function close() |
|
||||||
{ |
|
||||||
return $this->remoteStream->close() && $this->body->close(); |
|
||||||
} |
|
||||||
|
|
||||||
public function setStream($stream, $size = 0) |
|
||||||
{ |
|
||||||
$this->remoteStream->setStream($stream, $size); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentType() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getContentType(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentEncoding() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getContentEncoding(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getMetaData($key = null) |
|
||||||
{ |
|
||||||
return $this->remoteStream->getMetaData($key); |
|
||||||
} |
|
||||||
|
|
||||||
public function getStream() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getStream(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getWrapper() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getWrapper(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getWrapperData() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getWrapperData(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getStreamType() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getStreamType(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getUri() |
|
||||||
{ |
|
||||||
return $this->remoteStream->getUri(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Always retrieve custom data from the remote stream |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function getCustomData($key) |
|
||||||
{ |
|
||||||
return $this->remoteStream->getCustomData($key); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Always set custom data on the remote stream |
|
||||||
* {@inheritdoc} |
|
||||||
*/ |
|
||||||
public function setCustomData($key, $value) |
|
||||||
{ |
|
||||||
$this->remoteStream->setCustomData($key, $value); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
} |
|
@ -1,524 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Common\Collection; |
|
||||||
use Guzzle\Common\AbstractHasDispatcher; |
|
||||||
use Guzzle\Common\Exception\ExceptionCollection; |
|
||||||
use Guzzle\Common\Exception\InvalidArgumentException; |
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
use Guzzle\Common\Version; |
|
||||||
use Guzzle\Parser\ParserRegistry; |
|
||||||
use Guzzle\Parser\UriTemplate\UriTemplateInterface; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\Message\RequestFactory; |
|
||||||
use Guzzle\Http\Message\RequestFactoryInterface; |
|
||||||
use Guzzle\Http\Curl\CurlMultiInterface; |
|
||||||
use Guzzle\Http\Curl\CurlMultiProxy; |
|
||||||
use Guzzle\Http\Curl\CurlHandle; |
|
||||||
use Guzzle\Http\Curl\CurlVersion; |
|
||||||
|
|
||||||
/** |
|
||||||
* HTTP client |
|
||||||
*/ |
|
||||||
class Client extends AbstractHasDispatcher implements ClientInterface |
|
||||||
{ |
|
||||||
/** @deprecated Use [request.options][params] */ |
|
||||||
const REQUEST_PARAMS = 'request.params'; |
|
||||||
|
|
||||||
const REQUEST_OPTIONS = 'request.options'; |
|
||||||
const CURL_OPTIONS = 'curl.options'; |
|
||||||
const SSL_CERT_AUTHORITY = 'ssl.certificate_authority'; |
|
||||||
const DISABLE_REDIRECTS = RedirectPlugin::DISABLE; |
|
||||||
const DEFAULT_SELECT_TIMEOUT = 1.0; |
|
||||||
const MAX_HANDLES = 3; |
|
||||||
|
|
||||||
/** @var Collection Default HTTP headers to set on each request */ |
|
||||||
protected $defaultHeaders; |
|
||||||
|
|
||||||
/** @var string The user agent string to set on each request */ |
|
||||||
protected $userAgent; |
|
||||||
|
|
||||||
/** @var Collection Parameter object holding configuration data */ |
|
||||||
private $config; |
|
||||||
|
|
||||||
/** @var Url Base URL of the client */ |
|
||||||
private $baseUrl; |
|
||||||
|
|
||||||
/** @var CurlMultiInterface CurlMulti object used internally */ |
|
||||||
private $curlMulti; |
|
||||||
|
|
||||||
/** @var UriTemplateInterface URI template owned by the client */ |
|
||||||
private $uriTemplate; |
|
||||||
|
|
||||||
/** @var RequestFactoryInterface Request factory used by the client */ |
|
||||||
protected $requestFactory; |
|
||||||
|
|
||||||
public static function getAllEvents() |
|
||||||
{ |
|
||||||
return array(self::CREATE_REQUEST); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string $baseUrl Base URL of the web service |
|
||||||
* @param array|Collection $config Configuration settings |
|
||||||
* |
|
||||||
* @throws RuntimeException if cURL is not installed |
|
||||||
*/ |
|
||||||
public function __construct($baseUrl = '', $config = null) |
|
||||||
{ |
|
||||||
if (!extension_loaded('curl')) { |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.'); |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
} |
|
||||||
$this->setConfig($config ?: new Collection()); |
|
||||||
$this->initSsl(); |
|
||||||
$this->setBaseUrl($baseUrl); |
|
||||||
$this->defaultHeaders = new Collection(); |
|
||||||
$this->setRequestFactory(RequestFactory::getInstance()); |
|
||||||
$this->userAgent = $this->getDefaultUserAgent(); |
|
||||||
if (!$this->config[self::DISABLE_REDIRECTS]) { |
|
||||||
$this->addSubscriber(new RedirectPlugin()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
final public function setConfig($config) |
|
||||||
{ |
|
||||||
if ($config instanceof Collection) { |
|
||||||
$this->config = $config; |
|
||||||
} elseif (is_array($config)) { |
|
||||||
$this->config = new Collection($config); |
|
||||||
} else { |
|
||||||
throw new InvalidArgumentException('Config must be an array or Collection'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
final public function getConfig($key = false) |
|
||||||
{ |
|
||||||
return $key ? $this->config[$key] : $this->config; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a default request option on the client that will be used as a default for each request |
|
||||||
* |
|
||||||
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) |
|
||||||
* @param mixed $value Value to set |
|
||||||
* |
|
||||||
* @return $this |
|
||||||
*/ |
|
||||||
public function setDefaultOption($keyOrPath, $value) |
|
||||||
{ |
|
||||||
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; |
|
||||||
$this->config->setPath($keyOrPath, $value); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Retrieve a default request option from the client |
|
||||||
* |
|
||||||
* @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) |
|
||||||
* |
|
||||||
* @return mixed|null |
|
||||||
*/ |
|
||||||
public function getDefaultOption($keyOrPath) |
|
||||||
{ |
|
||||||
$keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; |
|
||||||
|
|
||||||
return $this->config->getPath($keyOrPath); |
|
||||||
} |
|
||||||
|
|
||||||
final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) |
|
||||||
{ |
|
||||||
$opts = $this->config[self::CURL_OPTIONS] ?: array(); |
|
||||||
|
|
||||||
if ($certificateAuthority === true) { |
|
||||||
// use bundled CA bundle, set secure defaults |
|
||||||
$opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; |
|
||||||
$opts[CURLOPT_SSL_VERIFYPEER] = true; |
|
||||||
$opts[CURLOPT_SSL_VERIFYHOST] = 2; |
|
||||||
} elseif ($certificateAuthority === false) { |
|
||||||
unset($opts[CURLOPT_CAINFO]); |
|
||||||
$opts[CURLOPT_SSL_VERIFYPEER] = false; |
|
||||||
$opts[CURLOPT_SSL_VERIFYHOST] = 0; |
|
||||||
} elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { |
|
||||||
throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); |
|
||||||
} elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { |
|
||||||
throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); |
|
||||||
} else { |
|
||||||
$opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; |
|
||||||
$opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; |
|
||||||
if (is_file($certificateAuthority)) { |
|
||||||
unset($opts[CURLOPT_CAPATH]); |
|
||||||
$opts[CURLOPT_CAINFO] = $certificateAuthority; |
|
||||||
} elseif (is_dir($certificateAuthority)) { |
|
||||||
unset($opts[CURLOPT_CAINFO]); |
|
||||||
$opts[CURLOPT_CAPATH] = $certificateAuthority; |
|
||||||
} else { |
|
||||||
throw new RuntimeException( |
|
||||||
'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$this->config->set(self::CURL_OPTIONS, $opts); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) |
|
||||||
{ |
|
||||||
if (!$uri) { |
|
||||||
$url = $this->getBaseUrl(); |
|
||||||
} else { |
|
||||||
if (!is_array($uri)) { |
|
||||||
$templateVars = null; |
|
||||||
} else { |
|
||||||
list($uri, $templateVars) = $uri; |
|
||||||
} |
|
||||||
if (strpos($uri, '://')) { |
|
||||||
// Use absolute URLs as-is |
|
||||||
$url = $this->expandTemplate($uri, $templateVars); |
|
||||||
} else { |
|
||||||
$url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// If default headers are provided, then merge them under any explicitly provided headers for the request |
|
||||||
if (count($this->defaultHeaders)) { |
|
||||||
if (!$headers) { |
|
||||||
$headers = $this->defaultHeaders->toArray(); |
|
||||||
} elseif (is_array($headers)) { |
|
||||||
$headers += $this->defaultHeaders->toArray(); |
|
||||||
} elseif ($headers instanceof Collection) { |
|
||||||
$headers = $headers->toArray() + $this->defaultHeaders->toArray(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function getBaseUrl($expand = true) |
|
||||||
{ |
|
||||||
return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; |
|
||||||
} |
|
||||||
|
|
||||||
public function setBaseUrl($url) |
|
||||||
{ |
|
||||||
$this->baseUrl = $url; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function setUserAgent($userAgent, $includeDefault = false) |
|
||||||
{ |
|
||||||
if ($includeDefault) { |
|
||||||
$userAgent .= ' ' . $this->getDefaultUserAgent(); |
|
||||||
} |
|
||||||
$this->userAgent = $userAgent; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the default User-Agent string to use with Guzzle |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getDefaultUserAgent() |
|
||||||
{ |
|
||||||
return 'Guzzle/' . Version::VERSION |
|
||||||
. ' curl/' . CurlVersion::getInstance()->get('version') |
|
||||||
. ' PHP/' . PHP_VERSION; |
|
||||||
} |
|
||||||
|
|
||||||
public function get($uri = null, $headers = null, $options = array()) |
|
||||||
{ |
|
||||||
// BC compat: $options can be a string, resource, etc to specify where the response body is downloaded |
|
||||||
return is_array($options) |
|
||||||
? $this->createRequest('GET', $uri, $headers, null, $options) |
|
||||||
: $this->createRequest('GET', $uri, $headers, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function head($uri = null, $headers = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('HEAD', $uri, $headers, null, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function delete($uri = null, $headers = null, $body = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('DELETE', $uri, $headers, $body, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function put($uri = null, $headers = null, $body = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('PUT', $uri, $headers, $body, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function patch($uri = null, $headers = null, $body = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('PATCH', $uri, $headers, $body, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function post($uri = null, $headers = null, $postBody = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('POST', $uri, $headers, $postBody, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function options($uri = null, array $options = array()) |
|
||||||
{ |
|
||||||
return $this->createRequest('OPTIONS', $uri, $options); |
|
||||||
} |
|
||||||
|
|
||||||
public function send($requests) |
|
||||||
{ |
|
||||||
if (!($requests instanceof RequestInterface)) { |
|
||||||
return $this->sendMultiple($requests); |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
/** @var $requests RequestInterface */ |
|
||||||
$this->getCurlMulti()->add($requests)->send(); |
|
||||||
return $requests->getResponse(); |
|
||||||
} catch (ExceptionCollection $e) { |
|
||||||
throw $e->getFirst(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a curl multi object to be used internally by the client for transferring requests. |
|
||||||
* |
|
||||||
* @param CurlMultiInterface $curlMulti Multi object |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setCurlMulti(CurlMultiInterface $curlMulti) |
|
||||||
{ |
|
||||||
$this->curlMulti = $curlMulti; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return CurlMultiInterface|CurlMultiProxy |
|
||||||
*/ |
|
||||||
public function getCurlMulti() |
|
||||||
{ |
|
||||||
if (!$this->curlMulti) { |
|
||||||
$this->curlMulti = new CurlMultiProxy( |
|
||||||
self::MAX_HANDLES, |
|
||||||
$this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->curlMulti; |
|
||||||
} |
|
||||||
|
|
||||||
public function setRequestFactory(RequestFactoryInterface $factory) |
|
||||||
{ |
|
||||||
$this->requestFactory = $factory; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the URI template expander to use with the client |
|
||||||
* |
|
||||||
* @param UriTemplateInterface $uriTemplate URI template expander |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setUriTemplate(UriTemplateInterface $uriTemplate) |
|
||||||
{ |
|
||||||
$this->uriTemplate = $uriTemplate; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Expand a URI template while merging client config settings into the template variables |
|
||||||
* |
|
||||||
* @param string $template Template to expand |
|
||||||
* @param array $variables Variables to inject |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
protected function expandTemplate($template, array $variables = null) |
|
||||||
{ |
|
||||||
$expansionVars = $this->getConfig()->toArray(); |
|
||||||
if ($variables) { |
|
||||||
$expansionVars = $variables + $expansionVars; |
|
||||||
} |
|
||||||
|
|
||||||
return $this->getUriTemplate()->expand($template, $expansionVars); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the URI template expander used by the client |
|
||||||
* |
|
||||||
* @return UriTemplateInterface |
|
||||||
*/ |
|
||||||
protected function getUriTemplate() |
|
||||||
{ |
|
||||||
if (!$this->uriTemplate) { |
|
||||||
$this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->uriTemplate; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Send multiple requests in parallel |
|
||||||
* |
|
||||||
* @param array $requests Array of RequestInterface objects |
|
||||||
* |
|
||||||
* @return array Returns an array of Response objects |
|
||||||
*/ |
|
||||||
protected function sendMultiple(array $requests) |
|
||||||
{ |
|
||||||
$curlMulti = $this->getCurlMulti(); |
|
||||||
foreach ($requests as $request) { |
|
||||||
$curlMulti->add($request); |
|
||||||
} |
|
||||||
$curlMulti->send(); |
|
||||||
|
|
||||||
/** @var $request RequestInterface */ |
|
||||||
$result = array(); |
|
||||||
foreach ($requests as $request) { |
|
||||||
$result[] = $request->getResponse(); |
|
||||||
} |
|
||||||
|
|
||||||
return $result; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to prepare for the client |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return RequestInterface |
|
||||||
*/ |
|
||||||
protected function prepareRequest(RequestInterface $request, array $options = array()) |
|
||||||
{ |
|
||||||
$request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); |
|
||||||
|
|
||||||
if ($curl = $this->config[self::CURL_OPTIONS]) { |
|
||||||
$request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); |
|
||||||
} |
|
||||||
|
|
||||||
if ($params = $this->config[self::REQUEST_PARAMS]) { |
|
||||||
Version::warn('request.params is deprecated. Use request.options to add default request options.'); |
|
||||||
$request->getParams()->overwriteWith($params); |
|
||||||
} |
|
||||||
|
|
||||||
if ($this->userAgent && !$request->hasHeader('User-Agent')) { |
|
||||||
$request->setHeader('User-Agent', $this->userAgent); |
|
||||||
} |
|
||||||
|
|
||||||
if ($defaults = $this->config[self::REQUEST_OPTIONS]) { |
|
||||||
$this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); |
|
||||||
} |
|
||||||
|
|
||||||
if ($options) { |
|
||||||
$this->requestFactory->applyOptions($request, $options); |
|
||||||
} |
|
||||||
|
|
||||||
$this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); |
|
||||||
|
|
||||||
return $request; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initializes SSL settings |
|
||||||
*/ |
|
||||||
protected function initSsl() |
|
||||||
{ |
|
||||||
$authority = $this->config[self::SSL_CERT_AUTHORITY]; |
|
||||||
|
|
||||||
if ($authority === 'system') { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
if ($authority === null) { |
|
||||||
$authority = true; |
|
||||||
} |
|
||||||
|
|
||||||
if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { |
|
||||||
$authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem'); |
|
||||||
} |
|
||||||
|
|
||||||
$this->setSslVerification($authority); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
*/ |
|
||||||
public function getDefaultHeaders() |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); |
|
||||||
return $this->defaultHeaders; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
*/ |
|
||||||
public function setDefaultHeaders($headers) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); |
|
||||||
if ($headers instanceof Collection) { |
|
||||||
$this->defaultHeaders = $headers; |
|
||||||
} elseif (is_array($headers)) { |
|
||||||
$this->defaultHeaders = new Collection($headers); |
|
||||||
} else { |
|
||||||
throw new InvalidArgumentException('Headers must be an array or Collection'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
*/ |
|
||||||
public function preparePharCacert($md5Check = true) |
|
||||||
{ |
|
||||||
return sys_get_temp_dir() . '/guzzle-cacert.pem'; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Copies the phar cacert from a phar into the temp directory. |
|
||||||
* |
|
||||||
* @param string $pharCacertPath Path to the phar cacert. For example: |
|
||||||
* 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem' |
|
||||||
* |
|
||||||
* @return string Returns the path to the extracted cacert file. |
|
||||||
* @throws \RuntimeException Throws if the phar cacert cannot be found or |
|
||||||
* the file cannot be copied to the temp dir. |
|
||||||
*/ |
|
||||||
public static function extractPharCacert($pharCacertPath) |
|
||||||
{ |
|
||||||
// Copy the cacert.pem file from the phar if it is not in the temp |
|
||||||
// folder. |
|
||||||
$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; |
|
||||||
|
|
||||||
if (!file_exists($pharCacertPath)) { |
|
||||||
throw new \RuntimeException("Could not find $pharCacertPath"); |
|
||||||
} |
|
||||||
|
|
||||||
if (!file_exists($certFile) || |
|
||||||
filesize($certFile) != filesize($pharCacertPath) |
|
||||||
) { |
|
||||||
if (!copy($pharCacertPath, $certFile)) { |
|
||||||
throw new \RuntimeException( |
|
||||||
"Could not copy {$pharCacertPath} to {$certFile}: " |
|
||||||
. var_export(error_get_last(), true) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $certFile; |
|
||||||
} |
|
||||||
} |
|
@ -1,223 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Common\HasDispatcherInterface; |
|
||||||
use Guzzle\Common\Collection; |
|
||||||
use Guzzle\Common\Exception\InvalidArgumentException; |
|
||||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Client interface for send HTTP requests |
|
||||||
*/ |
|
||||||
interface ClientInterface extends HasDispatcherInterface |
|
||||||
{ |
|
||||||
const CREATE_REQUEST = 'client.create_request'; |
|
||||||
|
|
||||||
/** @var string RFC 1123 HTTP-Date */ |
|
||||||
const HTTP_DATE = 'D, d M Y H:i:s \G\M\T'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the configuration object to use with the client |
|
||||||
* |
|
||||||
* @param array|Collection $config Parameters that define how the client behaves |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setConfig($config); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a configuration setting or all of the configuration settings. The Collection result of this method can be |
|
||||||
* modified to change the configuration settings of a client. |
|
||||||
* |
|
||||||
* A client should honor the following special values: |
|
||||||
* |
|
||||||
* - request.options: Associative array of default RequestFactory options to apply to each request |
|
||||||
* - request.params: Associative array of request parameters (data values) to apply to each request |
|
||||||
* - curl.options: Associative array of cURL configuration settings to apply to each request |
|
||||||
* - ssl.certificate_authority: Path a CAINFO, CAPATH, true to use strict defaults, or false to disable verification |
|
||||||
* - redirect.disable: Set to true to disable redirects |
|
||||||
* |
|
||||||
* @param bool|string $key Configuration value to retrieve. Set to FALSE to retrieve all values of the client. |
|
||||||
* The object return can be modified, and modifications will affect the client's config. |
|
||||||
* @return mixed|Collection |
|
||||||
* @see \Guzzle\Http\Message\RequestFactoryInterface::applyOptions for a full list of request.options options |
|
||||||
*/ |
|
||||||
public function getConfig($key = false); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create and return a new {@see RequestInterface} configured for the client. |
|
||||||
* |
|
||||||
* Use an absolute path to override the base path of the client, or a relative path to append to the base path of |
|
||||||
* the client. The URI can contain the query string as well. Use an array to provide a URI template and additional |
|
||||||
* variables to use in the URI template expansion. |
|
||||||
* |
|
||||||
* @param string $method HTTP method. Defaults to GET |
|
||||||
* @param string|array $uri Resource URI. |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param string|resource|array|EntityBodyInterface $body Entity body of request (POST/PUT) or response (GET) |
|
||||||
* @param array $options Array of options to apply to the request |
|
||||||
* |
|
||||||
* @return RequestInterface |
|
||||||
* @throws InvalidArgumentException if a URI array is passed that does not contain exactly two elements: the URI |
|
||||||
* followed by template variables |
|
||||||
*/ |
|
||||||
public function createRequest( |
|
||||||
$method = RequestInterface::GET, |
|
||||||
$uri = null, |
|
||||||
$headers = null, |
|
||||||
$body = null, |
|
||||||
array $options = array() |
|
||||||
); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a GET request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param array $options Options to apply to the request. For BC compatibility, you can also pass a |
|
||||||
* string to tell Guzzle to download the body of the response to a particular |
|
||||||
* location. Use the 'body' option instead for forward compatibility. |
|
||||||
* @return RequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function get($uri = null, $headers = null, $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a HEAD request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return RequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function head($uri = null, $headers = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a DELETE request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param string|resource|EntityBodyInterface $body Body to send in the request |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return EntityEnclosingRequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function delete($uri = null, $headers = null, $body = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a PUT request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param string|resource|EntityBodyInterface $body Body to send in the request |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return EntityEnclosingRequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function put($uri = null, $headers = null, $body = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a PATCH request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param string|resource|EntityBodyInterface $body Body to send in the request |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return EntityEnclosingRequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function patch($uri = null, $headers = null, $body = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a POST request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array|Collection $headers HTTP headers |
|
||||||
* @param array|Collection|string|EntityBodyInterface $postBody POST body. Can be a string, EntityBody, or |
|
||||||
* associative array of POST fields to send in the body of the |
|
||||||
* request. Prefix a value in the array with the @ symbol to |
|
||||||
* reference a file. |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return EntityEnclosingRequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function post($uri = null, $headers = null, $postBody = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Create an OPTIONS request for the client |
|
||||||
* |
|
||||||
* @param string|array $uri Resource URI |
|
||||||
* @param array $options Options to apply to the request |
|
||||||
* |
|
||||||
* @return RequestInterface |
|
||||||
* @see Guzzle\Http\ClientInterface::createRequest() |
|
||||||
*/ |
|
||||||
public function options($uri = null, array $options = array()); |
|
||||||
|
|
||||||
/** |
|
||||||
* Sends a single request or an array of requests in parallel |
|
||||||
* |
|
||||||
* @param array|RequestInterface $requests One or more RequestInterface objects to send |
|
||||||
* |
|
||||||
* @return \Guzzle\Http\Message\Response|array Returns a single Response or an array of Response objects |
|
||||||
*/ |
|
||||||
public function send($requests); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the client's base URL as either an expanded or raw URI template |
|
||||||
* |
|
||||||
* @param bool $expand Set to FALSE to get the raw base URL without URI template expansion |
|
||||||
* |
|
||||||
* @return string|null |
|
||||||
*/ |
|
||||||
public function getBaseUrl($expand = true); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the base URL of the client |
|
||||||
* |
|
||||||
* @param string $url The base service endpoint URL of the webservice |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setBaseUrl($url); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the User-Agent header to be used on all requests from the client |
|
||||||
* |
|
||||||
* @param string $userAgent User agent string |
|
||||||
* @param bool $includeDefault Set to true to prepend the value to Guzzle's default user agent string |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setUserAgent($userAgent, $includeDefault = false); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set SSL verification options. |
|
||||||
* |
|
||||||
* Setting $certificateAuthority to TRUE will result in the bundled cacert.pem being used to verify against the |
|
||||||
* remote host. |
|
||||||
* |
|
||||||
* Alternate certificates to verify against can be specified with the $certificateAuthority option set to the full |
|
||||||
* path to a certificate file, or the path to a directory containing certificates. |
|
||||||
* |
|
||||||
* Setting $certificateAuthority to FALSE will turn off peer verification, unset the bundled cacert.pem, and |
|
||||||
* disable host verification. Please don't do this unless you really know what you're doing, and why you're doing |
|
||||||
* it. |
|
||||||
* |
|
||||||
* @param string|bool $certificateAuthority bool, file path, or directory path |
|
||||||
* @param bool $verifyPeer FALSE to stop from verifying the peer's certificate. |
|
||||||
* @param int $verifyHost Set to 1 to check the existence of a common name in the SSL peer |
|
||||||
* certificate. 2 to check the existence of a common name and also verify |
|
||||||
* that it matches the hostname provided. |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2); |
|
||||||
} |
|
@ -1,464 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\InvalidArgumentException; |
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
use Guzzle\Common\Collection; |
|
||||||
use Guzzle\Http\Message\EntityEnclosingRequest; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Parser\ParserRegistry; |
|
||||||
use Guzzle\Http\Url; |
|
||||||
|
|
||||||
/** |
|
||||||
* Immutable wrapper for a cURL handle |
|
||||||
*/ |
|
||||||
class CurlHandle |
|
||||||
{ |
|
||||||
const BODY_AS_STRING = 'body_as_string'; |
|
||||||
const PROGRESS = 'progress'; |
|
||||||
const DEBUG = 'debug'; |
|
||||||
|
|
||||||
/** @var Collection Curl options */ |
|
||||||
protected $options; |
|
||||||
|
|
||||||
/** @var resource Curl resource handle */ |
|
||||||
protected $handle; |
|
||||||
|
|
||||||
/** @var int CURLE_* error */ |
|
||||||
protected $errorNo = CURLE_OK; |
|
||||||
|
|
||||||
/** |
|
||||||
* Factory method to create a new curl handle based on an HTTP request. |
|
||||||
* |
|
||||||
* There are some helpful options you can set to enable specific behavior: |
|
||||||
* - debug: Set to true to enable cURL debug functionality to track the actual headers sent over the wire. |
|
||||||
* - progress: Set to true to enable progress function callbacks. |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request |
|
||||||
* |
|
||||||
* @return CurlHandle |
|
||||||
* @throws RuntimeException |
|
||||||
*/ |
|
||||||
public static function factory(RequestInterface $request) |
|
||||||
{ |
|
||||||
$requestCurlOptions = $request->getCurlOptions(); |
|
||||||
$mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); |
|
||||||
$tempContentLength = null; |
|
||||||
$method = $request->getMethod(); |
|
||||||
$bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); |
|
||||||
|
|
||||||
// Prepare url |
|
||||||
$url = (string)$request->getUrl(); |
|
||||||
if(($pos = strpos($url, '#')) !== false ){ |
|
||||||
// strip fragment from url |
|
||||||
$url = substr($url, 0, $pos); |
|
||||||
} |
|
||||||
|
|
||||||
// Array of default cURL options. |
|
||||||
$curlOptions = array( |
|
||||||
CURLOPT_URL => $url, |
|
||||||
CURLOPT_CONNECTTIMEOUT => 150, |
|
||||||
CURLOPT_RETURNTRANSFER => false, |
|
||||||
CURLOPT_HEADER => false, |
|
||||||
CURLOPT_PORT => $request->getPort(), |
|
||||||
CURLOPT_HTTPHEADER => array(), |
|
||||||
CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), |
|
||||||
CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), |
|
||||||
CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' |
|
||||||
? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, |
|
||||||
// Verifies the authenticity of the peer's certificate |
|
||||||
CURLOPT_SSL_VERIFYPEER => 1, |
|
||||||
// Certificate must indicate that the server is the server to which you meant to connect |
|
||||||
CURLOPT_SSL_VERIFYHOST => 2 |
|
||||||
); |
|
||||||
|
|
||||||
if (defined('CURLOPT_PROTOCOLS')) { |
|
||||||
// Allow only HTTP and HTTPS protocols |
|
||||||
$curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; |
|
||||||
} |
|
||||||
|
|
||||||
// Add CURLOPT_ENCODING if Accept-Encoding header is provided |
|
||||||
if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { |
|
||||||
$curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; |
|
||||||
// Let cURL set the Accept-Encoding header, prevents duplicate values |
|
||||||
$request->removeHeader('Accept-Encoding'); |
|
||||||
} |
|
||||||
|
|
||||||
// Enable curl debug information if the 'debug' param was set |
|
||||||
if ($requestCurlOptions->get('debug')) { |
|
||||||
$curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
if (false === $curlOptions[CURLOPT_STDERR]) { |
|
||||||
throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); |
|
||||||
} |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
$curlOptions[CURLOPT_VERBOSE] = true; |
|
||||||
} |
|
||||||
|
|
||||||
// Specify settings according to the HTTP method |
|
||||||
if ($method == 'GET') { |
|
||||||
$curlOptions[CURLOPT_HTTPGET] = true; |
|
||||||
} elseif ($method == 'HEAD') { |
|
||||||
$curlOptions[CURLOPT_NOBODY] = true; |
|
||||||
// HEAD requests do not use a write function |
|
||||||
unset($curlOptions[CURLOPT_WRITEFUNCTION]); |
|
||||||
} elseif (!($request instanceof EntityEnclosingRequest)) { |
|
||||||
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method; |
|
||||||
} else { |
|
||||||
|
|
||||||
$curlOptions[CURLOPT_CUSTOMREQUEST] = $method; |
|
||||||
|
|
||||||
// Handle sending raw bodies in a request |
|
||||||
if ($request->getBody()) { |
|
||||||
// You can send the body as a string using curl's CURLOPT_POSTFIELDS |
|
||||||
if ($bodyAsString) { |
|
||||||
$curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); |
|
||||||
// Allow curl to add the Content-Length for us to account for the times when |
|
||||||
// POST redirects are followed by GET requests |
|
||||||
if ($tempContentLength = $request->getHeader('Content-Length')) { |
|
||||||
$tempContentLength = (int) (string) $tempContentLength; |
|
||||||
} |
|
||||||
// Remove the curl generated Content-Type header if none was set manually |
|
||||||
if (!$request->hasHeader('Content-Type')) { |
|
||||||
$curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; |
|
||||||
} |
|
||||||
} else { |
|
||||||
$curlOptions[CURLOPT_UPLOAD] = true; |
|
||||||
// Let cURL handle setting the Content-Length header |
|
||||||
if ($tempContentLength = $request->getHeader('Content-Length')) { |
|
||||||
$tempContentLength = (int) (string) $tempContentLength; |
|
||||||
$curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; |
|
||||||
} |
|
||||||
// Add a callback for curl to read data to send with the request only if a body was specified |
|
||||||
$curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); |
|
||||||
// Attempt to seek to the start of the stream |
|
||||||
$request->getBody()->seek(0); |
|
||||||
} |
|
||||||
|
|
||||||
} else { |
|
||||||
|
|
||||||
// Special handling for POST specific fields and files |
|
||||||
$postFields = false; |
|
||||||
if (count($request->getPostFiles())) { |
|
||||||
$postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); |
|
||||||
foreach ($request->getPostFiles() as $key => $data) { |
|
||||||
$prefixKeys = count($data) > 1; |
|
||||||
foreach ($data as $index => $file) { |
|
||||||
// Allow multiple files in the same key |
|
||||||
$fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; |
|
||||||
$postFields[$fieldKey] = $file->getCurlValue(); |
|
||||||
} |
|
||||||
} |
|
||||||
} elseif (count($request->getPostFields())) { |
|
||||||
$postFields = (string) $request->getPostFields()->useUrlEncoding(true); |
|
||||||
} |
|
||||||
|
|
||||||
if ($postFields !== false) { |
|
||||||
if ($method == 'POST') { |
|
||||||
unset($curlOptions[CURLOPT_CUSTOMREQUEST]); |
|
||||||
$curlOptions[CURLOPT_POST] = true; |
|
||||||
} |
|
||||||
$curlOptions[CURLOPT_POSTFIELDS] = $postFields; |
|
||||||
$request->removeHeader('Content-Length'); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// If the Expect header is not present, prevent curl from adding it |
|
||||||
if (!$request->hasHeader('Expect')) { |
|
||||||
$curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// If a Content-Length header was specified but we want to allow curl to set one for us |
|
||||||
if (null !== $tempContentLength) { |
|
||||||
$request->removeHeader('Content-Length'); |
|
||||||
} |
|
||||||
|
|
||||||
// Set custom cURL options |
|
||||||
foreach ($requestCurlOptions->toArray() as $key => $value) { |
|
||||||
if (is_numeric($key)) { |
|
||||||
$curlOptions[$key] = $value; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Do not set an Accept header by default |
|
||||||
if (!isset($curlOptions[CURLOPT_ENCODING])) { |
|
||||||
$curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; |
|
||||||
} |
|
||||||
|
|
||||||
// Add any custom headers to the request. Empty headers will cause curl to not send the header at all. |
|
||||||
foreach ($request->getHeaderLines() as $line) { |
|
||||||
$curlOptions[CURLOPT_HTTPHEADER][] = $line; |
|
||||||
} |
|
||||||
|
|
||||||
// Add the content-length header back if it was temporarily removed |
|
||||||
if ($tempContentLength) { |
|
||||||
$request->setHeader('Content-Length', $tempContentLength); |
|
||||||
} |
|
||||||
|
|
||||||
// Apply the options to a new cURL handle. |
|
||||||
$handle = curl_init(); |
|
||||||
|
|
||||||
// Enable the progress function if the 'progress' param was set |
|
||||||
if ($requestCurlOptions->get('progress')) { |
|
||||||
// Wrap the function in a function that provides the curl handle to the mediator's progress function |
|
||||||
// Using this rather than injecting the handle into the mediator prevents a circular reference |
|
||||||
$curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { |
|
||||||
$args = func_get_args(); |
|
||||||
$args[] = $handle; |
|
||||||
|
|
||||||
// PHP 5.5 pushed the handle onto the start of the args |
|
||||||
if (is_resource($args[0])) { |
|
||||||
array_shift($args); |
|
||||||
} |
|
||||||
|
|
||||||
call_user_func_array(array($mediator, 'progress'), $args); |
|
||||||
}; |
|
||||||
$curlOptions[CURLOPT_NOPROGRESS] = false; |
|
||||||
} |
|
||||||
|
|
||||||
curl_setopt_array($handle, $curlOptions); |
|
||||||
|
|
||||||
return new static($handle, $curlOptions); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Construct a new CurlHandle object that wraps a cURL handle |
|
||||||
* |
|
||||||
* @param resource $handle Configured cURL handle resource |
|
||||||
* @param Collection|array $options Curl options to use with the handle |
|
||||||
* |
|
||||||
* @throws InvalidArgumentException |
|
||||||
*/ |
|
||||||
public function __construct($handle, $options) |
|
||||||
{ |
|
||||||
if (!is_resource($handle)) { |
|
||||||
throw new InvalidArgumentException('Invalid handle provided'); |
|
||||||
} |
|
||||||
if (is_array($options)) { |
|
||||||
$this->options = new Collection($options); |
|
||||||
} elseif ($options instanceof Collection) { |
|
||||||
$this->options = $options; |
|
||||||
} else { |
|
||||||
throw new InvalidArgumentException('Expected array or Collection'); |
|
||||||
} |
|
||||||
$this->handle = $handle; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Destructor |
|
||||||
*/ |
|
||||||
public function __destruct() |
|
||||||
{ |
|
||||||
$this->close(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Close the curl handle |
|
||||||
*/ |
|
||||||
public function close() |
|
||||||
{ |
|
||||||
if (is_resource($this->handle)) { |
|
||||||
curl_close($this->handle); |
|
||||||
} |
|
||||||
$this->handle = null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check if the handle is available and still OK |
|
||||||
* |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function isAvailable() |
|
||||||
{ |
|
||||||
return is_resource($this->handle); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the last error that occurred on the cURL handle |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function getError() |
|
||||||
{ |
|
||||||
return $this->isAvailable() ? curl_error($this->handle) : ''; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the last error number that occurred on the cURL handle |
|
||||||
* |
|
||||||
* @return int |
|
||||||
*/ |
|
||||||
public function getErrorNo() |
|
||||||
{ |
|
||||||
if ($this->errorNo) { |
|
||||||
return $this->errorNo; |
|
||||||
} |
|
||||||
|
|
||||||
return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the curl error number |
|
||||||
* |
|
||||||
* @param int $error Error number to set |
|
||||||
* |
|
||||||
* @return CurlHandle |
|
||||||
*/ |
|
||||||
public function setErrorNo($error) |
|
||||||
{ |
|
||||||
$this->errorNo = $error; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get cURL curl_getinfo data |
|
||||||
* |
|
||||||
* @param int $option Option to retrieve. Pass null to retrieve all data as an array. |
|
||||||
* |
|
||||||
* @return array|mixed |
|
||||||
*/ |
|
||||||
public function getInfo($option = null) |
|
||||||
{ |
|
||||||
if (!is_resource($this->handle)) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
if (null !== $option) { |
|
||||||
return curl_getinfo($this->handle, $option) ?: null; |
|
||||||
} |
|
||||||
|
|
||||||
return curl_getinfo($this->handle) ?: array(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the stderr output |
|
||||||
* |
|
||||||
* @param bool $asResource Set to TRUE to get an fopen resource |
|
||||||
* |
|
||||||
* @return string|resource|null |
|
||||||
*/ |
|
||||||
public function getStderr($asResource = false) |
|
||||||
{ |
|
||||||
$stderr = $this->getOptions()->get(CURLOPT_STDERR); |
|
||||||
if (!$stderr) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
if ($asResource) { |
|
||||||
return $stderr; |
|
||||||
} |
|
||||||
|
|
||||||
fseek($stderr, 0); |
|
||||||
$e = stream_get_contents($stderr); |
|
||||||
fseek($stderr, 0, SEEK_END); |
|
||||||
|
|
||||||
return $e; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the URL that this handle is connecting to |
|
||||||
* |
|
||||||
* @return Url |
|
||||||
*/ |
|
||||||
public function getUrl() |
|
||||||
{ |
|
||||||
return Url::factory($this->options->get(CURLOPT_URL)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the wrapped curl handle |
|
||||||
* |
|
||||||
* @return resource|null Returns the cURL handle or null if it was closed |
|
||||||
*/ |
|
||||||
public function getHandle() |
|
||||||
{ |
|
||||||
return $this->isAvailable() ? $this->handle : null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl |
|
||||||
* handle after it is created. |
|
||||||
* |
|
||||||
* @return Collection |
|
||||||
*/ |
|
||||||
public function getOptions() |
|
||||||
{ |
|
||||||
return $this->options; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Update a request based on the log messages of the CurlHandle |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to update |
|
||||||
*/ |
|
||||||
public function updateRequestFromTransfer(RequestInterface $request) |
|
||||||
{ |
|
||||||
if (!$request->getResponse()) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Update the transfer stats of the response |
|
||||||
$request->getResponse()->setInfo($this->getInfo()); |
|
||||||
|
|
||||||
if (!$log = $this->getStderr(true)) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Parse the cURL stderr output for outgoing requests |
|
||||||
$headers = ''; |
|
||||||
fseek($log, 0); |
|
||||||
while (($line = fgets($log)) !== false) { |
|
||||||
if ($line && $line[0] == '>') { |
|
||||||
$headers = substr(trim($line), 2) . "\r\n"; |
|
||||||
while (($line = fgets($log)) !== false) { |
|
||||||
if ($line[0] == '*' || $line[0] == '<') { |
|
||||||
break; |
|
||||||
} else { |
|
||||||
$headers .= trim($line) . "\r\n"; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Add request headers to the request exactly as they were sent |
|
||||||
if ($headers) { |
|
||||||
$parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); |
|
||||||
if (!empty($parsed['headers'])) { |
|
||||||
$request->setHeaders(array()); |
|
||||||
foreach ($parsed['headers'] as $name => $value) { |
|
||||||
$request->setHeader($name, $value); |
|
||||||
} |
|
||||||
} |
|
||||||
if (!empty($parsed['version'])) { |
|
||||||
$request->setProtocolVersion($parsed['version']); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere |
|
||||||
* |
|
||||||
* @param array|Collection $config The configuration we want to parse |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public static function parseCurlConfig($config) |
|
||||||
{ |
|
||||||
$curlOptions = array(); |
|
||||||
foreach ($config as $key => $value) { |
|
||||||
if (is_string($key) && defined($key)) { |
|
||||||
// Convert constants represented as string to constant int values |
|
||||||
$key = constant($key); |
|
||||||
} |
|
||||||
if (is_string($value) && defined($value)) { |
|
||||||
$value = constant($value); |
|
||||||
} |
|
||||||
$curlOptions[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
return $curlOptions; |
|
||||||
} |
|
||||||
} |
|
@ -1,423 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
use Guzzle\Common\AbstractHasDispatcher; |
|
||||||
use Guzzle\Common\Event; |
|
||||||
use Guzzle\Http\Exception\MultiTransferException; |
|
||||||
use Guzzle\Http\Exception\CurlException; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\Message\EntityEnclosingRequestInterface; |
|
||||||
use Guzzle\Http\Exception\RequestException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Send {@see RequestInterface} objects in parallel using curl_multi |
|
||||||
*/ |
|
||||||
class CurlMulti extends AbstractHasDispatcher implements CurlMultiInterface |
|
||||||
{ |
|
||||||
/** @var resource cURL multi handle. */ |
|
||||||
protected $multiHandle; |
|
||||||
|
|
||||||
/** @var array Attached {@see RequestInterface} objects. */ |
|
||||||
protected $requests; |
|
||||||
|
|
||||||
/** @var \SplObjectStorage RequestInterface to CurlHandle hash */ |
|
||||||
protected $handles; |
|
||||||
|
|
||||||
/** @var array Hash mapping curl handle resource IDs to request objects */ |
|
||||||
protected $resourceHash; |
|
||||||
|
|
||||||
/** @var array Queued exceptions */ |
|
||||||
protected $exceptions = array(); |
|
||||||
|
|
||||||
/** @var array Requests that succeeded */ |
|
||||||
protected $successful = array(); |
|
||||||
|
|
||||||
/** @var array cURL multi error values and codes */ |
|
||||||
protected $multiErrors = array( |
|
||||||
CURLM_BAD_HANDLE => array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), |
|
||||||
CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), |
|
||||||
CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), |
|
||||||
CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') |
|
||||||
); |
|
||||||
|
|
||||||
/** @var float */ |
|
||||||
protected $selectTimeout; |
|
||||||
|
|
||||||
public function __construct($selectTimeout = 1.0) |
|
||||||
{ |
|
||||||
$this->selectTimeout = $selectTimeout; |
|
||||||
$this->multiHandle = curl_multi_init(); |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
if ($this->multiHandle === false) { |
|
||||||
throw new CurlException('Unable to create multi handle'); |
|
||||||
} |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
$this->reset(); |
|
||||||
} |
|
||||||
|
|
||||||
public function __destruct() |
|
||||||
{ |
|
||||||
if (is_resource($this->multiHandle)) { |
|
||||||
curl_multi_close($this->multiHandle); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function add(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->requests[] = $request; |
|
||||||
// If requests are currently transferring and this is async, then the |
|
||||||
// request must be prepared now as the send() method is not called. |
|
||||||
$this->beforeSend($request); |
|
||||||
$this->dispatch(self::ADD_REQUEST, array('request' => $request)); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function all() |
|
||||||
{ |
|
||||||
return $this->requests; |
|
||||||
} |
|
||||||
|
|
||||||
public function remove(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->removeHandle($request); |
|
||||||
if (($index = array_search($request, $this->requests, true)) !== false) { |
|
||||||
$request = $this->requests[$index]; |
|
||||||
unset($this->requests[$index]); |
|
||||||
$this->requests = array_values($this->requests); |
|
||||||
$this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
public function reset($hard = false) |
|
||||||
{ |
|
||||||
// Remove each request |
|
||||||
if ($this->requests) { |
|
||||||
foreach ($this->requests as $request) { |
|
||||||
$this->remove($request); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$this->handles = new \SplObjectStorage(); |
|
||||||
$this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); |
|
||||||
} |
|
||||||
|
|
||||||
public function send() |
|
||||||
{ |
|
||||||
$this->perform(); |
|
||||||
$exceptions = $this->exceptions; |
|
||||||
$successful = $this->successful; |
|
||||||
$this->reset(); |
|
||||||
|
|
||||||
if ($exceptions) { |
|
||||||
$this->throwMultiException($exceptions, $successful); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->requests); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Build and throw a MultiTransferException |
|
||||||
* |
|
||||||
* @param array $exceptions Exceptions encountered |
|
||||||
* @param array $successful Successful requests |
|
||||||
* @throws MultiTransferException |
|
||||||
*/ |
|
||||||
protected function throwMultiException(array $exceptions, array $successful) |
|
||||||
{ |
|
||||||
$multiException = new MultiTransferException('Errors during multi transfer'); |
|
||||||
|
|
||||||
while ($e = array_shift($exceptions)) { |
|
||||||
$multiException->addFailedRequestWithException($e['request'], $e['exception']); |
|
||||||
} |
|
||||||
|
|
||||||
// Add successful requests |
|
||||||
foreach ($successful as $request) { |
|
||||||
if (!$multiException->containsRequest($request)) { |
|
||||||
$multiException->addSuccessfulRequest($request); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
throw $multiException; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Prepare for sending |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to prepare |
|
||||||
* @throws \Exception on error preparing the request |
|
||||||
*/ |
|
||||||
protected function beforeSend(RequestInterface $request) |
|
||||||
{ |
|
||||||
try { |
|
||||||
$state = $request->setState(RequestInterface::STATE_TRANSFER); |
|
||||||
if ($state == RequestInterface::STATE_TRANSFER) { |
|
||||||
$this->addHandle($request); |
|
||||||
} else { |
|
||||||
// Requests might decide they don't need to be sent just before |
|
||||||
// transfer (e.g. CachePlugin) |
|
||||||
$this->remove($request); |
|
||||||
if ($state == RequestInterface::STATE_COMPLETE) { |
|
||||||
$this->successful[] = $request; |
|
||||||
} |
|
||||||
} |
|
||||||
} catch (\Exception $e) { |
|
||||||
// Queue the exception to be thrown when sent |
|
||||||
$this->removeErroredRequest($request, $e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private function addHandle(RequestInterface $request) |
|
||||||
{ |
|
||||||
$handle = $this->createCurlHandle($request)->getHandle(); |
|
||||||
$this->checkCurlResult( |
|
||||||
curl_multi_add_handle($this->multiHandle, $handle) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a curl handle for a request |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request |
|
||||||
* |
|
||||||
* @return CurlHandle |
|
||||||
*/ |
|
||||||
protected function createCurlHandle(RequestInterface $request) |
|
||||||
{ |
|
||||||
$wrapper = CurlHandle::factory($request); |
|
||||||
$this->handles[$request] = $wrapper; |
|
||||||
$this->resourceHash[(int) $wrapper->getHandle()] = $request; |
|
||||||
|
|
||||||
return $wrapper; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the data from the multi handle |
|
||||||
*/ |
|
||||||
protected function perform() |
|
||||||
{ |
|
||||||
$event = new Event(array('curl_multi' => $this)); |
|
||||||
|
|
||||||
while ($this->requests) { |
|
||||||
// Notify each request as polling |
|
||||||
$blocking = $total = 0; |
|
||||||
foreach ($this->requests as $request) { |
|
||||||
++$total; |
|
||||||
$event['request'] = $request; |
|
||||||
$request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); |
|
||||||
// The blocking variable just has to be non-falsey to block the loop |
|
||||||
if ($request->getParams()->hasKey(self::BLOCKING)) { |
|
||||||
++$blocking; |
|
||||||
} |
|
||||||
} |
|
||||||
if ($blocking == $total) { |
|
||||||
// Sleep to prevent eating CPU because no requests are actually pending a select call |
|
||||||
usleep(500); |
|
||||||
} else { |
|
||||||
$this->executeHandles(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Execute and select curl handles |
|
||||||
*/ |
|
||||||
private function executeHandles() |
|
||||||
{ |
|
||||||
// The first curl_multi_select often times out no matter what, but is usually required for fast transfers |
|
||||||
$selectTimeout = 0.001; |
|
||||||
$active = false; |
|
||||||
do { |
|
||||||
while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); |
|
||||||
$this->checkCurlResult($mrc); |
|
||||||
$this->processMessages(); |
|
||||||
if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { |
|
||||||
// Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 |
|
||||||
usleep(150); |
|
||||||
} |
|
||||||
$selectTimeout = $this->selectTimeout; |
|
||||||
} while ($active); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Process any received curl multi messages |
|
||||||
*/ |
|
||||||
private function processMessages() |
|
||||||
{ |
|
||||||
while ($done = curl_multi_info_read($this->multiHandle)) { |
|
||||||
$request = $this->resourceHash[(int) $done['handle']]; |
|
||||||
try { |
|
||||||
$this->processResponse($request, $this->handles[$request], $done); |
|
||||||
$this->successful[] = $request; |
|
||||||
} catch (\Exception $e) { |
|
||||||
$this->removeErroredRequest($request, $e); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a request that encountered an exception |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to remove |
|
||||||
* @param \Exception $e Exception encountered |
|
||||||
*/ |
|
||||||
protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) |
|
||||||
{ |
|
||||||
$this->exceptions[] = array('request' => $request, 'exception' => $e); |
|
||||||
$this->remove($request); |
|
||||||
$this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check for errors and fix headers of a request based on a curl response |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to process |
|
||||||
* @param CurlHandle $handle Curl handle object |
|
||||||
* @param array $curl Array returned from curl_multi_info_read |
|
||||||
* |
|
||||||
* @throws CurlException on Curl error |
|
||||||
*/ |
|
||||||
protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) |
|
||||||
{ |
|
||||||
// Set the transfer stats on the response |
|
||||||
$handle->updateRequestFromTransfer($request); |
|
||||||
// Check if a cURL exception occurred, and if so, notify things |
|
||||||
$curlException = $this->isCurlException($request, $handle, $curl); |
|
||||||
|
|
||||||
// Always remove completed curl handles. They can be added back again |
|
||||||
// via events if needed (e.g. ExponentialBackoffPlugin) |
|
||||||
$this->removeHandle($request); |
|
||||||
|
|
||||||
if (!$curlException) { |
|
||||||
if ($this->validateResponseWasSet($request)) { |
|
||||||
$state = $request->setState( |
|
||||||
RequestInterface::STATE_COMPLETE, |
|
||||||
array('handle' => $handle) |
|
||||||
); |
|
||||||
// Only remove the request if it wasn't resent as a result of |
|
||||||
// the state change |
|
||||||
if ($state != RequestInterface::STATE_TRANSFER) { |
|
||||||
$this->remove($request); |
|
||||||
} |
|
||||||
} |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Set the state of the request to an error |
|
||||||
$state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); |
|
||||||
// Allow things to ignore the error if possible |
|
||||||
if ($state != RequestInterface::STATE_TRANSFER) { |
|
||||||
$this->remove($request); |
|
||||||
} |
|
||||||
|
|
||||||
// The error was not handled, so fail |
|
||||||
if ($state == RequestInterface::STATE_ERROR) { |
|
||||||
/** @var CurlException $curlException */ |
|
||||||
throw $curlException; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a curl handle from the curl multi object |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request that owns the handle |
|
||||||
*/ |
|
||||||
protected function removeHandle(RequestInterface $request) |
|
||||||
{ |
|
||||||
if (isset($this->handles[$request])) { |
|
||||||
$handle = $this->handles[$request]; |
|
||||||
curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); |
|
||||||
unset($this->handles[$request]); |
|
||||||
unset($this->resourceHash[(int) $handle->getHandle()]); |
|
||||||
$handle->close(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check if a cURL transfer resulted in what should be an exception |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to check |
|
||||||
* @param CurlHandle $handle Curl handle object |
|
||||||
* @param array $curl Array returned from curl_multi_info_read |
|
||||||
* |
|
||||||
* @return CurlException|bool |
|
||||||
*/ |
|
||||||
private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) |
|
||||||
{ |
|
||||||
if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
$handle->setErrorNo($curl['result']); |
|
||||||
$e = new CurlException(sprintf('[curl] %s: %s [url] %s', |
|
||||||
$handle->getErrorNo(), $handle->getError(), $handle->getUrl())); |
|
||||||
$e->setCurlHandle($handle) |
|
||||||
->setRequest($request) |
|
||||||
->setCurlInfo($handle->getInfo()) |
|
||||||
->setError($handle->getError(), $handle->getErrorNo()); |
|
||||||
|
|
||||||
return $e; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Throw an exception for a cURL multi response if needed |
|
||||||
* |
|
||||||
* @param int $code Curl response code |
|
||||||
* @throws CurlException |
|
||||||
*/ |
|
||||||
private function checkCurlResult($code) |
|
||||||
{ |
|
||||||
if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { |
|
||||||
throw new CurlException(isset($this->multiErrors[$code]) |
|
||||||
? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" |
|
||||||
: 'Unexpected cURL error: ' . $code |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @link https://github.com/guzzle/guzzle/issues/710 |
|
||||||
*/ |
|
||||||
private function validateResponseWasSet(RequestInterface $request) |
|
||||||
{ |
|
||||||
if ($request->getResponse()) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
$body = $request instanceof EntityEnclosingRequestInterface |
|
||||||
? $request->getBody() |
|
||||||
: null; |
|
||||||
|
|
||||||
if (!$body) { |
|
||||||
$rex = new RequestException( |
|
||||||
'No response was received for a request with no body. This' |
|
||||||
. ' could mean that you are saturating your network.' |
|
||||||
); |
|
||||||
$rex->setRequest($request); |
|
||||||
$this->removeErroredRequest($request, $rex); |
|
||||||
} elseif (!$body->isSeekable() || !$body->seek(0)) { |
|
||||||
// Nothing we can do with this. Sorry! |
|
||||||
$rex = new RequestException( |
|
||||||
'The connection was unexpectedly closed. The request would' |
|
||||||
. ' have been retried, but attempting to rewind the' |
|
||||||
. ' request body failed.' |
|
||||||
); |
|
||||||
$rex->setRequest($request); |
|
||||||
$this->removeErroredRequest($request, $rex); |
|
||||||
} else { |
|
||||||
$this->remove($request); |
|
||||||
// Add the request back to the batch to retry automatically. |
|
||||||
$this->requests[] = $request; |
|
||||||
$this->addHandle($request); |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
@ -1,58 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
use Guzzle\Common\HasDispatcherInterface; |
|
||||||
use Guzzle\Common\Exception\ExceptionCollection; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Interface for sending a pool of {@see RequestInterface} objects in parallel |
|
||||||
*/ |
|
||||||
interface CurlMultiInterface extends \Countable, HasDispatcherInterface |
|
||||||
{ |
|
||||||
const POLLING_REQUEST = 'curl_multi.polling_request'; |
|
||||||
const ADD_REQUEST = 'curl_multi.add_request'; |
|
||||||
const REMOVE_REQUEST = 'curl_multi.remove_request'; |
|
||||||
const MULTI_EXCEPTION = 'curl_multi.exception'; |
|
||||||
const BLOCKING = 'curl_multi.blocking'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Add a request to the pool. |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to add |
|
||||||
* |
|
||||||
* @return CurlMultiInterface |
|
||||||
*/ |
|
||||||
public function add(RequestInterface $request); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an array of attached {@see RequestInterface} objects |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function all(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a request from the pool. |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to remove |
|
||||||
* |
|
||||||
* @return bool Returns true on success or false on failure |
|
||||||
*/ |
|
||||||
public function remove(RequestInterface $request); |
|
||||||
|
|
||||||
/** |
|
||||||
* Reset the state and remove any attached RequestInterface objects |
|
||||||
* |
|
||||||
* @param bool $hard Set to true to close and reopen any open multi handles |
|
||||||
*/ |
|
||||||
public function reset($hard = false); |
|
||||||
|
|
||||||
/** |
|
||||||
* Send a pool of {@see RequestInterface} requests. |
|
||||||
* |
|
||||||
* @throws ExceptionCollection if any requests threw exceptions during the transfer. |
|
||||||
*/ |
|
||||||
public function send(); |
|
||||||
} |
|
@ -1,150 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
use Guzzle\Common\AbstractHasDispatcher; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Proxies requests and connections to a pool of internal curl_multi handles. Each recursive call will add requests |
|
||||||
* to the next available CurlMulti handle. |
|
||||||
*/ |
|
||||||
class CurlMultiProxy extends AbstractHasDispatcher implements CurlMultiInterface |
|
||||||
{ |
|
||||||
protected $handles = array(); |
|
||||||
protected $groups = array(); |
|
||||||
protected $queued = array(); |
|
||||||
protected $maxHandles; |
|
||||||
protected $selectTimeout; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param int $maxHandles The maximum number of idle CurlMulti handles to allow to remain open |
|
||||||
* @param float $selectTimeout timeout for curl_multi_select |
|
||||||
*/ |
|
||||||
public function __construct($maxHandles = 3, $selectTimeout = 1.0) |
|
||||||
{ |
|
||||||
$this->maxHandles = $maxHandles; |
|
||||||
$this->selectTimeout = $selectTimeout; |
|
||||||
// You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. |
|
||||||
// These two statements autoload classes before a system runs out of file descriptors so that you can get back |
|
||||||
// valuable error messages if you run out. |
|
||||||
class_exists('Guzzle\Http\Message\Response'); |
|
||||||
class_exists('Guzzle\Http\Exception\CurlException'); |
|
||||||
} |
|
||||||
|
|
||||||
public function add(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->queued[] = $request; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function all() |
|
||||||
{ |
|
||||||
$requests = $this->queued; |
|
||||||
foreach ($this->handles as $handle) { |
|
||||||
$requests = array_merge($requests, $handle->all()); |
|
||||||
} |
|
||||||
|
|
||||||
return $requests; |
|
||||||
} |
|
||||||
|
|
||||||
public function remove(RequestInterface $request) |
|
||||||
{ |
|
||||||
foreach ($this->queued as $i => $r) { |
|
||||||
if ($request === $r) { |
|
||||||
unset($this->queued[$i]); |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
foreach ($this->handles as $handle) { |
|
||||||
if ($handle->remove($request)) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
public function reset($hard = false) |
|
||||||
{ |
|
||||||
$this->queued = array(); |
|
||||||
$this->groups = array(); |
|
||||||
foreach ($this->handles as $handle) { |
|
||||||
$handle->reset(); |
|
||||||
} |
|
||||||
if ($hard) { |
|
||||||
$this->handles = array(); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function send() |
|
||||||
{ |
|
||||||
if ($this->queued) { |
|
||||||
$group = $this->getAvailableHandle(); |
|
||||||
// Add this handle to a list of handles than is claimed |
|
||||||
$this->groups[] = $group; |
|
||||||
while ($request = array_shift($this->queued)) { |
|
||||||
$group->add($request); |
|
||||||
} |
|
||||||
try { |
|
||||||
$group->send(); |
|
||||||
array_pop($this->groups); |
|
||||||
$this->cleanupHandles(); |
|
||||||
} catch (\Exception $e) { |
|
||||||
// Remove the group and cleanup if an exception was encountered and no more requests in group |
|
||||||
if (!$group->count()) { |
|
||||||
array_pop($this->groups); |
|
||||||
$this->cleanupHandles(); |
|
||||||
} |
|
||||||
throw $e; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->all()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an existing available CurlMulti handle or create a new one |
|
||||||
* |
|
||||||
* @return CurlMulti |
|
||||||
*/ |
|
||||||
protected function getAvailableHandle() |
|
||||||
{ |
|
||||||
// Grab a handle that is not claimed |
|
||||||
foreach ($this->handles as $h) { |
|
||||||
if (!in_array($h, $this->groups, true)) { |
|
||||||
return $h; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// All are claimed, so create one |
|
||||||
$handle = new CurlMulti($this->selectTimeout); |
|
||||||
$handle->setEventDispatcher($this->getEventDispatcher()); |
|
||||||
$this->handles[] = $handle; |
|
||||||
|
|
||||||
return $handle; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Trims down unused CurlMulti handles to limit the number of open connections |
|
||||||
*/ |
|
||||||
protected function cleanupHandles() |
|
||||||
{ |
|
||||||
if ($diff = max(0, count($this->handles) - $this->maxHandles)) { |
|
||||||
for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { |
|
||||||
if (!count($this->handles[$i])) { |
|
||||||
unset($this->handles[$i]); |
|
||||||
$diff--; |
|
||||||
} |
|
||||||
} |
|
||||||
$this->handles = array_values($this->handles); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,66 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
/** |
|
||||||
* Class used for querying curl_version data |
|
||||||
*/ |
|
||||||
class CurlVersion |
|
||||||
{ |
|
||||||
/** @var array curl_version() information */ |
|
||||||
protected $version; |
|
||||||
|
|
||||||
/** @var CurlVersion */ |
|
||||||
protected static $instance; |
|
||||||
|
|
||||||
/** @var string Default user agent */ |
|
||||||
protected $userAgent; |
|
||||||
|
|
||||||
/** |
|
||||||
* @return CurlVersion |
|
||||||
*/ |
|
||||||
public static function getInstance() |
|
||||||
{ |
|
||||||
if (!self::$instance) { |
|
||||||
self::$instance = new self(); |
|
||||||
} |
|
||||||
|
|
||||||
return self::$instance; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all of the curl_version() data |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getAll() |
|
||||||
{ |
|
||||||
if (!$this->version) { |
|
||||||
$this->version = curl_version(); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->version; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a specific type of curl information |
|
||||||
* |
|
||||||
* @param string $type Version information to retrieve. This value is one of: |
|
||||||
* - version_number: cURL 24 bit version number |
|
||||||
* - version: cURL version number, as a string |
|
||||||
* - ssl_version_number: OpenSSL 24 bit version number |
|
||||||
* - ssl_version: OpenSSL version number, as a string |
|
||||||
* - libz_version: zlib version number, as a string |
|
||||||
* - host: Information about the host where cURL was built |
|
||||||
* - features: A bitmask of the CURL_VERSION_XXX constants |
|
||||||
* - protocols: An array of protocols names supported by cURL |
|
||||||
* |
|
||||||
* @return string|float|bool if the $type is found, and false if not found |
|
||||||
*/ |
|
||||||
public function get($type) |
|
||||||
{ |
|
||||||
$version = $this->getAll(); |
|
||||||
|
|
||||||
return isset($version[$type]) ? $version[$type] : false; |
|
||||||
} |
|
||||||
} |
|
@ -1,147 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Curl; |
|
||||||
|
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\EntityBody; |
|
||||||
use Guzzle\Http\Message\Response; |
|
||||||
|
|
||||||
/** |
|
||||||
* Mediator between curl handles and request objects |
|
||||||
*/ |
|
||||||
class RequestMediator |
|
||||||
{ |
|
||||||
/** @var RequestInterface */ |
|
||||||
protected $request; |
|
||||||
|
|
||||||
/** @var bool Whether or not to emit read/write events */ |
|
||||||
protected $emitIo; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param RequestInterface $request Request to mediate |
|
||||||
* @param bool $emitIo Set to true to dispatch events on input and output |
|
||||||
*/ |
|
||||||
public function __construct(RequestInterface $request, $emitIo = false) |
|
||||||
{ |
|
||||||
$this->request = $request; |
|
||||||
$this->emitIo = $emitIo; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Receive a response header from curl |
|
||||||
* |
|
||||||
* @param resource $curl Curl handle |
|
||||||
* @param string $header Received header |
|
||||||
* |
|
||||||
* @return int |
|
||||||
*/ |
|
||||||
public function receiveResponseHeader($curl, $header) |
|
||||||
{ |
|
||||||
static $normalize = array("\r", "\n"); |
|
||||||
$length = strlen($header); |
|
||||||
$header = str_replace($normalize, '', $header); |
|
||||||
|
|
||||||
if (strpos($header, 'HTTP/') === 0) { |
|
||||||
|
|
||||||
$startLine = explode(' ', $header, 3); |
|
||||||
$code = $startLine[1]; |
|
||||||
$status = isset($startLine[2]) ? $startLine[2] : ''; |
|
||||||
|
|
||||||
// Only download the body of the response to the specified response |
|
||||||
// body when a successful response is received. |
|
||||||
if ($code >= 200 && $code < 300) { |
|
||||||
$body = $this->request->getResponseBody(); |
|
||||||
} else { |
|
||||||
$body = EntityBody::factory(); |
|
||||||
} |
|
||||||
|
|
||||||
$response = new Response($code, null, $body); |
|
||||||
$response->setStatus($code, $status); |
|
||||||
$this->request->startResponse($response); |
|
||||||
|
|
||||||
$this->request->dispatch('request.receive.status_line', array( |
|
||||||
'request' => $this, |
|
||||||
'line' => $header, |
|
||||||
'status_code' => $code, |
|
||||||
'reason_phrase' => $status |
|
||||||
)); |
|
||||||
|
|
||||||
} elseif ($pos = strpos($header, ':')) { |
|
||||||
$this->request->getResponse()->addHeader( |
|
||||||
trim(substr($header, 0, $pos)), |
|
||||||
trim(substr($header, $pos + 1)) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $length; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Received a progress notification |
|
||||||
* |
|
||||||
* @param int $downloadSize Total download size |
|
||||||
* @param int $downloaded Amount of bytes downloaded |
|
||||||
* @param int $uploadSize Total upload size |
|
||||||
* @param int $uploaded Amount of bytes uploaded |
|
||||||
* @param resource $handle CurlHandle object |
|
||||||
*/ |
|
||||||
public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) |
|
||||||
{ |
|
||||||
$this->request->dispatch('curl.callback.progress', array( |
|
||||||
'request' => $this->request, |
|
||||||
'handle' => $handle, |
|
||||||
'download_size' => $downloadSize, |
|
||||||
'downloaded' => $downloaded, |
|
||||||
'upload_size' => $uploadSize, |
|
||||||
'uploaded' => $uploaded |
|
||||||
)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Write data to the response body of a request |
|
||||||
* |
|
||||||
* @param resource $curl Curl handle |
|
||||||
* @param string $write Data that was received |
|
||||||
* |
|
||||||
* @return int |
|
||||||
*/ |
|
||||||
public function writeResponseBody($curl, $write) |
|
||||||
{ |
|
||||||
if ($this->emitIo) { |
|
||||||
$this->request->dispatch('curl.callback.write', array( |
|
||||||
'request' => $this->request, |
|
||||||
'write' => $write |
|
||||||
)); |
|
||||||
} |
|
||||||
|
|
||||||
if ($response = $this->request->getResponse()) { |
|
||||||
return $response->getBody()->write($write); |
|
||||||
} else { |
|
||||||
// Unexpected data received before response headers - abort transfer |
|
||||||
return 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Read data from the request body and send it to curl |
|
||||||
* |
|
||||||
* @param resource $ch Curl handle |
|
||||||
* @param resource $fd File descriptor |
|
||||||
* @param int $length Amount of data to read |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function readRequestBody($ch, $fd, $length) |
|
||||||
{ |
|
||||||
if (!($body = $this->request->getBody())) { |
|
||||||
return ''; |
|
||||||
} |
|
||||||
|
|
||||||
$read = (string) $body->read($length); |
|
||||||
if ($this->emitIo) { |
|
||||||
$this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); |
|
||||||
} |
|
||||||
|
|
||||||
return $read; |
|
||||||
} |
|
||||||
} |
|
@ -1,201 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Common\Version; |
|
||||||
use Guzzle\Stream\Stream; |
|
||||||
use Guzzle\Common\Exception\InvalidArgumentException; |
|
||||||
use Guzzle\Http\Mimetypes; |
|
||||||
|
|
||||||
/** |
|
||||||
* Entity body used with an HTTP request or response |
|
||||||
*/ |
|
||||||
class EntityBody extends Stream implements EntityBodyInterface |
|
||||||
{ |
|
||||||
/** @var bool Content-Encoding of the entity body if known */ |
|
||||||
protected $contentEncoding = false; |
|
||||||
|
|
||||||
/** @var callable Method to invoke for rewinding a stream */ |
|
||||||
protected $rewindFunction; |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a new EntityBody based on the input type |
|
||||||
* |
|
||||||
* @param resource|string|EntityBody $resource Entity body data |
|
||||||
* @param int $size Size of the data contained in the resource |
|
||||||
* |
|
||||||
* @return EntityBody |
|
||||||
* @throws InvalidArgumentException if the $resource arg is not a resource or string |
|
||||||
*/ |
|
||||||
public static function factory($resource = '', $size = null) |
|
||||||
{ |
|
||||||
if ($resource instanceof EntityBodyInterface) { |
|
||||||
return $resource; |
|
||||||
} |
|
||||||
|
|
||||||
switch (gettype($resource)) { |
|
||||||
case 'string': |
|
||||||
return self::fromString($resource); |
|
||||||
case 'resource': |
|
||||||
return new static($resource, $size); |
|
||||||
case 'object': |
|
||||||
if (method_exists($resource, '__toString')) { |
|
||||||
return self::fromString((string) $resource); |
|
||||||
} |
|
||||||
break; |
|
||||||
case 'array': |
|
||||||
return self::fromString(http_build_query($resource)); |
|
||||||
} |
|
||||||
|
|
||||||
throw new InvalidArgumentException('Invalid resource type'); |
|
||||||
} |
|
||||||
|
|
||||||
public function setRewindFunction($callable) |
|
||||||
{ |
|
||||||
if (!is_callable($callable)) { |
|
||||||
throw new InvalidArgumentException('Must specify a callable'); |
|
||||||
} |
|
||||||
|
|
||||||
$this->rewindFunction = $callable; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function rewind() |
|
||||||
{ |
|
||||||
return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a new EntityBody from a string |
|
||||||
* |
|
||||||
* @param string $string String of data |
|
||||||
* |
|
||||||
* @return EntityBody |
|
||||||
*/ |
|
||||||
public static function fromString($string) |
|
||||||
{ |
|
||||||
$stream = fopen('php://temp', 'r+'); |
|
||||||
if ($string !== '') { |
|
||||||
fwrite($stream, $string); |
|
||||||
rewind($stream); |
|
||||||
} |
|
||||||
|
|
||||||
return new static($stream); |
|
||||||
} |
|
||||||
|
|
||||||
public function compress($filter = 'zlib.deflate') |
|
||||||
{ |
|
||||||
$result = $this->handleCompression($filter); |
|
||||||
$this->contentEncoding = $result ? $filter : false; |
|
||||||
|
|
||||||
return $result; |
|
||||||
} |
|
||||||
|
|
||||||
public function uncompress($filter = 'zlib.inflate') |
|
||||||
{ |
|
||||||
$offsetStart = 0; |
|
||||||
|
|
||||||
// When inflating gzipped data, the first 10 bytes must be stripped |
|
||||||
// if a gzip header is present |
|
||||||
if ($filter == 'zlib.inflate') { |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { |
|
||||||
$offsetStart = 10; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$this->contentEncoding = false; |
|
||||||
|
|
||||||
return $this->handleCompression($filter, $offsetStart); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentLength() |
|
||||||
{ |
|
||||||
return $this->getSize(); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentType() |
|
||||||
{ |
|
||||||
return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentMd5($rawOutput = false, $base64Encode = false) |
|
||||||
{ |
|
||||||
if ($hash = self::getHash($this, 'md5', $rawOutput)) { |
|
||||||
return $hash && $base64Encode ? base64_encode($hash) : $hash; |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Calculate the MD5 hash of an entity body |
|
||||||
* |
|
||||||
* @param EntityBodyInterface $body Entity body to calculate the hash for |
|
||||||
* @param bool $rawOutput Whether or not to use raw output |
|
||||||
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) |
|
||||||
* |
|
||||||
* @return bool|string Returns an MD5 string on success or FALSE on failure |
|
||||||
* @deprecated This will be deprecated soon |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) |
|
||||||
{ |
|
||||||
Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); |
|
||||||
return $body->getContentMd5($rawOutput, $base64Encode); |
|
||||||
} |
|
||||||
|
|
||||||
public function setStreamFilterContentEncoding($streamFilterContentEncoding) |
|
||||||
{ |
|
||||||
$this->contentEncoding = $streamFilterContentEncoding; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentEncoding() |
|
||||||
{ |
|
||||||
return strtr($this->contentEncoding, array( |
|
||||||
'zlib.deflate' => 'gzip', |
|
||||||
'bzip2.compress' => 'compress' |
|
||||||
)) ?: false; |
|
||||||
} |
|
||||||
|
|
||||||
protected function handleCompression($filter, $offsetStart = 0) |
|
||||||
{ |
|
||||||
// @codeCoverageIgnoreStart |
|
||||||
if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
// @codeCoverageIgnoreEnd |
|
||||||
|
|
||||||
$handle = fopen('php://temp', 'r+'); |
|
||||||
$filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); |
|
||||||
if (!$filter) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Seek to the offset start if possible |
|
||||||
$this->seek($offsetStart); |
|
||||||
while ($data = fread($this->stream, 8096)) { |
|
||||||
fwrite($handle, $data); |
|
||||||
} |
|
||||||
|
|
||||||
fclose($this->stream); |
|
||||||
$this->stream = $handle; |
|
||||||
stream_filter_remove($filter); |
|
||||||
$stat = fstat($this->stream); |
|
||||||
$this->size = $stat['size']; |
|
||||||
$this->rebuildCache(); |
|
||||||
$this->seek(0); |
|
||||||
|
|
||||||
// Remove any existing rewind function as the underlying stream has been replaced |
|
||||||
$this->rewindFunction = null; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
@ -1,73 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Stream\StreamInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Entity body used with an HTTP request or response |
|
||||||
*/ |
|
||||||
interface EntityBodyInterface extends StreamInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Specify a custom callback used to rewind a non-seekable stream. This can be useful entity enclosing requests |
|
||||||
* that are redirected. |
|
||||||
* |
|
||||||
* @param mixed $callable Callable to invoke to rewind a non-seekable stream. The callback must accept an |
|
||||||
* EntityBodyInterface object, perform the rewind if possible, and return a boolean |
|
||||||
* representing whether or not the rewind was successful. |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setRewindFunction($callable); |
|
||||||
|
|
||||||
/** |
|
||||||
* If the stream is readable, compress the data in the stream using deflate compression. The uncompressed stream is |
|
||||||
* then closed, and the compressed stream then becomes the wrapped stream. |
|
||||||
* |
|
||||||
* @param string $filter Compression filter |
|
||||||
* |
|
||||||
* @return bool Returns TRUE on success or FALSE on failure |
|
||||||
*/ |
|
||||||
public function compress($filter = 'zlib.deflate'); |
|
||||||
|
|
||||||
/** |
|
||||||
* Decompress a deflated string. Once uncompressed, the uncompressed string is then used as the wrapped stream. |
|
||||||
* |
|
||||||
* @param string $filter De-compression filter |
|
||||||
* |
|
||||||
* @return bool Returns TRUE on success or FALSE on failure |
|
||||||
*/ |
|
||||||
public function uncompress($filter = 'zlib.inflate'); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the Content-Length of the entity body if possible (alias of getSize) |
|
||||||
* |
|
||||||
* @return int|bool Returns the Content-Length or false on failure |
|
||||||
*/ |
|
||||||
public function getContentLength(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Guess the Content-Type of a local stream |
|
||||||
* |
|
||||||
* @return string|null |
|
||||||
* @see http://www.php.net/manual/en/function.finfo-open.php |
|
||||||
*/ |
|
||||||
public function getContentType(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an MD5 checksum of the stream's contents |
|
||||||
* |
|
||||||
* @param bool $rawOutput Whether or not to use raw output |
|
||||||
* @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) |
|
||||||
* |
|
||||||
* @return bool|string Returns an MD5 string on success or FALSE on failure |
|
||||||
*/ |
|
||||||
public function getContentMd5($rawOutput = false, $base64Encode = false); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the Content-Encoding of the EntityBody |
|
||||||
* |
|
||||||
* @return bool|string |
|
||||||
*/ |
|
||||||
public function getContentEncoding(); |
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
use Guzzle\Http\Message\Response; |
|
||||||
|
|
||||||
/** |
|
||||||
* Http request exception thrown when a bad response is received |
|
||||||
*/ |
|
||||||
class BadResponseException extends RequestException |
|
||||||
{ |
|
||||||
/** @var Response */ |
|
||||||
private $response; |
|
||||||
|
|
||||||
/** |
|
||||||
* Factory method to create a new response exception based on the response code. |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request |
|
||||||
* @param Response $response Response received |
|
||||||
* |
|
||||||
* @return BadResponseException |
|
||||||
*/ |
|
||||||
public static function factory(RequestInterface $request, Response $response) |
|
||||||
{ |
|
||||||
if ($response->isClientError()) { |
|
||||||
$label = 'Client error response'; |
|
||||||
$class = __NAMESPACE__ . '\\ClientErrorResponseException'; |
|
||||||
} elseif ($response->isServerError()) { |
|
||||||
$label = 'Server error response'; |
|
||||||
$class = __NAMESPACE__ . '\\ServerErrorResponseException'; |
|
||||||
} else { |
|
||||||
$label = 'Unsuccessful response'; |
|
||||||
$class = __CLASS__; |
|
||||||
} |
|
||||||
|
|
||||||
$message = $label . PHP_EOL . implode(PHP_EOL, array( |
|
||||||
'[status code] ' . $response->getStatusCode(), |
|
||||||
'[reason phrase] ' . $response->getReasonPhrase(), |
|
||||||
'[url] ' . $request->getUrl(), |
|
||||||
)); |
|
||||||
|
|
||||||
$e = new $class($message); |
|
||||||
$e->setResponse($response); |
|
||||||
$e->setRequest($request); |
|
||||||
|
|
||||||
return $e; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the response that caused the exception |
|
||||||
* |
|
||||||
* @param Response $response Response to set |
|
||||||
*/ |
|
||||||
public function setResponse(Response $response) |
|
||||||
{ |
|
||||||
$this->response = $response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the response that caused the exception |
|
||||||
* |
|
||||||
* @return Response |
|
||||||
*/ |
|
||||||
public function getResponse() |
|
||||||
{ |
|
||||||
return $this->response; |
|
||||||
} |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
/** |
|
||||||
* Exception when a client error is encountered (4xx codes) |
|
||||||
*/ |
|
||||||
class ClientErrorResponseException extends BadResponseException {} |
|
@ -1,7 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
|
|
||||||
class CouldNotRewindStreamException extends RuntimeException implements HttpException {} |
|
@ -1,101 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Http\Curl\CurlHandle; |
|
||||||
|
|
||||||
/** |
|
||||||
* cURL request exception |
|
||||||
*/ |
|
||||||
class CurlException extends RequestException |
|
||||||
{ |
|
||||||
private $curlError; |
|
||||||
private $curlErrorNo; |
|
||||||
private $handle; |
|
||||||
private $curlInfo = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the cURL error message |
|
||||||
* |
|
||||||
* @param string $error Curl error |
|
||||||
* @param int $number Curl error number |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setError($error, $number) |
|
||||||
{ |
|
||||||
$this->curlError = $error; |
|
||||||
$this->curlErrorNo = $number; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the associated curl handle |
|
||||||
* |
|
||||||
* @param CurlHandle $handle Curl handle |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setCurlHandle(CurlHandle $handle) |
|
||||||
{ |
|
||||||
$this->handle = $handle; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the associated cURL handle |
|
||||||
* |
|
||||||
* @return CurlHandle|null |
|
||||||
*/ |
|
||||||
public function getCurlHandle() |
|
||||||
{ |
|
||||||
return $this->handle; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the associated cURL error message |
|
||||||
* |
|
||||||
* @return string|null |
|
||||||
*/ |
|
||||||
public function getError() |
|
||||||
{ |
|
||||||
return $this->curlError; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the associated cURL error number |
|
||||||
* |
|
||||||
* @return int|null |
|
||||||
*/ |
|
||||||
public function getErrorNo() |
|
||||||
{ |
|
||||||
return $this->curlErrorNo; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns curl information about the transfer |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getCurlInfo() |
|
||||||
{ |
|
||||||
return $this->curlInfo; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set curl transfer information |
|
||||||
* |
|
||||||
* @param array $info Array of curl transfer information |
|
||||||
* |
|
||||||
* @return self |
|
||||||
* @link http://php.net/manual/en/function.curl-getinfo.php |
|
||||||
*/ |
|
||||||
public function setCurlInfo(array $info) |
|
||||||
{ |
|
||||||
$this->curlInfo = $info; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\GuzzleException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Http exception interface |
|
||||||
*/ |
|
||||||
interface HttpException extends GuzzleException {} |
|
@ -1,145 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\ExceptionCollection; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Exception encountered during a multi transfer |
|
||||||
*/ |
|
||||||
class MultiTransferException extends ExceptionCollection |
|
||||||
{ |
|
||||||
protected $successfulRequests = array(); |
|
||||||
protected $failedRequests = array(); |
|
||||||
protected $exceptionForRequest = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get all of the requests in the transfer |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getAllRequests() |
|
||||||
{ |
|
||||||
return array_merge($this->successfulRequests, $this->failedRequests); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add to the array of successful requests |
|
||||||
* |
|
||||||
* @param RequestInterface $request Successful request |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addSuccessfulRequest(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->successfulRequests[] = $request; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add to the array of failed requests |
|
||||||
* |
|
||||||
* @param RequestInterface $request Failed request |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addFailedRequest(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->failedRequests[] = $request; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add to the array of failed requests and associate with exceptions |
|
||||||
* |
|
||||||
* @param RequestInterface $request Failed request |
|
||||||
* @param \Exception $exception Exception to add and associate with |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) |
|
||||||
{ |
|
||||||
$this->add($exception) |
|
||||||
->addFailedRequest($request) |
|
||||||
->exceptionForRequest[spl_object_hash($request)] = $exception; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the Exception that caused the given $request to fail |
|
||||||
* |
|
||||||
* @param RequestInterface $request Failed command |
|
||||||
* |
|
||||||
* @return \Exception|null |
|
||||||
*/ |
|
||||||
public function getExceptionForFailedRequest(RequestInterface $request) |
|
||||||
{ |
|
||||||
$oid = spl_object_hash($request); |
|
||||||
|
|
||||||
return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set all of the successful requests |
|
||||||
* |
|
||||||
* @param array Array of requests |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setSuccessfulRequests(array $requests) |
|
||||||
{ |
|
||||||
$this->successfulRequests = $requests; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set all of the failed requests |
|
||||||
* |
|
||||||
* @param array Array of requests |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setFailedRequests(array $requests) |
|
||||||
{ |
|
||||||
$this->failedRequests = $requests; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an array of successful requests sent in the multi transfer |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getSuccessfulRequests() |
|
||||||
{ |
|
||||||
return $this->successfulRequests; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an array of failed requests sent in the multi transfer |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getFailedRequests() |
|
||||||
{ |
|
||||||
return $this->failedRequests; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check if the exception object contains a request |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to check |
|
||||||
* |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function containsRequest(RequestInterface $request) |
|
||||||
{ |
|
||||||
return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); |
|
||||||
} |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
use Guzzle\Common\Exception\RuntimeException; |
|
||||||
use Guzzle\Http\Message\RequestInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Http request exception |
|
||||||
*/ |
|
||||||
class RequestException extends RuntimeException implements HttpException |
|
||||||
{ |
|
||||||
/** @var RequestInterface */ |
|
||||||
protected $request; |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the request that caused the exception |
|
||||||
* |
|
||||||
* @param RequestInterface $request Request to set |
|
||||||
* |
|
||||||
* @return RequestException |
|
||||||
*/ |
|
||||||
public function setRequest(RequestInterface $request) |
|
||||||
{ |
|
||||||
$this->request = $request; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the request that caused the exception |
|
||||||
* |
|
||||||
* @return RequestInterface |
|
||||||
*/ |
|
||||||
public function getRequest() |
|
||||||
{ |
|
||||||
return $this->request; |
|
||||||
} |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
/** |
|
||||||
* Exception when a server error is encountered (5xx codes) |
|
||||||
*/ |
|
||||||
class ServerErrorResponseException extends BadResponseException {} |
|
@ -1,5 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Exception; |
|
||||||
|
|
||||||
class TooManyRedirectsException extends BadResponseException {} |
|
@ -1,83 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http; |
|
||||||
|
|
||||||
use Guzzle\Common\Event; |
|
||||||
use Guzzle\Common\HasDispatcherInterface; |
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher; |
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; |
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* EntityBody decorator that emits events for read and write methods |
|
||||||
*/ |
|
||||||
class IoEmittingEntityBody extends AbstractEntityBodyDecorator implements HasDispatcherInterface |
|
||||||
{ |
|
||||||
/** @var EventDispatcherInterface */ |
|
||||||
protected $eventDispatcher; |
|
||||||
|
|
||||||
public static function getAllEvents() |
|
||||||
{ |
|
||||||
return array('body.read', 'body.write'); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) |
|
||||||
{ |
|
||||||
$this->eventDispatcher = $eventDispatcher; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getEventDispatcher() |
|
||||||
{ |
|
||||||
if (!$this->eventDispatcher) { |
|
||||||
$this->eventDispatcher = new EventDispatcher(); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->eventDispatcher; |
|
||||||
} |
|
||||||
|
|
||||||
public function dispatch($eventName, array $context = array()) |
|
||||||
{ |
|
||||||
return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* {@inheritdoc} |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function addSubscriber(EventSubscriberInterface $subscriber) |
|
||||||
{ |
|
||||||
$this->getEventDispatcher()->addSubscriber($subscriber); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function read($length) |
|
||||||
{ |
|
||||||
$event = array( |
|
||||||
'body' => $this, |
|
||||||
'length' => $length, |
|
||||||
'read' => $this->body->read($length) |
|
||||||
); |
|
||||||
$this->dispatch('body.read', $event); |
|
||||||
|
|
||||||
return $event['read']; |
|
||||||
} |
|
||||||
|
|
||||||
public function write($string) |
|
||||||
{ |
|
||||||
$event = array( |
|
||||||
'body' => $this, |
|
||||||
'write' => $string, |
|
||||||
'result' => $this->body->write($string) |
|
||||||
); |
|
||||||
$this->dispatch('body.write', $event); |
|
||||||
|
|
||||||
return $event['result']; |
|
||||||
} |
|
||||||
} |
|
@ -1,220 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message; |
|
||||||
|
|
||||||
use Guzzle\Common\Version; |
|
||||||
use Guzzle\Common\Collection; |
|
||||||
use Guzzle\Http\Message\Header\HeaderCollection; |
|
||||||
use Guzzle\Http\Message\Header\HeaderFactory; |
|
||||||
use Guzzle\Http\Message\Header\HeaderFactoryInterface; |
|
||||||
use Guzzle\Http\Message\Header\HeaderInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Abstract HTTP request/response message |
|
||||||
*/ |
|
||||||
abstract class AbstractMessage implements MessageInterface |
|
||||||
{ |
|
||||||
/** @var array HTTP header collection */ |
|
||||||
protected $headers; |
|
||||||
|
|
||||||
/** @var HeaderFactoryInterface $headerFactory */ |
|
||||||
protected $headerFactory; |
|
||||||
|
|
||||||
/** @var Collection Custom message parameters that are extendable by plugins */ |
|
||||||
protected $params; |
|
||||||
|
|
||||||
/** @var string Message protocol */ |
|
||||||
protected $protocol = 'HTTP'; |
|
||||||
|
|
||||||
/** @var string HTTP protocol version of the message */ |
|
||||||
protected $protocolVersion = '1.1'; |
|
||||||
|
|
||||||
public function __construct() |
|
||||||
{ |
|
||||||
$this->params = new Collection(); |
|
||||||
$this->headerFactory = new HeaderFactory(); |
|
||||||
$this->headers = new HeaderCollection(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the header factory to use to create headers |
|
||||||
* |
|
||||||
* @param HeaderFactoryInterface $factory |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setHeaderFactory(HeaderFactoryInterface $factory) |
|
||||||
{ |
|
||||||
$this->headerFactory = $factory; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getParams() |
|
||||||
{ |
|
||||||
return $this->params; |
|
||||||
} |
|
||||||
|
|
||||||
public function addHeader($header, $value) |
|
||||||
{ |
|
||||||
if (isset($this->headers[$header])) { |
|
||||||
$this->headers[$header]->add($value); |
|
||||||
} elseif ($value instanceof HeaderInterface) { |
|
||||||
$this->headers[$header] = $value; |
|
||||||
} else { |
|
||||||
$this->headers[$header] = $this->headerFactory->createHeader($header, $value); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function addHeaders(array $headers) |
|
||||||
{ |
|
||||||
foreach ($headers as $key => $value) { |
|
||||||
$this->addHeader($key, $value); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getHeader($header) |
|
||||||
{ |
|
||||||
return $this->headers[$header]; |
|
||||||
} |
|
||||||
|
|
||||||
public function getHeaders() |
|
||||||
{ |
|
||||||
return $this->headers; |
|
||||||
} |
|
||||||
|
|
||||||
public function getHeaderLines() |
|
||||||
{ |
|
||||||
$headers = array(); |
|
||||||
foreach ($this->headers as $value) { |
|
||||||
$headers[] = $value->getName() . ': ' . $value; |
|
||||||
} |
|
||||||
|
|
||||||
return $headers; |
|
||||||
} |
|
||||||
|
|
||||||
public function setHeader($header, $value) |
|
||||||
{ |
|
||||||
unset($this->headers[$header]); |
|
||||||
$this->addHeader($header, $value); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function setHeaders(array $headers) |
|
||||||
{ |
|
||||||
$this->headers->clear(); |
|
||||||
foreach ($headers as $key => $value) { |
|
||||||
$this->addHeader($key, $value); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function hasHeader($header) |
|
||||||
{ |
|
||||||
return isset($this->headers[$header]); |
|
||||||
} |
|
||||||
|
|
||||||
public function removeHeader($header) |
|
||||||
{ |
|
||||||
unset($this->headers[$header]); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated Use $message->getHeader()->parseParams() |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function getTokenizedHeader($header, $token = ';') |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); |
|
||||||
if ($this->hasHeader($header)) { |
|
||||||
$data = new Collection(); |
|
||||||
foreach ($this->getHeader($header)->parseParams() as $values) { |
|
||||||
foreach ($values as $key => $value) { |
|
||||||
if ($value === '') { |
|
||||||
$data->set($data->count(), $key); |
|
||||||
} else { |
|
||||||
$data->add($key, $value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return $data; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function setTokenizedHeader($header, $data, $token = ';') |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated.'); |
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function getCacheControlDirective($directive) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); |
|
||||||
if (!($header = $this->getHeader('Cache-Control'))) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
return $header->getDirective($directive); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function hasCacheControlDirective($directive) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); |
|
||||||
if ($header = $this->getHeader('Cache-Control')) { |
|
||||||
return $header->hasDirective($directive); |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function addCacheControlDirective($directive, $value = true) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); |
|
||||||
if (!($header = $this->getHeader('Cache-Control'))) { |
|
||||||
$this->addHeader('Cache-Control', ''); |
|
||||||
$header = $this->getHeader('Cache-Control'); |
|
||||||
} |
|
||||||
|
|
||||||
$header->addDirective($directive, $value); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function removeCacheControlDirective($directive) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); |
|
||||||
if ($header = $this->getHeader('Cache-Control')) { |
|
||||||
$header->removeDirective($directive); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
} |
|
@ -1,247 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message; |
|
||||||
|
|
||||||
use Guzzle\Http\EntityBody; |
|
||||||
use Guzzle\Http\EntityBodyInterface; |
|
||||||
use Guzzle\Http\QueryString; |
|
||||||
use Guzzle\Http\RedirectPlugin; |
|
||||||
use Guzzle\Http\Exception\RequestException; |
|
||||||
|
|
||||||
/** |
|
||||||
* HTTP request that sends an entity-body in the request message (POST, PUT, PATCH, DELETE) |
|
||||||
*/ |
|
||||||
class EntityEnclosingRequest extends Request implements EntityEnclosingRequestInterface |
|
||||||
{ |
|
||||||
/** @var int When the size of the body is greater than 1MB, then send Expect: 100-Continue */ |
|
||||||
protected $expectCutoff = 1048576; |
|
||||||
|
|
||||||
/** @var EntityBodyInterface $body Body of the request */ |
|
||||||
protected $body; |
|
||||||
|
|
||||||
/** @var QueryString POST fields to use in the EntityBody */ |
|
||||||
protected $postFields; |
|
||||||
|
|
||||||
/** @var array POST files to send with the request */ |
|
||||||
protected $postFiles = array(); |
|
||||||
|
|
||||||
public function __construct($method, $url, $headers = array()) |
|
||||||
{ |
|
||||||
$this->postFields = new QueryString(); |
|
||||||
parent::__construct($method, $url, $headers); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
public function __toString() |
|
||||||
{ |
|
||||||
// Only attempt to include the POST data if it's only fields |
|
||||||
if (count($this->postFields) && empty($this->postFiles)) { |
|
||||||
return parent::__toString() . (string) $this->postFields; |
|
||||||
} |
|
||||||
|
|
||||||
return parent::__toString() . $this->body; |
|
||||||
} |
|
||||||
|
|
||||||
public function setState($state, array $context = array()) |
|
||||||
{ |
|
||||||
parent::setState($state, $context); |
|
||||||
if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { |
|
||||||
$this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->state; |
|
||||||
} |
|
||||||
|
|
||||||
public function setBody($body, $contentType = null) |
|
||||||
{ |
|
||||||
$this->body = EntityBody::factory($body); |
|
||||||
|
|
||||||
// Auto detect the Content-Type from the path of the request if possible |
|
||||||
if ($contentType === null && !$this->hasHeader('Content-Type')) { |
|
||||||
$contentType = $this->body->getContentType(); |
|
||||||
} |
|
||||||
|
|
||||||
if ($contentType) { |
|
||||||
$this->setHeader('Content-Type', $contentType); |
|
||||||
} |
|
||||||
|
|
||||||
// Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. |
|
||||||
if (!$this->body->isSeekable() && $this->expectCutoff !== false) { |
|
||||||
$this->setHeader('Expect', '100-Continue'); |
|
||||||
} |
|
||||||
|
|
||||||
// Set the Content-Length header if it can be determined |
|
||||||
$size = $this->body->getContentLength(); |
|
||||||
if ($size !== null && $size !== false) { |
|
||||||
$this->setHeader('Content-Length', $size); |
|
||||||
if ($size > $this->expectCutoff) { |
|
||||||
$this->setHeader('Expect', '100-Continue'); |
|
||||||
} |
|
||||||
} elseif (!$this->hasHeader('Content-Length')) { |
|
||||||
if ('1.1' == $this->protocolVersion) { |
|
||||||
$this->setHeader('Transfer-Encoding', 'chunked'); |
|
||||||
} else { |
|
||||||
throw new RequestException( |
|
||||||
'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getBody() |
|
||||||
{ |
|
||||||
return $this->body; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. |
|
||||||
* |
|
||||||
* @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setExpectHeaderCutoff($size) |
|
||||||
{ |
|
||||||
$this->expectCutoff = $size; |
|
||||||
if ($size === false || !$this->body) { |
|
||||||
$this->removeHeader('Expect'); |
|
||||||
} elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { |
|
||||||
$this->setHeader('Expect', '100-Continue'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function configureRedirects($strict = false, $maxRedirects = 5) |
|
||||||
{ |
|
||||||
$this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); |
|
||||||
if ($maxRedirects == 0) { |
|
||||||
$this->getParams()->set(RedirectPlugin::DISABLE, true); |
|
||||||
} else { |
|
||||||
$this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getPostField($field) |
|
||||||
{ |
|
||||||
return $this->postFields->get($field); |
|
||||||
} |
|
||||||
|
|
||||||
public function getPostFields() |
|
||||||
{ |
|
||||||
return $this->postFields; |
|
||||||
} |
|
||||||
|
|
||||||
public function setPostField($key, $value) |
|
||||||
{ |
|
||||||
$this->postFields->set($key, $value); |
|
||||||
$this->processPostFields(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function addPostFields($fields) |
|
||||||
{ |
|
||||||
$this->postFields->merge($fields); |
|
||||||
$this->processPostFields(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function removePostField($field) |
|
||||||
{ |
|
||||||
$this->postFields->remove($field); |
|
||||||
$this->processPostFields(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getPostFiles() |
|
||||||
{ |
|
||||||
return $this->postFiles; |
|
||||||
} |
|
||||||
|
|
||||||
public function getPostFile($fieldName) |
|
||||||
{ |
|
||||||
return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; |
|
||||||
} |
|
||||||
|
|
||||||
public function removePostFile($fieldName) |
|
||||||
{ |
|
||||||
unset($this->postFiles[$fieldName]); |
|
||||||
$this->processPostFields(); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function addPostFile($field, $filename = null, $contentType = null, $postname = null) |
|
||||||
{ |
|
||||||
$data = null; |
|
||||||
|
|
||||||
if ($field instanceof PostFileInterface) { |
|
||||||
$data = $field; |
|
||||||
} elseif (is_array($filename)) { |
|
||||||
// Allow multiple values to be set in a single key |
|
||||||
foreach ($filename as $file) { |
|
||||||
$this->addPostFile($field, $file, $contentType); |
|
||||||
} |
|
||||||
return $this; |
|
||||||
} elseif (!is_string($filename)) { |
|
||||||
throw new RequestException('The path to a file must be a string'); |
|
||||||
} elseif (!empty($filename)) { |
|
||||||
// Adding an empty file will cause cURL to error out |
|
||||||
$data = new PostFile($field, $filename, $contentType, $postname); |
|
||||||
} |
|
||||||
|
|
||||||
if ($data) { |
|
||||||
if (!isset($this->postFiles[$data->getFieldName()])) { |
|
||||||
$this->postFiles[$data->getFieldName()] = array($data); |
|
||||||
} else { |
|
||||||
$this->postFiles[$data->getFieldName()][] = $data; |
|
||||||
} |
|
||||||
$this->processPostFields(); |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function addPostFiles(array $files) |
|
||||||
{ |
|
||||||
foreach ($files as $key => $file) { |
|
||||||
if ($file instanceof PostFileInterface) { |
|
||||||
$this->addPostFile($file, null, null, false); |
|
||||||
} elseif (is_string($file)) { |
|
||||||
// Convert non-associative array keys into 'file' |
|
||||||
if (is_numeric($key)) { |
|
||||||
$key = 'file'; |
|
||||||
} |
|
||||||
$this->addPostFile($key, $file, null, false); |
|
||||||
} else { |
|
||||||
throw new RequestException('File must be a string or instance of PostFileInterface'); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Determine what type of request should be sent based on post fields |
|
||||||
*/ |
|
||||||
protected function processPostFields() |
|
||||||
{ |
|
||||||
if (!$this->postFiles) { |
|
||||||
$this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); |
|
||||||
} else { |
|
||||||
$this->setHeader('Content-Type', self::MULTIPART); |
|
||||||
if ($this->expectCutoff !== false) { |
|
||||||
$this->setHeader('Expect', '100-Continue'); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,137 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message; |
|
||||||
|
|
||||||
use Guzzle\Http\Exception\RequestException; |
|
||||||
use Guzzle\Http\EntityBodyInterface; |
|
||||||
use Guzzle\Http\QueryString; |
|
||||||
|
|
||||||
/** |
|
||||||
* HTTP request that sends an entity-body in the request message (POST, PUT) |
|
||||||
*/ |
|
||||||
interface EntityEnclosingRequestInterface extends RequestInterface |
|
||||||
{ |
|
||||||
const URL_ENCODED = 'application/x-www-form-urlencoded; charset=utf-8'; |
|
||||||
const MULTIPART = 'multipart/form-data'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Set the body of the request |
|
||||||
* |
|
||||||
* @param string|resource|EntityBodyInterface $body Body to use in the entity body of the request |
|
||||||
* @param string $contentType Content-Type to set. Leave null to use an existing |
|
||||||
* Content-Type or to guess the Content-Type |
|
||||||
* @return self |
|
||||||
* @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined |
|
||||||
*/ |
|
||||||
public function setBody($body, $contentType = null); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the body of the request if set |
|
||||||
* |
|
||||||
* @return EntityBodyInterface|null |
|
||||||
*/ |
|
||||||
public function getBody(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a POST field from the request |
|
||||||
* |
|
||||||
* @param string $field Field to retrieve |
|
||||||
* |
|
||||||
* @return mixed|null |
|
||||||
*/ |
|
||||||
public function getPostField($field); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the post fields that will be used in the request |
|
||||||
* |
|
||||||
* @return QueryString |
|
||||||
*/ |
|
||||||
public function getPostFields(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a POST field value |
|
||||||
* |
|
||||||
* @param string $key Key to set |
|
||||||
* @param string $value Value to set |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function setPostField($key, $value); |
|
||||||
|
|
||||||
/** |
|
||||||
* Add POST fields to use in the request |
|
||||||
* |
|
||||||
* @param QueryString|array $fields POST fields |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addPostFields($fields); |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a POST field or file by name |
|
||||||
* |
|
||||||
* @param string $field Name of the POST field or file to remove |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function removePostField($field); |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns an associative array of POST field names to PostFileInterface objects |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getPostFiles(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a POST file from the request |
|
||||||
* |
|
||||||
* @param string $fieldName POST fields to retrieve |
|
||||||
* |
|
||||||
* @return array|null Returns an array wrapping an array of PostFileInterface objects |
|
||||||
*/ |
|
||||||
public function getPostFile($fieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a POST file from the request |
|
||||||
* |
|
||||||
* @param string $fieldName POST file field name to remove |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function removePostFile($fieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Add a POST file to the upload |
|
||||||
* |
|
||||||
* @param string $field POST field to use (e.g. file). Used to reference content from the server. |
|
||||||
* @param string $filename Full path to the file. Do not include the @ symbol. |
|
||||||
* @param string $contentType Optional Content-Type to add to the Content-Disposition. |
|
||||||
* Default behavior is to guess. Set to false to not specify. |
|
||||||
* @param string $postname The name of the file, when posted. (e.g. rename the file) |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addPostFile($field, $filename = null, $contentType = null, $postname = null); |
|
||||||
|
|
||||||
/** |
|
||||||
* Add POST files to use in the upload |
|
||||||
* |
|
||||||
* @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterface |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addPostFiles(array $files); |
|
||||||
|
|
||||||
/** |
|
||||||
* Configure how redirects are handled for the request |
|
||||||
* |
|
||||||
* @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most |
|
||||||
* browsers with follow a 301-302 redirect for a POST request with a GET request. This is |
|
||||||
* the default behavior of Guzzle. Enable strict redirects to redirect these responses |
|
||||||
* with a POST rather than a GET request. |
|
||||||
* @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function configureRedirects($strict = false, $maxRedirects = 5); |
|
||||||
} |
|
@ -1,182 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message; |
|
||||||
|
|
||||||
use Guzzle\Common\Version; |
|
||||||
use Guzzle\Http\Message\Header\HeaderInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Represents a header and all of the values stored by that header |
|
||||||
*/ |
|
||||||
class Header implements HeaderInterface |
|
||||||
{ |
|
||||||
protected $values = array(); |
|
||||||
protected $header; |
|
||||||
protected $glue; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param string $header Name of the header |
|
||||||
* @param array|string $values Values of the header as an array or a scalar |
|
||||||
* @param string $glue Glue used to combine multiple values into a string |
|
||||||
*/ |
|
||||||
public function __construct($header, $values = array(), $glue = ',') |
|
||||||
{ |
|
||||||
$this->header = trim($header); |
|
||||||
$this->glue = $glue; |
|
||||||
|
|
||||||
foreach ((array) $values as $value) { |
|
||||||
foreach ((array) $value as $v) { |
|
||||||
$this->values[] = $v; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function __toString() |
|
||||||
{ |
|
||||||
return implode($this->glue . ' ', $this->toArray()); |
|
||||||
} |
|
||||||
|
|
||||||
public function add($value) |
|
||||||
{ |
|
||||||
$this->values[] = $value; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getName() |
|
||||||
{ |
|
||||||
return $this->header; |
|
||||||
} |
|
||||||
|
|
||||||
public function setName($name) |
|
||||||
{ |
|
||||||
$this->header = $name; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function setGlue($glue) |
|
||||||
{ |
|
||||||
$this->glue = $glue; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function getGlue() |
|
||||||
{ |
|
||||||
return $this->glue; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Normalize the header to be a single header with an array of values. |
|
||||||
* |
|
||||||
* If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into |
|
||||||
* multiple entries in the header. |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function normalize() |
|
||||||
{ |
|
||||||
$values = $this->toArray(); |
|
||||||
|
|
||||||
for ($i = 0, $total = count($values); $i < $total; $i++) { |
|
||||||
if (strpos($values[$i], $this->glue) !== false) { |
|
||||||
// Explode on glue when the glue is not inside of a comma |
|
||||||
foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { |
|
||||||
$values[] = trim($v); |
|
||||||
} |
|
||||||
unset($values[$i]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$this->values = array_values($values); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function hasValue($searchValue) |
|
||||||
{ |
|
||||||
return in_array($searchValue, $this->toArray()); |
|
||||||
} |
|
||||||
|
|
||||||
public function removeValue($searchValue) |
|
||||||
{ |
|
||||||
$this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { |
|
||||||
return $value != $searchValue; |
|
||||||
})); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
public function toArray() |
|
||||||
{ |
|
||||||
return $this->values; |
|
||||||
} |
|
||||||
|
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->toArray()); |
|
||||||
} |
|
||||||
|
|
||||||
public function getIterator() |
|
||||||
{ |
|
||||||
return new \ArrayIterator($this->toArray()); |
|
||||||
} |
|
||||||
|
|
||||||
public function parseParams() |
|
||||||
{ |
|
||||||
$params = $matches = array(); |
|
||||||
$callback = array($this, 'trimHeader'); |
|
||||||
|
|
||||||
// Normalize the header into a single array and iterate over all values |
|
||||||
foreach ($this->normalize()->toArray() as $val) { |
|
||||||
$part = array(); |
|
||||||
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { |
|
||||||
if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
$pieces = array_map($callback, $matches[0]); |
|
||||||
$part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; |
|
||||||
} |
|
||||||
if ($part) { |
|
||||||
$params[] = $part; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $params; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function hasExactHeader($header) |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated'); |
|
||||||
return $this->header == $header; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @deprecated |
|
||||||
* @codeCoverageIgnore |
|
||||||
*/ |
|
||||||
public function raw() |
|
||||||
{ |
|
||||||
Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); |
|
||||||
return $this->toArray(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Trim a header by removing excess spaces and wrapping quotes |
|
||||||
* |
|
||||||
* @param $str |
|
||||||
* |
|
||||||
* @return string |
|
||||||
*/ |
|
||||||
protected function trimHeader($str) |
|
||||||
{ |
|
||||||
static $trimmed = "\"' \n\t"; |
|
||||||
|
|
||||||
return trim($str, $trimmed); |
|
||||||
} |
|
||||||
} |
|
@ -1,121 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
use Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
/** |
|
||||||
* Provides helpful functionality for Cache-Control headers |
|
||||||
*/ |
|
||||||
class CacheControl extends Header |
|
||||||
{ |
|
||||||
/** @var array */ |
|
||||||
protected $directives; |
|
||||||
|
|
||||||
public function add($value) |
|
||||||
{ |
|
||||||
parent::add($value); |
|
||||||
$this->directives = null; |
|
||||||
} |
|
||||||
|
|
||||||
public function removeValue($searchValue) |
|
||||||
{ |
|
||||||
parent::removeValue($searchValue); |
|
||||||
$this->directives = null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check if a specific cache control directive exists |
|
||||||
* |
|
||||||
* @param string $param Directive to retrieve |
|
||||||
* |
|
||||||
* @return bool |
|
||||||
*/ |
|
||||||
public function hasDirective($param) |
|
||||||
{ |
|
||||||
$directives = $this->getDirectives(); |
|
||||||
|
|
||||||
return isset($directives[$param]); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get a specific cache control directive |
|
||||||
* |
|
||||||
* @param string $param Directive to retrieve |
|
||||||
* |
|
||||||
* @return string|bool|null |
|
||||||
*/ |
|
||||||
public function getDirective($param) |
|
||||||
{ |
|
||||||
$directives = $this->getDirectives(); |
|
||||||
|
|
||||||
return isset($directives[$param]) ? $directives[$param] : null; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Add a cache control directive |
|
||||||
* |
|
||||||
* @param string $param Directive to add |
|
||||||
* @param string $value Value to set |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function addDirective($param, $value) |
|
||||||
{ |
|
||||||
$directives = $this->getDirectives(); |
|
||||||
$directives[$param] = $value; |
|
||||||
$this->updateFromDirectives($directives); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Remove a cache control directive by name |
|
||||||
* |
|
||||||
* @param string $param Directive to remove |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function removeDirective($param) |
|
||||||
{ |
|
||||||
$directives = $this->getDirectives(); |
|
||||||
unset($directives[$param]); |
|
||||||
$this->updateFromDirectives($directives); |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an associative array of cache control directives |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getDirectives() |
|
||||||
{ |
|
||||||
if ($this->directives === null) { |
|
||||||
$this->directives = array(); |
|
||||||
foreach ($this->parseParams() as $collection) { |
|
||||||
foreach ($collection as $key => $value) { |
|
||||||
$this->directives[$key] = $value === '' ? true : $value; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $this->directives; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Updates the header value based on the parsed directives |
|
||||||
* |
|
||||||
* @param array $directives Array of cache control directives |
|
||||||
*/ |
|
||||||
protected function updateFromDirectives(array $directives) |
|
||||||
{ |
|
||||||
$this->directives = $directives; |
|
||||||
$this->values = array(); |
|
||||||
|
|
||||||
foreach ($directives as $key => $value) { |
|
||||||
$this->values[] = $value === true ? $key : "{$key}={$value}"; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,108 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
use Guzzle\Common\ToArrayInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* Provides a case-insensitive collection of headers |
|
||||||
*/ |
|
||||||
class HeaderCollection implements \IteratorAggregate, \Countable, \ArrayAccess, ToArrayInterface |
|
||||||
{ |
|
||||||
/** @var array */ |
|
||||||
protected $headers; |
|
||||||
|
|
||||||
public function __construct($headers = array()) |
|
||||||
{ |
|
||||||
$this->headers = $headers; |
|
||||||
} |
|
||||||
|
|
||||||
public function __clone() |
|
||||||
{ |
|
||||||
foreach ($this->headers as &$header) { |
|
||||||
$header = clone $header; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Clears the header collection |
|
||||||
*/ |
|
||||||
public function clear() |
|
||||||
{ |
|
||||||
$this->headers = array(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Set a header on the collection |
|
||||||
* |
|
||||||
* @param HeaderInterface $header Header to add |
|
||||||
* |
|
||||||
* @return self |
|
||||||
*/ |
|
||||||
public function add(HeaderInterface $header) |
|
||||||
{ |
|
||||||
$this->headers[strtolower($header->getName())] = $header; |
|
||||||
|
|
||||||
return $this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get an array of header objects |
|
||||||
* |
|
||||||
* @return array |
|
||||||
*/ |
|
||||||
public function getAll() |
|
||||||
{ |
|
||||||
return $this->headers; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Alias of offsetGet |
|
||||||
*/ |
|
||||||
public function get($key) |
|
||||||
{ |
|
||||||
return $this->offsetGet($key); |
|
||||||
} |
|
||||||
|
|
||||||
public function count() |
|
||||||
{ |
|
||||||
return count($this->headers); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetExists($offset) |
|
||||||
{ |
|
||||||
return isset($this->headers[strtolower($offset)]); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetGet($offset) |
|
||||||
{ |
|
||||||
$l = strtolower($offset); |
|
||||||
|
|
||||||
return isset($this->headers[$l]) ? $this->headers[$l] : null; |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetSet($offset, $value) |
|
||||||
{ |
|
||||||
$this->add($value); |
|
||||||
} |
|
||||||
|
|
||||||
public function offsetUnset($offset) |
|
||||||
{ |
|
||||||
unset($this->headers[strtolower($offset)]); |
|
||||||
} |
|
||||||
|
|
||||||
public function getIterator() |
|
||||||
{ |
|
||||||
return new \ArrayIterator($this->headers); |
|
||||||
} |
|
||||||
|
|
||||||
public function toArray() |
|
||||||
{ |
|
||||||
$result = array(); |
|
||||||
foreach ($this->headers as $header) { |
|
||||||
$result[$header->getName()] = $header->toArray(); |
|
||||||
} |
|
||||||
|
|
||||||
return $result; |
|
||||||
} |
|
||||||
} |
|
@ -1,26 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
use Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
/** |
|
||||||
* Default header factory implementation |
|
||||||
*/ |
|
||||||
class HeaderFactory implements HeaderFactoryInterface |
|
||||||
{ |
|
||||||
/** @var array */ |
|
||||||
protected $mapping = array( |
|
||||||
'cache-control' => 'Guzzle\Http\Message\Header\CacheControl', |
|
||||||
'link' => 'Guzzle\Http\Message\Header\Link', |
|
||||||
); |
|
||||||
|
|
||||||
public function createHeader($header, $value = null) |
|
||||||
{ |
|
||||||
$lowercase = strtolower($header); |
|
||||||
|
|
||||||
return isset($this->mapping[$lowercase]) |
|
||||||
? new $this->mapping[$lowercase]($header, $value) |
|
||||||
: new Header($header, $value); |
|
||||||
} |
|
||||||
} |
|
@ -1,19 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Guzzle\Http\Message\Header; |
|
||||||
|
|
||||||
/** |
|
||||||
* Interface for creating headers |
|
||||||
*/ |
|
||||||
interface HeaderFactoryInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Create a header from a header name and a single value |
|
||||||
* |
|
||||||
* @param string $header Name of the header to create |
|
||||||
* @param string $value Value to set on the header |
|
||||||
* |
|
||||||
* @return HeaderInterface |
|
||||||
*/ |
|
||||||
public function createHeader($header, $value = null); |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue