406 changed files with 22235 additions and 22043 deletions
@ -0,0 +1,22 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,6 @@
|
||||
<?php |
||||
namespace Ratchet\WebSocket; |
||||
use Ratchet\ComponentInterface; |
||||
|
||||
interface MessageComponentInterface extends ComponentInterface, MessageCallableInterface { |
||||
} |
@ -1,120 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -1,5 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Common\Exception; |
||||
|
||||
class BadMethodCallException extends \BadMethodCallException implements GuzzleException {} |
@ -1,108 +0,0 @@
@@ -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 @@
@@ -1,8 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Common\Exception; |
||||
|
||||
/** |
||||
* Guzzle exception |
||||
*/ |
||||
interface GuzzleException {} |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Common\Exception; |
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements GuzzleException {} |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Common\Exception; |
||||
|
||||
class RuntimeException extends \RuntimeException implements GuzzleException {} |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Common\Exception; |
||||
|
||||
class UnexpectedValueException extends \UnexpectedValueException implements GuzzleException {} |
@ -1,18 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -1,7 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Http\Exception; |
||||
|
||||
use Guzzle\Common\Exception\RuntimeException; |
||||
|
||||
class CouldNotRewindStreamException extends RuntimeException implements HttpException {} |
@ -1,101 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -1,5 +0,0 @@
|
||||
<?php |
||||
|
||||
namespace Guzzle\Http\Exception; |
||||
|
||||
class TooManyRedirectsException extends BadResponseException {} |
@ -1,83 +0,0 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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