init
This commit is contained in:
7
vendor/autoload.php
vendored
Executable file
7
vendor/autoload.php
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit331ae81537359437b02a96bc74f11b80::getLoader();
|
||||
21
vendor/binsoul/net-mqtt-client-react/LICENSE.md
vendored
Executable file
21
vendor/binsoul/net-mqtt-client-react/LICENSE.md
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Sebastian Mößler <code@binsoul.de>
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
145
vendor/binsoul/net-mqtt-client-react/README.md
vendored
Executable file
145
vendor/binsoul/net-mqtt-client-react/README.md
vendored
Executable file
@@ -0,0 +1,145 @@
|
||||
# net-mqtt-client-react
|
||||
|
||||
[![Latest Version on Packagist][ico-version]][link-packagist]
|
||||
[![Software License][ico-license]](LICENSE.md)
|
||||
[![Total Downloads][ico-downloads]][link-downloads]
|
||||
|
||||
This package provides an asynchronous MQTT client built on the [React socket](https://github.com/reactphp/socket) library. All client methods return a promise which is fulfilled if the operation succeeded or rejected if the operation failed. Incoming messages of subscribed topics are delivered via the "message" event.
|
||||
|
||||
## Install
|
||||
|
||||
Via composer:
|
||||
|
||||
``` bash
|
||||
$ composer require binsoul/net-mqtt-client-react
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Connect to a public broker and run forever.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
use BinSoul\Net\Mqtt\Client\React\ReactMqttClient;
|
||||
use BinSoul\Net\Mqtt\Connection;
|
||||
use BinSoul\Net\Mqtt\DefaultMessage;
|
||||
use BinSoul\Net\Mqtt\DefaultSubscription;
|
||||
use BinSoul\Net\Mqtt\Message;
|
||||
use BinSoul\Net\Mqtt\Subscription;
|
||||
use React\Socket\DnsConnector;
|
||||
use React\Socket\TcpConnector;
|
||||
|
||||
include 'vendor/autoload.php';
|
||||
|
||||
// Setup client
|
||||
$loop = \React\EventLoop\Factory::create();
|
||||
$dnsResolverFactory = new \React\Dns\Resolver\Factory();
|
||||
$connector = new DnsConnector(new TcpConnector($loop), $dnsResolverFactory->createCached('8.8.8.8', $loop));
|
||||
$client = new ReactMqttClient($connector, $loop);
|
||||
|
||||
// Bind to events
|
||||
$client->on('open', function () use ($client) {
|
||||
// Network connection established
|
||||
echo sprintf("Open: %s:%s\n", $client->getHost(), $client->getPort());
|
||||
});
|
||||
|
||||
$client->on('close', function () use ($client, $loop) {
|
||||
// Network connection closed
|
||||
echo sprintf("Close: %s:%s\n", $client->getHost(), $client->getPort());
|
||||
|
||||
$loop->stop();
|
||||
});
|
||||
|
||||
$client->on('connect', function (Connection $connection) {
|
||||
// Broker connected
|
||||
echo sprintf("Connect: client=%s\n", $connection->getClientID());
|
||||
});
|
||||
|
||||
$client->on('disconnect', function (Connection $connection) {
|
||||
// Broker disconnected
|
||||
echo sprintf("Disconnect: client=%s\n", $connection->getClientID());
|
||||
});
|
||||
|
||||
$client->on('message', function (Message $message) {
|
||||
// Incoming message
|
||||
echo 'Message';
|
||||
|
||||
if ($message->isDuplicate()) {
|
||||
echo ' (duplicate)';
|
||||
}
|
||||
|
||||
if ($message->isRetained()) {
|
||||
echo ' (retained)';
|
||||
}
|
||||
|
||||
echo ': '.$message->getTopic().' => '.mb_strimwidth($message->getPayload(), 0, 50, '...');
|
||||
echo "\n";
|
||||
});
|
||||
|
||||
$client->on('warning', function (\Exception $e) {
|
||||
echo sprintf("Warning: %s\n", $e->getMessage());
|
||||
});
|
||||
|
||||
$client->on('error', function (\Exception $e) use ($loop) {
|
||||
echo sprintf("Error: %s\n", $e->getMessage());
|
||||
|
||||
$loop->stop();
|
||||
});
|
||||
|
||||
// Connect to broker
|
||||
$client->connect('test.mosquitto.org')->then(
|
||||
function () use ($client) {
|
||||
// Subscribe to all topics
|
||||
$client->subscribe(new DefaultSubscription('#'))
|
||||
->then(function (Subscription $subscription) {
|
||||
echo sprintf("Subscribe: %s\n", $subscription->getFilter());
|
||||
})
|
||||
->otherwise(function (\Exception $e) {
|
||||
echo sprintf("Error: %s\n", $e->getMessage());
|
||||
});
|
||||
|
||||
// Publish humidity once
|
||||
$client->publish(new DefaultMessage('sensors/humidity', '55%'))
|
||||
->then(function (Message $message) {
|
||||
echo sprintf("Publish: %s => %s\n", $message->getTopic(), $message->getPayload());
|
||||
})
|
||||
->otherwise(function (\Exception $e) {
|
||||
echo sprintf("Error: %s\n", $e->getMessage());
|
||||
});
|
||||
|
||||
// Publish a random temperature every 10 seconds
|
||||
$generator = function () {
|
||||
return mt_rand(-20, 30);
|
||||
};
|
||||
|
||||
$client->publishPeriodically(10, new DefaultMessage('sensors/temperature'), $generator)
|
||||
->progress(function (Message $message) {
|
||||
echo sprintf("Publish: %s => %s\n", $message->getTopic(), $message->getPayload());
|
||||
})
|
||||
->otherwise(function (\Exception $e) {
|
||||
echo sprintf("Error: %s\n", $e->getMessage());
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
$loop->run();
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
||||
[ico-version]: https://img.shields.io/packagist/v/binsoul/net-mqtt-client-react.svg?style=flat-square
|
||||
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
|
||||
[ico-downloads]: https://img.shields.io/packagist/dt/binsoul/net-mqtt-client-react.svg?style=flat-square
|
||||
|
||||
[link-packagist]: https://packagist.org/packages/binsoul/net-mqtt-client-react
|
||||
[link-downloads]: https://packagist.org/packages/binsoul/net-mqtt-client-react
|
||||
[link-author]: https://github.com/binsoul
|
||||
51
vendor/binsoul/net-mqtt-client-react/composer.json
vendored
Executable file
51
vendor/binsoul/net-mqtt-client-react/composer.json
vendored
Executable file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "binsoul/net-mqtt-client-react",
|
||||
"description": "Asynchronous MQTT client built on React",
|
||||
"keywords": [
|
||||
"net",
|
||||
"mqtt",
|
||||
"client"
|
||||
],
|
||||
"homepage": "https://github.com/binsoul/net-mqtt-client-react",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Mößler",
|
||||
"email": "code@binsoul.de",
|
||||
"homepage": "https://github.com/binsoul",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~5.6|~7.0",
|
||||
"binsoul/net-mqtt": "~0.2",
|
||||
"react/promise": "~2.0",
|
||||
"react/socket": "~0.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0||~5.0",
|
||||
"friendsofphp/php-cs-fixer": "~1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BinSoul\\Net\\Mqtt\\Client\\React\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"BinSoul\\Test\\Net\\Mqtt\\Client\\React\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"fix-style": [
|
||||
"php-cs-fixer fix src",
|
||||
"php-cs-fixer fix tests"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
112
vendor/binsoul/net-mqtt-client-react/src/ReactFlow.php
vendored
Executable file
112
vendor/binsoul/net-mqtt-client-react/src/ReactFlow.php
vendored
Executable file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Client\React;
|
||||
|
||||
use BinSoul\Net\Mqtt\Flow;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
/**
|
||||
* Decorates flows with data required for the {@see ReactMqttClient} class.
|
||||
*/
|
||||
class ReactFlow implements Flow
|
||||
{
|
||||
/** @var Flow */
|
||||
private $decorated;
|
||||
/** @var Deferred */
|
||||
private $deferred;
|
||||
/** @var Packet */
|
||||
private $packet;
|
||||
/** @var bool */
|
||||
private $isSilent;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Flow $decorated
|
||||
* @param Deferred $deferred
|
||||
* @param Packet $packet
|
||||
* @param bool $isSilent
|
||||
*/
|
||||
public function __construct(Flow $decorated, Deferred $deferred, Packet $packet = null, $isSilent = false)
|
||||
{
|
||||
$this->decorated = $decorated;
|
||||
$this->deferred = $deferred;
|
||||
$this->packet = $packet;
|
||||
$this->isSilent = $isSilent;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->decorated->getCode();
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$this->packet = $this->decorated->start();
|
||||
|
||||
return $this->packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
return $this->decorated->accept($packet);
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
$this->packet = $this->decorated->next($packet);
|
||||
|
||||
return $this->packet;
|
||||
}
|
||||
|
||||
public function isFinished()
|
||||
{
|
||||
return $this->decorated->isFinished();
|
||||
}
|
||||
|
||||
public function isSuccess()
|
||||
{
|
||||
return $this->decorated->isSuccess();
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->decorated->getResult();
|
||||
}
|
||||
|
||||
public function getErrorMessage()
|
||||
{
|
||||
return $this->decorated->getErrorMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the associated deferred.
|
||||
*
|
||||
* @return Deferred
|
||||
*/
|
||||
public function getDeferred()
|
||||
{
|
||||
return $this->deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current packet.
|
||||
*
|
||||
* @return Packet
|
||||
*/
|
||||
public function getPacket()
|
||||
{
|
||||
return $this->packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the flow should emit events.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSilent()
|
||||
{
|
||||
return $this->isSilent;
|
||||
}
|
||||
}
|
||||
701
vendor/binsoul/net-mqtt-client-react/src/ReactMqttClient.php
vendored
Executable file
701
vendor/binsoul/net-mqtt-client-react/src/ReactMqttClient.php
vendored
Executable file
@@ -0,0 +1,701 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Client\React;
|
||||
|
||||
use BinSoul\Net\Mqtt\Connection;
|
||||
use BinSoul\Net\Mqtt\DefaultConnection;
|
||||
use BinSoul\Net\Mqtt\DefaultIdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Flow;
|
||||
use BinSoul\Net\Mqtt\Flow\IncomingPublishFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingConnectFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingDisconnectFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingPingFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingPublishFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingSubscribeFlow;
|
||||
use BinSoul\Net\Mqtt\Flow\OutgoingUnsubscribeFlow;
|
||||
use BinSoul\Net\Mqtt\DefaultMessage;
|
||||
use BinSoul\Net\Mqtt\IdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Message;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishRequestPacket;
|
||||
use BinSoul\Net\Mqtt\StreamParser;
|
||||
use BinSoul\Net\Mqtt\Subscription;
|
||||
use Evenement\EventEmitter;
|
||||
use React\EventLoop\Timer\TimerInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\ExtendedPromiseInterface;
|
||||
use React\Promise\RejectedPromise;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Stream\DuplexStreamInterface;
|
||||
|
||||
/**
|
||||
* Connects to a MQTT broker and subscribes to topics or publishes messages.
|
||||
*
|
||||
* The following events are emitted:
|
||||
* - open - The network connection to the server is established.
|
||||
* - close - The network connection to the server is closed.
|
||||
* - warning - An event of severity "warning" occurred.
|
||||
* - error - An event of severity "error" occurred.
|
||||
*
|
||||
* If a flow finishes it's result is also emitted, e.g.:
|
||||
* - connect - The client connected to the broker.
|
||||
* - disconnect - The client disconnected from the broker.
|
||||
* - subscribe - The client subscribed to a topic filter.
|
||||
* - unsubscribe - The client unsubscribed from topic filter.
|
||||
* - publish - A message was published.
|
||||
* - message - A message was received.
|
||||
*/
|
||||
class ReactMqttClient extends EventEmitter
|
||||
{
|
||||
/** @var ConnectorInterface */
|
||||
private $connector;
|
||||
/** @var LoopInterface */
|
||||
private $loop;
|
||||
/** @var DuplexStreamInterface */
|
||||
private $stream;
|
||||
/** @var StreamParser */
|
||||
private $parser;
|
||||
/** @var IdentifierGenerator */
|
||||
private $identifierGenerator;
|
||||
|
||||
/** @var string */
|
||||
private $host;
|
||||
/** @var int */
|
||||
private $port;
|
||||
/** @var Connection */
|
||||
private $connection;
|
||||
/** @var bool */
|
||||
private $isConnected = false;
|
||||
/** @var bool */
|
||||
private $isConnecting = false;
|
||||
/** @var bool */
|
||||
private $isDisconnecting = false;
|
||||
|
||||
/** @var TimerInterface[] */
|
||||
private $timer = [];
|
||||
|
||||
/** @var ReactFlow[] */
|
||||
private $receivingFlows = [];
|
||||
/** @var ReactFlow[] */
|
||||
private $sendingFlows = [];
|
||||
/** @var ReactFlow */
|
||||
private $writtenFlow;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param ConnectorInterface $connector
|
||||
* @param LoopInterface $loop
|
||||
* @param IdentifierGenerator $identifierGenerator
|
||||
* @param StreamParser $parser
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectorInterface $connector,
|
||||
LoopInterface $loop,
|
||||
IdentifierGenerator $identifierGenerator = null,
|
||||
StreamParser $parser = null
|
||||
) {
|
||||
$this->connector = $connector;
|
||||
$this->loop = $loop;
|
||||
|
||||
$this->parser = $parser;
|
||||
if ($this->parser === null) {
|
||||
$this->parser = new StreamParser();
|
||||
}
|
||||
|
||||
$this->parser->onError(function (\Exception $e) {
|
||||
$this->emitWarning($e);
|
||||
});
|
||||
|
||||
$this->identifierGenerator = $identifierGenerator;
|
||||
if ($this->identifierGenerator === null) {
|
||||
$this->identifierGenerator = new DefaultIdentifierGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the port.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the client is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected()
|
||||
{
|
||||
return $this->isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying stream or null if the client is not connected.
|
||||
*
|
||||
* @return DuplexStreamInterface|null
|
||||
*/
|
||||
public function getStream()
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a broker.
|
||||
*
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param Connection $connection
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function connect($host, $port = 1883, Connection $connection = null, $timeout = 5)
|
||||
{
|
||||
if ($this->isConnected || $this->isConnecting) {
|
||||
return new RejectedPromise(new \LogicException('The client is already connected.'));
|
||||
}
|
||||
|
||||
$this->isConnecting = true;
|
||||
$this->isConnected = false;
|
||||
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
|
||||
if ($connection === null) {
|
||||
$connection = new DefaultConnection();
|
||||
}
|
||||
|
||||
if ($connection->isCleanSession()) {
|
||||
$this->cleanPreviousSession();
|
||||
}
|
||||
|
||||
if ($connection->getClientID() === '') {
|
||||
$connection = $connection->withClientID($this->identifierGenerator->generateClientID());
|
||||
}
|
||||
|
||||
$deferred = new Deferred();
|
||||
|
||||
$this->establishConnection($this->host, $this->port, $timeout)
|
||||
->then(function (DuplexStreamInterface $stream) use ($connection, $deferred, $timeout) {
|
||||
$this->stream = $stream;
|
||||
|
||||
$this->emit('open', [$connection, $this]);
|
||||
|
||||
$this->registerClient($connection, $timeout)
|
||||
->then(function (Connection $connection) use ($deferred) {
|
||||
$this->isConnecting = false;
|
||||
$this->isConnected = true;
|
||||
$this->connection = $connection;
|
||||
|
||||
$this->emit('connect', [$connection, $this]);
|
||||
$deferred->resolve($this->connection);
|
||||
})
|
||||
->otherwise(function (\Exception $e) use ($deferred, $connection) {
|
||||
$this->isConnecting = false;
|
||||
|
||||
$this->emitError($e);
|
||||
$deferred->reject($e);
|
||||
|
||||
if ($this->stream !== null) {
|
||||
$this->stream->close();
|
||||
}
|
||||
|
||||
$this->emit('close', [$connection, $this]);
|
||||
});
|
||||
})
|
||||
->otherwise(function (\Exception $e) use ($deferred) {
|
||||
$this->isConnecting = false;
|
||||
|
||||
$this->emitError($e);
|
||||
$deferred->reject($e);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from a broker.
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if (!$this->isConnected || $this->isDisconnecting) {
|
||||
return new RejectedPromise(new \LogicException('The client is not connected.'));
|
||||
}
|
||||
|
||||
$this->isDisconnecting = true;
|
||||
|
||||
$deferred = new Deferred();
|
||||
|
||||
$this->startFlow(new OutgoingDisconnectFlow($this->connection), true)
|
||||
->then(function (Connection $connection) use ($deferred) {
|
||||
$this->isDisconnecting = false;
|
||||
$this->isConnected = false;
|
||||
|
||||
$this->emit('disconnect', [$connection, $this]);
|
||||
$deferred->resolve($connection);
|
||||
|
||||
if ($this->stream !== null) {
|
||||
$this->stream->close();
|
||||
}
|
||||
})
|
||||
->otherwise(function () use ($deferred) {
|
||||
$this->isDisconnecting = false;
|
||||
$deferred->reject($this->connection);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a topic filter.
|
||||
*
|
||||
* @param Subscription $subscription
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function subscribe(Subscription $subscription)
|
||||
{
|
||||
if (!$this->isConnected) {
|
||||
return new RejectedPromise(new \LogicException('The client is not connected.'));
|
||||
}
|
||||
|
||||
return $this->startFlow(new OutgoingSubscribeFlow([$subscription], $this->identifierGenerator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from a topic filter.
|
||||
*
|
||||
* @param Subscription $subscription
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function unsubscribe(Subscription $subscription)
|
||||
{
|
||||
if (!$this->isConnected) {
|
||||
return new RejectedPromise(new \LogicException('The client is not connected.'));
|
||||
}
|
||||
|
||||
return $this->startFlow(new OutgoingUnsubscribeFlow([$subscription], $this->identifierGenerator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes a message.
|
||||
*
|
||||
* @param Message $message
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function publish(Message $message)
|
||||
{
|
||||
if (!$this->isConnected) {
|
||||
return new RejectedPromise(new \LogicException('The client is not connected.'));
|
||||
}
|
||||
|
||||
return $this->startFlow(new OutgoingPublishFlow($message, $this->identifierGenerator));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given generator periodically and publishes the return value.
|
||||
*
|
||||
* @param int $interval
|
||||
* @param Message $message
|
||||
* @param callable $generator
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
public function publishPeriodically($interval, Message $message, callable $generator)
|
||||
{
|
||||
if (!$this->isConnected) {
|
||||
return new RejectedPromise(new \LogicException('The client is not connected.'));
|
||||
}
|
||||
|
||||
$deferred = new Deferred();
|
||||
|
||||
$this->timer[] = $this->loop->addPeriodicTimer(
|
||||
$interval,
|
||||
function () use ($message, $generator, $deferred) {
|
||||
$this->publish($message->withPayload($generator($message->getTopic())))->then(
|
||||
function ($value) use ($deferred) {
|
||||
$deferred->notify($value);
|
||||
},
|
||||
function (\Exception $e) use ($deferred) {
|
||||
$deferred->reject($e);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits warnings.
|
||||
*
|
||||
* @param \Exception $e
|
||||
*/
|
||||
private function emitWarning(\Exception $e)
|
||||
{
|
||||
$this->emit('warning', [$e, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits errors.
|
||||
*
|
||||
* @param \Exception $e
|
||||
*/
|
||||
private function emitError(\Exception $e)
|
||||
{
|
||||
$this->emit('error', [$e, $this]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a network connection to a server.
|
||||
*
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
private function establishConnection($host, $port, $timeout)
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
$timer = $this->loop->addTimer(
|
||||
$timeout,
|
||||
function () use ($deferred, $timeout) {
|
||||
$exception = new \RuntimeException(sprintf('Connection timed out after %d seconds.', $timeout));
|
||||
$deferred->reject($exception);
|
||||
}
|
||||
);
|
||||
|
||||
$this->connector->connect($host.':'.$port)
|
||||
->always(function () use ($timer) {
|
||||
$this->loop->cancelTimer($timer);
|
||||
})
|
||||
->then(function (DuplexStreamInterface $stream) use ($deferred) {
|
||||
$stream->on('data', function ($data) {
|
||||
$this->handleReceive($data);
|
||||
});
|
||||
|
||||
$stream->on('close', function () {
|
||||
$this->handleClose();
|
||||
});
|
||||
|
||||
$stream->on('error', function (\Exception $e) {
|
||||
$this->handleError($e);
|
||||
});
|
||||
|
||||
$deferred->resolve($stream);
|
||||
})
|
||||
->otherwise(function (\Exception $e) use ($deferred) {
|
||||
$deferred->reject($e);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new client with the broker.
|
||||
*
|
||||
* @param Connection $connection
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
private function registerClient(Connection $connection, $timeout)
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
$responseTimer = $this->loop->addTimer(
|
||||
$timeout,
|
||||
function () use ($deferred, $timeout) {
|
||||
$exception = new \RuntimeException(sprintf('No response after %d seconds.', $timeout));
|
||||
$deferred->reject($exception);
|
||||
}
|
||||
);
|
||||
|
||||
$this->startFlow(new OutgoingConnectFlow($connection, $this->identifierGenerator), true)
|
||||
->always(function () use ($responseTimer) {
|
||||
$this->loop->cancelTimer($responseTimer);
|
||||
})->then(function (Connection $connection) use ($deferred) {
|
||||
$this->timer[] = $this->loop->addPeriodicTimer(
|
||||
floor($connection->getKeepAlive() * 0.75),
|
||||
function () {
|
||||
$this->startFlow(new OutgoingPingFlow());
|
||||
}
|
||||
);
|
||||
|
||||
$deferred->resolve($connection);
|
||||
})->otherwise(function (\Exception $e) use ($deferred) {
|
||||
$deferred->reject($e);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming data.
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
private function handleReceive($data)
|
||||
{
|
||||
if (!$this->isConnected && !$this->isConnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flowCount = count($this->receivingFlows);
|
||||
|
||||
$packets = $this->parser->push($data);
|
||||
foreach ($packets as $packet) {
|
||||
$this->handlePacket($packet);
|
||||
}
|
||||
|
||||
if ($flowCount > count($this->receivingFlows)) {
|
||||
$this->receivingFlows = array_values($this->receivingFlows);
|
||||
}
|
||||
|
||||
$this->handleSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming packet.
|
||||
*
|
||||
* @param Packet $packet
|
||||
*/
|
||||
private function handlePacket(Packet $packet)
|
||||
{
|
||||
switch ($packet->getPacketType()) {
|
||||
case Packet::TYPE_PUBLISH:
|
||||
/* @var PublishRequestPacket $packet */
|
||||
$message = new DefaultMessage(
|
||||
$packet->getTopic(),
|
||||
$packet->getPayload(),
|
||||
$packet->getQosLevel(),
|
||||
$packet->isRetained(),
|
||||
$packet->isDuplicate()
|
||||
);
|
||||
|
||||
$this->startFlow(new IncomingPublishFlow($message, $packet->getIdentifier()));
|
||||
break;
|
||||
case Packet::TYPE_CONNACK:
|
||||
case Packet::TYPE_PINGRESP:
|
||||
case Packet::TYPE_SUBACK:
|
||||
case Packet::TYPE_UNSUBACK:
|
||||
case Packet::TYPE_PUBREL:
|
||||
case Packet::TYPE_PUBACK:
|
||||
case Packet::TYPE_PUBREC:
|
||||
case Packet::TYPE_PUBCOMP:
|
||||
$flowFound = false;
|
||||
foreach ($this->receivingFlows as $index => $flow) {
|
||||
if ($flow->accept($packet)) {
|
||||
$flowFound = true;
|
||||
|
||||
unset($this->receivingFlows[$index]);
|
||||
$this->continueFlow($flow, $packet);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$flowFound) {
|
||||
$this->emitWarning(
|
||||
new \LogicException(sprintf('Received unexpected packet of type %d.', $packet->getPacketType()))
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->emitWarning(
|
||||
new \LogicException(sprintf('Cannot handle packet of type %d.', $packet->getPacketType()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles outgoing packets.
|
||||
*/
|
||||
private function handleSend()
|
||||
{
|
||||
$flow = null;
|
||||
if ($this->writtenFlow !== null) {
|
||||
$flow = $this->writtenFlow;
|
||||
$this->writtenFlow = null;
|
||||
}
|
||||
|
||||
if (count($this->sendingFlows) > 0) {
|
||||
$this->writtenFlow = array_shift($this->sendingFlows);
|
||||
$this->stream->write($this->writtenFlow->getPacket());
|
||||
}
|
||||
|
||||
if ($flow !== null) {
|
||||
if ($flow->isFinished()) {
|
||||
$this->loop->nextTick(function () use ($flow) {
|
||||
$this->finishFlow($flow);
|
||||
});
|
||||
} else {
|
||||
$this->receivingFlows[] = $flow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles closing of the stream.
|
||||
*/
|
||||
private function handleClose()
|
||||
{
|
||||
foreach ($this->timer as $timer) {
|
||||
$this->loop->cancelTimer($timer);
|
||||
}
|
||||
|
||||
$this->timer = [];
|
||||
|
||||
$connection = $this->connection;
|
||||
|
||||
$this->isConnecting = false;
|
||||
$this->isDisconnecting = false;
|
||||
$this->isConnected = false;
|
||||
$this->connection = null;
|
||||
$this->stream = null;
|
||||
|
||||
if ($connection !== null) {
|
||||
$this->emit('close', [$connection, $this]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors of the stream.
|
||||
*
|
||||
* @param \Exception $e
|
||||
*/
|
||||
private function handleError(\Exception $e)
|
||||
{
|
||||
$this->emitError($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the given flow.
|
||||
*
|
||||
* @param Flow $flow
|
||||
* @param bool $isSilent
|
||||
*
|
||||
* @return ExtendedPromiseInterface
|
||||
*/
|
||||
private function startFlow(Flow $flow, $isSilent = false)
|
||||
{
|
||||
try {
|
||||
$packet = $flow->start();
|
||||
} catch (\Exception $e) {
|
||||
$this->emitError($e);
|
||||
|
||||
return new RejectedPromise($e);
|
||||
}
|
||||
|
||||
$deferred = new Deferred();
|
||||
$internalFlow = new ReactFlow($flow, $deferred, $packet, $isSilent);
|
||||
|
||||
if ($packet !== null) {
|
||||
if ($this->writtenFlow !== null) {
|
||||
$this->sendingFlows[] = $internalFlow;
|
||||
} else {
|
||||
$this->stream->write($packet);
|
||||
$this->writtenFlow = $internalFlow;
|
||||
$this->handleSend();
|
||||
}
|
||||
} else {
|
||||
$this->loop->nextTick(function () use ($internalFlow) {
|
||||
$this->finishFlow($internalFlow);
|
||||
});
|
||||
}
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the given flow.
|
||||
*
|
||||
* @param ReactFlow $flow
|
||||
* @param Packet $packet
|
||||
*/
|
||||
private function continueFlow(ReactFlow $flow, Packet $packet)
|
||||
{
|
||||
try {
|
||||
$response = $flow->next($packet);
|
||||
} catch (\Exception $e) {
|
||||
$this->emitError($e);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($response !== null) {
|
||||
if ($this->writtenFlow !== null) {
|
||||
$this->sendingFlows[] = $flow;
|
||||
} else {
|
||||
$this->stream->write($response);
|
||||
$this->writtenFlow = $flow;
|
||||
$this->handleSend();
|
||||
}
|
||||
} elseif ($flow->isFinished()) {
|
||||
$this->loop->nextTick(function () use ($flow) {
|
||||
$this->finishFlow($flow);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the given flow.
|
||||
*
|
||||
* @param ReactFlow $flow
|
||||
*/
|
||||
private function finishFlow(ReactFlow $flow)
|
||||
{
|
||||
if ($flow->isSuccess()) {
|
||||
if (!$flow->isSilent()) {
|
||||
$this->emit($flow->getCode(), [$flow->getResult(), $this]);
|
||||
}
|
||||
|
||||
$flow->getDeferred()->resolve($flow->getResult());
|
||||
} else {
|
||||
$result = new \RuntimeException($flow->getErrorMessage());
|
||||
$this->emitWarning($result);
|
||||
|
||||
$flow->getDeferred()->reject($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans previous session by rejecting all pending flows.
|
||||
*/
|
||||
private function cleanPreviousSession()
|
||||
{
|
||||
$error = new \RuntimeException('Connection has been closed.');
|
||||
|
||||
foreach ($this->receivingFlows as $receivingFlow) {
|
||||
$receivingFlow->getDeferred()->reject($error);
|
||||
}
|
||||
|
||||
foreach ($this->sendingFlows as $sendingFlow) {
|
||||
$sendingFlow->getDeferred()->reject($error);
|
||||
}
|
||||
|
||||
$this->receivingFlows = [];
|
||||
$this->sendingFlows = [];
|
||||
}
|
||||
}
|
||||
21
vendor/binsoul/net-mqtt/LICENSE.md
vendored
Executable file
21
vendor/binsoul/net-mqtt/LICENSE.md
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Sebastian Mößler <code@binsoul.de>
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
> of this software and associated documentation files (the "Software"), to deal
|
||||
> in the Software without restriction, including without limitation the rights
|
||||
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
> copies of the Software, and to permit persons to whom the Software is
|
||||
> furnished to do so, subject to the following conditions:
|
||||
>
|
||||
> The above copyright notice and this permission notice shall be included in
|
||||
> all copies or substantial portions of the Software.
|
||||
>
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
> THE SOFTWARE.
|
||||
36
vendor/binsoul/net-mqtt/README.md
vendored
Executable file
36
vendor/binsoul/net-mqtt/README.md
vendored
Executable file
@@ -0,0 +1,36 @@
|
||||
# net-mqtt
|
||||
|
||||
[![Latest Version on Packagist][ico-version]][link-packagist]
|
||||
[![Software License][ico-license]](LICENSE.md)
|
||||
[![Total Downloads][ico-downloads]][link-downloads]
|
||||
|
||||
MQTT is a machine-to-machine (M2M) / Internet of Things (IoT) connectivity protocol. It provides a lightweight method of carrying out messaging using a publish/subscribe model.
|
||||
|
||||
This package implements the MQTT protocol versions 3.1 and 3.1.1.
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
Via composer:
|
||||
|
||||
``` bash
|
||||
$ composer require binsoul/net-mqtt
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
||||
[ico-version]: https://img.shields.io/packagist/v/binsoul/net-mqtt.svg?style=flat-square
|
||||
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
|
||||
[ico-downloads]: https://img.shields.io/packagist/dt/binsoul/net-mqtt.svg?style=flat-square
|
||||
|
||||
[link-packagist]: https://packagist.org/packages/binsoul/net-mqtt
|
||||
[link-downloads]: https://packagist.org/packages/binsoul/net-mqtt
|
||||
[link-author]: https://github.com/binsoul
|
||||
47
vendor/binsoul/net-mqtt/composer.json
vendored
Executable file
47
vendor/binsoul/net-mqtt/composer.json
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "binsoul/net-mqtt",
|
||||
"description": "MQTT protocol implementation",
|
||||
"keywords": [
|
||||
"net",
|
||||
"mqtt"
|
||||
],
|
||||
"homepage": "https://github.com/binsoul/net-mqtt",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Mößler",
|
||||
"email": "code@binsoul.de",
|
||||
"homepage": "https://github.com/binsoul",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~5.6|~7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0||~5.0",
|
||||
"friendsofphp/php-cs-fixer": "~1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BinSoul\\Net\\Mqtt\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"BinSoul\\Test\\Net\\Mqtt\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"fix-style": [
|
||||
"php-cs-fixer fix src",
|
||||
"php-cs-fixer fix tests"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/binsoul/net-mqtt/src/Connection.php
vendored
Executable file
90
vendor/binsoul/net-mqtt/src/Connection.php
vendored
Executable file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Represents the connection of a MQTT client.
|
||||
*/
|
||||
interface Connection
|
||||
{
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getProtocol();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClientID();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCleanSession();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername();
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword();
|
||||
|
||||
/**
|
||||
* @return Message|null
|
||||
*/
|
||||
public function getWill();
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getKeepAlive();
|
||||
|
||||
/**
|
||||
* Returns a new connection with the given protocol.
|
||||
*
|
||||
* @param int $protocol
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withProtocol($protocol);
|
||||
|
||||
/**
|
||||
* Returns a new connection with the given client id.
|
||||
*
|
||||
* @param string $clientID
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withClientID($clientID);
|
||||
|
||||
/**
|
||||
* Returns a new connection with the given credentials.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withCredentials($username, $password);
|
||||
|
||||
/**
|
||||
* Returns a new connection with the given will.
|
||||
*
|
||||
* @param Message $will
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withWill(Message $will);
|
||||
|
||||
/**
|
||||
* Returns a new connection with the given keep alive timeout.
|
||||
*
|
||||
* @param int $timeout
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withKeepAlive($timeout);
|
||||
}
|
||||
129
vendor/binsoul/net-mqtt/src/DefaultConnection.php
vendored
Executable file
129
vendor/binsoul/net-mqtt/src/DefaultConnection.php
vendored
Executable file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Provides a default implementation of the {@see Connection} interface.
|
||||
*/
|
||||
class DefaultConnection implements Connection
|
||||
{
|
||||
/** @var string */
|
||||
private $username;
|
||||
/** @var string */
|
||||
private $password;
|
||||
/** @var Message|null */
|
||||
private $will;
|
||||
/** @var string */
|
||||
private $clientID;
|
||||
/** @var int */
|
||||
private $keepAlive;
|
||||
/** @var int */
|
||||
private $protocol;
|
||||
/** @var bool */
|
||||
private $clean;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param Message|null $will
|
||||
* @param string $clientID
|
||||
* @param int $keepAlive
|
||||
* @param int $protocol
|
||||
* @param bool $clean
|
||||
*/
|
||||
public function __construct(
|
||||
$username = '',
|
||||
$password = '',
|
||||
Message $will = null,
|
||||
$clientID = '',
|
||||
$keepAlive = 60,
|
||||
$protocol = 4,
|
||||
$clean = true
|
||||
) {
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->will = $will;
|
||||
$this->clientID = $clientID;
|
||||
$this->keepAlive = $keepAlive;
|
||||
$this->protocol = $protocol;
|
||||
$this->clean = $clean;
|
||||
}
|
||||
|
||||
public function getProtocol()
|
||||
{
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
public function getClientID()
|
||||
{
|
||||
return $this->clientID;
|
||||
}
|
||||
|
||||
public function isCleanSession()
|
||||
{
|
||||
return $this->clean;
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function getWill()
|
||||
{
|
||||
return $this->will;
|
||||
}
|
||||
|
||||
public function getKeepAlive()
|
||||
{
|
||||
return $this->keepAlive;
|
||||
}
|
||||
|
||||
public function withProtocol($protocol)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->protocol = $protocol;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withClientID($clientID)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->clientID = $clientID;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withCredentials($username, $password)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->username = $username;
|
||||
$result->password = $password;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withWill(Message $will = null)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->will = $will;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withKeepAlive($timeout)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->keepAlive = $timeout;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
38
vendor/binsoul/net-mqtt/src/DefaultIdentifierGenerator.php
vendored
Executable file
38
vendor/binsoul/net-mqtt/src/DefaultIdentifierGenerator.php
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Provides a default implementation of the {@see IdentifierGenerator} interface.
|
||||
*/
|
||||
class DefaultIdentifierGenerator implements IdentifierGenerator
|
||||
{
|
||||
/** @var int */
|
||||
private $currentIdentifier = 0;
|
||||
|
||||
public function generatePacketID()
|
||||
{
|
||||
++$this->currentIdentifier;
|
||||
if ($this->currentIdentifier > 0xFFFF) {
|
||||
$this->currentIdentifier = 1;
|
||||
}
|
||||
|
||||
return $this->currentIdentifier;
|
||||
}
|
||||
|
||||
public function generateClientID()
|
||||
{
|
||||
if (function_exists('random_bytes')) {
|
||||
$data = random_bytes(9);
|
||||
} elseif (function_exists('openssl_random_pseudo_bytes')) {
|
||||
$data = openssl_random_pseudo_bytes(9);
|
||||
} else {
|
||||
$data = '';
|
||||
for ($i = 1; $i <= 8; ++$i) {
|
||||
$data = chr(mt_rand(0, 255)).$data;
|
||||
}
|
||||
}
|
||||
|
||||
return 'BNMCR'.bin2hex($data);
|
||||
}
|
||||
}
|
||||
119
vendor/binsoul/net-mqtt/src/DefaultMessage.php
vendored
Executable file
119
vendor/binsoul/net-mqtt/src/DefaultMessage.php
vendored
Executable file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Provides a default implementation of the {@see Message} interface.
|
||||
*/
|
||||
class DefaultMessage implements Message
|
||||
{
|
||||
/** @var string */
|
||||
private $topic;
|
||||
/** @var string */
|
||||
private $payload;
|
||||
/** @var bool */
|
||||
private $isRetained;
|
||||
/** @var bool */
|
||||
private $isDuplicate = false;
|
||||
/** @var int */
|
||||
private $qosLevel;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param string $topic
|
||||
* @param string $payload
|
||||
* @param int $qosLevel
|
||||
* @param bool $retain
|
||||
* @param bool $isDuplicate
|
||||
*/
|
||||
public function __construct($topic, $payload = '', $qosLevel = 0, $retain = false, $isDuplicate = false)
|
||||
{
|
||||
$this->topic = $topic;
|
||||
$this->payload = $payload;
|
||||
$this->isRetained = $retain;
|
||||
$this->qosLevel = $qosLevel;
|
||||
$this->isDuplicate = $isDuplicate;
|
||||
}
|
||||
|
||||
public function getTopic()
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
public function getQosLevel()
|
||||
{
|
||||
return $this->qosLevel;
|
||||
}
|
||||
|
||||
public function isDuplicate()
|
||||
{
|
||||
return $this->isDuplicate;
|
||||
}
|
||||
|
||||
public function isRetained()
|
||||
{
|
||||
return $this->isRetained;
|
||||
}
|
||||
|
||||
public function withTopic($topic)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->topic = $topic;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withPayload($payload)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->payload = $payload;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withQosLevel($level)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->qosLevel = $level;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function retain()
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->isRetained = true;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function release()
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->isRetained = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function duplicate()
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->isDuplicate = true;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function original()
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->isDuplicate = false;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
52
vendor/binsoul/net-mqtt/src/DefaultSubscription.php
vendored
Executable file
52
vendor/binsoul/net-mqtt/src/DefaultSubscription.php
vendored
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Provides a default implementation of the {@see Subscription} interface.
|
||||
*/
|
||||
class DefaultSubscription implements Subscription
|
||||
{
|
||||
/** @var string */
|
||||
private $filter;
|
||||
/** @var int */
|
||||
private $qosLevel;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param string $filter
|
||||
* @param int $qosLevel
|
||||
*/
|
||||
public function __construct($filter, $qosLevel = 0)
|
||||
{
|
||||
$this->filter = $filter;
|
||||
$this->qosLevel = $qosLevel;
|
||||
}
|
||||
|
||||
public function getFilter()
|
||||
{
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
public function getQosLevel()
|
||||
{
|
||||
return $this->qosLevel;
|
||||
}
|
||||
|
||||
public function withFilter($filter)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->filter = $filter;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function withQosLevel($level)
|
||||
{
|
||||
$result = clone $this;
|
||||
$result->qosLevel = $level;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
10
vendor/binsoul/net-mqtt/src/Exception/EndOfStreamException.php
vendored
Executable file
10
vendor/binsoul/net-mqtt/src/Exception/EndOfStreamException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Exception;
|
||||
|
||||
/**
|
||||
* Will be thrown if the end of a stream is reached but more bytes were requested.
|
||||
*/
|
||||
class EndOfStreamException extends \Exception
|
||||
{
|
||||
}
|
||||
10
vendor/binsoul/net-mqtt/src/Exception/MalformedPacketException.php
vendored
Executable file
10
vendor/binsoul/net-mqtt/src/Exception/MalformedPacketException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Exception;
|
||||
|
||||
/**
|
||||
* Will be thrown if a packet is malformed.
|
||||
*/
|
||||
class MalformedPacketException extends \Exception
|
||||
{
|
||||
}
|
||||
10
vendor/binsoul/net-mqtt/src/Exception/UnknownPacketTypeException.php
vendored
Executable file
10
vendor/binsoul/net-mqtt/src/Exception/UnknownPacketTypeException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Exception;
|
||||
|
||||
/**
|
||||
* Will be thrown if a packet type is unknown.
|
||||
*/
|
||||
class UnknownPacketTypeException extends \Exception
|
||||
{
|
||||
}
|
||||
71
vendor/binsoul/net-mqtt/src/Flow.php
vendored
Executable file
71
vendor/binsoul/net-mqtt/src/Flow.php
vendored
Executable file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Represents a sequence of packages exchanged between clients and brokers.
|
||||
*/
|
||||
interface Flow
|
||||
{
|
||||
/**
|
||||
* Returns the unique code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCode();
|
||||
|
||||
/**
|
||||
* Starts the flow.
|
||||
*
|
||||
* @return Packet|null First packet of the flow
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function start();
|
||||
|
||||
/**
|
||||
* Indicates if the flow can handle the given packet.
|
||||
*
|
||||
* @param Packet $packet Packet to accept
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function accept(Packet $packet);
|
||||
|
||||
/**
|
||||
* Continues the flow.
|
||||
*
|
||||
* @param Packet $packet Packet to respond
|
||||
*
|
||||
* @return Packet|null Next packet of the flow
|
||||
*/
|
||||
public function next(Packet $packet);
|
||||
|
||||
/**
|
||||
* Indicates if the flow is finished.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFinished();
|
||||
|
||||
/**
|
||||
* Indicates if the flow finished successfully.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess();
|
||||
|
||||
/**
|
||||
* Returns the result of the flow if it finished successfully.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getResult();
|
||||
|
||||
/**
|
||||
* Returns an error message if the flow didn't finish successfully.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage();
|
||||
}
|
||||
74
vendor/binsoul/net-mqtt/src/Flow/AbstractFlow.php
vendored
Executable file
74
vendor/binsoul/net-mqtt/src/Flow/AbstractFlow.php
vendored
Executable file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Flow;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Provides an abstract implementation of the {@see Flow} interface.
|
||||
*/
|
||||
abstract class AbstractFlow implements Flow
|
||||
{
|
||||
/** @var bool */
|
||||
private $isFinished = false;
|
||||
/** @var bool */
|
||||
private $isSuccess = false;
|
||||
/** @var mixed */
|
||||
private $result;
|
||||
/** @var string */
|
||||
private $error = '';
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
}
|
||||
|
||||
public function isFinished()
|
||||
{
|
||||
return $this->isFinished;
|
||||
}
|
||||
|
||||
public function isSuccess()
|
||||
{
|
||||
return $this->isFinished && $this->isSuccess;
|
||||
}
|
||||
|
||||
public function getResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function getErrorMessage()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the flow as successful and sets the result.
|
||||
*
|
||||
* @param mixed|null $result
|
||||
*/
|
||||
protected function succeed($result = null)
|
||||
{
|
||||
$this->isFinished = true;
|
||||
$this->isSuccess = true;
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the flow as failed and sets the error message.
|
||||
*
|
||||
* @param string $error
|
||||
*/
|
||||
protected function fail($error = '')
|
||||
{
|
||||
$this->isFinished = true;
|
||||
$this->isSuccess = false;
|
||||
$this->error = $error;
|
||||
}
|
||||
}
|
||||
23
vendor/binsoul/net-mqtt/src/Flow/IncomingPingFlow.php
vendored
Executable file
23
vendor/binsoul/net-mqtt/src/Flow/IncomingPingFlow.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet\PingResponsePacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an incoming PING packet.
|
||||
*/
|
||||
class IncomingPingFlow extends AbstractFlow
|
||||
{
|
||||
public function getCode()
|
||||
{
|
||||
return 'pong';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$this->succeed();
|
||||
|
||||
return new PingResponsePacket();
|
||||
}
|
||||
}
|
||||
80
vendor/binsoul/net-mqtt/src/Flow/IncomingPublishFlow.php
vendored
Executable file
80
vendor/binsoul/net-mqtt/src/Flow/IncomingPublishFlow.php
vendored
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Message;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishAckPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishCompletePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReceivedPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReleasePacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an incoming PUBLISH packet.
|
||||
*/
|
||||
class IncomingPublishFlow extends AbstractFlow
|
||||
{
|
||||
/** @var int|null */
|
||||
private $identifier;
|
||||
/** @var Message */
|
||||
private $message;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Message $message
|
||||
* @param int|null $identifier
|
||||
*/
|
||||
public function __construct(Message $message, $identifier = null)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->identifier = $identifier;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'message';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$packet = null;
|
||||
$emit = true;
|
||||
if ($this->message->getQosLevel() === 1) {
|
||||
$packet = new PublishAckPacket();
|
||||
} elseif ($this->message->getQosLevel() === 2) {
|
||||
$packet = new PublishReceivedPacket();
|
||||
$emit = false;
|
||||
}
|
||||
|
||||
if ($packet !== null) {
|
||||
$packet->setIdentifier($this->identifier);
|
||||
}
|
||||
|
||||
if ($emit) {
|
||||
$this->succeed($this->message);
|
||||
}
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
if ($this->message->getQosLevel() !== 2 || $packet->getPacketType() !== Packet::TYPE_PUBREL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @var PublishReleasePacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
$this->succeed($this->message);
|
||||
|
||||
$response = new PublishCompletePacket();
|
||||
$response->setIdentifier($this->identifier);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
70
vendor/binsoul/net-mqtt/src/Flow/OutgoingConnectFlow.php
vendored
Executable file
70
vendor/binsoul/net-mqtt/src/Flow/OutgoingConnectFlow.php
vendored
Executable file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Connection;
|
||||
use BinSoul\Net\Mqtt\IdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\ConnectRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\ConnectResponsePacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing CONNECT packet.
|
||||
*/
|
||||
class OutgoingConnectFlow extends AbstractFlow
|
||||
{
|
||||
/** @var Connection */
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Connection $connection
|
||||
* @param IdentifierGenerator $generator
|
||||
*/
|
||||
public function __construct(Connection $connection, IdentifierGenerator $generator)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
if ($this->connection->getClientID() === '') {
|
||||
$this->connection = $this->connection->withClientID($generator->generateClientID());
|
||||
}
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'connect';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$packet = new ConnectRequestPacket();
|
||||
$packet->setProtocolLevel($this->connection->getProtocol());
|
||||
$packet->setKeepAlive($this->connection->getKeepAlive());
|
||||
$packet->setClientID($this->connection->getClientID());
|
||||
$packet->setCleanSession($this->connection->isCleanSession());
|
||||
$packet->setUsername($this->connection->getUsername());
|
||||
$packet->setPassword($this->connection->getPassword());
|
||||
$will = $this->connection->getWill();
|
||||
if ($will !== null && $will->getTopic() !== '' && $will->getPayload() !== '') {
|
||||
$packet->setWill($will->getTopic(), $will->getPayload(), $will->getQosLevel(), $will->isRetained());
|
||||
}
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
return $packet->getPacketType() === Packet::TYPE_CONNACK;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
/** @var ConnectResponsePacket $packet */
|
||||
if ($packet->isSuccess()) {
|
||||
$this->succeed($this->connection);
|
||||
} else {
|
||||
$this->fail($packet->getErrorName());
|
||||
}
|
||||
}
|
||||
}
|
||||
37
vendor/binsoul/net-mqtt/src/Flow/OutgoingDisconnectFlow.php
vendored
Executable file
37
vendor/binsoul/net-mqtt/src/Flow/OutgoingDisconnectFlow.php
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Connection;
|
||||
use BinSoul\Net\Mqtt\Packet\DisconnectRequestPacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing DISCONNECT packet.
|
||||
*/
|
||||
class OutgoingDisconnectFlow extends AbstractFlow
|
||||
{
|
||||
/** @var Connection */
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Connection $connection
|
||||
*/
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'disconnect';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$this->succeed($this->connection);
|
||||
|
||||
return new DisconnectRequestPacket();
|
||||
}
|
||||
}
|
||||
32
vendor/binsoul/net-mqtt/src/Flow/OutgoingPingFlow.php
vendored
Executable file
32
vendor/binsoul/net-mqtt/src/Flow/OutgoingPingFlow.php
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\PingRequestPacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing PING packet.
|
||||
*/
|
||||
class OutgoingPingFlow extends AbstractFlow
|
||||
{
|
||||
public function getCode()
|
||||
{
|
||||
return 'ping';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
return new PingRequestPacket();
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
return $packet->getPacketType() === Packet::TYPE_PINGRESP;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
$this->succeed();
|
||||
}
|
||||
}
|
||||
102
vendor/binsoul/net-mqtt/src/Flow/OutgoingPublishFlow.php
vendored
Executable file
102
vendor/binsoul/net-mqtt/src/Flow/OutgoingPublishFlow.php
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\IdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Message;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishAckPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishCompletePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReceivedPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReleasePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishRequestPacket;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing PUBLISH packet.
|
||||
*/
|
||||
class OutgoingPublishFlow extends AbstractFlow
|
||||
{
|
||||
/** @var int|null */
|
||||
private $identifier;
|
||||
/** @var Message */
|
||||
private $message;
|
||||
/** @var bool */
|
||||
private $receivedPubRec = false;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Message $message
|
||||
* @param IdentifierGenerator $generator
|
||||
*/
|
||||
public function __construct(Message $message, IdentifierGenerator $generator)
|
||||
{
|
||||
$this->message = $message;
|
||||
if ($this->message->getQosLevel() > 0) {
|
||||
$this->identifier = $generator->generatePacketID();
|
||||
}
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'publish';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$packet = new PublishRequestPacket();
|
||||
$packet->setTopic($this->message->getTopic());
|
||||
$packet->setPayload($this->message->getPayload());
|
||||
$packet->setRetained($this->message->isRetained());
|
||||
$packet->setDuplicate($this->message->isDuplicate());
|
||||
$packet->setQosLevel($this->message->getQosLevel());
|
||||
|
||||
if ($this->message->getQosLevel() === 0) {
|
||||
$this->succeed($this->message);
|
||||
} else {
|
||||
$packet->setIdentifier($this->identifier);
|
||||
}
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
if ($this->message->getQosLevel() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$packetType = $packet->getPacketType();
|
||||
|
||||
if ($packetType === Packet::TYPE_PUBACK && $this->message->getQosLevel() === 1) {
|
||||
/* @var PublishAckPacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
} elseif ($this->message->getQosLevel() === 2) {
|
||||
if ($packetType === Packet::TYPE_PUBREC) {
|
||||
/* @var PublishReceivedPacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
} elseif ($this->receivedPubRec && $packetType === Packet::TYPE_PUBCOMP) {
|
||||
/* @var PublishCompletePacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
$packetType = $packet->getPacketType();
|
||||
|
||||
if ($packetType === Packet::TYPE_PUBACK || $packetType === Packet::TYPE_PUBCOMP) {
|
||||
$this->succeed($this->message);
|
||||
} elseif ($packetType === Packet::TYPE_PUBREC) {
|
||||
$this->receivedPubRec = true;
|
||||
|
||||
$response = new PublishReleasePacket();
|
||||
$response->setIdentifier($this->identifier);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
vendor/binsoul/net-mqtt/src/Flow/OutgoingSubscribeFlow.php
vendored
Executable file
82
vendor/binsoul/net-mqtt/src/Flow/OutgoingSubscribeFlow.php
vendored
Executable file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\IdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\SubscribeRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\SubscribeResponsePacket;
|
||||
use BinSoul\Net\Mqtt\Subscription;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing SUBSCRIBE packet.
|
||||
*/
|
||||
class OutgoingSubscribeFlow extends AbstractFlow
|
||||
{
|
||||
/** @var int */
|
||||
private $identifier;
|
||||
/** @var Subscription[] */
|
||||
private $subscriptions;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Subscription[] $subscriptions
|
||||
* @param IdentifierGenerator $generator
|
||||
*/
|
||||
public function __construct(array $subscriptions, IdentifierGenerator $generator)
|
||||
{
|
||||
$this->subscriptions = array_values($subscriptions);
|
||||
$this->identifier = $generator->generatePacketID();
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'subscribe';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$packet = new SubscribeRequestPacket();
|
||||
$packet->setTopic($this->subscriptions[0]->getFilter());
|
||||
$packet->setQosLevel($this->subscriptions[0]->getQosLevel());
|
||||
$packet->setIdentifier($this->identifier);
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
if ($packet->getPacketType() !== Packet::TYPE_SUBACK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @var SubscribeResponsePacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
/* @var SubscribeResponsePacket $packet */
|
||||
$returnCodes = $packet->getReturnCodes();
|
||||
if (count($returnCodes) !== count($this->subscriptions)) {
|
||||
throw new \LogicException(
|
||||
sprintf(
|
||||
'SUBACK: Expected %d return codes but got %d.',
|
||||
count($this->subscriptions),
|
||||
count($returnCodes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($returnCodes as $index => $code) {
|
||||
if ($packet->isError($code)) {
|
||||
$this->fail(sprintf('Failed to subscribe to "%s".', $this->subscriptions[$index]->getFilter()));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->succeed($this->subscriptions[0]);
|
||||
}
|
||||
}
|
||||
61
vendor/binsoul/net-mqtt/src/Flow/OutgoingUnsubscribeFlow.php
vendored
Executable file
61
vendor/binsoul/net-mqtt/src/Flow/OutgoingUnsubscribeFlow.php
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Flow;
|
||||
|
||||
use BinSoul\Net\Mqtt\IdentifierGenerator;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
use BinSoul\Net\Mqtt\Packet\UnsubscribeRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\UnsubscribeResponsePacket;
|
||||
use BinSoul\Net\Mqtt\Subscription;
|
||||
|
||||
/**
|
||||
* Represents a flow starting with an outgoing UNSUBSCRIBE packet.
|
||||
*/
|
||||
class OutgoingUnsubscribeFlow extends AbstractFlow
|
||||
{
|
||||
/** @var int */
|
||||
private $identifier;
|
||||
/** @var Subscription[] */
|
||||
private $subscriptions;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param Subscription[] $subscriptions
|
||||
* @param IdentifierGenerator $generator
|
||||
*/
|
||||
public function __construct(array $subscriptions, IdentifierGenerator $generator)
|
||||
{
|
||||
$this->subscriptions = array_values($subscriptions);
|
||||
$this->identifier = $generator->generatePacketID();
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return 'unsubscribe';
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$packet = new UnsubscribeRequestPacket();
|
||||
$packet->setTopic($this->subscriptions[0]->getFilter());
|
||||
$packet->setIdentifier($this->identifier);
|
||||
|
||||
return $packet;
|
||||
}
|
||||
|
||||
public function accept(Packet $packet)
|
||||
{
|
||||
if ($packet->getPacketType() !== Packet::TYPE_UNSUBACK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @var UnsubscribeResponsePacket $packet */
|
||||
return $packet->getIdentifier() === $this->identifier;
|
||||
}
|
||||
|
||||
public function next(Packet $packet)
|
||||
{
|
||||
$this->succeed($this->subscriptions[0]);
|
||||
}
|
||||
}
|
||||
23
vendor/binsoul/net-mqtt/src/IdentifierGenerator.php
vendored
Executable file
23
vendor/binsoul/net-mqtt/src/IdentifierGenerator.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Generates identifiers.
|
||||
*/
|
||||
interface IdentifierGenerator
|
||||
{
|
||||
/**
|
||||
* Generates a packet identifier between 1 and 0xFFFF.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function generatePacketID();
|
||||
|
||||
/**
|
||||
* Generates a client identifier of up to 23 bytes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateClientID();
|
||||
}
|
||||
99
vendor/binsoul/net-mqtt/src/Message.php
vendored
Executable file
99
vendor/binsoul/net-mqtt/src/Message.php
vendored
Executable file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Represents a message.
|
||||
*/
|
||||
interface Message
|
||||
{
|
||||
/**
|
||||
* Returns the topic.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTopic();
|
||||
|
||||
/**
|
||||
* Returns the payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPayload();
|
||||
|
||||
/**
|
||||
* Returns the quality of service level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQosLevel();
|
||||
|
||||
/**
|
||||
* Indicates if the message is a duplicate.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDuplicate();
|
||||
|
||||
/**
|
||||
* Indicates if the message is retained.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRetained();
|
||||
|
||||
/**
|
||||
* Returns a new message with the given topic.
|
||||
*
|
||||
* @param string $topic
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withTopic($topic);
|
||||
|
||||
/**
|
||||
* Returns a new message with the given payload.
|
||||
*
|
||||
* @param string $payload
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withPayload($payload);
|
||||
|
||||
/**
|
||||
* Returns a new message with the given quality of service level.
|
||||
*
|
||||
* @param int $level
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withQosLevel($level);
|
||||
|
||||
/**
|
||||
* Returns a new message flagged as retained.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function retain();
|
||||
|
||||
/**
|
||||
* Returns a new message flagged as not retained.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function release();
|
||||
|
||||
/**
|
||||
* Returns a new message flagged as duplicate.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function duplicate();
|
||||
|
||||
/**
|
||||
* Returns a new message flagged as original.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function original();
|
||||
}
|
||||
52
vendor/binsoul/net-mqtt/src/Packet.php
vendored
Executable file
52
vendor/binsoul/net-mqtt/src/Packet.php
vendored
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Represent a packet of the MQTT protocol.
|
||||
*/
|
||||
interface Packet
|
||||
{
|
||||
const TYPE_CONNECT = 1;
|
||||
const TYPE_CONNACK = 2;
|
||||
const TYPE_PUBLISH = 3;
|
||||
const TYPE_PUBACK = 4;
|
||||
const TYPE_PUBREC = 5;
|
||||
const TYPE_PUBREL = 6;
|
||||
const TYPE_PUBCOMP = 7;
|
||||
const TYPE_SUBSCRIBE = 8;
|
||||
const TYPE_SUBACK = 9;
|
||||
const TYPE_UNSUBSCRIBE = 10;
|
||||
const TYPE_UNSUBACK = 11;
|
||||
const TYPE_PINGREQ = 12;
|
||||
const TYPE_PINGRESP = 13;
|
||||
const TYPE_DISCONNECT = 14;
|
||||
|
||||
/**
|
||||
* Returns the type of the packet.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPacketType();
|
||||
|
||||
/**
|
||||
* Reads the packet from the given stream.
|
||||
*
|
||||
* @param PacketStream $stream
|
||||
*/
|
||||
public function read(PacketStream $stream);
|
||||
|
||||
/**
|
||||
* Writes the packet to the given stream.
|
||||
*
|
||||
* @param PacketStream $stream
|
||||
*/
|
||||
public function write(PacketStream $stream);
|
||||
|
||||
/**
|
||||
* Returns the serialized form of the packet.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString();
|
||||
}
|
||||
279
vendor/binsoul/net-mqtt/src/Packet/BasePacket.php
vendored
Executable file
279
vendor/binsoul/net-mqtt/src/Packet/BasePacket.php
vendored
Executable file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the base class for all packets.
|
||||
*/
|
||||
abstract class BasePacket implements Packet
|
||||
{
|
||||
/**
|
||||
* Type of the packet. See {@see Packet}.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $packetType = 0;
|
||||
/**
|
||||
* Flags of the packet.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $packetFlags = 0;
|
||||
|
||||
/**
|
||||
* Number of bytes of a variable length packet.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $remainingPacketLength = 0;
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$output = new PacketStream();
|
||||
$this->write($output);
|
||||
|
||||
return $output->getData();
|
||||
}
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
$byte = $stream->readByte();
|
||||
|
||||
if ($byte >> 4 !== static::$packetType) {
|
||||
throw new MalformedPacketException(
|
||||
sprintf(
|
||||
'Expected packet type %02x but got %02x.',
|
||||
$byte >> 4,
|
||||
static::$packetType
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->packetFlags = $byte & 0x0F;
|
||||
$this->readRemainingLength($stream);
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$stream->writeByte(((static::$packetType & 0x0F) << 4) + ($this->packetFlags & 0x0F));
|
||||
$this->writeRemainingLength($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the remaining length from the given stream.
|
||||
*
|
||||
* @param PacketStream $stream
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
*/
|
||||
private function readRemainingLength(PacketStream $stream)
|
||||
{
|
||||
$this->remainingPacketLength = 0;
|
||||
$multiplier = 1;
|
||||
|
||||
do {
|
||||
$encodedByte = $stream->readByte();
|
||||
|
||||
$this->remainingPacketLength += ($encodedByte & 127) * $multiplier;
|
||||
$multiplier *= 128;
|
||||
|
||||
if ($multiplier > 128 * 128 * 128 * 128) {
|
||||
throw new MalformedPacketException('Malformed remaining length.');
|
||||
}
|
||||
} while (($encodedByte & 128) !== 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the remaining length to the given stream.
|
||||
*
|
||||
* @param PacketStream $stream
|
||||
*/
|
||||
private function writeRemainingLength(PacketStream $stream)
|
||||
{
|
||||
$x = $this->remainingPacketLength;
|
||||
do {
|
||||
$encodedByte = $x % 128;
|
||||
$x = (int) ($x / 128);
|
||||
if ($x > 0) {
|
||||
$encodedByte |= 128;
|
||||
}
|
||||
|
||||
$stream->writeByte($encodedByte);
|
||||
} while ($x > 0);
|
||||
}
|
||||
|
||||
public function getPacketType()
|
||||
{
|
||||
return static::$packetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the packet flags.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPacketFlags()
|
||||
{
|
||||
return $this->packetFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remaining length.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRemainingPacketLength()
|
||||
{
|
||||
return $this->remainingPacketLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the packet flags have a specific value.
|
||||
*
|
||||
* @param int $value
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function assertPacketFlags($value, $fromPacket = true)
|
||||
{
|
||||
if ($this->packetFlags !== $value) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected flags %02x but got %02x.',
|
||||
$value,
|
||||
$this->packetFlags
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the remaining length is greater than zero and has a specific value.
|
||||
*
|
||||
* @param int|null $value value to test or null if any value greater than zero is valid
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function assertRemainingPacketLength($value = null, $fromPacket = true)
|
||||
{
|
||||
if ($value === null && $this->remainingPacketLength === 0) {
|
||||
$this->throwException('Expected payload but remaining packet length is zero.', $fromPacket);
|
||||
}
|
||||
|
||||
if ($value !== null && $this->remainingPacketLength !== $value) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected remaining packet length of %d bytes but got %d.',
|
||||
$value,
|
||||
$this->remainingPacketLength
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given string is a well-formed MQTT string.
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function assertValidStringLength($value, $fromPacket = true)
|
||||
{
|
||||
if (strlen($value) > 0xFFFF) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'The string "%s" is longer than 65535 byte.',
|
||||
substr($value, 0, 50)
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given string is a well-formed MQTT string.
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function assertValidString($value, $fromPacket = true)
|
||||
{
|
||||
$this->assertValidStringLength($value, $fromPacket);
|
||||
|
||||
if (!mb_check_encoding($value, 'UTF-8')) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'The string "%s" is not well-formed UTF-8.',
|
||||
substr($value, 0, 50)
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
|
||||
if (preg_match('/[\xD8-\xDF][\x00-\xFF]|\x00\x00/x', $value)) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'The string "%s" contains invalid characters.',
|
||||
substr($value, 0, 50)
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given quality of service level is valid.
|
||||
*
|
||||
* @param int $level
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function assertValidQosLevel($level, $fromPacket = true)
|
||||
{
|
||||
if ($level < 0 || $level > 2) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected a quality of service level between 0 and 2 but got %d.',
|
||||
$level
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a MalformedPacketException for packet validation and an InvalidArgumentException otherwise.
|
||||
*
|
||||
* @param string $message
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function throwException($message, $fromPacket)
|
||||
{
|
||||
if ($fromPacket) {
|
||||
throw new MalformedPacketException($message);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
}
|
||||
405
vendor/binsoul/net-mqtt/src/Packet/ConnectRequestPacket.php
vendored
Executable file
405
vendor/binsoul/net-mqtt/src/Packet/ConnectRequestPacket.php
vendored
Executable file
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the CONNECT packet.
|
||||
*/
|
||||
class ConnectRequestPacket extends BasePacket
|
||||
{
|
||||
/** @var int */
|
||||
private $protocolLevel = 4;
|
||||
/** @var string */
|
||||
private $protocolName = 'MQTT';
|
||||
/** @var int */
|
||||
private $flags = 2;
|
||||
/** @var string */
|
||||
protected $clientID = '';
|
||||
/** @var int */
|
||||
private $keepAlive = 60;
|
||||
/** @var string */
|
||||
private $willTopic = '';
|
||||
/** @var string */
|
||||
private $willMessage = '';
|
||||
/** @var string */
|
||||
private $username = '';
|
||||
/** @var string */
|
||||
private $password = '';
|
||||
|
||||
protected static $packetType = Packet::TYPE_CONNECT;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength();
|
||||
|
||||
$this->protocolName = $stream->readString();
|
||||
$this->protocolLevel = $stream->readByte();
|
||||
$this->flags = $stream->readByte();
|
||||
$this->keepAlive = $stream->readWord();
|
||||
$this->clientID = $stream->readString();
|
||||
|
||||
if ($this->hasWill()) {
|
||||
$this->willTopic = $stream->readString();
|
||||
$this->willMessage = $stream->readString();
|
||||
}
|
||||
|
||||
if ($this->hasUsername()) {
|
||||
$this->username = $stream->readString();
|
||||
}
|
||||
|
||||
if ($this->hasPassword()) {
|
||||
$this->password = $stream->readString();
|
||||
}
|
||||
|
||||
$this->assertValidWill();
|
||||
$this->assertValidString($this->clientID);
|
||||
$this->assertValidString($this->willTopic);
|
||||
$this->assertValidString($this->username);
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
if ($this->clientID === '') {
|
||||
$this->clientID = 'BinSoul'.mt_rand(100000, 999999);
|
||||
}
|
||||
|
||||
$data = new PacketStream();
|
||||
|
||||
$data->writeString($this->protocolName);
|
||||
$data->writeByte($this->protocolLevel);
|
||||
$data->writeByte($this->flags);
|
||||
$data->writeWord($this->keepAlive);
|
||||
$data->writeString($this->clientID);
|
||||
|
||||
if ($this->hasWill()) {
|
||||
$data->writeString($this->willTopic);
|
||||
$data->writeString($this->willMessage);
|
||||
}
|
||||
|
||||
if ($this->hasUsername()) {
|
||||
$data->writeString($this->username);
|
||||
}
|
||||
|
||||
if ($this->hasPassword()) {
|
||||
$data->writeString($this->password);
|
||||
}
|
||||
|
||||
$this->remainingPacketLength = $data->length();
|
||||
|
||||
parent::write($stream);
|
||||
$stream->write($data->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the protocol level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getProtocolLevel()
|
||||
{
|
||||
return $this->protocolLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the protocol level.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setProtocolLevel($value)
|
||||
{
|
||||
if ($value < 3 || $value > 4) {
|
||||
throw new \InvalidArgumentException(sprintf('Unknown protocol level %d.', $value));
|
||||
}
|
||||
|
||||
$this->protocolLevel = $value;
|
||||
if ($this->protocolLevel === 3) {
|
||||
$this->protocolName = 'MQIsdp';
|
||||
} elseif ($this->protocolLevel === 4) {
|
||||
$this->protocolName = 'MQTT';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientID()
|
||||
{
|
||||
return $this->clientID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client id.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setClientID($value)
|
||||
{
|
||||
$this->clientID = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keep alive time in seconds.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getKeepAlive()
|
||||
{
|
||||
return $this->keepAlive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keep alive time in seconds.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setKeepAlive($value)
|
||||
{
|
||||
if ($value > 65535) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'Expected a keep alive time lower than 65535 but got %d.',
|
||||
$value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->keepAlive = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the clean session flag is set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCleanSession()
|
||||
{
|
||||
return ($this->flags & 2) === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the clean session flag.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setCleanSession($value)
|
||||
{
|
||||
if ($value) {
|
||||
$this->flags |= 2;
|
||||
} else {
|
||||
$this->flags &= ~2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a will is set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasWill()
|
||||
{
|
||||
return ($this->flags & 4) === 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the desired quality of service level of the will.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWillQosLevel()
|
||||
{
|
||||
return ($this->flags & 24) >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the will should be retained.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWillRetained()
|
||||
{
|
||||
return ($this->flags & 32) === 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the will topic.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWillTopic()
|
||||
{
|
||||
return $this->willTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the will message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWillMessage()
|
||||
{
|
||||
return $this->willMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the will.
|
||||
*
|
||||
* @param string $topic
|
||||
* @param string $message
|
||||
* @param int $qosLevel
|
||||
* @param bool $isRetained
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setWill($topic, $message, $qosLevel = 0, $isRetained = false)
|
||||
{
|
||||
$this->assertValidString($topic, false);
|
||||
if ($topic === '') {
|
||||
throw new \InvalidArgumentException('The topic must not be empty.');
|
||||
}
|
||||
|
||||
$this->assertValidStringLength($message, false);
|
||||
if ($message === '') {
|
||||
throw new \InvalidArgumentException('The message must not be empty.');
|
||||
}
|
||||
|
||||
$this->assertValidQosLevel($qosLevel, false);
|
||||
|
||||
$this->willTopic = $topic;
|
||||
$this->willMessage = $message;
|
||||
|
||||
$this->flags |= 4;
|
||||
$this->flags |= ($qosLevel << 3);
|
||||
|
||||
if ($isRetained) {
|
||||
$this->flags |= 32;
|
||||
} else {
|
||||
$this->flags &= ~32;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the will.
|
||||
*/
|
||||
public function removeWill()
|
||||
{
|
||||
$this->flags &= ~60;
|
||||
$this->willTopic = '';
|
||||
$this->willMessage = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a username is set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUsername()
|
||||
{
|
||||
return $this->flags & 64;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setUsername($value)
|
||||
{
|
||||
$this->assertValidString($value, false);
|
||||
|
||||
$this->username = $value;
|
||||
if ($this->username !== '') {
|
||||
$this->flags |= 64;
|
||||
} else {
|
||||
$this->flags &= ~64;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a password is set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPassword()
|
||||
{
|
||||
return $this->flags & 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPassword($value)
|
||||
{
|
||||
$this->assertValidStringLength($value, false);
|
||||
|
||||
$this->password = $value;
|
||||
if ($this->password !== '') {
|
||||
$this->flags |= 128;
|
||||
} else {
|
||||
$this->flags &= ~128;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that all will flags and quality of service are correct.
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
*/
|
||||
private function assertValidWill()
|
||||
{
|
||||
if ($this->hasWill()) {
|
||||
$this->assertValidQosLevel($this->getWillQosLevel(), true);
|
||||
} else {
|
||||
if ($this->getWillQosLevel() > 0) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected a will quality of service level of zero but got %d.',
|
||||
$this->getWillQosLevel()
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->isWillRetained()) {
|
||||
$this->throwException('There is not will but the will retain flag is set.', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
vendor/binsoul/net-mqtt/src/Packet/ConnectResponsePacket.php
vendored
Executable file
111
vendor/binsoul/net-mqtt/src/Packet/ConnectResponsePacket.php
vendored
Executable file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the CONNACK packet.
|
||||
*/
|
||||
class ConnectResponsePacket extends BasePacket
|
||||
{
|
||||
/** @var string[][] */
|
||||
private static $returnCodes = [
|
||||
0 => [
|
||||
'Connection accepted',
|
||||
'',
|
||||
],
|
||||
1 => [
|
||||
'Unacceptable protocol version',
|
||||
'The Server does not support the level of the MQTT protocol requested by the client.',
|
||||
],
|
||||
2 => [
|
||||
'Identifier rejected',
|
||||
'The client identifier is correct UTF-8 but not allowed by the server.',
|
||||
],
|
||||
3 => [
|
||||
'Server unavailable',
|
||||
'The network connection has been made but the MQTT service is unavailable',
|
||||
],
|
||||
4 => [
|
||||
'Bad user name or password',
|
||||
'The data in the user name or password is malformed.',
|
||||
],
|
||||
5 => [
|
||||
'Not authorized',
|
||||
'The client is not authorized to connect.',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var int */
|
||||
private $flags = 0;
|
||||
/** @var int */
|
||||
private $returnCode;
|
||||
|
||||
protected static $packetType = Packet::TYPE_CONNACK;
|
||||
protected $remainingPacketLength = 2;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength(2);
|
||||
|
||||
$this->flags = $stream->readByte();
|
||||
$this->returnCode = $stream->readByte();
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$this->remainingPacketLength = 2;
|
||||
parent::write($stream);
|
||||
|
||||
$stream->writeByte($this->flags);
|
||||
$stream->writeByte($this->returnCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getReturnCode()
|
||||
{
|
||||
return $this->returnCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the connection was successful.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
return $this->returnCode === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the connection failed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isError()
|
||||
{
|
||||
return $this->returnCode > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the returned error code.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getErrorName()
|
||||
{
|
||||
if (isset(self::$returnCodes[$this->returnCode])) {
|
||||
return self::$returnCodes[$this->returnCode][0];
|
||||
}
|
||||
|
||||
return 'Error '.$this->returnCode;
|
||||
}
|
||||
}
|
||||
22
vendor/binsoul/net-mqtt/src/Packet/DisconnectRequestPacket.php
vendored
Executable file
22
vendor/binsoul/net-mqtt/src/Packet/DisconnectRequestPacket.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the DISCONNECT packet.
|
||||
*/
|
||||
class DisconnectRequestPacket extends BasePacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_DISCONNECT;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength(0);
|
||||
}
|
||||
}
|
||||
62
vendor/binsoul/net-mqtt/src/Packet/IdentifiablePacket.php
vendored
Executable file
62
vendor/binsoul/net-mqtt/src/Packet/IdentifiablePacket.php
vendored
Executable file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Provides methods for packets with an identifier.
|
||||
*/
|
||||
trait IdentifiablePacket
|
||||
{
|
||||
/** @var int */
|
||||
private static $nextIdentifier = 0;
|
||||
/** @var int|null */
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Returns the identifier or generates a new one.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function generateIdentifier()
|
||||
{
|
||||
if ($this->identifier === null) {
|
||||
++self::$nextIdentifier;
|
||||
self::$nextIdentifier &= 0xFFFF;
|
||||
|
||||
$this->identifier = self::$nextIdentifier;
|
||||
}
|
||||
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier.
|
||||
*
|
||||
* @param int|null $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setIdentifier($value)
|
||||
{
|
||||
if ($value !== null && ($value < 0 || $value > 0xFFFF)) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'Expected an identifier between 0x0000 and 0xFFFF but got %x',
|
||||
$value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->identifier = $value;
|
||||
}
|
||||
}
|
||||
42
vendor/binsoul/net-mqtt/src/Packet/IdentifierOnlyPacket.php
vendored
Executable file
42
vendor/binsoul/net-mqtt/src/Packet/IdentifierOnlyPacket.php
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
|
||||
/**
|
||||
* Provides a base class for PUB* packets.
|
||||
*/
|
||||
abstract class IdentifierOnlyPacket extends BasePacket
|
||||
{
|
||||
use IdentifiablePacket;
|
||||
|
||||
protected $remainingPacketLength = 2;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertPacketFlags($this->getExpectedPacketFlags());
|
||||
$this->assertRemainingPacketLength(2);
|
||||
|
||||
$this->identifier = $stream->readWord();
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$this->remainingPacketLength = 2;
|
||||
parent::write($stream);
|
||||
|
||||
$stream->writeWord($this->generateIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected packet flags.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getExpectedPacketFlags()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
22
vendor/binsoul/net-mqtt/src/Packet/PingRequestPacket.php
vendored
Executable file
22
vendor/binsoul/net-mqtt/src/Packet/PingRequestPacket.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PINGREQ packet.
|
||||
*/
|
||||
class PingRequestPacket extends BasePacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PINGREQ;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength(0);
|
||||
}
|
||||
}
|
||||
22
vendor/binsoul/net-mqtt/src/Packet/PingResponsePacket.php
vendored
Executable file
22
vendor/binsoul/net-mqtt/src/Packet/PingResponsePacket.php
vendored
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PINGRESP packet.
|
||||
*/
|
||||
class PingResponsePacket extends BasePacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PINGRESP;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength(0);
|
||||
}
|
||||
}
|
||||
13
vendor/binsoul/net-mqtt/src/Packet/PublishAckPacket.php
vendored
Executable file
13
vendor/binsoul/net-mqtt/src/Packet/PublishAckPacket.php
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PUBACK packet.
|
||||
*/
|
||||
class PublishAckPacket extends IdentifierOnlyPacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PUBACK;
|
||||
}
|
||||
13
vendor/binsoul/net-mqtt/src/Packet/PublishCompletePacket.php
vendored
Executable file
13
vendor/binsoul/net-mqtt/src/Packet/PublishCompletePacket.php
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PUBCOMP packet.
|
||||
*/
|
||||
class PublishCompletePacket extends IdentifierOnlyPacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PUBCOMP;
|
||||
}
|
||||
13
vendor/binsoul/net-mqtt/src/Packet/PublishReceivedPacket.php
vendored
Executable file
13
vendor/binsoul/net-mqtt/src/Packet/PublishReceivedPacket.php
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PUBREC packet.
|
||||
*/
|
||||
class PublishReceivedPacket extends IdentifierOnlyPacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PUBREC;
|
||||
}
|
||||
19
vendor/binsoul/net-mqtt/src/Packet/PublishReleasePacket.php
vendored
Executable file
19
vendor/binsoul/net-mqtt/src/Packet/PublishReleasePacket.php
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PUBREL packet.
|
||||
*/
|
||||
class PublishReleasePacket extends IdentifierOnlyPacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_PUBREL;
|
||||
protected $packetFlags = 2;
|
||||
|
||||
protected function getExpectedPacketFlags()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
176
vendor/binsoul/net-mqtt/src/Packet/PublishRequestPacket.php
vendored
Executable file
176
vendor/binsoul/net-mqtt/src/Packet/PublishRequestPacket.php
vendored
Executable file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the PUBLISH packet.
|
||||
*/
|
||||
class PublishRequestPacket extends BasePacket
|
||||
{
|
||||
use IdentifiablePacket;
|
||||
|
||||
/** @var string */
|
||||
private $topic;
|
||||
/** @var string */
|
||||
private $payload;
|
||||
|
||||
protected static $packetType = Packet::TYPE_PUBLISH;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertRemainingPacketLength();
|
||||
|
||||
$originalPosition = $stream->getPosition();
|
||||
$this->topic = $stream->readString();
|
||||
$this->identifier = null;
|
||||
if ($this->getQosLevel() > 0) {
|
||||
$this->identifier = $stream->readWord();
|
||||
}
|
||||
|
||||
$payloadLength = $this->remainingPacketLength - ($stream->getPosition() - $originalPosition);
|
||||
$this->payload = $stream->read($payloadLength);
|
||||
|
||||
$this->assertValidQosLevel($this->getQosLevel());
|
||||
$this->assertValidString($this->topic);
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$data = new PacketStream();
|
||||
|
||||
$data->writeString($this->topic);
|
||||
if ($this->getQosLevel() > 0) {
|
||||
$data->writeWord($this->generateIdentifier());
|
||||
}
|
||||
|
||||
$data->write($this->payload);
|
||||
|
||||
$this->remainingPacketLength = $data->length();
|
||||
|
||||
parent::write($stream);
|
||||
$stream->write($data->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTopic()
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the topic.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setTopic($value)
|
||||
{
|
||||
$this->assertValidString($value, false);
|
||||
if ($value === '') {
|
||||
throw new \InvalidArgumentException('The topic must not be empty.');
|
||||
}
|
||||
|
||||
$this->topic = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPayload()
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the payload.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setPayload($value)
|
||||
{
|
||||
$this->payload = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the packet is a duplicate.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDuplicate()
|
||||
{
|
||||
return ($this->packetFlags & 8) === 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the packet as duplicate.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setDuplicate($value)
|
||||
{
|
||||
if ($value) {
|
||||
$this->packetFlags |= 8;
|
||||
} else {
|
||||
$this->packetFlags &= ~8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the packet is retained.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRetained()
|
||||
{
|
||||
return ($this->packetFlags & 1) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the packet as retained.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setRetained($value)
|
||||
{
|
||||
if ($value) {
|
||||
$this->packetFlags |= 1;
|
||||
} else {
|
||||
$this->packetFlags &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quality of service level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQosLevel()
|
||||
{
|
||||
return ($this->packetFlags & 6) >> 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of service level.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setQosLevel($value)
|
||||
{
|
||||
$this->assertValidQosLevel($value, false);
|
||||
|
||||
$this->packetFlags |= ($value & 3) << 1;
|
||||
}
|
||||
}
|
||||
66
vendor/binsoul/net-mqtt/src/Packet/StrictConnectRequestPacket.php
vendored
Executable file
66
vendor/binsoul/net-mqtt/src/Packet/StrictConnectRequestPacket.php
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
|
||||
/**
|
||||
* Represents the CONNECT packet with strict rules for client ids.
|
||||
*/
|
||||
class StrictConnectRequestPacket extends ConnectRequestPacket
|
||||
{
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
|
||||
$this->assertValidClientID($this->clientID, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client id.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setClientID($value)
|
||||
{
|
||||
$this->assertValidClientID($value, false);
|
||||
|
||||
$this->clientID = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a client id is shorter than 24 bytes and only contains characters 0-9, a-z or A-Z.
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function assertValidClientID($value, $fromPacket)
|
||||
{
|
||||
|
||||
if (strlen($value) > 23) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected client id shorter than 24 bytes but got "%s".',
|
||||
$value
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
|
||||
if ($value !== '' && !ctype_alnum($value)) {
|
||||
$this->throwException(
|
||||
sprintf(
|
||||
'Expected a client id containing characters 0-9, a-z or A-Z but got "%s".',
|
||||
$value
|
||||
),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
vendor/binsoul/net-mqtt/src/Packet/SubscribeRequestPacket.php
vendored
Executable file
101
vendor/binsoul/net-mqtt/src/Packet/SubscribeRequestPacket.php
vendored
Executable file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the SUBSCRIBE packet.
|
||||
*/
|
||||
class SubscribeRequestPacket extends BasePacket
|
||||
{
|
||||
use IdentifiablePacket;
|
||||
|
||||
/** @var string */
|
||||
private $topic;
|
||||
/** @var int */
|
||||
private $qosLevel;
|
||||
|
||||
protected static $packetType = Packet::TYPE_SUBSCRIBE;
|
||||
protected $packetFlags = 2;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertPacketFlags(2);
|
||||
$this->assertRemainingPacketLength();
|
||||
|
||||
$this->identifier = $stream->readWord();
|
||||
$this->topic = $stream->readString();
|
||||
$this->qosLevel = $stream->readByte();
|
||||
|
||||
$this->assertValidQosLevel($this->qosLevel);
|
||||
$this->assertValidString($this->topic);
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$data = new PacketStream();
|
||||
|
||||
$data->writeWord($this->generateIdentifier());
|
||||
$data->writeString($this->topic);
|
||||
$data->writeByte($this->qosLevel);
|
||||
|
||||
$this->remainingPacketLength = $data->length();
|
||||
|
||||
parent::write($stream);
|
||||
$stream->write($data->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTopic()
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the topic.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setTopic($value)
|
||||
{
|
||||
$this->assertValidString($value, false);
|
||||
if ($value === '') {
|
||||
throw new \InvalidArgumentException('The topic must not be empty.');
|
||||
}
|
||||
|
||||
$this->topic = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quality of service level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQosLevel()
|
||||
{
|
||||
return $this->qosLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the quality of service level.
|
||||
*
|
||||
* @param int $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setQosLevel($value)
|
||||
{
|
||||
$this->assertValidQosLevel($value, false);
|
||||
|
||||
$this->qosLevel = $value;
|
||||
}
|
||||
}
|
||||
132
vendor/binsoul/net-mqtt/src/Packet/SubscribeResponsePacket.php
vendored
Executable file
132
vendor/binsoul/net-mqtt/src/Packet/SubscribeResponsePacket.php
vendored
Executable file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the SUBACK packet.
|
||||
*/
|
||||
class SubscribeResponsePacket extends BasePacket
|
||||
{
|
||||
use IdentifiablePacket;
|
||||
|
||||
private static $qosLevels = [
|
||||
0 => ['Maximum QoS 0'],
|
||||
1 => ['Maximum QoS 1'],
|
||||
2 => ['Maximum QoS 2'],
|
||||
128 => ['Failure'],
|
||||
];
|
||||
|
||||
/** @var int[] */
|
||||
private $returnCodes;
|
||||
|
||||
protected static $packetType = Packet::TYPE_SUBACK;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
$this->assertPacketFlags(0);
|
||||
$this->assertRemainingPacketLength();
|
||||
|
||||
$this->identifier = $stream->readWord();
|
||||
|
||||
$returnCodeLength = $this->remainingPacketLength - 2;
|
||||
for ($n = 0; $n < $returnCodeLength; ++$n) {
|
||||
$returnCode = $stream->readByte();
|
||||
$this->assertValidReturnCode($returnCode);
|
||||
|
||||
$this->returnCodes[] = $returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$data = new PacketStream();
|
||||
|
||||
$data->writeWord($this->generateIdentifier());
|
||||
foreach ($this->returnCodes as $returnCode) {
|
||||
$data->writeByte($returnCode);
|
||||
}
|
||||
|
||||
$this->remainingPacketLength = $data->length();
|
||||
|
||||
parent::write($stream);
|
||||
$stream->write($data->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the given return code is an error.
|
||||
*
|
||||
* @param int $returnCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isError($returnCode)
|
||||
{
|
||||
return $returnCode === 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the given return code is an error.
|
||||
*
|
||||
* @param int $returnCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReturnCodeName($returnCode)
|
||||
{
|
||||
if (isset(self::$qosLevels[$returnCode])) {
|
||||
return self::$qosLevels[$returnCode][0];
|
||||
}
|
||||
|
||||
return 'Unknown '.$returnCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return codes.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getReturnCodes()
|
||||
{
|
||||
return $this->returnCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the return codes.
|
||||
*
|
||||
* @param int[] $value
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setReturnCodes(array $value)
|
||||
{
|
||||
foreach ($value as $returnCode) {
|
||||
$this->assertValidReturnCode($returnCode, false);
|
||||
}
|
||||
|
||||
$this->returnCodes = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a return code is valid.
|
||||
*
|
||||
* @param int $returnCode
|
||||
* @param bool $fromPacket
|
||||
*
|
||||
* @throws MalformedPacketException
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function assertValidReturnCode($returnCode, $fromPacket = true)
|
||||
{
|
||||
if (!in_array($returnCode, [0, 1, 2, 128])) {
|
||||
$this->throwException(
|
||||
sprintf('Malformed return code %02x.', $returnCode),
|
||||
$fromPacket
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
vendor/binsoul/net-mqtt/src/Packet/UnsubscribeRequestPacket.php
vendored
Executable file
68
vendor/binsoul/net-mqtt/src/Packet/UnsubscribeRequestPacket.php
vendored
Executable file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\PacketStream;
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the UNSUBSCRIBE packet.
|
||||
*/
|
||||
class UnsubscribeRequestPacket extends BasePacket
|
||||
{
|
||||
use IdentifiablePacket;
|
||||
|
||||
/** @var string */
|
||||
private $topic;
|
||||
|
||||
protected static $packetType = Packet::TYPE_UNSUBSCRIBE;
|
||||
protected $packetFlags = 2;
|
||||
|
||||
public function read(PacketStream $stream)
|
||||
{
|
||||
parent::read($stream);
|
||||
|
||||
$this->assertPacketFlags(2);
|
||||
$this->assertRemainingPacketLength();
|
||||
|
||||
$originalPosition = $stream->getPosition();
|
||||
|
||||
do {
|
||||
$this->identifier = $stream->readWord();
|
||||
$this->topic = $stream->readString();
|
||||
} while (($stream->getPosition() - $originalPosition) <= $this->remainingPacketLength);
|
||||
}
|
||||
|
||||
public function write(PacketStream $stream)
|
||||
{
|
||||
$data = new PacketStream();
|
||||
|
||||
$data->writeWord($this->generateIdentifier());
|
||||
$data->writeString($this->topic);
|
||||
|
||||
$this->remainingPacketLength = $data->length();
|
||||
|
||||
parent::write($stream);
|
||||
$stream->write($data->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTopic()
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the topic.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function setTopic($value)
|
||||
{
|
||||
$this->topic = $value;
|
||||
}
|
||||
}
|
||||
13
vendor/binsoul/net-mqtt/src/Packet/UnsubscribeResponsePacket.php
vendored
Executable file
13
vendor/binsoul/net-mqtt/src/Packet/UnsubscribeResponsePacket.php
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
use BinSoul\Net\Mqtt\Packet;
|
||||
|
||||
/**
|
||||
* Represents the UNSUBACK packet.
|
||||
*/
|
||||
class UnsubscribeResponsePacket extends IdentifierOnlyPacket
|
||||
{
|
||||
protected static $packetType = Packet::TYPE_UNSUBACK;
|
||||
}
|
||||
67
vendor/binsoul/net-mqtt/src/PacketFactory.php
vendored
Executable file
67
vendor/binsoul/net-mqtt/src/PacketFactory.php
vendored
Executable file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\UnknownPacketTypeException;
|
||||
use BinSoul\Net\Mqtt\Packet\ConnectRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\ConnectResponsePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\DisconnectRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PingRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PingResponsePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishAckPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishCompletePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReceivedPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\PublishReleasePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\SubscribeRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\SubscribeResponsePacket;
|
||||
use BinSoul\Net\Mqtt\Packet\UnsubscribeRequestPacket;
|
||||
use BinSoul\Net\Mqtt\Packet\UnsubscribeResponsePacket;
|
||||
|
||||
/**
|
||||
* Builds instances of the {@see Packet} interface.
|
||||
*/
|
||||
class PacketFactory
|
||||
{
|
||||
/**
|
||||
* Map of packet types to packet classes.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private static $mapping = [
|
||||
Packet::TYPE_CONNECT => ConnectRequestPacket::class,
|
||||
Packet::TYPE_CONNACK => ConnectResponsePacket::class,
|
||||
Packet::TYPE_PUBLISH => PublishRequestPacket::class,
|
||||
Packet::TYPE_PUBACK => PublishAckPacket::class,
|
||||
Packet::TYPE_PUBREC => PublishReceivedPacket::class,
|
||||
Packet::TYPE_PUBREL => PublishReleasePacket::class,
|
||||
Packet::TYPE_PUBCOMP => PublishCompletePacket::class,
|
||||
Packet::TYPE_SUBSCRIBE => SubscribeRequestPacket::class,
|
||||
Packet::TYPE_SUBACK => SubscribeResponsePacket::class,
|
||||
Packet::TYPE_UNSUBSCRIBE => UnsubscribeRequestPacket::class,
|
||||
Packet::TYPE_UNSUBACK => UnsubscribeResponsePacket::class,
|
||||
Packet::TYPE_PINGREQ => PingRequestPacket::class,
|
||||
Packet::TYPE_PINGRESP => PingResponsePacket::class,
|
||||
Packet::TYPE_DISCONNECT => DisconnectRequestPacket::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Builds a packet object for the given type.
|
||||
*
|
||||
* @param int $type
|
||||
*
|
||||
* @throws UnknownPacketTypeException
|
||||
*
|
||||
* @return Packet
|
||||
*/
|
||||
public function build($type)
|
||||
{
|
||||
if (!isset(self::$mapping[$type])) {
|
||||
throw new UnknownPacketTypeException(sprintf('Unknown packet type %d.', $type));
|
||||
}
|
||||
|
||||
$class = self::$mapping[$type];
|
||||
|
||||
return new $class();
|
||||
}
|
||||
}
|
||||
216
vendor/binsoul/net-mqtt/src/PacketStream.php
vendored
Executable file
216
vendor/binsoul/net-mqtt/src/PacketStream.php
vendored
Executable file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\EndOfStreamException;
|
||||
|
||||
/**
|
||||
* Provides methods to operate on a stream of bytes.
|
||||
*/
|
||||
class PacketStream
|
||||
{
|
||||
/** @var string */
|
||||
private $data;
|
||||
/** @var int */
|
||||
private $position;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param string $data initial data of the stream
|
||||
*/
|
||||
public function __construct($data = '')
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the desired number of bytes.
|
||||
*
|
||||
* @param int $count
|
||||
*
|
||||
* @throws EndOfStreamException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function read($count)
|
||||
{
|
||||
$contentLength = strlen($this->data);
|
||||
if ($this->position > $contentLength || $count > $contentLength - $this->position) {
|
||||
throw new EndOfStreamException(
|
||||
sprintf(
|
||||
'End of stream reached when trying to read %d bytes. content length=%d, position=%d',
|
||||
$count,
|
||||
$contentLength,
|
||||
$this->position
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$chunk = substr($this->data, $this->position, $count);
|
||||
if ($chunk === false) {
|
||||
$chunk = '';
|
||||
}
|
||||
|
||||
$readBytes = strlen($chunk);
|
||||
$this->position += $readBytes;
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single byte.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function readByte()
|
||||
{
|
||||
return ord($this->read(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single word.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function readWord()
|
||||
{
|
||||
return ($this->readByte() << 8) + $this->readByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a length prefixed string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function readString()
|
||||
{
|
||||
$length = $this->readWord();
|
||||
|
||||
return $this->read($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given value.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function write($value)
|
||||
{
|
||||
$this->data .= $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a single byte.
|
||||
*
|
||||
* @param int $value
|
||||
*/
|
||||
public function writeByte($value)
|
||||
{
|
||||
$this->write(chr($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a single word.
|
||||
*
|
||||
* @param int $value
|
||||
*/
|
||||
public function writeWord($value)
|
||||
{
|
||||
$this->write(chr(($value & 0xFFFF) >> 8));
|
||||
$this->write(chr($value & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a length prefixed string.
|
||||
*
|
||||
* @param string $string
|
||||
*/
|
||||
public function writeString($string)
|
||||
{
|
||||
$this->writeWord(strlen($string));
|
||||
$this->write($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the stream.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function length()
|
||||
{
|
||||
return strlen($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes until the end of the stream.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRemainingBytes()
|
||||
{
|
||||
return $this->length() - $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whole content of the stream.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the internal position of the stream relative to the current position.
|
||||
*
|
||||
* @param int $offset
|
||||
*/
|
||||
public function seek($offset)
|
||||
{
|
||||
$this->position += $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal position of the stream.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal position of the stream.
|
||||
*
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPosition($value)
|
||||
{
|
||||
$this->position = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all bytes from the beginning to the current position.
|
||||
*/
|
||||
public function cut()
|
||||
{
|
||||
$this->data = substr($this->data, $this->position);
|
||||
if ($this->data === false) {
|
||||
$this->data = '';
|
||||
}
|
||||
|
||||
$this->position = 0;
|
||||
}
|
||||
}
|
||||
90
vendor/binsoul/net-mqtt/src/StreamParser.php
vendored
Executable file
90
vendor/binsoul/net-mqtt/src/StreamParser.php
vendored
Executable file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
use BinSoul\Net\Mqtt\Exception\EndOfStreamException;
|
||||
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
|
||||
use BinSoul\Net\Mqtt\Exception\UnknownPacketTypeException;
|
||||
|
||||
/**
|
||||
* Provides methods to parse a stream of bytes into packets.
|
||||
*/
|
||||
class StreamParser
|
||||
{
|
||||
/** @var PacketStream */
|
||||
private $buffer;
|
||||
/** @var PacketFactory */
|
||||
private $factory;
|
||||
/** @var callable */
|
||||
private $errorCallback;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->buffer = new PacketStream();
|
||||
$this->factory = new PacketFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an error callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function onError($callback)
|
||||
{
|
||||
$this->errorCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the given data to the internal buffer and parses it.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return Packet[]
|
||||
*/
|
||||
public function push($data)
|
||||
{
|
||||
$this->buffer->write($data);
|
||||
|
||||
$result = [];
|
||||
while ($this->buffer->getRemainingBytes() > 0) {
|
||||
$type = $this->buffer->readByte() >> 4;
|
||||
try {
|
||||
$packet = $this->factory->build($type);
|
||||
} catch (UnknownPacketTypeException $e) {
|
||||
$this->handleError($e);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->buffer->seek(-1);
|
||||
$position = $this->buffer->getPosition();
|
||||
try {
|
||||
$packet->read($this->buffer);
|
||||
$result[] = $packet;
|
||||
$this->buffer->cut();
|
||||
} catch (EndOfStreamException $e) {
|
||||
$this->buffer->setPosition($position);
|
||||
break;
|
||||
} catch (MalformedPacketException $e) {
|
||||
$this->handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the registered error callback.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
*/
|
||||
private function handleError($exception)
|
||||
{
|
||||
if ($this->errorCallback !== null) {
|
||||
$callback = $this->errorCallback;
|
||||
$callback($exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
vendor/binsoul/net-mqtt/src/Subscription.php
vendored
Executable file
41
vendor/binsoul/net-mqtt/src/Subscription.php
vendored
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Represents a subscription.
|
||||
*/
|
||||
interface Subscription
|
||||
{
|
||||
/**
|
||||
* Returns the topic filter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilter();
|
||||
|
||||
/**
|
||||
* Returns the quality of service level.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getQosLevel();
|
||||
|
||||
/**
|
||||
* Returns a new subscription with the given topic filter.
|
||||
*
|
||||
* @param string $filter
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withFilter($filter);
|
||||
|
||||
/**
|
||||
* Returns a new subscription with the given quality of service level.
|
||||
*
|
||||
* @param int $level
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function withQosLevel($level);
|
||||
}
|
||||
53
vendor/binsoul/net-mqtt/src/TopicMatcher.php
vendored
Executable file
53
vendor/binsoul/net-mqtt/src/TopicMatcher.php
vendored
Executable file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace BinSoul\Net\Mqtt;
|
||||
|
||||
/**
|
||||
* Matches a topic filter with an actual topic.
|
||||
*
|
||||
* @author Alin Eugen Deac <ade@vestergaardcompany.com>
|
||||
*/
|
||||
class TopicMatcher
|
||||
{
|
||||
/**
|
||||
* Check if the given topic matches the filter.
|
||||
*
|
||||
* @param string $filter e.g. A/B/+, A/B/#
|
||||
* @param string $topic e.g. A/B/C, A/B/foo/bar/baz
|
||||
*
|
||||
* @return bool true if topic matches the pattern
|
||||
*/
|
||||
public function matches($filter, $topic)
|
||||
{
|
||||
// Created by Steffen (https://github.com/kernelguy)
|
||||
$tokens = explode('/', $filter);
|
||||
$parts = [];
|
||||
for ($i = 0, $count = count($tokens); $i < $count; ++$i) {
|
||||
$token = $tokens[$i];
|
||||
switch ($token) {
|
||||
case '+':
|
||||
$parts[] = '[^/#\+]*';
|
||||
|
||||
break;
|
||||
case '#':
|
||||
if ($i === 0) {
|
||||
$parts[] = '[^\+\$]*';
|
||||
} else {
|
||||
$parts[] = '[^\+]*';
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$parts[] = str_replace('+', '\+', $token);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$regex = implode('/', $parts);
|
||||
$regex = str_replace('$', '\$', $regex);
|
||||
$regex = ';^'.$regex.'$;';
|
||||
|
||||
return preg_match($regex, $topic) === 1;
|
||||
}
|
||||
}
|
||||
2
vendor/clue/http-proxy-react/.gitignore
vendored
Executable file
2
vendor/clue/http-proxy-react/.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
/vendor/
|
||||
/composer.lock
|
||||
27
vendor/clue/http-proxy-react/.travis.yml
vendored
Executable file
27
vendor/clue/http-proxy-react/.travis.yml
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
# - 5.3 # requires old distro, see below
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7
|
||||
- hhvm # ignore errors, see below
|
||||
|
||||
# lock distro so new future defaults will not break the build
|
||||
dist: trusty
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
sudo: false
|
||||
|
||||
install:
|
||||
- composer install --no-interaction
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-text
|
||||
103
vendor/clue/http-proxy-react/CHANGELOG.md
vendored
Executable file
103
vendor/clue/http-proxy-react/CHANGELOG.md
vendored
Executable file
@@ -0,0 +1,103 @@
|
||||
# Changelog
|
||||
|
||||
## 1.3.0 (2018-02-13)
|
||||
|
||||
* Feature: Support communication over Unix domain sockets (UDS)
|
||||
(#20 by @clue)
|
||||
|
||||
```php
|
||||
// new: now supports communication over Unix domain sockets (UDS)
|
||||
$proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $connector);
|
||||
```
|
||||
|
||||
* Reduce memory consumption by avoiding circular reference from stream reader
|
||||
(#18 by @valga)
|
||||
|
||||
* Improve documentation
|
||||
(#19 by @clue)
|
||||
|
||||
## 1.2.0 (2017-08-30)
|
||||
|
||||
* Feature: Use socket error codes for connection rejections
|
||||
(#17 by @clue)
|
||||
|
||||
```php
|
||||
$promise = $proxy->connect('imap.example.com:143');
|
||||
$promise->then(null, function (Exeption $e) {
|
||||
if ($e->getCode() === SOCKET_EACCES) {
|
||||
echo 'Failed to authenticate with proxy!';
|
||||
}
|
||||
throw $e;
|
||||
});
|
||||
```
|
||||
|
||||
* Improve test suite by locking Travis distro so new defaults will not break the build and
|
||||
optionally exclude tests that rely on working internet connection
|
||||
(#15 and #16 by @clue)
|
||||
|
||||
## 1.1.0 (2017-06-11)
|
||||
|
||||
* Feature: Support proxy authentication if proxy URL contains username/password
|
||||
(#14 by @clue)
|
||||
|
||||
```php
|
||||
// new: username/password will now be passed to HTTP proxy server
|
||||
$proxy = new ProxyConnector('user:pass@127.0.0.1:8080', $connector);
|
||||
```
|
||||
|
||||
## 1.0.0 (2017-06-10)
|
||||
|
||||
* First stable release, now following SemVer
|
||||
|
||||
> Contains no other changes, so it's actually fully compatible with the v0.3.2 release.
|
||||
|
||||
## 0.3.2 (2017-06-10)
|
||||
|
||||
* Fix: Fix rejecting invalid URIs and unexpected URI schemes
|
||||
(#13 by @clue)
|
||||
|
||||
* Fix HHVM build for now again and ignore future HHVM build errors
|
||||
(#12 by @clue)
|
||||
|
||||
* Documentation for Connector concepts (TCP/TLS, timeouts, DNS resolution)
|
||||
(#11 by @clue)
|
||||
|
||||
## 0.3.1 (2017-05-10)
|
||||
|
||||
* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8
|
||||
(#10 by @clue)
|
||||
|
||||
## 0.3.0 (2017-04-10)
|
||||
|
||||
* Feature / BC break: Replace deprecated SocketClient with new Socket component
|
||||
(#9 by @clue)
|
||||
|
||||
This implies that the `ProxyConnector` from this package now implements the
|
||||
`React\Socket\ConnectorInterface` instead of the legacy
|
||||
`React\SocketClient\ConnectorInterface`.
|
||||
|
||||
## 0.2.0 (2017-04-10)
|
||||
|
||||
* Feature / BC break: Update SocketClient to v0.7 or v0.6 and
|
||||
use `connect($uri)` instead of `create($host, $port)`
|
||||
(#8 by @clue)
|
||||
|
||||
```php
|
||||
// old
|
||||
$connector->create($host, $port)->then(function (Stream $conn) {
|
||||
$conn->write("…");
|
||||
});
|
||||
|
||||
// new
|
||||
$connector->connect($uri)->then(function (ConnectionInterface $conn) {
|
||||
$conn->write("…");
|
||||
});
|
||||
```
|
||||
|
||||
* Improve test suite by adding PHPUnit to require-dev
|
||||
(#7 by @clue)
|
||||
|
||||
|
||||
## 0.1.0 (2016-11-01)
|
||||
|
||||
* First tagged release
|
||||
21
vendor/clue/http-proxy-react/LICENSE
vendored
Executable file
21
vendor/clue/http-proxy-react/LICENSE
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
422
vendor/clue/http-proxy-react/README.md
vendored
Executable file
422
vendor/clue/http-proxy-react/README.md
vendored
Executable file
@@ -0,0 +1,422 @@
|
||||
# clue/http-proxy-react [](https://travis-ci.org/clue/php-http-proxy-react)
|
||||
|
||||
Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP
|
||||
CONNECT proxy server, built on top of [ReactPHP](https://reactphp.org).
|
||||
|
||||
HTTP CONNECT proxy servers (also commonly known as "HTTPS proxy" or "SSL proxy")
|
||||
are commonly used to tunnel HTTPS traffic through an intermediary ("proxy"), to
|
||||
conceal the origin address (anonymity) or to circumvent address blocking
|
||||
(geoblocking). While many (public) HTTP CONNECT proxy servers often limit this
|
||||
to HTTPS port `443` only, this can technically be used to tunnel any
|
||||
TCP/IP-based protocol (HTTP, SMTP, IMAP etc.).
|
||||
This library provides a simple API to create these tunneled connection for you.
|
||||
Because it implements ReactPHP's standard
|
||||
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface),
|
||||
it can simply be used in place of a normal connector.
|
||||
This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any
|
||||
existing higher-level protocol implementation.
|
||||
|
||||
* **Async execution of connections** -
|
||||
Send any number of HTTP CONNECT requests in parallel and process their
|
||||
responses as soon as results come in.
|
||||
The Promise-based design provides a *sane* interface to working with out of
|
||||
bound responses and possible connection errors.
|
||||
* **Standard interfaces** -
|
||||
Allows easy integration with existing higher-level components by implementing
|
||||
ReactPHP's standard
|
||||
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface).
|
||||
* **Lightweight, SOLID design** -
|
||||
Provides a thin abstraction that is [*just good enough*](http://en.wikipedia.org/wiki/Principle_of_good_enough)
|
||||
and does not get in your way.
|
||||
Builds on top of well-tested components and well-established concepts instead of reinventing the wheel.
|
||||
* **Good test coverage** -
|
||||
Comes with an automated tests suite and is regularly tested against actual proxy servers in the wild
|
||||
|
||||
**Table of contents**
|
||||
|
||||
* [Quickstart example](#quickstart-example)
|
||||
* [Usage](#usage)
|
||||
* [ProxyConnector](#proxyconnector)
|
||||
* [Plain TCP connections](#plain-tcp-connections)
|
||||
* [Secure TLS connections](#secure-tls-connections)
|
||||
* [Connection timeout](#connection-timeout)
|
||||
* [DNS resolution](#dns-resolution)
|
||||
* [Authentication](#authentication)
|
||||
* [Advanced secure proxy connections](#advanced-secure-proxy-connections)
|
||||
* [Advanced Unix domain sockets](#advanced-unix-domain-sockets)
|
||||
* [Install](#install)
|
||||
* [Tests](#tests)
|
||||
* [License](#license)
|
||||
* [More](#more)
|
||||
|
||||
### Quickstart example
|
||||
|
||||
The following example code demonstrates how this library can be used to send a
|
||||
secure HTTPS request to google.com through a local HTTP proxy server:
|
||||
|
||||
```php
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$proxy = new ProxyConnector('127.0.0.1:8080', new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
|
||||
$stream->on('data', function ($chunk) {
|
||||
echo $chunk;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
```
|
||||
|
||||
See also the [examples](examples).
|
||||
|
||||
## Usage
|
||||
|
||||
### ProxyConnector
|
||||
|
||||
The `ProxyConnector` is responsible for creating plain TCP/IP connections to
|
||||
any destination by using an intermediary HTTP CONNECT proxy.
|
||||
|
||||
```
|
||||
[you] -> [proxy] -> [destination]
|
||||
```
|
||||
|
||||
Its constructor simply accepts an HTTP proxy URL and a connector used to connect
|
||||
to the proxy server address:
|
||||
|
||||
```php
|
||||
$connector = new Connector($loop);
|
||||
$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector);
|
||||
```
|
||||
|
||||
The proxy URL may or may not contain a scheme and port definition. The default
|
||||
port will be `80` for HTTP (or `443` for HTTPS), but many common HTTP proxy
|
||||
servers use custom ports (often the alternative HTTP port `8080`).
|
||||
In its most simple form, the given connector will be a
|
||||
[`\React\Socket\Connector`](https://github.com/reactphp/socket#connector) if you
|
||||
want to connect to a given IP address as above.
|
||||
|
||||
This is the main class in this package.
|
||||
Because it implements ReactPHP's standard
|
||||
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface),
|
||||
it can simply be used in place of a normal connector.
|
||||
Accordingly, it provides only a single public method, the
|
||||
[`connect()`](https://github.com/reactphp/socket#connect) method.
|
||||
The `connect(string $uri): PromiseInterface<ConnectionInterface, Exception>`
|
||||
method can be used to establish a streaming connection.
|
||||
It returns a [Promise](https://github.com/reactphp/promise) which either
|
||||
fulfills with a [ConnectionInterface](https://github.com/reactphp/socket#connectioninterface)
|
||||
on success or rejects with an `Exception` on error.
|
||||
|
||||
This makes it fairly simple to add HTTP CONNECT proxy support to pretty much any
|
||||
higher-level component:
|
||||
|
||||
```diff
|
||||
- $client = new SomeClient($connector);
|
||||
+ $proxy = new ProxyConnector('http://127.0.0.1:8080', $connector);
|
||||
+ $client = new SomeClient($proxy);
|
||||
```
|
||||
|
||||
#### Plain TCP connections
|
||||
|
||||
HTTP CONNECT proxies are most frequently used to issue HTTPS requests to your destination.
|
||||
However, this is actually performed on a higher protocol layer and this
|
||||
connector is actually inherently a general-purpose plain TCP/IP connector.
|
||||
As documented above, you can simply invoke its `connect()` method to establish
|
||||
a streaming plain TCP/IP connection and use any higher level protocol like so:
|
||||
|
||||
```php
|
||||
$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector);
|
||||
|
||||
$proxy->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("EHLO local\r\n");
|
||||
$stream->on('data', function ($chunk) use ($stream) {
|
||||
echo $chunk;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
You can either use the `ProxyConnector` directly or you may want to wrap this connector
|
||||
in ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector):
|
||||
|
||||
```php
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("EHLO local\r\n");
|
||||
$stream->on('data', function ($chunk) use ($stream) {
|
||||
echo $chunk;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Note that HTTP CONNECT proxies often restrict which ports one may connect to.
|
||||
Many (public) proxy servers do in fact limit this to HTTPS (443) only.
|
||||
|
||||
#### Secure TLS connections
|
||||
|
||||
This class can also be used if you want to establish a secure TLS connection
|
||||
(formerly known as SSL) between you and your destination, such as when using
|
||||
secure HTTPS to your destination site. You can simply wrap this connector in
|
||||
ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector) or the
|
||||
low-level [`SecureConnector`](https://github.com/reactphp/socket#secureconnector):
|
||||
|
||||
```php
|
||||
$proxy = new ProxyConnector('http://127.0.0.1:8080', $connector);
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("EHLO local\r\n");
|
||||
$stream->on('data', function ($chunk) use ($stream) {
|
||||
echo $chunk;
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
> Note how secure TLS connections are in fact entirely handled outside of
|
||||
this HTTP CONNECT client implementation.
|
||||
|
||||
#### Connection timeout
|
||||
|
||||
By default, the `ProxyConnector` does not implement any timeouts for establishing remote
|
||||
connections.
|
||||
Your underlying operating system may impose limits on pending and/or idle TCP/IP
|
||||
connections, anywhere in a range of a few minutes to several hours.
|
||||
|
||||
Many use cases require more control over the timeout and likely values much
|
||||
smaller, usually in the range of a few seconds only.
|
||||
|
||||
You can use ReactPHP's [`Connector`](https://github.com/reactphp/socket#connector)
|
||||
or the low-level
|
||||
[`TimeoutConnector`](https://github.com/reactphp/socket#timeoutconnector)
|
||||
to decorate any given `ConnectorInterface` instance.
|
||||
It provides the same `connect()` method, but will automatically reject the
|
||||
underlying connection attempt if it takes too long:
|
||||
|
||||
```php
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'dns' => false,
|
||||
'timeout' => 3.0
|
||||
));
|
||||
|
||||
$connector->connect('tcp://google.com:80')->then(function ($stream) {
|
||||
// connection succeeded within 3.0 seconds
|
||||
});
|
||||
```
|
||||
|
||||
See also any of the [examples](examples).
|
||||
|
||||
> Note how connection timeout is in fact entirely handled outside of this
|
||||
HTTP CONNECT client implementation.
|
||||
|
||||
#### DNS resolution
|
||||
|
||||
By default, the `ProxyConnector` does not perform any DNS resolution at all and simply
|
||||
forwards any hostname you're trying to connect to the remote proxy server.
|
||||
The remote proxy server is thus responsible for looking up any hostnames via DNS
|
||||
(this default mode is thus called *remote DNS resolution*).
|
||||
|
||||
As an alternative, you can also send the destination IP to the remote proxy
|
||||
server.
|
||||
In this mode you either have to stick to using IPs only (which is ofen unfeasable)
|
||||
or perform any DNS lookups locally and only transmit the resolved destination IPs
|
||||
(this mode is thus called *local DNS resolution*).
|
||||
|
||||
The default *remote DNS resolution* is useful if your local `ProxyConnector` either can
|
||||
not resolve target hostnames because it has no direct access to the internet or
|
||||
if it should not resolve target hostnames because its outgoing DNS traffic might
|
||||
be intercepted.
|
||||
|
||||
As noted above, the `ProxyConnector` defaults to using remote DNS resolution.
|
||||
However, wrapping the `ProxyConnector` in ReactPHP's
|
||||
[`Connector`](https://github.com/reactphp/socket#connector) actually
|
||||
performs local DNS resolution unless explicitly defined otherwise.
|
||||
Given that remote DNS resolution is assumed to be the preferred mode, all
|
||||
other examples explicitly disable DNS resoltion like this:
|
||||
|
||||
```php
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'dns' => false
|
||||
));
|
||||
```
|
||||
|
||||
If you want to explicitly use *local DNS resolution*, you can use the following code:
|
||||
|
||||
```php
|
||||
// set up Connector which uses Google's public DNS (8.8.8.8)
|
||||
$connector = Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'dns' => '8.8.8.8'
|
||||
));
|
||||
```
|
||||
|
||||
> Note how local DNS resolution is in fact entirely handled outside of this
|
||||
HTTP CONNECT client implementation.
|
||||
|
||||
#### Authentication
|
||||
|
||||
If your HTTP proxy server requires authentication, you may pass the username and
|
||||
password as part of the HTTP proxy URL like this:
|
||||
|
||||
```php
|
||||
$proxy = new ProxyConnector('http://user:pass@127.0.0.1:8080', $connector);
|
||||
```
|
||||
|
||||
Note that both the username and password must be percent-encoded if they contain
|
||||
special characters:
|
||||
|
||||
```php
|
||||
$user = 'he:llo';
|
||||
$pass = 'p@ss';
|
||||
|
||||
$proxy = new ProxyConnector(
|
||||
rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1:8080',
|
||||
$connector
|
||||
);
|
||||
```
|
||||
|
||||
> The authentication details will be used for basic authentication and will be
|
||||
transferred in the `Proxy-Authorization` HTTP request header for each
|
||||
connection attempt.
|
||||
If the authentication details are missing or not accepted by the remote HTTP
|
||||
proxy server, it is expected to reject each connection attempt with a
|
||||
`407` (Proxy Authentication Required) response status code and an exception
|
||||
error code of `SOCKET_EACCES` (13).
|
||||
|
||||
#### Advanced secure proxy connections
|
||||
|
||||
Note that communication between the client and the proxy is usually via an
|
||||
unencrypted, plain TCP/IP HTTP connection. Note that this is the most common
|
||||
setup, because you can still establish a TLS connection between you and the
|
||||
destination host as above.
|
||||
|
||||
If you want to connect to a (rather rare) HTTPS proxy, you may want use the
|
||||
`https://` scheme (HTTPS default port 443) and use ReactPHP's
|
||||
[`Connector`](https://github.com/reactphp/socket#connector) or the low-level
|
||||
[`SecureConnector`](https://github.com/reactphp/socket#secureconnector)
|
||||
instance to create a secure connection to the proxy:
|
||||
|
||||
```php
|
||||
$connector = new Connector($loop);
|
||||
$proxy = new ProxyConnector('https://127.0.0.1:443', $connector);
|
||||
|
||||
$proxy->connect('tcp://smtp.googlemail.com:587');
|
||||
```
|
||||
|
||||
#### Advanced Unix domain sockets
|
||||
|
||||
HTTP CONNECT proxy servers support forwarding TCP/IP based connections and
|
||||
higher level protocols.
|
||||
In some advanced cases, it may be useful to let your HTTP CONNECT proxy server
|
||||
listen on a Unix domain socket (UDS) path instead of a IP:port combination.
|
||||
For example, this allows you to rely on file system permissions instead of
|
||||
having to rely on explicit [authentication](#authentication).
|
||||
|
||||
You can simply use the `http+unix://` URI scheme like this:
|
||||
|
||||
```php
|
||||
$proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $connector);
|
||||
|
||||
$proxy->connect('tcp://google.com:80')->then(function (ConnectionInterface $stream) {
|
||||
// connected…
|
||||
});
|
||||
```
|
||||
|
||||
Similarly, you can also combine this with [authentication](#authentication)
|
||||
like this:
|
||||
|
||||
```php
|
||||
$proxy = new ProxyConnector('http+unix://user:pass@/tmp/proxy.sock', $connector);
|
||||
```
|
||||
|
||||
> Note that Unix domain sockets (UDS) are considered advanced usage and PHP only
|
||||
has limited support for this.
|
||||
In particular, enabling [secure TLS](#secure-tls-connections) may not be
|
||||
supported.
|
||||
|
||||
> Note that the HTTP CONNECT protocol does not support the notion of UDS paths.
|
||||
The above works reasonably well because UDS is only used for the connection between
|
||||
client and proxy server and the path will not actually passed over the protocol.
|
||||
This implies that this does not support connecting to UDS destination paths.
|
||||
|
||||
## Install
|
||||
|
||||
The recommended way to install this library is [through Composer](https://getcomposer.org).
|
||||
[New to Composer?](https://getcomposer.org/doc/00-intro.md)
|
||||
|
||||
This project follows [SemVer](http://semver.org/).
|
||||
This will install the latest supported version:
|
||||
|
||||
```bash
|
||||
$ composer require clue/http-proxy-react:^1.3
|
||||
```
|
||||
|
||||
See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades.
|
||||
|
||||
This project aims to run on any platform and thus does not require any PHP
|
||||
extensions and supports running on legacy PHP 5.3 through current PHP 7+ and
|
||||
HHVM.
|
||||
It's *highly recommended to use PHP 7+* for this project.
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, you first need to clone this repo and then install all
|
||||
dependencies [through Composer](https://getcomposer.org):
|
||||
|
||||
```bash
|
||||
$ composer install
|
||||
```
|
||||
|
||||
To run the test suite, go to the project root and run:
|
||||
|
||||
```bash
|
||||
$ php vendor/bin/phpunit
|
||||
```
|
||||
|
||||
The test suite contains tests that rely on a working internet connection,
|
||||
alternatively you can also run it like this:
|
||||
|
||||
```bash
|
||||
$ php vendor/bin/phpunit --exclude-group internet
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## More
|
||||
|
||||
* If you want to learn more about how the
|
||||
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface)
|
||||
and its usual implementations look like, refer to the documentation of the underlying
|
||||
[react/socket](https://github.com/reactphp/socket) component.
|
||||
* If you want to learn more about processing streams of data, refer to the
|
||||
documentation of the underlying
|
||||
[react/stream](https://github.com/reactphp/stream) component.
|
||||
* As an alternative to an HTTP CONNECT proxy, you may also want to look into
|
||||
using a SOCKS (SOCKS4/SOCKS5) proxy instead.
|
||||
You may want to use [clue/socks-react](https://github.com/clue/php-socks-react)
|
||||
which also provides an implementation of the same
|
||||
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface)
|
||||
so that supporting either proxy protocol should be fairly trivial.
|
||||
* If you're dealing with public proxies, you'll likely have to work with mixed
|
||||
quality and unreliable proxies. You may want to look into using
|
||||
[clue/connection-manager-extra](https://github.com/clue/php-connection-manager-extra)
|
||||
which allows retrying unreliable ones, implying connection timeouts,
|
||||
concurrently working with multiple connectors and more.
|
||||
* If you're looking for an end-user HTTP CONNECT proxy server daemon, you may
|
||||
want to use [LeProxy](https://leproxy.org/).
|
||||
30
vendor/clue/http-proxy-react/composer.json
vendored
Executable file
30
vendor/clue/http-proxy-react/composer.json
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "clue/http-proxy-react",
|
||||
"description": "Async HTTP proxy connector, use any TCP/IP-based protocol through an HTTP CONNECT proxy server, built on top of ReactPHP",
|
||||
"keywords": ["HTTP", "CONNECT", "proxy", "ReactPHP", "async"],
|
||||
"homepage": "https://github.com/clue/php-http-proxy-react",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@lueck.tv"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": { "Clue\\React\\HttpProxy\\": "src/" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Tests\\Clue\\React\\HttpProxy\\": "tests/" }
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"react/promise": " ^2.1 || ^1.2.1",
|
||||
"react/socket": "^1.0 || ^0.8.4",
|
||||
"ringcentral/psr7": "^1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.0 || ^4.8",
|
||||
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3",
|
||||
"clue/block-react": "^1.1"
|
||||
}
|
||||
}
|
||||
30
vendor/clue/http-proxy-react/examples/01-proxy-https.php
vendored
Executable file
30
vendor/clue/http-proxy-react/examples/01-proxy-https.php
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
// A simple example which requests https://google.com/ through an HTTP CONNECT proxy.
|
||||
// The proxy can be given as first argument and defaults to localhost:8080 otherwise.
|
||||
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$url = isset($argv[1]) ? $argv[1] : '127.0.0.1:8080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$proxy = new ProxyConnector($url, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
|
||||
$stream->on('data', function ($chunk) {
|
||||
echo $chunk;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
37
vendor/clue/http-proxy-react/examples/02-optional-proxy-https.php
vendored
Executable file
37
vendor/clue/http-proxy-react/examples/02-optional-proxy-https.php
vendored
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
// A simple example which requests https://google.com/ either directly or through
|
||||
// an HTTP CONNECT proxy.
|
||||
// The Proxy can be given as first argument or does not use a proxy otherwise.
|
||||
// This example highlights how changing from direct connection to using a proxy
|
||||
// actually adds very little complexity and does not mess with your actual
|
||||
// network protocol otherwise.
|
||||
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$connector = new Connector($loop);
|
||||
|
||||
// first argument given? use this as the proxy URL
|
||||
if (isset($argv[1])) {
|
||||
$proxy = new ProxyConnector($argv[1], $connector);
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
}
|
||||
|
||||
$connector->connect('tls://google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n");
|
||||
$stream->on('data', function ($chunk) {
|
||||
echo $chunk;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
32
vendor/clue/http-proxy-react/examples/11-proxy-smtp.php
vendored
Executable file
32
vendor/clue/http-proxy-react/examples/11-proxy-smtp.php
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
// A simple example which uses a plain SMTP connection to Googlemail through a HTTP CONNECT proxy.
|
||||
// Proxy can be given as first argument and defaults to localhost:8080 otherwise.
|
||||
// Please note that MANY public proxies do not allow SMTP connections, YMMV.
|
||||
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$url = isset($argv[1]) ? $argv[1] : '127.0.0.1:8080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$proxy = new ProxyConnector($url, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tcp://smtp.googlemail.com:587')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("EHLO local\r\n");
|
||||
$stream->on('data', function ($chunk) use ($stream) {
|
||||
echo $chunk;
|
||||
$stream->write("QUIT\r\n");
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
35
vendor/clue/http-proxy-react/examples/12-proxy-smtps.php
vendored
Executable file
35
vendor/clue/http-proxy-react/examples/12-proxy-smtps.php
vendored
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
// A simple example which uses a secure SMTP connection to Googlemail through a HTTP CONNECT proxy.
|
||||
// Proxy can be given as first argument and defaults to localhost:8080 otherwise.
|
||||
// This example highlights how changing from plain connections (see previous
|
||||
// example) to using a secure connection actually adds very little complexity
|
||||
// and does not mess with your actual network protocol otherwise.
|
||||
// Please note that MANY public proxies do not allow SMTP connections, YMMV.
|
||||
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$url = isset($argv[1]) ? $argv[1] : '127.0.0.1:8080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$proxy = new ProxyConnector($url, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $proxy,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
$connector->connect('tls://smtp.googlemail.com:465')->then(function (ConnectionInterface $stream) {
|
||||
$stream->write("EHLO local\r\n");
|
||||
$stream->on('data', function ($chunk) use ($stream) {
|
||||
echo $chunk;
|
||||
$stream->write("QUIT\r\n");
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
14
vendor/clue/http-proxy-react/phpunit.xml.dist
vendored
Executable file
14
vendor/clue/http-proxy-react/phpunit.xml.dist
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="vendor/autoload.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
213
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
Executable file
213
vendor/clue/http-proxy-react/src/ProxyConnector.php
vendored
Executable file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\HttpProxy;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use RingCentral\Psr7;
|
||||
use React\Promise;
|
||||
use React\Promise\Deferred;
|
||||
use React\Socket\ConnectionInterface;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Socket\FixedUriConnector;
|
||||
|
||||
/**
|
||||
* A simple Connector that uses an HTTP CONNECT proxy to create plain TCP/IP connections to any destination
|
||||
*
|
||||
* [you] -> [proxy] -> [destination]
|
||||
*
|
||||
* This is most frequently used to issue HTTPS requests to your destination.
|
||||
* However, this is actually performed on a higher protocol layer and this
|
||||
* connector is actually inherently a general-purpose plain TCP/IP connector.
|
||||
*
|
||||
* Note that HTTP CONNECT proxies often restrict which ports one may connect to.
|
||||
* Many (public) proxy servers do in fact limit this to HTTPS (443) only.
|
||||
*
|
||||
* If you want to establish a TLS connection (such as HTTPS) between you and
|
||||
* your destination, you may want to wrap this connector in a SecureConnector
|
||||
* instance.
|
||||
*
|
||||
* Note that communication between the client and the proxy is usually via an
|
||||
* unencrypted, plain TCP/IP HTTP connection. Note that this is the most common
|
||||
* setup, because you can still establish a TLS connection between you and the
|
||||
* destination host as above.
|
||||
*
|
||||
* If you want to connect to a (rather rare) HTTPS proxy, you may want use its
|
||||
* HTTPS port (443) and use a SecureConnector instance to create a secure
|
||||
* connection to the proxy.
|
||||
*
|
||||
* @link https://tools.ietf.org/html/rfc7231#section-4.3.6
|
||||
*/
|
||||
class ProxyConnector implements ConnectorInterface
|
||||
{
|
||||
private $connector;
|
||||
private $proxyUri;
|
||||
private $proxyAuth = '';
|
||||
|
||||
/**
|
||||
* Instantiate a new ProxyConnector which uses the given $proxyUrl
|
||||
*
|
||||
* @param string $proxyUrl The proxy URL may or may not contain a scheme and
|
||||
* port definition. The default port will be `80` for HTTP (or `443` for
|
||||
* HTTPS), but many common HTTP proxy servers use custom ports.
|
||||
* @param ConnectorInterface $connector In its most simple form, the given
|
||||
* connector will be a \React\Socket\Connector if you want to connect to
|
||||
* a given IP address.
|
||||
* @throws InvalidArgumentException if the proxy URL is invalid
|
||||
*/
|
||||
public function __construct($proxyUrl, ConnectorInterface $connector)
|
||||
{
|
||||
// support `http+unix://` scheme for Unix domain socket (UDS) paths
|
||||
if (preg_match('/^http\+unix:\/\/(.*?@)?(.+?)$/', $proxyUrl, $match)) {
|
||||
// rewrite URI to parse authentication from dummy host
|
||||
$proxyUrl = 'http://' . $match[1] . 'localhost';
|
||||
|
||||
// connector uses Unix transport scheme and explicit path given
|
||||
$connector = new FixedUriConnector(
|
||||
'unix://' . $match[2],
|
||||
$connector
|
||||
);
|
||||
}
|
||||
|
||||
if (strpos($proxyUrl, '://') === false) {
|
||||
$proxyUrl = 'http://' . $proxyUrl;
|
||||
}
|
||||
|
||||
$parts = parse_url($proxyUrl);
|
||||
if (!$parts || !isset($parts['scheme'], $parts['host']) || ($parts['scheme'] !== 'http' && $parts['scheme'] !== 'https')) {
|
||||
throw new InvalidArgumentException('Invalid proxy URL "' . $proxyUrl . '"');
|
||||
}
|
||||
|
||||
// apply default port and TCP/TLS transport for given scheme
|
||||
if (!isset($parts['port'])) {
|
||||
$parts['port'] = $parts['scheme'] === 'https' ? 443 : 80;
|
||||
}
|
||||
$parts['scheme'] = $parts['scheme'] === 'https' ? 'tls' : 'tcp';
|
||||
|
||||
$this->connector = $connector;
|
||||
$this->proxyUri = $parts['scheme'] . '://' . $parts['host'] . ':' . $parts['port'];
|
||||
|
||||
// prepare Proxy-Authorization header if URI contains username/password
|
||||
if (isset($parts['user']) || isset($parts['pass'])) {
|
||||
$this->proxyAuth = 'Proxy-Authorization: Basic ' . base64_encode(
|
||||
rawurldecode($parts['user'] . ':' . (isset($parts['pass']) ? $parts['pass'] : ''))
|
||||
) . "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function connect($uri)
|
||||
{
|
||||
if (strpos($uri, '://') === false) {
|
||||
$uri = 'tcp://' . $uri;
|
||||
}
|
||||
|
||||
$parts = parse_url($uri);
|
||||
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
|
||||
return Promise\reject(new InvalidArgumentException('Invalid target URI specified'));
|
||||
}
|
||||
|
||||
$host = trim($parts['host'], '[]');
|
||||
$port = $parts['port'];
|
||||
|
||||
// construct URI to HTTP CONNECT proxy server to connect to
|
||||
$proxyUri = $this->proxyUri;
|
||||
|
||||
// append path from URI if given
|
||||
if (isset($parts['path'])) {
|
||||
$proxyUri .= $parts['path'];
|
||||
}
|
||||
|
||||
// parse query args
|
||||
$args = array();
|
||||
if (isset($parts['query'])) {
|
||||
parse_str($parts['query'], $args);
|
||||
}
|
||||
|
||||
// append hostname from URI to query string unless explicitly given
|
||||
if (!isset($args['hostname'])) {
|
||||
$args['hostname'] = $parts['host'];
|
||||
}
|
||||
|
||||
// append query string
|
||||
$proxyUri .= '?' . http_build_query($args, '', '&');;
|
||||
|
||||
// append fragment from URI if given
|
||||
if (isset($parts['fragment'])) {
|
||||
$proxyUri .= '#' . $parts['fragment'];
|
||||
}
|
||||
|
||||
$auth = $this->proxyAuth;
|
||||
|
||||
return $this->connector->connect($proxyUri)->then(function (ConnectionInterface $stream) use ($host, $port, $auth) {
|
||||
$deferred = new Deferred(function ($_, $reject) use ($stream) {
|
||||
$reject(new RuntimeException('Connection canceled while waiting for response from proxy (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103));
|
||||
$stream->close();
|
||||
});
|
||||
|
||||
// keep buffering data until headers are complete
|
||||
$buffer = '';
|
||||
$fn = function ($chunk) use (&$buffer, $deferred, $stream) {
|
||||
$buffer .= $chunk;
|
||||
|
||||
$pos = strpos($buffer, "\r\n\r\n");
|
||||
if ($pos !== false) {
|
||||
// try to parse headers as response message
|
||||
try {
|
||||
$response = Psr7\parse_response(substr($buffer, 0, $pos));
|
||||
} catch (Exception $e) {
|
||||
$deferred->reject(new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $e));
|
||||
$stream->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($response->getStatusCode() === 407) {
|
||||
// map status code 407 (Proxy Authentication Required) to EACCES
|
||||
$deferred->reject(new RuntimeException('Proxy denied connection due to invalid authentication ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13));
|
||||
return $stream->close();
|
||||
} elseif ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
// map non-2xx status code to ECONNREFUSED
|
||||
$deferred->reject(new RuntimeException('Proxy refused connection with HTTP error code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ') (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111));
|
||||
return $stream->close();
|
||||
}
|
||||
|
||||
// all okay, resolve with stream instance
|
||||
$deferred->resolve($stream);
|
||||
|
||||
// emit remaining incoming as data event
|
||||
$buffer = (string)substr($buffer, $pos + 4);
|
||||
if ($buffer !== '') {
|
||||
$stream->emit('data', array($buffer));
|
||||
$buffer = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// stop buffering when 8 KiB have been read
|
||||
if (isset($buffer[8192])) {
|
||||
$deferred->reject(new RuntimeException('Proxy must not send more than 8 KiB of headers (EMSGSIZE)', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90));
|
||||
$stream->close();
|
||||
}
|
||||
};
|
||||
$stream->on('data', $fn);
|
||||
|
||||
$stream->on('error', function (Exception $e) use ($deferred) {
|
||||
$deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e));
|
||||
});
|
||||
|
||||
$stream->on('close', function () use ($deferred) {
|
||||
$deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104));
|
||||
});
|
||||
|
||||
$stream->write("CONNECT " . $host . ":" . $port . " HTTP/1.1\r\nHost: " . $host . ":" . $port . "\r\n" . $auth . "\r\n");
|
||||
|
||||
return $deferred->promise()->then(function (ConnectionInterface $stream) use ($fn) {
|
||||
// Stop buffering when connection has been established.
|
||||
$stream->removeListener('data', $fn);
|
||||
return new Promise\FulfilledPromise($stream);
|
||||
});
|
||||
}, function (Exception $e) use ($proxyUri) {
|
||||
throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e);
|
||||
});
|
||||
}
|
||||
}
|
||||
80
vendor/clue/http-proxy-react/tests/AbstractTestCase.php
vendored
Executable file
80
vendor/clue/http-proxy-react/tests/AbstractTestCase.php
vendored
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Clue\React\HttpProxy;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
abstract class AbstractTestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function expectCallableNever()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->never())
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnce()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnceWith($value)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->equalTo($value));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnceWithExceptionCode($code)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->callback(function ($e) use ($code) {
|
||||
return $e->getCode() === $code;
|
||||
}));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
|
||||
protected function expectCallableOnceParameter($type)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->isInstanceOf($type));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react)
|
||||
*/
|
||||
protected function createCallableMock()
|
||||
{
|
||||
return $this->getMockBuilder('Tests\\Clue\\React\\HttpProxy\\CallableStub')->getMock();
|
||||
}
|
||||
}
|
||||
|
||||
class CallableStub
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
75
vendor/clue/http-proxy-react/tests/FunctionalTest.php
vendored
Executable file
75
vendor/clue/http-proxy-react/tests/FunctionalTest.php
vendored
Executable file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Clue\React\HttpProxy;
|
||||
|
||||
use React\EventLoop\Factory;
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Socket\TcpConnector;
|
||||
use React\Socket\DnsConnector;
|
||||
use Clue\React\Block;
|
||||
use React\Socket\SecureConnector;
|
||||
|
||||
/** @group internet */
|
||||
class FunctionalTest extends AbstractTestCase
|
||||
{
|
||||
private $loop;
|
||||
private $tcpConnector;
|
||||
private $dnsConnector;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->loop = Factory::create();
|
||||
|
||||
$this->tcpConnector = new TcpConnector($this->loop);
|
||||
|
||||
$f = new \React\Dns\Resolver\Factory();
|
||||
$resolver = $f->create('8.8.8.8', $this->loop);
|
||||
|
||||
$this->dnsConnector = new DnsConnector($this->tcpConnector, $resolver);
|
||||
}
|
||||
|
||||
public function testNonListeningSocketRejectsConnection()
|
||||
{
|
||||
$proxy = new ProxyConnector('127.0.0.1:9999', $this->dnsConnector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->setExpectedException('RuntimeException', 'Unable to connect to proxy', SOCKET_ECONNREFUSED);
|
||||
Block\await($promise, $this->loop, 3.0);
|
||||
}
|
||||
|
||||
public function testPlainGoogleDoesNotAcceptConnectMethod()
|
||||
{
|
||||
$proxy = new ProxyConnector('google.com', $this->dnsConnector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->setExpectedException('RuntimeException', '405 (Method Not Allowed)', SOCKET_ECONNREFUSED);
|
||||
Block\await($promise, $this->loop, 3.0);
|
||||
}
|
||||
|
||||
public function testSecureGoogleDoesNotAcceptConnectMethod()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('TLS not supported on really old platforms (HHVM < 3.8)');
|
||||
}
|
||||
|
||||
$secure = new SecureConnector($this->dnsConnector, $this->loop);
|
||||
$proxy = new ProxyConnector('https://google.com:443', $secure);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->setExpectedException('RuntimeException', '405 (Method Not Allowed)', SOCKET_ECONNREFUSED);
|
||||
Block\await($promise, $this->loop, 3.0);
|
||||
}
|
||||
|
||||
public function testSecureGoogleDoesNotAcceptPlainStream()
|
||||
{
|
||||
$proxy = new ProxyConnector('google.com:443', $this->dnsConnector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->setExpectedException('RuntimeException', 'Connection to proxy lost', SOCKET_ECONNRESET);
|
||||
Block\await($promise, $this->loop, 3.0);
|
||||
}
|
||||
}
|
||||
333
vendor/clue/http-proxy-react/tests/ProxyConnectorTest.php
vendored
Executable file
333
vendor/clue/http-proxy-react/tests/ProxyConnectorTest.php
vendored
Executable file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Clue\React\HttpProxy;
|
||||
|
||||
use Clue\React\HttpProxy\ProxyConnector;
|
||||
use React\Promise\Promise;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
class ProxyConnectorTest extends AbstractTestCase
|
||||
{
|
||||
private $connector;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidProxy()
|
||||
{
|
||||
new ProxyConnector('///', $this->connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidProxyScheme()
|
||||
{
|
||||
new ProxyConnector('ftp://example.com', $this->connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidHttpsUnixScheme()
|
||||
{
|
||||
new ProxyConnector('https+unix:///tmp/proxy.sock', $this->connector);
|
||||
}
|
||||
|
||||
public function testCreatesConnectionToHttpPort()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
$this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testCreatesConnectionToHttpPortAndPassesThroughUriComponents()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
$this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80/path?foo=bar&hostname=google.com#segment')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80/path?foo=bar#segment');
|
||||
}
|
||||
|
||||
public function testCreatesConnectionToHttpPortAndObeysExplicitHostname()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
$this->connector->expects($this->once())->method('connect')->with('tcp://proxy.example.com:80?hostname=www.google.com')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80?hostname=www.google.com');
|
||||
}
|
||||
|
||||
public function testCreatesConnectionToHttpsPort()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
$this->connector->expects($this->once())->method('connect')->with('tls://proxy.example.com:443?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('https://proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testCreatesConnectionToUnixPath()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
$this->connector->expects($this->once())->method('connect')->with('unix:///tmp/proxy.sock')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('http+unix:///tmp/proxy.sock', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testCancelPromiseWillCancelPendingConnection()
|
||||
{
|
||||
$promise = new Promise(function () { }, $this->expectCallableOnce());
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->assertInstanceOf('React\Promise\CancellablePromiseInterface', $promise);
|
||||
|
||||
$promise->cancel();
|
||||
}
|
||||
|
||||
public function testWillWriteToOpenConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\n\r\n");
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testWillProxyAuthorizationHeaderIfProxyUriContainsAuthentication()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n");
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('user:pass@proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testWillProxyAuthorizationHeaderIfProxyUriContainsOnlyUsernameWithoutPassword()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjo=\r\n\r\n");
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('user@proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testWillProxyAuthorizationHeaderIfProxyUriContainsAuthenticationWithPercentEncoding()
|
||||
{
|
||||
$user = 'h@llÖ';
|
||||
$pass = '%secret?';
|
||||
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic " . base64_encode($user . ':' . $pass) . "\r\n\r\n");
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector(rawurlencode($user) . ':' . rawurlencode($pass) . '@proxy.example.com', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testWillProxyAuthorizationHeaderIfUnixProxyUriContainsAuthentication()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('write')->with("CONNECT google.com:80 HTTP/1.1\r\nHost: google.com:80\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n");
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->with('unix:///tmp/proxy.sock')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('http+unix://user:pass@/tmp/proxy.sock', $this->connector);
|
||||
|
||||
$proxy->connect('google.com:80');
|
||||
}
|
||||
|
||||
public function testRejectsInvalidUri()
|
||||
{
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('///');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testRejectsUriWithNonTcpScheme()
|
||||
{
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('tls://google.com:80');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testRejectsIfConnectorRejects()
|
||||
{
|
||||
$promise = \React\Promise\reject(new \RuntimeException());
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testRejectsAndClosesIfStreamWritesNonHttp()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$stream->expects($this->once())->method('close');
|
||||
$stream->emit('data', array("invalid\r\n\r\n"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG));
|
||||
}
|
||||
|
||||
public function testRejectsAndClosesIfStreamWritesTooMuchData()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$stream->expects($this->once())->method('close');
|
||||
$stream->emit('data', array(str_repeat('*', 100000)));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EMSGSIZE));
|
||||
}
|
||||
|
||||
public function testRejectsAndClosesIfStreamReturnsProyAuthenticationRequired()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$stream->expects($this->once())->method('close');
|
||||
$stream->emit('data', array("HTTP/1.1 407 Proxy Authentication Required\r\n\r\n"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EACCES));
|
||||
}
|
||||
|
||||
public function testRejectsAndClosesIfStreamReturnsNonSuccess()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$stream->expects($this->once())->method('close');
|
||||
$stream->emit('data', array("HTTP/1.1 403 Not allowed\r\n\r\n"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED));
|
||||
}
|
||||
|
||||
public function testResolvesIfStreamReturnsSuccess()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$promise->then($this->expectCallableOnce('React\Stream\Stream'));
|
||||
$never = $this->expectCallableNever();
|
||||
$promise->then(function (ConnectionInterface $stream) use ($never) {
|
||||
$stream->on('data', $never);
|
||||
});
|
||||
|
||||
$stream->emit('data', array("HTTP/1.1 200 OK\r\n\r\n"));
|
||||
}
|
||||
|
||||
public function testResolvesIfStreamReturnsSuccessAndEmitsExcessiveData()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$once = $this->expectCallableOnceWith('hello!');
|
||||
$promise->then(function (ConnectionInterface $stream) use ($once) {
|
||||
$stream->on('data', $once);
|
||||
});
|
||||
|
||||
$stream->emit('data', array("HTTP/1.1 200 OK\r\n\r\nhello!"));
|
||||
}
|
||||
|
||||
public function testCancelPromiseWillCloseOpenConnectionAndReject()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close', 'write'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$proxy = new ProxyConnector('proxy.example.com', $this->connector);
|
||||
|
||||
$promise = $proxy->connect('google.com:80');
|
||||
|
||||
$this->assertInstanceOf('React\Promise\CancellablePromiseInterface', $promise);
|
||||
|
||||
$promise->cancel();
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNABORTED));
|
||||
}
|
||||
}
|
||||
2
vendor/clue/socks-react/.gitignore
vendored
Executable file
2
vendor/clue/socks-react/.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
/vendor
|
||||
/composer.lock
|
||||
27
vendor/clue/socks-react/.travis.yml
vendored
Executable file
27
vendor/clue/socks-react/.travis.yml
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
language: php
|
||||
|
||||
php:
|
||||
# - 5.3 # requires old distro, see below
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7
|
||||
- hhvm # ignore errors, see below
|
||||
|
||||
# lock distro so new future defaults will not break the build
|
||||
dist: trusty
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
dist: precise
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
|
||||
sudo: false
|
||||
|
||||
install:
|
||||
- composer install --no-interaction
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-text
|
||||
338
vendor/clue/socks-react/CHANGELOG.md
vendored
Executable file
338
vendor/clue/socks-react/CHANGELOG.md
vendored
Executable file
@@ -0,0 +1,338 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.7 (2017-12-17)
|
||||
|
||||
* Feature: Support SOCKS over TLS (`sockss://` URI scheme)
|
||||
(#70 and #71 by @clue)
|
||||
|
||||
```php
|
||||
// new: now supports SOCKS over TLS
|
||||
$client = new Client('socks5s://localhost', $connector);
|
||||
```
|
||||
|
||||
* Feature: Support communication over Unix domain sockets (UDS)
|
||||
(#69 by @clue)
|
||||
|
||||
```php
|
||||
// new: now supports SOCKS over Unix domain sockets (UDS)
|
||||
$client = new Client('socks5+unix:///tmp/proxy.sock', $connector);
|
||||
```
|
||||
|
||||
* Improve test suite by adding forward compatibility with PHPUnit 6
|
||||
(#68 by @clue)
|
||||
|
||||
## 0.8.6 (2017-09-17)
|
||||
|
||||
* Feature: Forward compatibility with Evenement v3.0
|
||||
(#67 by @WyriHaximus)
|
||||
|
||||
## 0.8.5 (2017-09-01)
|
||||
|
||||
* Feature: Use socket error codes for connection rejections
|
||||
(#63 by @clue)
|
||||
|
||||
```php
|
||||
$promise = $proxy->connect('imap.example.com:143');
|
||||
$promise->then(null, function (Exeption $e) {
|
||||
if ($e->getCode() === SOCKET_EACCES) {
|
||||
echo 'Failed to authenticate with proxy!';
|
||||
}
|
||||
throw $e;
|
||||
});
|
||||
```
|
||||
|
||||
* Feature: Report matching SOCKS5 error codes for server side connection errors
|
||||
(#62 by @clue)
|
||||
|
||||
* Fix: Fix SOCKS5 client receiving destination hostnames and
|
||||
fix IPv6 addresses as hostnames for TLS certificates
|
||||
(#64 and #65 by @clue)
|
||||
|
||||
* Improve test suite by locking Travis distro so new defaults will not break the build and
|
||||
optionally exclude tests that rely on working internet connection
|
||||
(#61 and #66 by @clue)
|
||||
|
||||
## 0.8.4 (2017-07-27)
|
||||
|
||||
* Feature: Server now passes client source address to Connector
|
||||
(#60 by @clue)
|
||||
|
||||
## 0.8.3 (2017-07-18)
|
||||
|
||||
* Feature: Pass full remote URI as parameter to authentication callback
|
||||
(#58 by @clue)
|
||||
|
||||
```php
|
||||
// new third parameter passed to authentication callback
|
||||
$server->setAuth(function ($user, $pass, $remote) {
|
||||
$ip = parse_url($remote, PHP_URL_HOST);
|
||||
|
||||
return ($ip === '127.0.0.1');
|
||||
});
|
||||
```
|
||||
|
||||
* Fix: Fix connecting to IPv6 address via SOCKS5 server and validate target
|
||||
URI so hostname can not contain excessive URI components
|
||||
(#59 by @clue)
|
||||
|
||||
* Improve test suite by fixing HHVM build for now again and ignore future HHVM build errors
|
||||
(#57 by @clue)
|
||||
|
||||
## 0.8.2 (2017-05-09)
|
||||
|
||||
* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8
|
||||
(#56 by @clue)
|
||||
|
||||
## 0.8.1 (2017-04-21)
|
||||
|
||||
* Update examples to use URIs with default port 1080 and accept proxy URI arguments
|
||||
(#54 by @clue)
|
||||
|
||||
* Remove now unneeded dependency on `react/stream`
|
||||
(#55 by @clue)
|
||||
|
||||
## 0.8.0 (2017-04-18)
|
||||
|
||||
* Feature: Merge `Server` class from clue/socks-server
|
||||
(#52 by @clue)
|
||||
|
||||
```php
|
||||
$socket = new React\Socket\Server(1080, $loop);
|
||||
$server = new Clue\React\Socks\Server($loop, $socket);
|
||||
```
|
||||
|
||||
> Upgrading from [clue/socks-server](https://github.com/clue/php-socks-server)?
|
||||
The classes have been moved as-is, so you can simply start using the new
|
||||
class name `Clue\React\Socks\Server` with no other changes required.
|
||||
|
||||
## 0.7.0 (2017-04-14)
|
||||
|
||||
* Feature / BC break: Replace depreacted SocketClient with Socket v0.7 and
|
||||
use `connect($uri)` instead of `create($host, $port)`
|
||||
(#51 by @clue)
|
||||
|
||||
```php
|
||||
// old
|
||||
$connector = new React\SocketClient\TcpConnector($loop);
|
||||
$client = new Client(1080, $connector);
|
||||
$client->create('google.com', 80)->then(function (Stream $conn) {
|
||||
$conn->write("…");
|
||||
});
|
||||
|
||||
// new
|
||||
$connector = new React\Socket\TcpConnector($loop);
|
||||
$client = new Client(1080, $connector);
|
||||
$client->connect('google.com:80')->then(function (ConnectionInterface $conn) {
|
||||
$conn->write("…");
|
||||
});
|
||||
```
|
||||
|
||||
* Improve test suite by adding PHPUnit to require-dev
|
||||
(#50 by @clue)
|
||||
|
||||
## 0.6.0 (2016-11-29)
|
||||
|
||||
* Feature / BC break: Pass connector into `Client` instead of loop, remove unneeded deps
|
||||
(#49 by @clue)
|
||||
|
||||
```php
|
||||
// old (connector is create implicitly)
|
||||
$client = new Client('127.0.0.1', $loop);
|
||||
|
||||
// old (connector can optionally be passed)
|
||||
$client = new Client('127.0.0.1', $loop, $connector);
|
||||
|
||||
// new (connector is now mandatory)
|
||||
$connector = new React\SocketClient\TcpConnector($loop);
|
||||
$client = new Client('127.0.0.1', $connector);
|
||||
```
|
||||
|
||||
* Feature / BC break: `Client` now implements `ConnectorInterface`, remove `Connector` adapter
|
||||
(#47 by @clue)
|
||||
|
||||
```php
|
||||
// old (explicit connector functions as an adapter)
|
||||
$connector = $client->createConnector();
|
||||
$promise = $connector->create('google.com', 80);
|
||||
|
||||
// new (client can be used as connector right away)
|
||||
$promise = $client->create('google.com', 80);
|
||||
```
|
||||
|
||||
* Feature / BC break: Remove `createSecureConnector()`, use `SecureConnector` instead
|
||||
(#47 by @clue)
|
||||
|
||||
```php
|
||||
// old (tight coupling and hidden dependency)
|
||||
$tls = $client->createSecureConnector();
|
||||
$promise = $tls->create('google.com', 443);
|
||||
|
||||
// new (more explicit, loose coupling)
|
||||
$tls = new React\SocketClient\SecureConnector($client, $loop);
|
||||
$promise = $tls->create('google.com', 443);
|
||||
```
|
||||
|
||||
* Feature / BC break: Remove `setResolveLocal()` and local DNS resolution and default to remote DNS resolution, use `DnsConnector` instead
|
||||
(#44 by @clue)
|
||||
|
||||
```php
|
||||
// old (implicitly defaults to true, can be disabled)
|
||||
$client->setResolveLocal(false);
|
||||
$tcp = $client->createConnector();
|
||||
$promise = $tcp->create('google.com', 80);
|
||||
|
||||
// new (always disabled, can be re-enabled like this)
|
||||
$factory = new React\Dns\Resolver\Factory();
|
||||
$resolver = $factory->createCached('8.8.8.8', $loop);
|
||||
$tcp = new React\SocketClient\DnsConnector($client, $resolver);
|
||||
$promise = $tcp->create('google.com', 80);
|
||||
```
|
||||
|
||||
* Feature / BC break: Remove `setTimeout()`, use `TimeoutConnector` instead
|
||||
(#45 by @clue)
|
||||
|
||||
```php
|
||||
// old (timeout only applies to TCP/IP connection)
|
||||
$client = new Client('127.0.0.1', …);
|
||||
$client->setTimeout(3.0);
|
||||
$tcp = $client->createConnector();
|
||||
$promise = $tcp->create('google.com', 80);
|
||||
|
||||
// new (timeout can be added to any layer)
|
||||
$client = new Client('127.0.0.1', …);
|
||||
$tcp = new React\SocketClient\TimeoutConnector($client, 3.0, $loop);
|
||||
$promise = $tcp->create('google.com', 80);
|
||||
```
|
||||
|
||||
* Feature / BC break: Remove `setProtocolVersion()` and `setAuth()` mutators, only support SOCKS URI for protocol version and authentication (immutable API)
|
||||
(#46 by @clue)
|
||||
|
||||
```php
|
||||
// old (state can be mutated after instantiation)
|
||||
$client = new Client('127.0.0.1', …);
|
||||
$client->setProtocolVersion('5');
|
||||
$client->setAuth('user', 'pass');
|
||||
|
||||
// new (immutable after construction, already supported as of v0.5.2 - now mandatory)
|
||||
$client = new Client('socks5://user:pass@127.0.0.1', …);
|
||||
```
|
||||
|
||||
## 0.5.2 (2016-11-25)
|
||||
|
||||
* Feature: Apply protocol version and username/password auth from SOCKS URI
|
||||
(#43 by @clue)
|
||||
|
||||
```php
|
||||
// explicitly use SOCKS5
|
||||
$client = new Client('socks5://127.0.0.1', $loop);
|
||||
|
||||
// use authentication (automatically SOCKS5)
|
||||
$client = new Client('user:pass@127.0.0.1', $loop);
|
||||
```
|
||||
|
||||
* More explicit client examples, including proxy chaining
|
||||
(#42 by @clue)
|
||||
|
||||
## 0.5.1 (2016-11-21)
|
||||
|
||||
* Feature: Support Promise cancellation
|
||||
(#39 by @clue)
|
||||
|
||||
```php
|
||||
$promise = $connector->create($host, $port);
|
||||
|
||||
$promise->cancel();
|
||||
```
|
||||
|
||||
* Feature: Timeout now cancels pending connection attempt
|
||||
(#39, #22 by @clue)
|
||||
|
||||
## 0.5.0 (2016-11-07)
|
||||
|
||||
* Remove / BC break: Split off Server to clue/socks-server
|
||||
(#35 by @clue)
|
||||
|
||||
> Upgrading? Check [clue/socks-server](https://github.com/clue/php-socks-server) for details.
|
||||
|
||||
* Improve documentation and project structure
|
||||
|
||||
## 0.4.0 (2016-03-19)
|
||||
|
||||
* Feature: Support proper SSL/TLS connections with additional SSL context options
|
||||
(#31, #33 by @clue)
|
||||
|
||||
* Documentation for advanced Connector setups (bindto, multihop)
|
||||
(#32 by @clue)
|
||||
|
||||
## 0.3.0 (2015-06-20)
|
||||
|
||||
* BC break / Feature: Client ctor now accepts a SOCKS server URI
|
||||
([#24](https://github.com/clue/php-socks-react/pull/24))
|
||||
|
||||
```php
|
||||
// old
|
||||
$client = new Client($loop, 'localhost', 9050);
|
||||
|
||||
// new
|
||||
$client = new Client('localhost:9050', $loop);
|
||||
```
|
||||
|
||||
* Feature: Automatically assume default SOCKS port (1080) if not given explicitly
|
||||
([#26](https://github.com/clue/php-socks-react/pull/26))
|
||||
|
||||
* Improve documentation and test suite
|
||||
|
||||
## 0.2.1 (2014-11-13)
|
||||
|
||||
* Support React PHP v0.4 (while preserving BC with React PHP v0.3)
|
||||
([#16](https://github.com/clue/php-socks-react/pull/16))
|
||||
|
||||
* Improve examples and add first class support for HHVM
|
||||
([#15](https://github.com/clue/php-socks-react/pull/15) and [#17](https://github.com/clue/php-socks-react/pull/17))
|
||||
|
||||
## 0.2.0 (2014-09-27)
|
||||
|
||||
* BC break / Feature: Simplify constructors by making parameters optional.
|
||||
([#10](https://github.com/clue/php-socks-react/pull/10))
|
||||
|
||||
The `Factory` has been removed, you can now create instances of the `Client`
|
||||
and `Server` yourself:
|
||||
|
||||
```php
|
||||
// old
|
||||
$factory = new Factory($loop, $dns);
|
||||
$client = $factory->createClient('localhost', 9050);
|
||||
$server = $factory->createSever($socket);
|
||||
|
||||
// new
|
||||
$client = new Client($loop, 'localhost', 9050);
|
||||
$server = new Server($loop, $socket);
|
||||
```
|
||||
|
||||
* BC break: Remove HTTP support and link to [clue/buzz-react](https://github.com/clue/php-buzz-react) instead.
|
||||
([#9](https://github.com/clue/php-socks-react/pull/9))
|
||||
|
||||
HTTP operates on a different layer than this low-level SOCKS library.
|
||||
Removing this reduces the footprint of this library.
|
||||
|
||||
> Upgrading? Check the [README](https://github.com/clue/php-socks-react#http-requests) for details.
|
||||
|
||||
* Fix: Refactored to support other, faster loops (libev/libevent)
|
||||
([#12](https://github.com/clue/php-socks-react/pull/12))
|
||||
|
||||
* Explicitly list dependencies, clean up examples and extend test suite significantly
|
||||
|
||||
## 0.1.0 (2014-05-19)
|
||||
|
||||
* First stable release
|
||||
* Async SOCKS `Client` and `Server` implementation
|
||||
* Project was originally part of [clue/socks](https://github.com/clue/php-socks)
|
||||
and was split off from its latest releave v0.4.0
|
||||
([#1](https://github.com/clue/reactphp-socks/issues/1))
|
||||
|
||||
> Upgrading from clue/socks v0.4.0? Use namespace `Clue\React\Socks` instead of `Socks` and you're ready to go!
|
||||
|
||||
## 0.0.0 (2011-04-26)
|
||||
|
||||
* Initial concept, originally tracked as part of
|
||||
[clue/socks](https://github.com/clue/php-socks)
|
||||
21
vendor/clue/socks-react/LICENSE
vendored
Executable file
21
vendor/clue/socks-react/LICENSE
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2011 Christian Lück
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
1017
vendor/clue/socks-react/README.md
vendored
Executable file
1017
vendor/clue/socks-react/README.md
vendored
Executable file
File diff suppressed because it is too large
Load Diff
28
vendor/clue/socks-react/composer.json
vendored
Executable file
28
vendor/clue/socks-react/composer.json
vendored
Executable file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "clue/socks-react",
|
||||
"description": "Async SOCKS4, SOCKS4a and SOCKS5 proxy client and server implementation, built on top of ReactPHP",
|
||||
"keywords": ["socks client", "socks server", "proxy", "tcp tunnel", "socks protocol", "async", "ReactPHP"],
|
||||
"homepage": "https://github.com/clue/php-socks-react",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Christian Lück",
|
||||
"email": "christian@lueck.tv"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {"Clue\\React\\Socks\\": "src/"}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3",
|
||||
"react/socket": "^1.0 || ^0.8.6",
|
||||
"react/promise": "^2.1 || ^1.2",
|
||||
"evenement/evenement": "~3.0|~1.0|~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35",
|
||||
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3",
|
||||
"clue/connection-manager-extra": "^1.0 || ^0.7",
|
||||
"clue/block-react": "^1.1"
|
||||
}
|
||||
}
|
||||
30
vendor/clue/socks-react/examples/01-http.php
vendored
Executable file
30
vendor/clue/socks-react/examples/01-http.php
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$proxy = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$client = new Client($proxy, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $client,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL;
|
||||
|
||||
$connector->connect('tcp://www.google.com:80')->then(function (ConnectionInterface $stream) {
|
||||
echo 'connected' . PHP_EOL;
|
||||
$stream->write("GET / HTTP/1.0\r\n\r\n");
|
||||
$stream->on('data', function ($data) {
|
||||
echo $data;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
30
vendor/clue/socks-react/examples/02-https.php
vendored
Executable file
30
vendor/clue/socks-react/examples/02-https.php
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$proxy = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$client = new Client($proxy, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $client,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL;
|
||||
|
||||
$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
echo 'connected' . PHP_EOL;
|
||||
$stream->write("GET / HTTP/1.0\r\n\r\n");
|
||||
$stream->on('data', function ($data) {
|
||||
echo $data;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
46
vendor/clue/socks-react/examples/03-proxy-chaining.php
vendored
Executable file
46
vendor/clue/socks-react/examples/03-proxy-chaining.php
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!isset($argv[1])) {
|
||||
echo 'No arguments given! Run with <proxy1> [<proxyN>...]' . PHP_EOL;
|
||||
echo 'You can add 1..n proxies in the path' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$path = array_slice($argv, 1);
|
||||
|
||||
// Alternatively, you can also hard-code this value like this:
|
||||
//$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053');
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// set next SOCKS server chain via p1 -> p2 -> p3 -> destination
|
||||
$connector = new Connector($loop);
|
||||
foreach ($path as $proxy) {
|
||||
$connector = new Client($proxy, $connector);
|
||||
}
|
||||
|
||||
// please note how the client uses p3 (not p1!), which in turn then uses the complete chain
|
||||
// this creates a TCP/IP connection to p1, which then connects to p2, then to p3, which then connects to the target
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $connector,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
echo 'Demo SOCKS client connecting to SOCKS proxy server chain ' . implode(' -> ', $path) . PHP_EOL;
|
||||
|
||||
$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
echo 'connected' . PHP_EOL;
|
||||
$stream->write("GET / HTTP/1.0\r\n\r\n");
|
||||
$stream->on('data', function ($data) {
|
||||
echo $data;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
31
vendor/clue/socks-react/examples/04-local-dns.php
vendored
Executable file
31
vendor/clue/socks-react/examples/04-local-dns.php
vendored
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$proxy = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// set up DNS server to use (Google's public DNS)
|
||||
$client = new Client($proxy, new Connector($loop));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $client,
|
||||
'timeout' => 3.0,
|
||||
'dns' => '8.8.8.8'
|
||||
));
|
||||
|
||||
echo 'Demo SOCKS client connecting to SOCKS server ' . $proxy . PHP_EOL;
|
||||
|
||||
$connector->connect('tls://www.google.com:443')->then(function (ConnectionInterface $stream) {
|
||||
echo 'connected' . PHP_EOL;
|
||||
$stream->write("GET / HTTP/1.0\r\n\r\n");
|
||||
$stream->on('data', function ($data) {
|
||||
echo $data;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
19
vendor/clue/socks-react/examples/11-server.php
vendored
Executable file
19
vendor/clue/socks-react/examples/11-server.php
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Server;
|
||||
use React\Socket\Server as Socket;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// listen on 127.0.0.1:1080 or first argument
|
||||
$listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
$socket = new Socket($listen, $loop);
|
||||
|
||||
// start a new server listening for incoming connection on the given socket
|
||||
$server = new Server($loop, $socket);
|
||||
|
||||
echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
24
vendor/clue/socks-react/examples/12-server-with-password.php
vendored
Executable file
24
vendor/clue/socks-react/examples/12-server-with-password.php
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Server;
|
||||
use React\Socket\Server as Socket;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// listen on 127.0.0.1:1080 or first argument
|
||||
$listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
$socket = new Socket($listen, $loop);
|
||||
|
||||
// start a new server listening for incoming connection on the given socket
|
||||
// require authentication and hence make this a SOCKS5-only server
|
||||
$server = new Server($loop, $socket);
|
||||
$server->setAuthArray(array(
|
||||
'tom' => 'god',
|
||||
'user' => 'p@ssw0rd'
|
||||
));
|
||||
|
||||
echo 'SOCKS5 server requiring authentication listening on ' . $socket->getAddress() . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
40
vendor/clue/socks-react/examples/13-server-blacklist.php
vendored
Executable file
40
vendor/clue/socks-react/examples/13-server-blacklist.php
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
// A SOCKS server that rejects connections to some domains (blacklist / filtering)
|
||||
|
||||
use React\EventLoop\Factory as LoopFactory;
|
||||
use ConnectionManager\Extra\Multiple\ConnectionManagerSelective;
|
||||
use React\Socket\Server as Socket;
|
||||
use Clue\React\Socks\Server;
|
||||
use ConnectionManager\Extra\ConnectionManagerReject;
|
||||
use React\Socket\Connector;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$loop = LoopFactory::create();
|
||||
|
||||
// create a connector that rejects the connection
|
||||
$reject = new ConnectionManagerReject();
|
||||
|
||||
// create an actual connector that establishes real connections
|
||||
$permit = new Connector($loop);
|
||||
|
||||
// this connector selectively picks one of the the attached connectors depending on the target address
|
||||
// reject youtube.com and unencrypted HTTP for google.com
|
||||
// default connctor: permit everything
|
||||
$connector = new ConnectionManagerSelective(array(
|
||||
'*.youtube.com' => $reject,
|
||||
'www.google.com:80' => $reject,
|
||||
'*' => $permit
|
||||
));
|
||||
|
||||
// listen on 127.0.0.1:1080 or first argument
|
||||
$listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
$socket = new Socket($listen, $loop);
|
||||
|
||||
// start the actual socks server on the given server socket and using our connection manager for outgoing connections
|
||||
$server = new Server($loop, $socket, $connector);
|
||||
|
||||
echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
42
vendor/clue/socks-react/examples/21-server-proxy-chaining.php
vendored
Executable file
42
vendor/clue/socks-react/examples/21-server-proxy-chaining.php
vendored
Executable file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// A SOCKS server that forwards (proxy chaining) to other SOCKS servers
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use Clue\React\Socks\Server;
|
||||
use React\Socket\Server as Socket;
|
||||
use React\Socket\Connector;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!isset($argv[2])) {
|
||||
echo 'No arguments given! Run with <listen> <proxy1> [<proxyN>...]' . PHP_EOL;
|
||||
echo 'You can add 1..n proxies in the path' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$listen = $argv[1];
|
||||
$path = array_slice($argv, 2);
|
||||
|
||||
// Alternatively, you can also hard-code these values like this:
|
||||
//$listen = '127.0.0.1:9050';
|
||||
//$path = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053');
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// set next SOCKS server chain -> p1 -> p2 -> p3 -> destination
|
||||
$connector = new Connector($loop);
|
||||
foreach ($path as $proxy) {
|
||||
$connector = new Client($proxy, $connector);
|
||||
}
|
||||
|
||||
// listen on 127.0.0.1:1080 or first argument
|
||||
$socket = new Socket($listen, $loop);
|
||||
|
||||
// start a new server which forwards all connections to the other SOCKS server
|
||||
$server = new Server($loop, $socket, $connector);
|
||||
|
||||
echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL;
|
||||
echo 'Forwarding via: ' . implode(' -> ', $path) . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
46
vendor/clue/socks-react/examples/22-server-proxy-chaining-from-random-pool.php
vendored
Executable file
46
vendor/clue/socks-react/examples/22-server-proxy-chaining-from-random-pool.php
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
// A SOCKS server that randomly forwards (proxy chaining) to a pool of SOCKS servers
|
||||
|
||||
use React\EventLoop\Factory as LoopFactory;
|
||||
use ConnectionManager\Extra\Multiple\ConnectionManagerRandom;
|
||||
use React\Socket\Server as Socket;
|
||||
use Clue\React\Socks\Server;
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!isset($argv[3])) {
|
||||
echo 'No arguments given! Run with <listen> <proxy1> <proxyN>...' . PHP_EOL;
|
||||
echo 'You can add 2..n proxies in the pool' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$listen = $argv[1];
|
||||
$pool = array_slice($argv, 2);
|
||||
|
||||
// Alternatively, you can also hard-code these values like this:
|
||||
//$listen = '127.0.0.1:9050';
|
||||
//$pool = array('127.0.0.1:9051', '127.0.0.1:9052', '127.0.0.1:9053');
|
||||
|
||||
$loop = LoopFactory::create();
|
||||
|
||||
// forward to socks server listening on 127.0.0.1:9051-9053
|
||||
// this connector randomly picks one of the the attached connectors from the pool
|
||||
$connector = new Connector($loop);
|
||||
$clients = array();
|
||||
foreach ($pool as $proxy) {
|
||||
$clients []= new Client($proxy, $connector);
|
||||
}
|
||||
$connector = new ConnectionManagerRandom($clients);
|
||||
|
||||
$socket = new Socket($listen, $loop);
|
||||
|
||||
// start the actual socks server on the given server socket and using our connection manager for outgoing connections
|
||||
$server = new Server($loop, $socket, $connector);
|
||||
|
||||
echo 'SOCKS server listening on ' . $socket->getAddress() . PHP_EOL;
|
||||
echo 'Randomly picking from: ' . implode(', ', $pool) . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
21
vendor/clue/socks-react/examples/31-server-secure.php
vendored
Executable file
21
vendor/clue/socks-react/examples/31-server-secure.php
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Server;
|
||||
use React\Socket\Server as Socket;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
// listen on tls://127.0.0.1:1080 or first argument
|
||||
$listen = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
$socket = new Socket('tls://' . $listen, $loop, array('tls' => array(
|
||||
'local_cert' => __DIR__ . '/localhost.pem',
|
||||
)));
|
||||
|
||||
// start a new server listening for incoming connection on the given socket
|
||||
$server = new Server($loop, $socket);
|
||||
|
||||
echo 'SOCKS over TLS server listening on ' . str_replace('tls:', 'sockss:', $socket->getAddress()) . PHP_EOL;
|
||||
|
||||
$loop->run();
|
||||
33
vendor/clue/socks-react/examples/32-http-secure.php
vendored
Executable file
33
vendor/clue/socks-react/examples/32-http-secure.php
vendored
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$proxy = isset($argv[1]) ? $argv[1] : '127.0.0.1:1080';
|
||||
|
||||
$loop = React\EventLoop\Factory::create();
|
||||
|
||||
$client = new Client('sockss://' . $proxy, new Connector($loop, array('tls' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false
|
||||
))));
|
||||
$connector = new Connector($loop, array(
|
||||
'tcp' => $client,
|
||||
'timeout' => 3.0,
|
||||
'dns' => false
|
||||
));
|
||||
|
||||
echo 'Demo SOCKS over TLS client connecting to secure SOCKS server ' . $proxy . PHP_EOL;
|
||||
|
||||
$connector->connect('tcp://www.google.com:80')->then(function (ConnectionInterface $stream) {
|
||||
echo 'connected' . PHP_EOL;
|
||||
$stream->write("GET / HTTP/1.0\r\n\r\n");
|
||||
$stream->on('data', function ($data) {
|
||||
echo $data;
|
||||
});
|
||||
}, 'printf');
|
||||
|
||||
$loop->run();
|
||||
49
vendor/clue/socks-react/examples/localhost.pem
vendored
Executable file
49
vendor/clue/socks-react/examples/localhost.pem
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBZMRIwEAYDVQQDDAkxMjcu
|
||||
MC4wLjExCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK
|
||||
DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMwMTQ1OTA2WhcNMjYx
|
||||
MjI4MTQ1OTA2WjBZMRIwEAYDVQQDDAkxMjcuMC4wLjExCzAJBgNVBAYTAkFVMRMw
|
||||
EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
|
||||
eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8SZWNS+Ktg0Py
|
||||
W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
|
||||
2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
|
||||
zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
|
||||
UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
|
||||
wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
|
||||
YCUE54G/AgMBAAGjUDBOMB0GA1UdDgQWBBQ2GRz3QsQzdXaTMnPVCKfpigA10DAf
|
||||
BgNVHSMEGDAWgBQ2GRz3QsQzdXaTMnPVCKfpigA10DAMBgNVHRMEBTADAQH/MA0G
|
||||
CSqGSIb3DQEBBQUAA4IBAQA77iZ4KrpPY18Ezjt0mngYAuAxunKddXYdLZ2khywN
|
||||
0uI/VzYnkFVtrsC7y2jLHSxlmE2/viPPGZDUplENV2acN6JNW+tlt7/bsrQHDQw3
|
||||
7VCF27EWiDxHsaghhLkqC+kcop5YR5c0oDQTdEWEKSbow2zayUXDYbRRs76SClTe
|
||||
824Yul+Ts8Mka+AX2PXDg47iZ84fJRN/nKavcJUTJ2iS1uYw0GNnFMge/uwsfMR3
|
||||
V47qN0X5emky8fcq99FlMCbcy0gHAeSWAjClgr2dd2i0LDatUbj7YmdmFcskOgII
|
||||
IwGfvuWR2yPevYGAE0QgFeLHniN3RW8zmpnX/XtrJ4a7
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8SZWNS+Ktg0Py
|
||||
W8dx5uXZ+ZUawd3wnzLMHW7EhoUpIrIdp3kDU9NezF68dOhPMJY/Kh+6btRCxWXN
|
||||
2OVTqS5Xi826j3TSE07iF83JRLeveW0PcodjUBd+RzdwCWWo2pfMJz4v7x1wu1c9
|
||||
zNi6JxxpDAXTFSB4GiWsI4tFu2XmMRhfm6LRK4WPfsZIJKokdiG5fKSPDn7nrVj0
|
||||
UUXr2eBsEAzdwL14U9+mwbLdaAkz3qK3fqi8sEC09lEWm95gKMOhkQf5qvXODtT4
|
||||
wdVrrKDTyehLv0xaItnUDnXzrkMBU5QS9TQzzqSW6ZaBsSxtONEFUiXiN9dtyXsY
|
||||
YCUE54G/AgMBAAECggEBAKiO/3FE1CMddkCLZVtUp8ShqJgRokx9WI5ecwFApAkV
|
||||
ZHsjqDQQYRNmxhDUX/w0tOzLGyhde2xjJyZG29YviKsbHwu6zYwbeOzy/mkGOaK/
|
||||
g6DmmMmRs9Z6juifoQCu4GIFZ6il2adIL2vF7OeJh+eKudQj/7NFRSB7mXzNrQWK
|
||||
tZY3eux5zXWmio7pgZrx1HFZQiiL9NVLwT9J7oBnaoO3fREiu5J2xBpljG9Cr0j1
|
||||
LLiVLhukWJYRlHDtGt1CzI9w8iKo44PCRzpKyxpbsOrQxeSyEWUYQRv9VHA59LC7
|
||||
tVAJTbnTX1BNHkGZkOkoOpoZLwBaM2XbbDtcOGCAZMECgYEA+mTURFQ85/pxawvk
|
||||
9ndqZ+5He1u/bMLYIJDp0hdB/vgD+vw3gb2UyRwp0I6Wc6Si4FEEnbY7L0pzWsiR
|
||||
43CpLs+cyLfnD9NycuIasxs5fKb/1s1nGTkRAp7x9x/ZTtEf8v4YTmmMXFHzdo7V
|
||||
pv+czO89ppEDkxEtMf/b5SifhO8CgYEAwIDIUvXLduGhL+RPDwjc2SKdydXGV6om
|
||||
OEdt/V8oS801Z7k8l3gHXFm7zL/MpHmh9cag+F9dHK42kw2RSjDGsBlXXiAO1Z0I
|
||||
2A34OdPw/kow8fmIKWTMu3+28Kca+3RmUqeyaq0vazQ/bWMO9px+Ud3YfLo1Tn5I
|
||||
li0MecAx8DECgYEAvsLceKYYtL83c09fg2oc1ctSCCgw4WJcGAtvJ9DyRZacKbXH
|
||||
b/+H/+OF8879zmKqd+0hcCnqUzAMTCisBLPLIM+o6b45ufPkqKObpcJi/JWaKgLY
|
||||
vf2c+Psw6o4IF6T5Cz4MNIjzF06UBknxecYZpoPJ20F1kLCwVvxPgfl99l8CgYAb
|
||||
XfOcv67WTstgiJ+oroTfJamy+P5ClkDqvVTosW+EHz9ZaJ8xlXHOcj9do2LPey9I
|
||||
Rp250azmF+pQS5x9JKQKgv/FtN8HBVUtigbhCb14GUoODICMCfWFLmnumoMefnTR
|
||||
iV+3BLn6Dqp5vZxx+NuIffZ5/Or5JsDhALSGVomC8QKBgAi3Z/dNQrDHfkXMNn/L
|
||||
+EAoLuAbFgLs76r9VGgNaRQ/q5gex2bZEGoBj4Sxvs95NUIcfD9wKT7FF8HdxARv
|
||||
y3o6Bfc8Xp9So9SlFXrje+gkdEJ0rQR67d+XBuJZh86bXJHVrMwpoNL+ahLGdVSe
|
||||
81oh1uCH1YPLM29hPyaohxL8
|
||||
-----END PRIVATE KEY-----
|
||||
14
vendor/clue/socks-react/phpunit.xml.dist
vendored
Executable file
14
vendor/clue/socks-react/phpunit.xml.dist
vendored
Executable file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit colors="true" bootstrap="./tests/bootstrap.php">
|
||||
<testsuites>
|
||||
<testsuite name="Socks Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
382
vendor/clue/socks-react/src/Client.php
vendored
Executable file
382
vendor/clue/socks-react/src/Client.php
vendored
Executable file
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Socks;
|
||||
|
||||
use React\Promise;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Socket\ConnectionInterface;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Socket\FixedUriConnector;
|
||||
use \Exception;
|
||||
use \InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
class Client implements ConnectorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var ConnectorInterface
|
||||
*/
|
||||
private $connector;
|
||||
|
||||
private $socksUri;
|
||||
|
||||
private $protocolVersion = null;
|
||||
|
||||
private $auth = null;
|
||||
|
||||
public function __construct($socksUri, ConnectorInterface $connector)
|
||||
{
|
||||
// support `sockss://` scheme for SOCKS over TLS
|
||||
// support `socks+unix://` scheme for Unix domain socket (UDS) paths
|
||||
if (preg_match('/^(socks(?:5|4|4a)?)(s|\+unix):\/\/(.*?@)?(.+?)$/', $socksUri, $match)) {
|
||||
// rewrite URI to parse SOCKS scheme, authentication and dummy host
|
||||
$socksUri = $match[1] . '://' . $match[3] . 'localhost';
|
||||
|
||||
// connector uses appropriate transport scheme and explicit host given
|
||||
$connector = new FixedUriConnector(
|
||||
($match[2] === 's' ? 'tls://' : 'unix://') . $match[4],
|
||||
$connector
|
||||
);
|
||||
}
|
||||
|
||||
// assume default scheme if none is given
|
||||
if (strpos($socksUri, '://') === false) {
|
||||
$socksUri = 'socks://' . $socksUri;
|
||||
}
|
||||
|
||||
// parse URI into individual parts
|
||||
$parts = parse_url($socksUri);
|
||||
if (!$parts || !isset($parts['scheme'], $parts['host'])) {
|
||||
throw new \InvalidArgumentException('Invalid SOCKS server URI "' . $socksUri . '"');
|
||||
}
|
||||
|
||||
// assume default port
|
||||
if (!isset($parts['port'])) {
|
||||
$parts['port'] = 1080;
|
||||
}
|
||||
|
||||
// user or password in URI => SOCKS5 authentication
|
||||
if (isset($parts['user']) || isset($parts['pass'])) {
|
||||
if ($parts['scheme'] === 'socks') {
|
||||
// default to using SOCKS5 if not given explicitly
|
||||
$parts['scheme'] = 'socks5';
|
||||
} elseif ($parts['scheme'] !== 'socks5') {
|
||||
// fail if any other protocol version given explicitly
|
||||
throw new InvalidArgumentException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication');
|
||||
}
|
||||
$parts += array('user' => '', 'pass' => '');
|
||||
$this->setAuth(rawurldecode($parts['user']), rawurldecode($parts['pass']));
|
||||
}
|
||||
|
||||
// check for valid protocol version from URI scheme
|
||||
$this->setProtocolVersionFromScheme($parts['scheme']);
|
||||
|
||||
$this->socksUri = $parts['host'] . ':' . $parts['port'];
|
||||
$this->connector = $connector;
|
||||
}
|
||||
|
||||
private function setProtocolVersionFromScheme($scheme)
|
||||
{
|
||||
if ($scheme === 'socks' || $scheme === 'socks4a') {
|
||||
$this->protocolVersion = '4a';
|
||||
} elseif ($scheme === 'socks5') {
|
||||
$this->protocolVersion = '5';
|
||||
} elseif ($scheme === 'socks4') {
|
||||
$this->protocolVersion = '4';
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid protocol version given "' . $scheme . '://"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set login data for username/password authentication method (RFC1929)
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @link http://tools.ietf.org/html/rfc1929
|
||||
*/
|
||||
private function setAuth($username, $password)
|
||||
{
|
||||
if (strlen($username) > 255 || strlen($password) > 255) {
|
||||
throw new InvalidArgumentException('Both username and password MUST NOT exceed a length of 255 bytes each');
|
||||
}
|
||||
$this->auth = pack('C2', 0x01, strlen($username)) . $username . pack('C', strlen($password)) . $password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish a TCP/IP connection to the given target URI through the SOCKS server
|
||||
*
|
||||
* Many higher-level networking protocols build on top of TCP. It you're dealing
|
||||
* with one such client implementation, it probably uses/accepts an instance
|
||||
* implementing React's `ConnectorInterface` (and usually its default `Connector`
|
||||
* instance). In this case you can also pass this `Connector` instance instead
|
||||
* to make this client implementation SOCKS-aware. That's it.
|
||||
*
|
||||
* @param string $uri
|
||||
* @return PromiseInterface Promise<ConnectionInterface,Exception>
|
||||
*/
|
||||
public function connect($uri)
|
||||
{
|
||||
if (strpos($uri, '://') === false) {
|
||||
$uri = 'tcp://' . $uri;
|
||||
}
|
||||
|
||||
$parts = parse_url($uri);
|
||||
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
|
||||
return Promise\reject(new InvalidArgumentException('Invalid target URI specified'));
|
||||
}
|
||||
|
||||
$host = trim($parts['host'], '[]');
|
||||
$port = $parts['port'];
|
||||
|
||||
if ($this->protocolVersion === '4' && false === filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
return Promise\reject(new InvalidArgumentException('Requires an IPv4 address for SOCKS4'));
|
||||
}
|
||||
|
||||
if (strlen($host) > 255 || $port > 65535 || $port < 0 || (string)$port !== (string)(int)$port) {
|
||||
return Promise\reject(new InvalidArgumentException('Invalid target specified'));
|
||||
}
|
||||
|
||||
// construct URI to SOCKS server to connect to
|
||||
$socksUri = $this->socksUri;
|
||||
|
||||
// append path from URI if given
|
||||
if (isset($parts['path'])) {
|
||||
$socksUri .= $parts['path'];
|
||||
}
|
||||
|
||||
// parse query args
|
||||
$args = array();
|
||||
if (isset($parts['query'])) {
|
||||
parse_str($parts['query'], $args);
|
||||
}
|
||||
|
||||
// append hostname from URI to query string unless explicitly given
|
||||
if (!isset($args['hostname'])) {
|
||||
$args['hostname'] = $host;
|
||||
}
|
||||
|
||||
// append query string
|
||||
$socksUri .= '?' . http_build_query($args, '', '&');
|
||||
|
||||
// append fragment from URI if given
|
||||
if (isset($parts['fragment'])) {
|
||||
$socksUri .= '#' . $parts['fragment'];
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
|
||||
// start TCP/IP connection to SOCKS server and then
|
||||
// handle SOCKS protocol once connection is ready
|
||||
// resolve plain connection once SOCKS protocol is completed
|
||||
return $this->connector->connect($socksUri)->then(
|
||||
function (ConnectionInterface $stream) use ($that, $host, $port) {
|
||||
return $that->handleConnectedSocks($stream, $host, $port);
|
||||
},
|
||||
function (Exception $e) {
|
||||
throw new RuntimeException('Unable to connect to proxy (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $e);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper used to handle the communication with the SOCKS server
|
||||
*
|
||||
* @param ConnectionInterface $stream
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @return Promise Promise<ConnectionInterface, Exception>
|
||||
* @internal
|
||||
*/
|
||||
public function handleConnectedSocks(ConnectionInterface $stream, $host, $port)
|
||||
{
|
||||
$deferred = new Deferred(function ($_, $reject) {
|
||||
$reject(new RuntimeException('Connection canceled while establishing SOCKS session (ECONNABORTED)', defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103));
|
||||
});
|
||||
|
||||
$reader = new StreamReader();
|
||||
$stream->on('data', array($reader, 'write'));
|
||||
|
||||
$stream->on('error', $onError = function (Exception $e) use ($deferred) {
|
||||
$deferred->reject(new RuntimeException('Stream error while waiting for response from proxy (EIO)', defined('SOCKET_EIO') ? SOCKET_EIO : 5, $e));
|
||||
});
|
||||
|
||||
$stream->on('close', $onClose = function () use ($deferred) {
|
||||
$deferred->reject(new RuntimeException('Connection to proxy lost while waiting for response (ECONNRESET)', defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104));
|
||||
});
|
||||
|
||||
if ($this->protocolVersion === '5') {
|
||||
$promise = $this->handleSocks5($stream, $host, $port, $reader);
|
||||
} else {
|
||||
$promise = $this->handleSocks4($stream, $host, $port, $reader);
|
||||
}
|
||||
$promise->then(function () use ($deferred, $stream) {
|
||||
$deferred->resolve($stream);
|
||||
}, function (Exception $error) use ($deferred) {
|
||||
// pass custom RuntimeException through as-is, otherwise wrap in protocol error
|
||||
if (!$error instanceof RuntimeException) {
|
||||
$error = new RuntimeException('Invalid response received from proxy (EBADMSG)', defined('SOCKET_EBADMSG') ? SOCKET_EBADMSG: 71, $error);
|
||||
}
|
||||
|
||||
$deferred->reject($error);
|
||||
});
|
||||
|
||||
return $deferred->promise()->then(
|
||||
function (ConnectionInterface $stream) use ($reader, $onError, $onClose) {
|
||||
$stream->removeListener('data', array($reader, 'write'));
|
||||
$stream->removeListener('error', $onError);
|
||||
$stream->removeListener('close', $onClose);
|
||||
|
||||
return $stream;
|
||||
},
|
||||
function ($error) use ($stream, $onClose) {
|
||||
$stream->removeListener('close', $onClose);
|
||||
$stream->close();
|
||||
|
||||
throw $error;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function handleSocks4(ConnectionInterface $stream, $host, $port, StreamReader $reader)
|
||||
{
|
||||
// do not resolve hostname. only try to convert to IP
|
||||
$ip = ip2long($host);
|
||||
|
||||
// send IP or (0.0.0.1) if invalid
|
||||
$data = pack('C2nNC', 0x04, 0x01, $port, $ip === false ? 1 : $ip, 0x00);
|
||||
|
||||
if ($ip === false) {
|
||||
// host is not a valid IP => send along hostname (SOCKS4a)
|
||||
$data .= $host . pack('C', 0x00);
|
||||
}
|
||||
|
||||
$stream->write($data);
|
||||
|
||||
return $reader->readBinary(array(
|
||||
'null' => 'C',
|
||||
'status' => 'C',
|
||||
'port' => 'n',
|
||||
'ip' => 'N'
|
||||
))->then(function ($data) {
|
||||
if ($data['null'] !== 0x00) {
|
||||
throw new Exception('Invalid SOCKS response');
|
||||
}
|
||||
if ($data['status'] !== 0x5a) {
|
||||
throw new RuntimeException('Proxy refused connection with SOCKS error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function handleSocks5(ConnectionInterface $stream, $host, $port, StreamReader $reader)
|
||||
{
|
||||
// protocol version 5
|
||||
$data = pack('C', 0x05);
|
||||
|
||||
$auth = $this->auth;
|
||||
if ($auth === null) {
|
||||
// one method, no authentication
|
||||
$data .= pack('C2', 0x01, 0x00);
|
||||
} else {
|
||||
// two methods, username/password and no authentication
|
||||
$data .= pack('C3', 0x02, 0x02, 0x00);
|
||||
}
|
||||
$stream->write($data);
|
||||
|
||||
$that = $this;
|
||||
|
||||
return $reader->readBinary(array(
|
||||
'version' => 'C',
|
||||
'method' => 'C'
|
||||
))->then(function ($data) use ($auth, $stream, $reader) {
|
||||
if ($data['version'] !== 0x05) {
|
||||
throw new Exception('Version/Protocol mismatch');
|
||||
}
|
||||
|
||||
if ($data['method'] === 0x02 && $auth !== null) {
|
||||
// username/password authentication requested and provided
|
||||
$stream->write($auth);
|
||||
|
||||
return $reader->readBinary(array(
|
||||
'version' => 'C',
|
||||
'status' => 'C'
|
||||
))->then(function ($data) {
|
||||
if ($data['version'] !== 0x01 || $data['status'] !== 0x00) {
|
||||
throw new RuntimeException('Username/Password authentication failed (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||
}
|
||||
});
|
||||
} else if ($data['method'] !== 0x00) {
|
||||
// any other method than "no authentication"
|
||||
throw new RuntimeException('No acceptable authentication method found (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||
}
|
||||
})->then(function () use ($stream, $reader, $host, $port) {
|
||||
// do not resolve hostname. only try to convert to (binary/packed) IP
|
||||
$ip = @inet_pton($host);
|
||||
|
||||
$data = pack('C3', 0x05, 0x01, 0x00);
|
||||
if ($ip === false) {
|
||||
// not an IP, send as hostname
|
||||
$data .= pack('C2', 0x03, strlen($host)) . $host;
|
||||
} else {
|
||||
// send as IPv4 / IPv6
|
||||
$data .= pack('C', (strpos($host, ':') === false) ? 0x01 : 0x04) . $ip;
|
||||
}
|
||||
$data .= pack('n', $port);
|
||||
|
||||
$stream->write($data);
|
||||
|
||||
return $reader->readBinary(array(
|
||||
'version' => 'C',
|
||||
'status' => 'C',
|
||||
'null' => 'C',
|
||||
'type' => 'C'
|
||||
));
|
||||
})->then(function ($data) use ($reader) {
|
||||
if ($data['version'] !== 0x05 || $data['null'] !== 0x00) {
|
||||
throw new Exception('Invalid SOCKS response');
|
||||
}
|
||||
if ($data['status'] !== 0x00) {
|
||||
// map limited list of SOCKS error codes to common socket error conditions
|
||||
// @link https://tools.ietf.org/html/rfc1928#section-6
|
||||
if ($data['status'] === Server::ERROR_GENERAL) {
|
||||
throw new RuntimeException('SOCKS server reported a general server failure (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||
} elseif ($data['status'] === Server::ERROR_NOT_ALLOWED_BY_RULESET) {
|
||||
throw new RuntimeException('SOCKS server reported connection is not allowed by ruleset (EACCES)', defined('SOCKET_EACCES') ? SOCKET_EACCES : 13);
|
||||
} elseif ($data['status'] === Server::ERROR_NETWORK_UNREACHABLE) {
|
||||
throw new RuntimeException('SOCKS server reported network unreachable (ENETUNREACH)', defined('SOCKET_ENETUNREACH') ? SOCKET_ENETUNREACH : 101);
|
||||
} elseif ($data['status'] === Server::ERROR_HOST_UNREACHABLE) {
|
||||
throw new RuntimeException('SOCKS server reported host unreachable (EHOSTUNREACH)', defined('SOCKET_EHOSTUNREACH') ? SOCKET_EHOSTUNREACH : 113);
|
||||
} elseif ($data['status'] === Server::ERROR_CONNECTION_REFUSED) {
|
||||
throw new RuntimeException('SOCKS server reported connection refused (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||
} elseif ($data['status'] === Server::ERROR_TTL) {
|
||||
throw new RuntimeException('SOCKS server reported TTL/timeout expired (ETIMEDOUT)', defined('SOCKET_ETIMEDOUT') ? SOCKET_ETIMEDOUT : 110);
|
||||
} elseif ($data['status'] === Server::ERROR_COMMAND_UNSUPPORTED) {
|
||||
throw new RuntimeException('SOCKS server does not support the CONNECT command (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71);
|
||||
} elseif ($data['status'] === Server::ERROR_ADDRESS_UNSUPPORTED) {
|
||||
throw new RuntimeException('SOCKS server does not support this address type (EPROTO)', defined('SOCKET_EPROTO') ? SOCKET_EPROTO : 71);
|
||||
}
|
||||
|
||||
throw new RuntimeException('SOCKS server reported an unassigned error code ' . sprintf('0x%02X', $data['status']) . ' (ECONNREFUSED)', defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111);
|
||||
}
|
||||
if ($data['type'] === 0x01) {
|
||||
// IPv4 address => skip IP and port
|
||||
return $reader->readLength(6);
|
||||
} elseif ($data['type'] === 0x03) {
|
||||
// domain name => read domain name length
|
||||
return $reader->readBinary(array(
|
||||
'length' => 'C'
|
||||
))->then(function ($data) use ($reader) {
|
||||
// skip domain name and port
|
||||
return $reader->readLength($data['length'] + 2);
|
||||
});
|
||||
} elseif ($data['type'] === 0x04) {
|
||||
// IPv6 address => skip IP and port
|
||||
return $reader->readLength(18);
|
||||
} else {
|
||||
throw new Exception('Invalid SOCKS reponse: Invalid address type');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
422
vendor/clue/socks-react/src/Server.php
vendored
Executable file
422
vendor/clue/socks-react/src/Server.php
vendored
Executable file
@@ -0,0 +1,422 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Socks;
|
||||
|
||||
use Evenement\EventEmitter;
|
||||
use React\Socket\ServerInterface;
|
||||
use React\Promise;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Socket\ConnectorInterface;
|
||||
use React\Socket\Connector;
|
||||
use React\Socket\ConnectionInterface;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use \UnexpectedValueException;
|
||||
use \InvalidArgumentException;
|
||||
use \Exception;
|
||||
use React\Promise\Timer\TimeoutException;
|
||||
|
||||
class Server extends EventEmitter
|
||||
{
|
||||
// the following error codes are only used for SOCKS5 only
|
||||
/** @internal */
|
||||
const ERROR_GENERAL = 0x01;
|
||||
/** @internal */
|
||||
const ERROR_NOT_ALLOWED_BY_RULESET = 0x02;
|
||||
/** @internal */
|
||||
const ERROR_NETWORK_UNREACHABLE = 0x03;
|
||||
/** @internal */
|
||||
const ERROR_HOST_UNREACHABLE = 0x04;
|
||||
/** @internal */
|
||||
const ERROR_CONNECTION_REFUSED = 0x05;
|
||||
/** @internal */
|
||||
const ERROR_TTL = 0x06;
|
||||
/** @internal */
|
||||
const ERROR_COMMAND_UNSUPPORTED = 0x07;
|
||||
/** @internal */
|
||||
const ERROR_ADDRESS_UNSUPPORTED = 0x08;
|
||||
|
||||
protected $loop;
|
||||
|
||||
private $connector;
|
||||
|
||||
private $auth = null;
|
||||
|
||||
private $protocolVersion = null;
|
||||
|
||||
public function __construct(LoopInterface $loop, ServerInterface $serverInterface, ConnectorInterface $connector = null)
|
||||
{
|
||||
if ($connector === null) {
|
||||
$connector = new Connector($loop);
|
||||
}
|
||||
|
||||
$this->loop = $loop;
|
||||
$this->connector = $connector;
|
||||
|
||||
$that = $this;
|
||||
$serverInterface->on('connection', function ($connection) use ($that) {
|
||||
$that->emit('connection', array($connection));
|
||||
$that->onConnection($connection);
|
||||
});
|
||||
}
|
||||
|
||||
public function setProtocolVersion($version)
|
||||
{
|
||||
if ($version !== null) {
|
||||
$version = (string)$version;
|
||||
if (!in_array($version, array('4', '4a', '5'), true)) {
|
||||
throw new InvalidArgumentException('Invalid protocol version given');
|
||||
}
|
||||
if ($version !== '5' && $this->auth !== null){
|
||||
throw new UnexpectedValueException('Unable to change protocol version to anything but SOCKS5 while authentication is used. Consider removing authentication info or sticking to SOCKS5');
|
||||
}
|
||||
}
|
||||
$this->protocolVersion = $version;
|
||||
}
|
||||
|
||||
public function setAuth($auth)
|
||||
{
|
||||
if (!is_callable($auth)) {
|
||||
throw new InvalidArgumentException('Given authenticator is not a valid callable');
|
||||
}
|
||||
if ($this->protocolVersion !== null && $this->protocolVersion !== '5') {
|
||||
throw new UnexpectedValueException('Authentication requires SOCKS5. Consider using protocol version 5 or waive authentication');
|
||||
}
|
||||
// wrap authentication callback in order to cast its return value to a promise
|
||||
$this->auth = function($username, $password, $remote) use ($auth) {
|
||||
$ret = call_user_func($auth, $username, $password, $remote);
|
||||
if ($ret instanceof PromiseInterface) {
|
||||
return $ret;
|
||||
}
|
||||
$deferred = new Deferred();
|
||||
$ret ? $deferred->resolve() : $deferred->reject();
|
||||
return $deferred->promise();
|
||||
};
|
||||
}
|
||||
|
||||
public function setAuthArray(array $login)
|
||||
{
|
||||
$this->setAuth(function ($username, $password) use ($login) {
|
||||
return (isset($login[$username]) && (string)$login[$username] === $password);
|
||||
});
|
||||
}
|
||||
|
||||
public function unsetAuth()
|
||||
{
|
||||
$this->auth = null;
|
||||
}
|
||||
|
||||
public function onConnection(ConnectionInterface $connection)
|
||||
{
|
||||
$that = $this;
|
||||
$handling = $this->handleSocks($connection)->then(function($remote) use ($connection){
|
||||
$connection->emit('ready',array($remote));
|
||||
}, function ($error) use ($connection, $that) {
|
||||
if (!($error instanceof \Exception)) {
|
||||
$error = new \Exception($error);
|
||||
}
|
||||
$connection->emit('error', array($error));
|
||||
$that->endConnection($connection);
|
||||
});
|
||||
|
||||
$connection->on('close', function () use ($handling) {
|
||||
$handling->cancel();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* gracefully shutdown connection by flushing all remaining data and closing stream
|
||||
*/
|
||||
public function endConnection(ConnectionInterface $stream)
|
||||
{
|
||||
$tid = true;
|
||||
$loop = $this->loop;
|
||||
|
||||
// cancel below timer in case connection is closed in time
|
||||
$stream->once('close', function () use (&$tid, $loop) {
|
||||
// close event called before the timer was set up, so everything is okay
|
||||
if ($tid === true) {
|
||||
// make sure to not start a useless timer
|
||||
$tid = false;
|
||||
} else {
|
||||
$loop->cancelTimer($tid);
|
||||
}
|
||||
});
|
||||
|
||||
// shut down connection by pausing input data, flushing outgoing buffer and then exit
|
||||
$stream->pause();
|
||||
$stream->end();
|
||||
|
||||
// check if connection is not already closed
|
||||
if ($tid === true) {
|
||||
// fall back to forcefully close connection in 3 seconds if buffer can not be flushed
|
||||
$tid = $loop->addTimer(3.0, array($stream,'close'));
|
||||
}
|
||||
}
|
||||
|
||||
private function handleSocks(ConnectionInterface $stream)
|
||||
{
|
||||
$reader = new StreamReader();
|
||||
$stream->on('data', array($reader, 'write'));
|
||||
|
||||
$that = $this;
|
||||
$that = $this;
|
||||
|
||||
$auth = $this->auth;
|
||||
$protocolVersion = $this->protocolVersion;
|
||||
|
||||
// authentication requires SOCKS5
|
||||
if ($auth !== null) {
|
||||
$protocolVersion = '5';
|
||||
}
|
||||
|
||||
return $reader->readByte()->then(function ($version) use ($stream, $that, $protocolVersion, $auth, $reader){
|
||||
if ($version === 0x04) {
|
||||
if ($protocolVersion === '5') {
|
||||
throw new UnexpectedValueException('SOCKS4 not allowed due to configuration');
|
||||
}
|
||||
return $that->handleSocks4($stream, $protocolVersion, $reader);
|
||||
} else if ($version === 0x05) {
|
||||
if ($protocolVersion !== null && $protocolVersion !== '5') {
|
||||
throw new UnexpectedValueException('SOCKS5 not allowed due to configuration');
|
||||
}
|
||||
return $that->handleSocks5($stream, $auth, $reader);
|
||||
}
|
||||
throw new UnexpectedValueException('Unexpected/unknown version number');
|
||||
});
|
||||
}
|
||||
|
||||
public function handleSocks4(ConnectionInterface $stream, $protocolVersion, StreamReader $reader)
|
||||
{
|
||||
// suppliying hostnames is only allowed for SOCKS4a (or automatically detected version)
|
||||
$supportsHostname = ($protocolVersion === null || $protocolVersion === '4a');
|
||||
|
||||
$remote = $stream->getRemoteAddress();
|
||||
if ($remote !== null) {
|
||||
// remove transport scheme and prefix socks4:// instead
|
||||
$secure = strpos($remote, 'tls://') === 0;
|
||||
if (($pos = strpos($remote, '://')) !== false) {
|
||||
$remote = substr($remote, $pos + 3);
|
||||
}
|
||||
$remote = 'socks4' . ($secure ? 's' : '') . '://' . $remote;
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
return $reader->readByteAssert(0x01)->then(function () use ($reader) {
|
||||
return $reader->readBinary(array(
|
||||
'port' => 'n',
|
||||
'ipLong' => 'N',
|
||||
'null' => 'C'
|
||||
));
|
||||
})->then(function ($data) use ($reader, $supportsHostname, $remote) {
|
||||
if ($data['null'] !== 0x00) {
|
||||
throw new Exception('Not a null byte');
|
||||
}
|
||||
if ($data['ipLong'] === 0) {
|
||||
throw new Exception('Invalid IP');
|
||||
}
|
||||
if ($data['port'] === 0) {
|
||||
throw new Exception('Invalid port');
|
||||
}
|
||||
if ($data['ipLong'] < 256 && $supportsHostname) {
|
||||
// invalid IP => probably a SOCKS4a request which appends the hostname
|
||||
return $reader->readStringNull()->then(function ($string) use ($data, $remote){
|
||||
return array($string, $data['port'], $remote);
|
||||
});
|
||||
} else {
|
||||
$ip = long2ip($data['ipLong']);
|
||||
return array($ip, $data['port'], $remote);
|
||||
}
|
||||
})->then(function ($target) use ($stream, $that) {
|
||||
return $that->connectTarget($stream, $target)->then(function (ConnectionInterface $remote) use ($stream){
|
||||
$stream->write(pack('C8', 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||||
|
||||
return $remote;
|
||||
}, function($error) use ($stream){
|
||||
$stream->end(pack('C8', 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||||
|
||||
throw $error;
|
||||
});
|
||||
}, function($error) {
|
||||
throw new UnexpectedValueException('SOCKS4 protocol error',0,$error);
|
||||
});
|
||||
}
|
||||
|
||||
public function handleSocks5(ConnectionInterface $stream, $auth=null, StreamReader $reader)
|
||||
{
|
||||
$remote = $stream->getRemoteAddress();
|
||||
if ($remote !== null) {
|
||||
// remove transport scheme and prefix socks5:// instead
|
||||
$secure = strpos($remote, 'tls://') === 0;
|
||||
if (($pos = strpos($remote, '://')) !== false) {
|
||||
$remote = substr($remote, $pos + 3);
|
||||
}
|
||||
$remote = 'socks5' . ($secure ? 's' : '') . '://' . $remote;
|
||||
}
|
||||
|
||||
$that = $this;
|
||||
return $reader->readByte()->then(function ($num) use ($reader) {
|
||||
// $num different authentication mechanisms offered
|
||||
return $reader->readLength($num);
|
||||
})->then(function ($methods) use ($reader, $stream, $auth, &$remote) {
|
||||
if ($auth === null && strpos($methods,"\x00") !== false) {
|
||||
// accept "no authentication"
|
||||
$stream->write(pack('C2', 0x05, 0x00));
|
||||
|
||||
return 0x00;
|
||||
} else if ($auth !== null && strpos($methods,"\x02") !== false) {
|
||||
// username/password authentication (RFC 1929) sub negotiation
|
||||
$stream->write(pack('C2', 0x05, 0x02));
|
||||
return $reader->readByteAssert(0x01)->then(function () use ($reader) {
|
||||
return $reader->readByte();
|
||||
})->then(function ($length) use ($reader) {
|
||||
return $reader->readLength($length);
|
||||
})->then(function ($username) use ($reader, $auth, $stream, &$remote) {
|
||||
return $reader->readByte()->then(function ($length) use ($reader) {
|
||||
return $reader->readLength($length);
|
||||
})->then(function ($password) use ($username, $auth, $stream, &$remote) {
|
||||
// username and password given => authenticate
|
||||
|
||||
// prefix username/password to remote URI
|
||||
if ($remote !== null) {
|
||||
$remote = str_replace('://', '://' . rawurlencode($username) . ':' . rawurlencode($password) . '@', $remote);
|
||||
}
|
||||
|
||||
return $auth($username, $password, $remote)->then(function () use ($stream, $username) {
|
||||
// accept
|
||||
$stream->emit('auth', array($username));
|
||||
$stream->write(pack('C2', 0x01, 0x00));
|
||||
}, function() use ($stream) {
|
||||
// reject => send any code but 0x00
|
||||
$stream->end(pack('C2', 0x01, 0xFF));
|
||||
throw new UnexpectedValueException('Unable to authenticate');
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// reject all offered authentication methods
|
||||
$stream->write(pack('C2', 0x05, 0xFF));
|
||||
throw new UnexpectedValueException('No acceptable authentication mechanism found');
|
||||
}
|
||||
})->then(function ($method) use ($reader, $stream) {
|
||||
return $reader->readBinary(array(
|
||||
'version' => 'C',
|
||||
'command' => 'C',
|
||||
'null' => 'C',
|
||||
'type' => 'C'
|
||||
));
|
||||
})->then(function ($data) use ($reader) {
|
||||
if ($data['version'] !== 0x05) {
|
||||
throw new UnexpectedValueException('Invalid SOCKS version');
|
||||
}
|
||||
if ($data['command'] !== 0x01) {
|
||||
throw new UnexpectedValueException('Only CONNECT requests supported', Server::ERROR_COMMAND_UNSUPPORTED);
|
||||
}
|
||||
// if ($data['null'] !== 0x00) {
|
||||
// throw new UnexpectedValueException('Reserved byte has to be NULL');
|
||||
// }
|
||||
if ($data['type'] === 0x03) {
|
||||
// target hostname string
|
||||
return $reader->readByte()->then(function ($len) use ($reader) {
|
||||
return $reader->readLength($len);
|
||||
});
|
||||
} else if ($data['type'] === 0x01) {
|
||||
// target IPv4
|
||||
return $reader->readLength(4)->then(function ($addr) {
|
||||
return inet_ntop($addr);
|
||||
});
|
||||
} else if ($data['type'] === 0x04) {
|
||||
// target IPv6
|
||||
return $reader->readLength(16)->then(function ($addr) {
|
||||
return inet_ntop($addr);
|
||||
});
|
||||
} else {
|
||||
throw new UnexpectedValueException('Invalid address type', Server::ERROR_ADDRESS_UNSUPPORTED);
|
||||
}
|
||||
})->then(function ($host) use ($reader, &$remote) {
|
||||
return $reader->readBinary(array('port'=>'n'))->then(function ($data) use ($host, &$remote) {
|
||||
return array($host, $data['port'], $remote);
|
||||
});
|
||||
})->then(function ($target) use ($that, $stream) {
|
||||
return $that->connectTarget($stream, $target);
|
||||
}, function($error) use ($stream) {
|
||||
throw new UnexpectedValueException('SOCKS5 protocol error', $error->getCode(), $error);
|
||||
})->then(function (ConnectionInterface $remote) use ($stream) {
|
||||
$stream->write(pack('C4Nn', 0x05, 0x00, 0x00, 0x01, 0, 0));
|
||||
|
||||
return $remote;
|
||||
}, function(Exception $error) use ($stream){
|
||||
$stream->write(pack('C4Nn', 0x05, $error->getCode() === 0 ? Server::ERROR_GENERAL : $error->getCode(), 0x00, 0x01, 0, 0));
|
||||
|
||||
throw $error;
|
||||
});
|
||||
}
|
||||
|
||||
public function connectTarget(ConnectionInterface $stream, array $target)
|
||||
{
|
||||
$uri = $target[0];
|
||||
if (strpos($uri, ':') !== false) {
|
||||
$uri = '[' . $uri . ']';
|
||||
}
|
||||
$uri .= ':' . $target[1];
|
||||
|
||||
// validate URI so a string hostname can not pass excessive URI parts
|
||||
$parts = parse_url('tcp://' . $uri);
|
||||
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || count($parts) !== 3) {
|
||||
return Promise\reject(new InvalidArgumentException('Invalid target URI given'));
|
||||
}
|
||||
|
||||
if (isset($target[2])) {
|
||||
$uri .= '?source=' . rawurlencode($target[2]);
|
||||
}
|
||||
|
||||
$stream->emit('target', $target);
|
||||
$that = $this;
|
||||
$connecting = $this->connector->connect($uri);
|
||||
|
||||
$stream->on('close', function () use ($connecting) {
|
||||
$connecting->cancel();
|
||||
});
|
||||
|
||||
return $connecting->then(function (ConnectionInterface $remote) use ($stream, $that) {
|
||||
$stream->pipe($remote, array('end'=>false));
|
||||
$remote->pipe($stream, array('end'=>false));
|
||||
|
||||
// remote end closes connection => stop reading from local end, try to flush buffer to local and disconnect local
|
||||
$remote->on('end', function() use ($stream, $that) {
|
||||
$stream->emit('shutdown', array('remote', null));
|
||||
$that->endConnection($stream);
|
||||
});
|
||||
|
||||
// local end closes connection => stop reading from remote end, try to flush buffer to remote and disconnect remote
|
||||
$stream->on('end', function() use ($remote, $that) {
|
||||
$that->endConnection($remote);
|
||||
});
|
||||
|
||||
// set bigger buffer size of 100k to improve performance
|
||||
$stream->bufferSize = $remote->bufferSize = 100 * 1024 * 1024;
|
||||
|
||||
return $remote;
|
||||
}, function(Exception $error) {
|
||||
// default to general/unknown error
|
||||
$code = Server::ERROR_GENERAL;
|
||||
|
||||
// map common socket error conditions to limited list of SOCKS error codes
|
||||
if ((defined('SOCKET_EACCES') && $error->getCode() === SOCKET_EACCES) || $error->getCode() === 13) {
|
||||
$code = Server::ERROR_NOT_ALLOWED_BY_RULESET;
|
||||
} elseif ((defined('SOCKET_EHOSTUNREACH') && $error->getCode() === SOCKET_EHOSTUNREACH) || $error->getCode() === 113) {
|
||||
$code = Server::ERROR_HOST_UNREACHABLE;
|
||||
} elseif ((defined('SOCKET_ENETUNREACH') && $error->getCode() === SOCKET_ENETUNREACH) || $error->getCode() === 101) {
|
||||
$code = Server::ERROR_NETWORK_UNREACHABLE;
|
||||
} elseif ((defined('SOCKET_ECONNREFUSED') && $error->getCode() === SOCKET_ECONNREFUSED) || $error->getCode() === 111 || $error->getMessage() === 'Connection refused') {
|
||||
// Socket component does not currently assign an error code for this, so we have to resort to checking the exception message
|
||||
$code = Server::ERROR_CONNECTION_REFUSED;
|
||||
} elseif ((defined('SOCKET_ETIMEDOUT') && $error->getCode() === SOCKET_ETIMEDOUT) || $error->getCode() === 110 || $error instanceof TimeoutException) {
|
||||
// Socket component does not currently assign an error code for this, but we can rely on the TimeoutException
|
||||
$code = Server::ERROR_TTL;
|
||||
}
|
||||
|
||||
throw new UnexpectedValueException('Unable to connect to remote target', $code, $error);
|
||||
});
|
||||
}
|
||||
}
|
||||
149
vendor/clue/socks-react/src/StreamReader.php
vendored
Executable file
149
vendor/clue/socks-react/src/StreamReader.php
vendored
Executable file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace Clue\React\Socks;
|
||||
|
||||
use React\Promise\Deferred;
|
||||
use \InvalidArgumentException;
|
||||
use \UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class StreamReader
|
||||
{
|
||||
const RET_DONE = true;
|
||||
const RET_INCOMPLETE = null;
|
||||
|
||||
private $buffer = '';
|
||||
private $queue = array();
|
||||
|
||||
public function write($data)
|
||||
{
|
||||
$this->buffer .= $data;
|
||||
|
||||
do {
|
||||
$current = reset($this->queue);
|
||||
|
||||
if ($current === false) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* @var $current Closure */
|
||||
|
||||
$ret = $current($this->buffer);
|
||||
|
||||
if ($ret === self::RET_INCOMPLETE) {
|
||||
// current is incomplete, so wait for further data to arrive
|
||||
break;
|
||||
} else {
|
||||
// current is done, remove from list and continue with next
|
||||
array_shift($this->queue);
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
public function readBinary($structure)
|
||||
{
|
||||
$length = 0;
|
||||
$unpack = '';
|
||||
foreach ($structure as $name=>$format) {
|
||||
if ($length !== 0) {
|
||||
$unpack .= '/';
|
||||
}
|
||||
$unpack .= $format . $name;
|
||||
|
||||
if ($format === 'C') {
|
||||
++$length;
|
||||
} else if ($format === 'n') {
|
||||
$length += 2;
|
||||
} else if ($format === 'N') {
|
||||
$length += 4;
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid format given');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->readLength($length)->then(function ($response) use ($unpack) {
|
||||
return unpack($unpack, $response);
|
||||
});
|
||||
}
|
||||
|
||||
public function readLength($bytes)
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
$this->readBufferCallback(function (&$buffer) use ($bytes, $deferred) {
|
||||
if (strlen($buffer) >= $bytes) {
|
||||
$deferred->resolve((string)substr($buffer, 0, $bytes));
|
||||
$buffer = (string)substr($buffer, $bytes);
|
||||
|
||||
return StreamReader::RET_DONE;
|
||||
}
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
public function readByte()
|
||||
{
|
||||
return $this->readBinary(array(
|
||||
'byte' => 'C'
|
||||
))->then(function ($data) {
|
||||
return $data['byte'];
|
||||
});
|
||||
}
|
||||
|
||||
public function readByteAssert($expect)
|
||||
{
|
||||
return $this->readByte()->then(function ($byte) use ($expect) {
|
||||
if ($byte !== $expect) {
|
||||
throw new UnexpectedValueException('Unexpected byte encountered');
|
||||
}
|
||||
return $byte;
|
||||
});
|
||||
}
|
||||
|
||||
public function readStringNull()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$string = '';
|
||||
|
||||
$that = $this;
|
||||
$readOne = function () use (&$readOne, $that, $deferred, &$string) {
|
||||
$that->readByte()->then(function ($byte) use ($deferred, &$string, $readOne) {
|
||||
if ($byte === 0x00) {
|
||||
$deferred->resolve($string);
|
||||
} else {
|
||||
$string .= chr($byte);
|
||||
$readOne();
|
||||
}
|
||||
});
|
||||
};
|
||||
$readOne();
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
public function readBufferCallback(/* callable */ $callable)
|
||||
{
|
||||
if (!is_callable($callable)) {
|
||||
throw new InvalidArgumentException('Given function must be callable');
|
||||
}
|
||||
|
||||
if ($this->queue) {
|
||||
$this->queue []= $callable;
|
||||
} else {
|
||||
$this->queue = array($callable);
|
||||
|
||||
if ($this->buffer !== '') {
|
||||
// this is the first element in the queue and the buffer is filled => trigger write procedure
|
||||
$this->write('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getBuffer()
|
||||
{
|
||||
return $this->buffer;
|
||||
}
|
||||
}
|
||||
403
vendor/clue/socks-react/tests/ClientTest.php
vendored
Executable file
403
vendor/clue/socks-react/tests/ClientTest.php
vendored
Executable file
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use React\Promise\Promise;
|
||||
use Clue\React\Socks\Server;
|
||||
|
||||
class ClientTest extends TestCase
|
||||
{
|
||||
private $loop;
|
||||
|
||||
private $connector;
|
||||
|
||||
/** @var Client */
|
||||
private $client;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->loop = React\EventLoop\Factory::create();
|
||||
$this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
|
||||
$this->client = new Client('127.0.0.1:1080', $this->connector);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithHostAndPort()
|
||||
{
|
||||
$client = new Client('127.0.0.1:9050', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithScheme()
|
||||
{
|
||||
$client = new Client('socks://127.0.0.1:9050', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithHostOnlyAssumesDefaultPort()
|
||||
{
|
||||
$client = new Client('127.0.0.1', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithSecureScheme()
|
||||
{
|
||||
$client = new Client('sockss://127.0.0.1:9050', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithSecureVersionScheme()
|
||||
{
|
||||
$client = new Client('socks5s://127.0.0.1:9050', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithSocksUnixScheme()
|
||||
{
|
||||
$client = new Client('socks+unix:///tmp/socks.socket', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testCtorAcceptsUriWithSocks5UnixScheme()
|
||||
{
|
||||
$client = new Client('socks5+unix:///tmp/socks.socket', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testCtorThrowsForInvalidUri()
|
||||
{
|
||||
new Client('////', $this->connector);
|
||||
}
|
||||
|
||||
public function testValidAuthFromUri()
|
||||
{
|
||||
$this->client = new Client('username:password@127.0.0.1', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidAuthInformation()
|
||||
{
|
||||
new Client(str_repeat('a', 256) . ':test@127.0.0.1', $this->connector);
|
||||
}
|
||||
|
||||
public function testValidAuthAndVersionFromUri()
|
||||
{
|
||||
$this->client = new Client('socks5://username:password@127.0.0.1:9050', $this->connector);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidCanNotSetAuthenticationForSocks4Uri()
|
||||
{
|
||||
$this->client = new Client('socks4://username:password@127.0.0.1:9050', $this->connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidProtocolVersion()
|
||||
{
|
||||
$this->client = new Client('socks3://127.0.0.1:9050', $this->connector);
|
||||
}
|
||||
|
||||
public function testCreateWillConnectToProxy()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=localhost')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('localhost:80');
|
||||
|
||||
$this->assertInstanceOf('\React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testCreateWillConnectToProxyWithFullUri()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080/?hostname=test#fragment')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('localhost:80/?hostname=test#fragment');
|
||||
|
||||
$this->assertInstanceOf('\React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidHostDoesNotConnect()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$promise = $this->client->connect(str_repeat('a', '256') . ':80');
|
||||
|
||||
$this->assertInstanceOf('\React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testCreateWithInvalidPortDoesNotConnect()
|
||||
{
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$promise = $this->client->connect('some-random-site:some-random-port');
|
||||
|
||||
$this->assertInstanceOf('\React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testConnectorRejectsWillRejectConnection()
|
||||
{
|
||||
$promise = \React\Promise\reject(new RuntimeException());
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED));
|
||||
}
|
||||
|
||||
public function testCancelConnectionDuringConnectionWillCancelConnection()
|
||||
{
|
||||
$promise = new Promise(function () { }, function () {
|
||||
throw new \RuntimeException();
|
||||
});
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
$promise->cancel();
|
||||
|
||||
$this->expectPromiseReject($promise);
|
||||
}
|
||||
|
||||
public function testCancelConnectionDuringSessionWillCloseStream()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = new Promise(function ($resolve) use ($stream) { $resolve($stream); });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
$promise->cancel();
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNABORTED));
|
||||
}
|
||||
|
||||
public function testEmitConnectionCloseDuringSessionWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('close');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNRESET));
|
||||
}
|
||||
|
||||
public function testEmitConnectionErrorDuringSessionWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('error', array(new RuntimeException()));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EIO));
|
||||
}
|
||||
|
||||
public function testEmitInvalidSocks4DataDuringSessionWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("HTTP/1.1 400 Bad Request\r\n\r\n"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG));
|
||||
}
|
||||
|
||||
public function testEmitInvalidSocks5DataDuringSessionWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("HTTP/1.1 400 Bad Request\r\n\r\n"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG));
|
||||
}
|
||||
|
||||
public function testEmitSocks5DataErrorDuringSessionWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("\x05\x00" . "\x05\x01\x00\x00"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_ECONNREFUSED));
|
||||
}
|
||||
|
||||
public function testEmitSocks5DataInvalidAddressTypeWillRejectConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x00"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode(SOCKET_EBADMSG));
|
||||
}
|
||||
|
||||
public function testEmitSocks5DataIpv6AddressWillResolveConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->never())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=%3A%3A1')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('[::1]:80');
|
||||
|
||||
$stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x04" . inet_pton('::1') . "\x00\x50"));
|
||||
|
||||
$promise->then($this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testEmitSocks5DataHostnameAddressWillResolveConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->never())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("\x05\x00" . "\x05\x00\x00\x03\x0Agoogle.com\x00\x50"));
|
||||
|
||||
$promise->then($this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function provideConnectionErrors()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
Server::ERROR_GENERAL,
|
||||
SOCKET_ECONNREFUSED
|
||||
),
|
||||
array(
|
||||
Server::ERROR_NOT_ALLOWED_BY_RULESET,
|
||||
SOCKET_EACCES
|
||||
),
|
||||
array(
|
||||
Server::ERROR_NETWORK_UNREACHABLE,
|
||||
SOCKET_ENETUNREACH
|
||||
),
|
||||
array(
|
||||
Server::ERROR_HOST_UNREACHABLE,
|
||||
SOCKET_EHOSTUNREACH
|
||||
),
|
||||
array(
|
||||
Server::ERROR_CONNECTION_REFUSED,
|
||||
SOCKET_ECONNREFUSED
|
||||
),
|
||||
array(
|
||||
Server::ERROR_TTL,
|
||||
SOCKET_ETIMEDOUT
|
||||
),
|
||||
array(
|
||||
Server::ERROR_COMMAND_UNSUPPORTED,
|
||||
SOCKET_EPROTO
|
||||
),
|
||||
array(
|
||||
Server::ERROR_ADDRESS_UNSUPPORTED,
|
||||
SOCKET_EPROTO
|
||||
),
|
||||
array(
|
||||
200,
|
||||
SOCKET_ECONNREFUSED
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideConnectionErrors
|
||||
* @param int $error
|
||||
* @param int $expectedCode
|
||||
*/
|
||||
public function testEmitSocks5DataErrorMapsToExceptionCode($error, $expectedCode)
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
|
||||
$stream->expects($this->once())->method('close');
|
||||
|
||||
$promise = \React\Promise\resolve($stream);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:1080?hostname=google.com')->willReturn($promise);
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:1080', $this->connector);
|
||||
|
||||
$promise = $this->client->connect('google.com:80');
|
||||
|
||||
$stream->emit('data', array("\x05\x00" . "\x05" . chr($error) . "\x00\x00"));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnceWithExceptionCode($expectedCode));
|
||||
}
|
||||
}
|
||||
437
vendor/clue/socks-react/tests/FunctionalTest.php
vendored
Executable file
437
vendor/clue/socks-react/tests/FunctionalTest.php
vendored
Executable file
@@ -0,0 +1,437 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Client;
|
||||
use Clue\React\Socks\Server;
|
||||
use Clue\React\Block;
|
||||
use React\Socket\TimeoutConnector;
|
||||
use React\Socket\SecureConnector;
|
||||
use React\Socket\TcpConnector;
|
||||
use React\Socket\UnixServer;
|
||||
use React\Socket\Connector;
|
||||
|
||||
class FunctionalTest extends TestCase
|
||||
{
|
||||
private $loop;
|
||||
private $connector;
|
||||
private $client;
|
||||
|
||||
private $port;
|
||||
private $server;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->loop = React\EventLoop\Factory::create();
|
||||
|
||||
$socket = new React\Socket\Server(0, $this->loop);
|
||||
$address = $socket->getAddress();
|
||||
if (strpos($address, '://') === false) {
|
||||
$address = 'tcp://' . $address;
|
||||
}
|
||||
$this->port = parse_url($address, PHP_URL_PORT);
|
||||
$this->assertNotEquals(0, $this->port);
|
||||
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
$this->connector = new TcpConnector($this->loop);
|
||||
$this->client = new Client('127.0.0.1:' . $this->port, $this->connector);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnection()
|
||||
{
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionInvalid()
|
||||
{
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com.invalid:80'));
|
||||
}
|
||||
|
||||
public function testConnectionWithIpViaSocks4()
|
||||
{
|
||||
$this->server->setProtocolVersion('4');
|
||||
|
||||
$this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('127.0.0.1:' . $this->port));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionWithHostnameViaSocks4Fails()
|
||||
{
|
||||
$this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionWithInvalidPortFails()
|
||||
{
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:100000'));
|
||||
}
|
||||
|
||||
public function testConnectionWithIpv6ViaSocks4Fails()
|
||||
{
|
||||
$this->client = new Client('socks4://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('[::1]:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocks4a()
|
||||
{
|
||||
$this->server->setProtocolVersion('4a');
|
||||
$this->client = new Client('socks4a://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocks5()
|
||||
{
|
||||
$this->server->setProtocolVersion(5);
|
||||
$this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocksOverTls()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$socket = new \React\Socket\Server('tls://127.0.0.1:0', $this->loop, array('tls' => array(
|
||||
'local_cert' => __DIR__ . '/../examples/localhost.pem',
|
||||
)));
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
|
||||
$this->connector = new Connector($this->loop, array('tls' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false
|
||||
)));
|
||||
$this->client = new Client(str_replace('tls:', 'sockss:', $socket->getAddress()), $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group internet
|
||||
* @requires PHP 5.6
|
||||
*/
|
||||
public function testConnectionSocksOverTlsUsesPeerNameFromSocksUri()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$socket = new \React\Socket\Server('tls://127.0.0.1:0', $this->loop, array('tls' => array(
|
||||
'local_cert' => __DIR__ . '/../examples/localhost.pem',
|
||||
)));
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
|
||||
$this->connector = new Connector($this->loop, array('tls' => array(
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => true
|
||||
)));
|
||||
$this->client = new Client(str_replace('tls:', 'sockss:', $socket->getAddress()), $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocksOverUnix()
|
||||
{
|
||||
if (!in_array('unix', stream_get_transports())) {
|
||||
$this->markTestSkipped('System does not support unix:// scheme');
|
||||
}
|
||||
|
||||
$path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock';
|
||||
$socket = new UnixServer($path, $this->loop);
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
|
||||
$this->connector = new Connector($this->loop);
|
||||
$this->client = new Client('socks+unix://' . $path, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
|
||||
unlink($path);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocks5OverUnix()
|
||||
{
|
||||
if (!in_array('unix', stream_get_transports())) {
|
||||
$this->markTestSkipped('System does not support unix:// scheme');
|
||||
}
|
||||
|
||||
$path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock';
|
||||
$socket = new UnixServer($path, $this->loop);
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
$this->server->setProtocolVersion(5);
|
||||
|
||||
$this->connector = new Connector($this->loop);
|
||||
$this->client = new Client('socks5+unix://' . $path, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
|
||||
unlink($path);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionSocksWithAuthenticationOverUnix()
|
||||
{
|
||||
if (!in_array('unix', stream_get_transports())) {
|
||||
$this->markTestSkipped('System does not support unix:// scheme');
|
||||
}
|
||||
|
||||
$path = sys_get_temp_dir() . '/test' . mt_rand(1000, 9999) . '.sock';
|
||||
$socket = new UnixServer($path, $this->loop);
|
||||
$this->server = new Server($this->loop, $socket);
|
||||
$this->server->setAuthArray(array('name' => 'pass'));
|
||||
|
||||
$this->connector = new Connector($this->loop);
|
||||
$this->client = new Client('socks+unix://name:pass@' . $path, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
|
||||
unlink($path);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationFromUri()
|
||||
{
|
||||
$this->server->setAuthArray(array('name' => 'pass'));
|
||||
|
||||
$this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationCallback()
|
||||
{
|
||||
$called = 0;
|
||||
$that = $this;
|
||||
$this->server->setAuth(function ($name, $pass, $remote) use ($that, &$called) {
|
||||
++$called;
|
||||
$that->assertEquals('name', $name);
|
||||
$that->assertEquals('pass', $pass);
|
||||
$that->assertStringStartsWith('socks5://name:pass@127.0.0.1:', $remote);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
$this->assertEquals(1, $called);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationCallbackWillNotBeInvokedIfClientsSendsNoAuth()
|
||||
{
|
||||
$called = 0;
|
||||
$this->server->setAuth(function () use (&$called) {
|
||||
++$called;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->client = new Client('127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'));
|
||||
$this->assertEquals(0, $called);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationFromUriEncoded()
|
||||
{
|
||||
$this->server->setAuthArray(array('name' => 'p@ss:w0rd'));
|
||||
|
||||
$this->client = new Client(rawurlencode('name') . ':' . rawurlencode('p@ss:w0rd') . '@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationFromUriWithOnlyUserAndNoPassword()
|
||||
{
|
||||
$this->server->setAuthArray(array('empty' => ''));
|
||||
|
||||
$this->client = new Client('empty@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationEmptyPassword()
|
||||
{
|
||||
$this->server->setAuthArray(array('user' => ''));
|
||||
$this->client = new Client('user@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectionAuthenticationUnused()
|
||||
{
|
||||
$this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
public function testConnectionInvalidProtocolDoesNotMatchSocks5()
|
||||
{
|
||||
$this->server->setProtocolVersion(5);
|
||||
$this->client = new Client('socks4a://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_ECONNRESET);
|
||||
}
|
||||
|
||||
public function testConnectionInvalidProtocolDoesNotMatchSocks4()
|
||||
{
|
||||
$this->server->setProtocolVersion(4);
|
||||
$this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_ECONNRESET);
|
||||
}
|
||||
|
||||
public function testConnectionInvalidNoAuthentication()
|
||||
{
|
||||
$this->server->setAuthArray(array('name' => 'pass'));
|
||||
|
||||
$this->client = new Client('socks5://127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_EACCES);
|
||||
}
|
||||
|
||||
public function testConnectionInvalidAuthenticationMismatch()
|
||||
{
|
||||
$this->server->setAuthArray(array('name' => 'pass'));
|
||||
|
||||
$this->client = new Client('user:pass@127.0.0.1:' . $this->port, $this->connector);
|
||||
|
||||
$this->assertRejectPromise($this->client->connect('www.google.com:80'), null, SOCKET_EACCES);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectorOkay()
|
||||
{
|
||||
$this->assertResolveStream($this->client->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectorInvalidDomain()
|
||||
{
|
||||
$this->assertRejectPromise($this->client->connect('www.google.commm:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectorCancelConnection()
|
||||
{
|
||||
$promise = $this->client->connect('www.google.com:80');
|
||||
$promise->cancel();
|
||||
|
||||
$this->assertRejectPromise($promise);
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testConnectorInvalidUnboundPortTimeout()
|
||||
{
|
||||
// time out the connection attempt in 0.1s (as expected)
|
||||
$tcp = new TimeoutConnector($this->client, 0.1, $this->loop);
|
||||
|
||||
$this->assertRejectPromise($tcp->connect('www.google.com:8080'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testSecureConnectorOkay()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$ssl = new SecureConnector($this->client, $this->loop);
|
||||
|
||||
$this->assertResolveStream($ssl->connect('www.google.com:443'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testSecureConnectorToBadSslWithVerifyFails()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$ssl = new SecureConnector($this->client, $this->loop, array('verify_peer' => true));
|
||||
|
||||
$this->assertRejectPromise($ssl->connect('self-signed.badssl.com:443'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testSecureConnectorToBadSslWithoutVerifyWorks()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$ssl = new SecureConnector($this->client, $this->loop, array('verify_peer' => false));
|
||||
|
||||
$this->assertResolveStream($ssl->connect('self-signed.badssl.com:443'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testSecureConnectorInvalidPlaintextIsNotSsl()
|
||||
{
|
||||
if (!function_exists('stream_socket_enable_crypto')) {
|
||||
$this->markTestSkipped('Required function does not exist in your environment (HHVM?)');
|
||||
}
|
||||
|
||||
$ssl = new SecureConnector($this->client, $this->loop);
|
||||
|
||||
$this->assertRejectPromise($ssl->connect('www.google.com:80'));
|
||||
}
|
||||
|
||||
/** @group internet */
|
||||
public function testSecureConnectorInvalidUnboundPortTimeout()
|
||||
{
|
||||
$ssl = new SecureConnector($this->client, $this->loop);
|
||||
|
||||
// time out the connection attempt in 0.1s (as expected)
|
||||
$ssl = new TimeoutConnector($ssl, 0.1, $this->loop);
|
||||
|
||||
$this->assertRejectPromise($ssl->connect('www.google.com:8080'));
|
||||
}
|
||||
|
||||
private function assertResolveStream($promise)
|
||||
{
|
||||
$this->expectPromiseResolve($promise);
|
||||
|
||||
$promise->then(function ($stream) {
|
||||
$stream->close();
|
||||
});
|
||||
|
||||
Block\await($promise, $this->loop, 2.0);
|
||||
}
|
||||
|
||||
private function assertRejectPromise($promise, $message = null, $code = null)
|
||||
{
|
||||
$this->expectPromiseReject($promise);
|
||||
|
||||
if (method_exists($this, 'expectException')) {
|
||||
$this->expectException('Exception');
|
||||
if ($message !== null) {
|
||||
$this->expectExceptionMessage($message);
|
||||
}
|
||||
if ($code !== null) {
|
||||
$this->expectExceptionCode($code);
|
||||
}
|
||||
} else {
|
||||
$this->setExpectedException('Exception', $message, $code);
|
||||
}
|
||||
|
||||
Block\await($promise, $this->loop, 2.0);
|
||||
}
|
||||
}
|
||||
428
vendor/clue/socks-react/tests/ServerTest.php
vendored
Executable file
428
vendor/clue/socks-react/tests/ServerTest.php
vendored
Executable file
@@ -0,0 +1,428 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\Server;
|
||||
use React\Promise\Promise;
|
||||
use React\Promise\Timer\TimeoutException;
|
||||
|
||||
class ServerTest extends TestCase
|
||||
{
|
||||
/** @var Server */
|
||||
private $server;
|
||||
private $connector;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$socket = $this->getMockBuilder('React\Socket\ServerInterface')
|
||||
->getMock();
|
||||
|
||||
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')
|
||||
->getMock();
|
||||
|
||||
$this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')
|
||||
->getMock();
|
||||
|
||||
$this->server = new Server($loop, $socket, $this->connector);
|
||||
}
|
||||
|
||||
public function testSetProtocolVersion()
|
||||
{
|
||||
$this->server->setProtocolVersion(4);
|
||||
$this->server->setProtocolVersion('4a');
|
||||
$this->server->setProtocolVersion(5);
|
||||
$this->server->setProtocolVersion(null);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testSetInvalidProtocolVersion()
|
||||
{
|
||||
$this->server->setProtocolVersion(6);
|
||||
}
|
||||
|
||||
public function testSetAuthArray()
|
||||
{
|
||||
$this->server->setAuthArray(array());
|
||||
|
||||
$this->server->setAuthArray(array(
|
||||
'name1' => 'password1',
|
||||
'name2' => 'password2'
|
||||
));
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testSetAuthInvalid()
|
||||
{
|
||||
$this->server->setAuth(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException UnexpectedValueException
|
||||
*/
|
||||
public function testUnableToSetAuthIfProtocolDoesNotSupportAuth()
|
||||
{
|
||||
$this->server->setProtocolVersion(4);
|
||||
|
||||
$this->server->setAuthArray(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException UnexpectedValueException
|
||||
*/
|
||||
public function testUnableToSetProtocolWhichDoesNotSupportAuth()
|
||||
{
|
||||
$this->server->setAuthArray(array());
|
||||
|
||||
// this is okay
|
||||
$this->server->setProtocolVersion(5);
|
||||
|
||||
$this->server->setProtocolVersion(4);
|
||||
}
|
||||
|
||||
public function testConnectWillCreateConnection()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80));
|
||||
|
||||
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testConnectWillCreateConnectionWithSourceUri()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('google.com:80?source=socks5%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80, 'socks5://10.20.30.40:5060'));
|
||||
|
||||
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
|
||||
}
|
||||
|
||||
public function testConnectWillRejectIfConnectionFails()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
|
||||
|
||||
$promise = new Promise(function ($_, $reject) { $reject(new \RuntimeException()); });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80));
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testConnectWillCancelConnectionIfStreamCloses()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('close'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { }, function () {
|
||||
throw new \RuntimeException();
|
||||
});
|
||||
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80));
|
||||
|
||||
$stream->emit('close');
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function testConnectWillAbortIfPromiseIsCanceled()
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
|
||||
|
||||
$promise = new Promise(function () { }, function () {
|
||||
throw new \RuntimeException();
|
||||
});
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('google.com:80')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80));
|
||||
|
||||
$promise->cancel();
|
||||
|
||||
$promise->then(null, $this->expectCallableOnce());
|
||||
}
|
||||
|
||||
public function provideConnectionErrors()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
new RuntimeException('', SOCKET_EACCES),
|
||||
Server::ERROR_NOT_ALLOWED_BY_RULESET
|
||||
),
|
||||
array(
|
||||
new RuntimeException('', SOCKET_ENETUNREACH),
|
||||
Server::ERROR_NETWORK_UNREACHABLE
|
||||
),
|
||||
array(
|
||||
new RuntimeException('', SOCKET_EHOSTUNREACH),
|
||||
Server::ERROR_HOST_UNREACHABLE,
|
||||
),
|
||||
array(
|
||||
new RuntimeException('', SOCKET_ECONNREFUSED),
|
||||
Server::ERROR_CONNECTION_REFUSED
|
||||
),
|
||||
array(
|
||||
new RuntimeException('Connection refused'),
|
||||
Server::ERROR_CONNECTION_REFUSED
|
||||
),
|
||||
array(
|
||||
new RuntimeException('', SOCKET_ETIMEDOUT),
|
||||
Server::ERROR_TTL
|
||||
),
|
||||
array(
|
||||
new TimeoutException(1.0),
|
||||
Server::ERROR_TTL
|
||||
),
|
||||
array(
|
||||
new RuntimeException(),
|
||||
Server::ERROR_GENERAL
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideConnectionErrors
|
||||
* @param Exception $error
|
||||
* @param int $expectedCode
|
||||
*/
|
||||
public function testConnectWillReturnMappedSocks5ErrorCodeFromConnector($error, $expectedCode)
|
||||
{
|
||||
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
|
||||
|
||||
$promise = \React\Promise\reject($error);
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->willReturn($promise);
|
||||
|
||||
$promise = $this->server->connectTarget($stream, array('google.com', 80));
|
||||
|
||||
$code = null;
|
||||
$promise->then(null, function ($error) use (&$code) {
|
||||
$code = $error->getCode();
|
||||
});
|
||||
|
||||
$this->assertEquals($expectedCode, $code);
|
||||
}
|
||||
|
||||
public function testHandleSocksConnectionWillEndOnInvalidData()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
|
||||
$connection->expects($this->once())->method('pause');
|
||||
$connection->expects($this->once())->method('end');
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array('asdasdasdasdasd'));
|
||||
}
|
||||
|
||||
public function testHandleSocks4ConnectionWithIpv4WillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . pack('N', ip2long('127.0.0.1')) . "\x00"));
|
||||
}
|
||||
|
||||
public function testHandleSocks4aConnectionWithHostnameWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00"));
|
||||
}
|
||||
|
||||
public function testHandleSocks4aConnectionWithHostnameAndSourceAddressWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'getRemoteAddress'))->getMock();
|
||||
$connection->expects($this->once())->method('getRemoteAddress')->willReturn('tcp://10.20.30.40:5060');
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('example.com:80?source=socks4%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00"));
|
||||
}
|
||||
|
||||
public function testHandleSocks4aConnectionWithSecureTlsSourceAddressWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'getRemoteAddress'))->getMock();
|
||||
$connection->expects($this->once())->method('getRemoteAddress')->willReturn('tls://10.20.30.40:5060');
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('example.com:80?source=socks4s%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00"));
|
||||
}
|
||||
|
||||
public function testHandleSocks4aConnectionWithInvalidHostnameWillNotEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
|
||||
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "tls://example.com:80?" . "\x00"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithIpv4WillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithIpv4AndSourceAddressWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write', 'getRemoteAddress'))->getMock();
|
||||
$connection->expects($this->once())->method('getRemoteAddress')->willReturn('tcp://10.20.30.40:5060');
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80?source=socks5%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithSecureTlsIpv4AndSourceAddressWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write', 'getRemoteAddress'))->getMock();
|
||||
$connection->expects($this->once())->method('getRemoteAddress')->willReturn('tls://10.20.30.40:5060');
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80?source=socks5s%3A%2F%2F10.20.30.40%3A5060')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x01" . pack('N', ip2long('127.0.0.1')) . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithIpv6WillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('[::1]:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x04" . inet_pton('::1') . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithHostnameWillEstablishOutgoingConnection()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { });
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x0B" . "example.com" . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithConnectorRefusedWillReturnReturnRefusedError()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$promise = \React\Promise\reject(new RuntimeException('Connection refused'));
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x05" . "\x00\x01\x00\x00\x00\x00\x00\x00"));
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x0B" . "example.com" . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5UdpCommandWillNotEstablishOutgoingConnectionAndReturnCommandError()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x07" . "\x00\x01\x00\x00\x00\x00\x00\x00"));
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x03\x00\x03\x0B" . "example.com" . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocks5ConnectionWithInvalidHostnameWillNotEstablishOutgoingConnectionAndReturnGeneralError()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
|
||||
|
||||
$this->connector->expects($this->never())->method('connect');
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->expects($this->exactly(2))->method('write')->withConsecutive(array("\x05\x00"), array("\x05\x01" . "\x00\x01\x00\x00\x00\x00\x00\x00"));
|
||||
|
||||
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x15" . "tls://example.com:80?" . "\x00\x50"));
|
||||
}
|
||||
|
||||
public function testHandleSocksConnectionWillCancelOutputConnectionIfIncomingCloses()
|
||||
{
|
||||
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
|
||||
|
||||
$promise = new Promise(function () { }, $this->expectCallableOnce());
|
||||
|
||||
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:80')->willReturn($promise);
|
||||
|
||||
$this->server->onConnection($connection);
|
||||
|
||||
$connection->emit('data', array("\x04\x01" . "\x00\x50" . pack('N', ip2long('127.0.0.1')) . "\x00"));
|
||||
$connection->emit('close');
|
||||
}
|
||||
|
||||
public function testUnsetAuth()
|
||||
{
|
||||
$this->server->unsetAuth();
|
||||
$this->server->unsetAuth();
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
82
vendor/clue/socks-react/tests/StreamReaderTest.php
vendored
Executable file
82
vendor/clue/socks-react/tests/StreamReaderTest.php
vendored
Executable file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
use Clue\React\Socks\StreamReader;
|
||||
|
||||
class StreamReaderTest extends TestCase
|
||||
{
|
||||
private $reader;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->reader = new StreamReader();
|
||||
}
|
||||
|
||||
public function testReadByteAssertCorrect()
|
||||
{
|
||||
$this->reader->readByteAssert(0x01)->then($this->expectCallableOnce(0x01));
|
||||
|
||||
$this->reader->write("\x01");
|
||||
}
|
||||
|
||||
public function testReadByteAssertInvalid()
|
||||
{
|
||||
$this->reader->readByteAssert(0x02)->then(null, $this->expectCallableOnce());
|
||||
|
||||
$this->reader->write("\x03");
|
||||
}
|
||||
|
||||
public function testReadStringNull()
|
||||
{
|
||||
$this->reader->readStringNull()->then($this->expectCallableOnce('hello'));
|
||||
|
||||
$this->reader->write("hello\x00");
|
||||
}
|
||||
|
||||
public function testReadStringLength()
|
||||
{
|
||||
$this->reader->readLength(5)->then($this->expectCallableOnce('hello'));
|
||||
|
||||
$this->reader->write('he');
|
||||
$this->reader->write('ll');
|
||||
$this->reader->write('o ');
|
||||
|
||||
$this->assertEquals(' ', $this->reader->getBuffer());
|
||||
}
|
||||
|
||||
public function testReadBuffered()
|
||||
{
|
||||
$this->reader->write('hello');
|
||||
|
||||
$this->reader->readLength(5)->then($this->expectCallableOnce('hello'));
|
||||
|
||||
$this->assertEquals('', $this->reader->getBuffer());
|
||||
}
|
||||
|
||||
public function testSequence()
|
||||
{
|
||||
$this->reader->readByte()->then($this->expectCallableOnce(ord('h')));
|
||||
$this->reader->readByteAssert(ord('e'))->then($this->expectCallableOnce(ord('e')));
|
||||
$this->reader->readLength(4)->then($this->expectCallableOnce('llo '));
|
||||
$this->reader->readBinary(array('w'=>'C', 'o' => 'C'))->then($this->expectCallableOnce(array('w' => ord('w'), 'o' => ord('o'))));
|
||||
|
||||
$this->reader->write('hello world');
|
||||
|
||||
$this->assertEquals('rld', $this->reader->getBuffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidStructure()
|
||||
{
|
||||
$this->reader->readBinary(array('invalid' => 'y'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidCallback()
|
||||
{
|
||||
$this->reader->readBufferCallback(array());
|
||||
}
|
||||
}
|
||||
103
vendor/clue/socks-react/tests/bootstrap.php
vendored
Executable file
103
vendor/clue/socks-react/tests/bootstrap.php
vendored
Executable file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
(include_once __DIR__.'/../vendor/autoload.php') OR die(PHP_EOL.'ERROR: composer autoloader not found, run "composer install" or see README for instructions'.PHP_EOL);
|
||||
|
||||
class TestCase extends PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected function expectCallableOnce()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
|
||||
|
||||
if (func_num_args() > 0) {
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->equalTo(func_get_arg(0)));
|
||||
} else {
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke');
|
||||
}
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableNever()
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->never())
|
||||
->method('__invoke');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnceWithExceptionCode($code)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->callback(function ($e) use ($code) {
|
||||
return $e->getCode() === $code;
|
||||
}));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
protected function expectCallableOnceParameter($type)
|
||||
{
|
||||
$mock = $this->createCallableMock();
|
||||
$mock
|
||||
->expects($this->once())
|
||||
->method('__invoke')
|
||||
->with($this->isInstanceOf($type));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://github.com/reactphp/react/blob/master/tests/React/Tests/Socket/TestCase.php (taken from reactphp/react)
|
||||
*/
|
||||
protected function createCallableMock()
|
||||
{
|
||||
return $this->getMockBuilder('CallableStub')->getMock();
|
||||
}
|
||||
|
||||
protected function expectPromiseResolve($promise)
|
||||
{
|
||||
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
|
||||
|
||||
$that = $this;
|
||||
$promise->then(null, function($error) use ($that) {
|
||||
$that->assertNull($error);
|
||||
$that->fail('promise rejected');
|
||||
});
|
||||
$promise->then($this->expectCallableOnce(), $this->expectCallableNever());
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
protected function expectPromiseReject($promise)
|
||||
{
|
||||
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
|
||||
|
||||
$that = $this;
|
||||
$promise->then(function($value) use ($that) {
|
||||
$that->assertNull($value);
|
||||
$that->fail('promise resolved');
|
||||
});
|
||||
|
||||
$promise->then($this->expectCallableNever(), $this->expectCallableOnce());
|
||||
|
||||
return $promise;
|
||||
}
|
||||
}
|
||||
|
||||
class CallableStub
|
||||
{
|
||||
public function __invoke()
|
||||
{
|
||||
}
|
||||
}
|
||||
445
vendor/composer/ClassLoader.php
vendored
Executable file
445
vendor/composer/ClassLoader.php
vendored
Executable file
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
56
vendor/composer/LICENSE
vendored
Executable file
56
vendor/composer/LICENSE
vendored
Executable file
@@ -0,0 +1,56 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: Composer
|
||||
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
|
||||
Source: https://github.com/composer/composer
|
||||
|
||||
Files: *
|
||||
Copyright: 2016, Nils Adermann <naderman@naderman.de>
|
||||
2016, Jordi Boggiano <j.boggiano@seld.be>
|
||||
License: Expat
|
||||
|
||||
Files: src/Composer/Util/TlsHelper.php
|
||||
Copyright: 2016, Nils Adermann <naderman@naderman.de>
|
||||
2016, Jordi Boggiano <j.boggiano@seld.be>
|
||||
2013, Evan Coury <me@evancoury.com>
|
||||
License: Expat and BSD-2-Clause
|
||||
|
||||
License: BSD-2-Clause
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
.
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
License: Expat
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
9
vendor/composer/autoload_classmap.php
vendored
Executable file
9
vendor/composer/autoload_classmap.php
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
16
vendor/composer/autoload_files.php
vendored
Executable file
16
vendor/composer/autoload_files.php
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||
'972fda704d680a3a53c68e34e193cb22' => $vendorDir . '/react/promise-timer/src/functions_include.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'ebf8799635f67b5d7248946fe2154f4a' => $vendorDir . '/ringcentral/psr7/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
);
|
||||
10
vendor/composer/autoload_namespaces.php
vendored
Executable file
10
vendor/composer/autoload_namespaces.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Evenement' => array($vendorDir . '/evenement/evenement/src'),
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user