diff options
Diffstat (limited to 'vendor/textalk/websocket')
89 files changed, 8648 insertions, 0 deletions
diff --git a/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d402046 --- /dev/null +++ b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,21 @@ +--- +name: Bug report +about: Use this if you believe there is a bug in this repo +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +Please provide a clear and concise description of the suspected issue. + +**How to reproduce** +If possible, provide information - possibly including code snippets - on how to reproduce the issue. + +**Logs** +If possible, provide logs that indicate the issue. See https://github.com/Textalk/websocket-php/blob/master/docs/Examples.md#echo-logger on how to use the EchoLog. + +**Versions** +* Version of this library +* PHP version diff --git a/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..ce777f6 --- /dev/null +++ b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for this library +title: '' +labels: feature request +assignees: '' + +--- + +**Is it within the scope of this library?** +Consider and describe why the feature would be beneficial in this library, and not implemented as a separate project using this as a dependency. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. diff --git a/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md new file mode 100644 index 0000000..fe5cc8d --- /dev/null +++ b/vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md @@ -0,0 +1,10 @@ +--- +name: Other issue +about: Use this for other issues +title: '' +labels: '' +assignees: '' + +--- + +**Describe your issue** diff --git a/vendor/textalk/websocket/.github/workflows/acceptance.yml b/vendor/textalk/websocket/.github/workflows/acceptance.yml new file mode 100644 index 0000000..7bab97c --- /dev/null +++ b/vendor/textalk/websocket/.github/workflows/acceptance.yml @@ -0,0 +1,97 @@ +name: Acceptance + +on: [push, pull_request] + +jobs: + test-7-2: + runs-on: ubuntu-latest + name: Test PHP 7.2 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 7.2 + uses: shivammathur/setup-php@v2 + with: + php-version: '7.2' + - name: Composer + run: make install + - name: Test + run: make test + + test-7-3: + runs-on: ubuntu-latest + name: Test PHP 7.3 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 7.3 + uses: shivammathur/setup-php@v2 + with: + php-version: '7.3' + - name: Composer + run: make install + - name: Test + run: make test + + test-7-4: + runs-on: ubuntu-latest + name: Test PHP 7.4 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 7.4 + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + - name: Composer + run: make install + - name: Test + run: make test + + test-8-0: + runs-on: ubuntu-latest + name: Test PHP 8.0 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 8.0 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + - name: Composer + run: make install + - name: Test + run: make test + + cs-check: + runs-on: ubuntu-latest + name: Code standard + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 8.0 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + - name: Composer + run: make install + - name: Code standard + run: make cs-check + + coverage: + runs-on: ubuntu-latest + name: Code coverage + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up PHP 8.0 + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + extensions: xdebug + - name: Composer + run: make install + - name: Code coverage + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: make coverage diff --git a/vendor/textalk/websocket/.gitignore b/vendor/textalk/websocket/.gitignore new file mode 100644 index 0000000..379ab4b --- /dev/null +++ b/vendor/textalk/websocket/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +.phpunit.result.cache +build/ +composer.lock +composer.phar +vendor/
\ No newline at end of file diff --git a/vendor/textalk/websocket/COPYING.md b/vendor/textalk/websocket/COPYING.md new file mode 100644 index 0000000..ba96480 --- /dev/null +++ b/vendor/textalk/websocket/COPYING.md @@ -0,0 +1,16 @@ +# Websocket: License + +Websocket PHP is free software released under the following license: + +[ISC License](http://en.wikipedia.org/wiki/ISC_license) + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without +fee is hereby granted, provided that the above copyright notice and this permission notice appear +in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/vendor/textalk/websocket/Makefile b/vendor/textalk/websocket/Makefile new file mode 100644 index 0000000..930a9ed --- /dev/null +++ b/vendor/textalk/websocket/Makefile @@ -0,0 +1,32 @@ +install: composer.phar + ./composer.phar install + +update: composer.phar + ./composer.phar self-update + ./composer.phar update + +test: composer.lock + ./vendor/bin/phpunit + +cs-check: composer.lock + ./vendor/bin/phpcs --standard=codestandard.xml lib tests examples + +coverage: composer.lock build + XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml + ./vendor/bin/php-coveralls -v + +composer.phar: + curl -s http://getcomposer.org/installer | php + +composer.lock: composer.phar + ./composer.phar --no-interaction install + +vendor/bin/phpunit: install + +build: + mkdir build + +clean: + rm composer.phar + rm -r vendor + rm -r build diff --git a/vendor/textalk/websocket/README.md b/vendor/textalk/websocket/README.md new file mode 100644 index 0000000..921efef --- /dev/null +++ b/vendor/textalk/websocket/README.md @@ -0,0 +1,67 @@ +# Websocket Client and Server for PHP + +[![Build Status](https://github.com/Textalk/websocket-php/actions/workflows/acceptance.yml/badge.svg)](https://github.com/Textalk/websocket-php/actions) +[![Coverage Status](https://coveralls.io/repos/github/Textalk/websocket-php/badge.svg?branch=master)](https://coveralls.io/github/Textalk/websocket-php) + +This library contains WebSocket client and server for PHP. + +The client and server provides methods for reading and writing to WebSocket streams. +It does not include convenience operations such as listeners and implicit error handling. + +## Documentation + +- [Client](docs/Client.md) +- [Server](docs/Server.md) +- [Message](docs/Message.md) +- [Examples](docs/Examples.md) +- [Changelog](docs/Changelog.md) +- [Contributing](docs/Contributing.md) + +## Installing + +Preferred way to install is with [Composer](https://getcomposer.org/). +``` +composer require textalk/websocket +``` + +* Current version support PHP versions `^7.2|8.0`. +* For PHP `7.1` support use version `1.4`. +* For PHP `^5.4` and `7.0` support use version `1.3`. + +## Client + +The [client](docs/Client.md) can read and write on a WebSocket stream. +It internally supports Upgrade handshake and implicit close and ping/pong operations. + +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); +$client->text("Hello WebSocket.org!"); +echo $client->receive(); +$client->close(); +``` + +## Server + +The library contains a rudimentary single stream/single thread [server](docs/Server.md). +It internally supports Upgrade handshake and implicit close and ping/pong operations. + +Note that it does **not** support threading or automatic association ot continuous client requests. +If you require this kind of server behavior, you need to build it on top of provided server implementation. + +```php +$server = new WebSocket\Server(); +$server->accept(); +$message = $server->receive(); +$server->text($message); +$server->close(); +``` + +### License and Contributors + +[ISC License](COPYING.md) + +Fredrik Liljegren, Armen Baghumian Sankbarani, Ruslan Bekenev, +Joshua Thijssen, Simon Lipp, Quentin Bellus, Patrick McCarren, swmcdonnell, +Ignas Bernotas, Mark Herhold, Andreas Palm, Sören Jensen, pmaasz, Alexey Stavrov, +Michael Slezak, Pierre Seznec, rmeisler, Nickolay V. Shmyrev, Christoph Kempen, +Marc Roberts, Antonio Mora, Simon Podlipsky. diff --git a/vendor/textalk/websocket/codestandard.xml b/vendor/textalk/websocket/codestandard.xml new file mode 100644 index 0000000..bb1cd26 --- /dev/null +++ b/vendor/textalk/websocket/codestandard.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<ruleset name="PSR with exception"> + <arg name="encoding" value="UTF-8"/> + <arg name="report" value="full"/> + <arg name="colors"/> + + <rule ref="PSR1"/> + <rule ref="PSR12"/> +</ruleset>
\ No newline at end of file diff --git a/vendor/textalk/websocket/composer.json b/vendor/textalk/websocket/composer.json new file mode 100644 index 0000000..9bc0dcc --- /dev/null +++ b/vendor/textalk/websocket/composer.json @@ -0,0 +1,34 @@ +{ + "name": "textalk/websocket", + "description": "WebSocket client and server", + "license": "ISC", + "type": "library", + "authors": [ + { + "name": "Fredrik Liljegren" + }, + { + "name": "Sören Jensen", + "email": "soren@abicart.se" + } + ], + "autoload": { + "psr-4": { + "WebSocket\\": "lib" + } + }, + "autoload-dev": { + "psr-4": { + "WebSocket\\": "tests/mock" + } + }, + "require": { + "php": "^7.2 | ^8.0", + "psr/log": "^1 | ^2 | ^3" + }, + "require-dev": { + "phpunit/phpunit": "^8.0|^9.0", + "php-coveralls/php-coveralls": "^2.0", + "squizlabs/php_codesniffer": "^3.5" + } +} diff --git a/vendor/textalk/websocket/docs/Changelog.md b/vendor/textalk/websocket/docs/Changelog.md new file mode 100644 index 0000000..6a45453 --- /dev/null +++ b/vendor/textalk/websocket/docs/Changelog.md @@ -0,0 +1,130 @@ +[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • Changelog • [Contributing](Contributing.md) + +# Websocket: Changelog + +## `v1.5` + + > PHP version `^7.2|^8.0` + +### `1.5.5` + + * Support for psr/log v2 and v3 (@simPod) + * GitHub Actions replaces Travis (@sirn-se) + +### `1.5.4` + + * Keep open connection on read timeout (@marcroberts) + +### `1.5.3` + + * Fix for persistent connection (@sirn-se) + +### `1.5.2` + + * Fix for getName() method (@sirn-se) + +### `1.5.1` + + * Fix for persistent connections (@rmeisler) + +### `1.5.0` + + * Convenience send methods; text(), binary(), ping(), pong() (@sirn-se) + * Optional Message instance as receive() method return (@sirn-se) + * Opcode filter for receive() method (@sirn-se) + * Added PHP `8.0` support (@webpatser) + * Dropped PHP `7.1` support (@sirn-se) + * Fix for unordered fragmented messages (@sirn-se) + * Improved error handling on stream calls (@sirn-se) + * Various code re-write (@sirn-se) + +## `v1.4` + + > PHP version `^7.1` + +#### `1.4.3` + + * Solve stream closure/get meta conflict (@sirn-se) + * Examples and documentation overhaul (@sirn-se) + +#### `1.4.2` + + * Force stream close on read error (@sirn-se) + * Authorization headers line feed (@sirn-se) + * Documentation (@matias-pool, @sirn-se) + +#### `1.4.1` + + * Ping/Pong, handled internally to avoid breaking fragmented messages (@nshmyrev, @sirn-se) + * Fix for persistent connections (@rmeisler) + * Fix opcode bitmask (@peterjah) + +#### `1.4.0` + + * Dropped support of old PHP versions (@sirn-se) + * Added PSR-3 Logging support (@sirn-se) + * Persistent connection option (@slezakattack) + * TimeoutException on connection time out (@slezakattack) + +## `v1.3` + + > PHP version `^5.4` and `^7.0` + +#### `1.3.1` + + * Allow control messages without payload (@Logioniz) + * Error code in ConnectionException (@sirn-se) + +#### `1.3.0` + + * Implements ping/pong frames (@pmccarren @Logioniz) + * Close behaviour (@sirn-se) + * Various fixes concerning connection handling (@sirn-se) + * Overhaul of Composer, Travis and Coveralls setup, PSR code standard and unit tests (@sirn-se) + +## `v1.2` + + > PHP version `^5.4` and `^7.0` + +#### `1.2.0` + + * Adding stream context options (to set e.g. SSL `allow_self_signed`). + +## `v1.1` + + > PHP version `^5.4` and `^7.0` + +#### `1.1.2` + + * Fixed error message on broken frame. + +#### `1.1.1` + + * Adding license information. + +#### `1.1.0` + + * Supporting huge payloads. + +## `v1.0` + + > PHP version `^5.4` and `^7.0` + +#### `1.0.3` + + * Bugfix: Correcting address in error-message + +#### `1.0.2` + + * Bugfix: Add port in request-header. + +#### `1.0.1` + + * Fixing a bug from empty payloads. + +#### `1.0.0` + + * Release as production ready. + * Adding option to set/override headers. + * Supporting basic authentication from user:pass in URL. + diff --git a/vendor/textalk/websocket/docs/Client.md b/vendor/textalk/websocket/docs/Client.md new file mode 100644 index 0000000..e6154b6 --- /dev/null +++ b/vendor/textalk/websocket/docs/Client.md @@ -0,0 +1,137 @@ +Client • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md) + +# Websocket: Client + +The client can read and write on a WebSocket stream. +It internally supports Upgrade handshake and implicit close and ping/pong operations. + +## Class synopsis + +```php +WebSocket\Client { + + public __construct(string $uri, array $options = []) + public __destruct() + public __toString() : string + + public text(string $payload) : void + public binary(string $payload) : void + public ping(string $payload = '') : void + public pong(string $payload = '') : void + public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void + public receive() : mixed + public close(int $status = 1000, mixed $message = 'ttfn') : mixed + + public getName() : string|null + public getPier() : string|null + public getLastOpcode() : string + public getCloseStatus() : int + public isConnected() : bool + public setTimeout(int $seconds) : void + public setFragmentSize(int $fragment_size) : self + public getFragmentSize() : int + public setLogger(Psr\Log\LoggerInterface $logger = null) : void +} +``` + +## Examples + +### Simple send-receive operation + +This example send a single message to a server, and output the response. + +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); +$client->text("Hello WebSocket.org!"); +echo $client->receive(); +$client->close(); +``` + +### Listening to a server + +To continuously listen to incoming messages, you need to put the receive operation within a loop. +Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out. +By consuming exceptions, the code will re-connect the socket in next loop iteration. + +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); +while (true) { + try { + $message = $client->receive(); + // Act on received message + // Break while loop to stop listening + } catch (\WebSocket\ConnectionException $e) { + // Possibly log errors + } +} +$client->close(); +``` + +### Filtering received messages + +By default the `receive()` method return messages of 'text' and 'binary' opcode. +The filter option allows you to specify which message types to return. + +```php +$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text']]); +$client->receive(); // Only return 'text' messages + +$client = new WebSocket\Client("ws://echo.websocket.org/", ['filter' => ['text', 'binary', 'ping', 'pong', 'close']]); +$client->receive(); // Return all messages +``` + +### Sending messages + +There are convenience methods to send messages with different opcodes. +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); + +// Convenience methods +$client->text('A plain text message'); // Send an opcode=text message +$client->binary($binary_string); // Send an opcode=binary message +$client->ping(); // Send an opcode=ping frame +$client->pong(); // Send an unsolicited opcode=pong frame + +// Generic send method +$client->send($payload); // Sent as masked opcode=text +$client->send($payload, 'binary'); // Sent as masked opcode=binary +$client->send($payload, 'binary', false); // Sent as unmasked opcode=binary +``` + +## Constructor options + +The `$options` parameter in constructor accepts an associative array of options. + +* `context` - A stream context created using [stream_context_create](https://www.php.net/manual/en/function.stream-context-create). +* `filter` - Array of opcodes to return on receive, default `['text', 'binary']` +* `fragment_size` - Maximum payload size. Default 4096 chars. +* `headers` - Additional headers as associative array name => content. +* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger. +* `persistent` - Connection is re-used between requests until time out is reached. Default false. +* `return_obj` - Return a [Message](Message.md) instance on receive, default false +* `timeout` - Time out in seconds. Default 5 seconds. + +```php +$context = stream_context_create(); +stream_context_set_option($context, 'ssl', 'verify_peer', false); +stream_context_set_option($context, 'ssl', 'verify_peer_name', false); + +$client = new WebSocket\Client("ws://echo.websocket.org/", [ + 'context' => $context, // Attach stream context created above + 'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return + 'headers' => [ // Additional headers, used to specify subprotocol + 'Sec-WebSocket-Protocol' => 'soap', + 'origin' => 'localhost', + ], + 'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger + 'return_obj' => true, // Return Message instance rather than just text + 'timeout' => 60, // 1 minute time out +]); +``` + +## Exceptions + +* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid. +* `WebSocket\BadUriException` - Thrown if provided URI is invalid. +* `WebSocket\ConnectionException` - Thrown on any socket I/O failure. +* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out. diff --git a/vendor/textalk/websocket/docs/Contributing.md b/vendor/textalk/websocket/docs/Contributing.md new file mode 100644 index 0000000..263d868 --- /dev/null +++ b/vendor/textalk/websocket/docs/Contributing.md @@ -0,0 +1,44 @@ +[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • Contributing + +# Websocket: Contributing + +Everyone is welcome to help out! +But to keep this project sustainable, please ensure your contribution respects the requirements below. + +## PR Requirements + +Requirements on pull requests; +* All tests **MUST** pass. +* Code coverage **MUST** remain at 100%. +* Code **MUST** adhere to PSR-1 and PSR-12 code standards. + +## Dependency management + +Install or update dependencies using [Composer](https://getcomposer.org/). + +``` +# Install dependencies +make install + +# Update dependencies +make update +``` + +## Code standard + +This project uses [PSR-1](https://www.php-fig.org/psr/psr-1/) and [PSR-12](https://www.php-fig.org/psr/psr-12/) code standards. +``` +# Check code standard adherence +make cs-check +``` + +## Unit testing + +Unit tests with [PHPUnit](https://phpunit.readthedocs.io/), coverage with [Coveralls](https://github.com/php-coveralls/php-coveralls) +``` +# Run unit tests +make test + +# Create coverage +make coverage +``` diff --git a/vendor/textalk/websocket/docs/Examples.md b/vendor/textalk/websocket/docs/Examples.md new file mode 100644 index 0000000..7dd4e0c --- /dev/null +++ b/vendor/textalk/websocket/docs/Examples.md @@ -0,0 +1,98 @@ +[Client](Client.md) • [Server](Server.md) • [Message](Message.md) • Examples • [Changelog](Changelog.md) • [Contributing](Contributing.md) + +# Websocket: Examples + +Here are some examples on how to use the WebSocket library. + +## Echo logger + +In dev environment (as in having run composer to include dev dependencies) you have +access to a simple echo logger that print out information synchronously. + +This is usable for debugging. For production, use a proper logger. + +```php +namespace WebSocket; + +$logger = new EchoLogger(); + +$client = new Client('ws://echo.websocket.org/'); +$client->setLogger($logger); + +$server = new Server(); +$server->setLogger($logger); +``` + +An example of server output; +``` +info | Server listening to port 8000 [] +debug | Wrote 129 of 129 bytes. [] +info | Server connected to port 8000 [] +info | Received 'text' message [] +debug | Wrote 9 of 9 bytes. [] +info | Sent 'text' message [] +debug | Received 'close', status: 1000. [] +debug | Wrote 32 of 32 bytes. [] +info | Sent 'close' message [] +info | Received 'close' message [] +``` + +## The `send` client + +Source: [examples/send.php](../examples/send.php) + +A simple, single send/receive client. + +Example use: +``` +php examples/send.php --opcode text "A text message" // Send a text message to localhost +php examples/send.php --opcode ping "ping it" // Send a ping message to localhost +php examples/send.php --uri ws://echo.websocket.org "A text message" // Send a text message to echo.websocket.org +php examples/send.php --opcode text --debug "A text message" // Use runtime debugging +``` + +## The `echoserver` server + +Source: [examples/echoserver.php](../examples/echoserver.php) + +A simple server that responds to recevied commands. + +Example use: +``` +php examples/echoserver.php // Run with default settings +php examples/echoserver.php --port 8080 // Listen on port 8080 +php examples/echoserver.php --debug // Use runtime debugging +``` + +These strings can be sent as message to trigger server to perform actions; +* `exit` - Server will initiate close procedure +* `ping` - Server will send a ping message +* `headers` - Server will respond with all headers provided by client +* `auth` - Server will respond with auth header if provided by client +* For other sent strings, server will respond with the same strings + +## The `random` client + +Source: [examples/random_client.php](../examples/random_client.php) + +The random client will use random options and continuously send/receive random messages. + +Example use: +``` +php examples/random_client.php --uri ws://echo.websocket.org // Connect to echo.websocket.org +php examples/random_client.php --timeout 5 --fragment_size 16 // Specify settings +php examples/random_client.php --debug // Use runtime debugging +``` + +## The `random` server + +Source: [examples/random_server.php](../examples/random_server.php) + +The random server will use random options and continuously send/receive random messages. + +Example use: +``` +php examples/random_server.php --port 8080 // // Listen on port 8080 +php examples/random_server.php --timeout 5 --fragment_size 16 // Specify settings +php examples/random_server.php --debug // Use runtime debugging +``` diff --git a/vendor/textalk/websocket/docs/Message.md b/vendor/textalk/websocket/docs/Message.md new file mode 100644 index 0000000..9bd0f2b --- /dev/null +++ b/vendor/textalk/websocket/docs/Message.md @@ -0,0 +1,60 @@ +[Client](Client.md) • [Server](Server.md) • Message • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md) + +# Websocket: Messages + +If option `return_obj` is set to `true` on [client](Client.md) or [server](Server.md), +the `receive()` method will return a Message instance instead of a string. + +Available classes correspond to opcode; +* WebSocket\Message\Text +* WebSocket\Message\Binary +* WebSocket\Message\Ping +* WebSocket\Message\Pong +* WebSocket\Message\Close + +Additionally; +* WebSocket\Message\Message - abstract base class for all messages above +* WebSocket\Message\Factory - Factory class to create Msssage instances + +## Message abstract class synopsis + +```php +WebSocket\Message\Message { + + public __construct(string $payload = '') + public __toString() : string + + public getOpcode() : string + public getLength() : int + public getTimestamp() : DateTime + public getContent() : string + public setContent(string $payload = '') : void + public hasContent() : bool +} +``` + +## Factory class synopsis + +```php +WebSocket\Message\Factory { + + public create(string $opcode, string $payload = '') : Message +} +``` + +## Example + +Receving a Message and echo some methods. + +```php +$client = new WebSocket\Client('ws://echo.websocket.org/', ['return_obj' => true]); +$client->text('Hello WebSocket.org!'); +// Echo return same message as sent +$message = $client->receive(); +echo $message->getOpcode(); // -> "text" +echo $message->getLength(); // -> 20 +echo $message->getContent(); // -> "Hello WebSocket.org!" +echo $message->hasContent(); // -> true +echo $message->getTimestamp()->format('H:i:s'); // -> 19:37:18 +$client->close(); +``` diff --git a/vendor/textalk/websocket/docs/Server.md b/vendor/textalk/websocket/docs/Server.md new file mode 100644 index 0000000..7d01a41 --- /dev/null +++ b/vendor/textalk/websocket/docs/Server.md @@ -0,0 +1,136 @@ +[Client](Client.md) • Server • [Message](Message.md) • [Examples](Examples.md) • [Changelog](Changelog.md) • [Contributing](Contributing.md) + +# Websocket: Server + +The library contains a rudimentary single stream/single thread server. +It internally supports Upgrade handshake and implicit close and ping/pong operations. + +Note that it does **not** support threading or automatic association ot continuous client requests. +If you require this kind of server behavior, you need to build it on top of provided server implementation. + +## Class synopsis + +```php +WebSocket\Server { + + public __construct(array $options = []) + public __destruct() + public __toString() : string + + public accept() : bool + public text(string $payload) : void + public binary(string $payload) : void + public ping(string $payload = '') : void + public pong(string $payload = '') : void + public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void + public receive() : mixed + public close(int $status = 1000, mixed $message = 'ttfn') : mixed + + public getPort() : int + public getPath() : string + public getRequest() : array + public getHeader(string $header_name) : string|null + + public getName() : string|null + public getPier() : string|null + public getLastOpcode() : string + public getCloseStatus() : int + public isConnected() : bool + public setTimeout(int $seconds) : void + public setFragmentSize(int $fragment_size) : self + public getFragmentSize() : int + public setLogger(Psr\Log\LoggerInterface $logger = null) : void +} +``` + +## Examples + +### Simple receive-send operation + +This example reads a single message from a client, and respond with the same message. + +```php +$server = new WebSocket\Server(); +$server->accept(); +$message = $server->receive(); +$server->text($message); +$server->close(); +``` + +### Listening to clients + +To continuously listen to incoming messages, you need to put the receive operation within a loop. +Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out. +By consuming exceptions, the code will re-connect the socket in next loop iteration. + +```php +$server = new WebSocket\Server(); +while ($server->accept()) { + try { + $message = $server->receive(); + // Act on received message + // Break while loop to stop listening + } catch (\WebSocket\ConnectionException $e) { + // Possibly log errors + } +} +$server->close(); +``` + +### Filtering received messages + +By default the `receive()` method return messages of 'text' and 'binary' opcode. +The filter option allows you to specify which message types to return. + +```php +$server = new WebSocket\Server(['filter' => ['text']]); +$server->receive(); // only return 'text' messages + +$server = new WebSocket\Server(['filter' => ['text', 'binary', 'ping', 'pong', 'close']]); +$server->receive(); // return all messages +``` + +### Sending messages + +There are convenience methods to send messages with different opcodes. +```php +$server = new WebSocket\Server(); + +// Convenience methods +$server->text('A plain text message'); // Send an opcode=text message +$server->binary($binary_string); // Send an opcode=binary message +$server->ping(); // Send an opcode=ping frame +$server->pong(); // Send an unsolicited opcode=pong frame + +// Generic send method +$server->send($payload); // Sent as masked opcode=text +$server->send($payload, 'binary'); // Sent as masked opcode=binary +$server->send($payload, 'binary', false); // Sent as unmasked opcode=binary +``` + +## Constructor options + +The `$options` parameter in constructor accepts an associative array of options. + +* `filter` - Array of opcodes to return on receive, default `['text', 'binary']` +* `fragment_size` - Maximum payload size. Default 4096 chars. +* `logger` - A [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger. +* `port` - The server port to listen to. Default 8000. +* `return_obj` - Return a [Message](Message.md) instance on receive, default false +* `timeout` - Time out in seconds. Default 5 seconds. + +```php +$server = new WebSocket\Server([ + 'filter' => ['text', 'binary', 'ping'], // Specify message types for receive() to return + 'logger' => $my_psr3_logger, // Attach a PSR3 compatible logger + 'port' => 9000, // Listening port + 'return_obj' => true, // Return Message insatnce rather than just text + 'timeout' => 60, // 1 minute time out +]); +``` + +## Exceptions + +* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid. +* `WebSocket\ConnectionException` - Thrown on any socket I/O failure. +* `WebSocket\TimeoutException` - Thrown when the socket experiences a time out. diff --git a/vendor/textalk/websocket/examples/echoserver.php b/vendor/textalk/websocket/examples/echoserver.php new file mode 100644 index 0000000..231c4c9 --- /dev/null +++ b/vendor/textalk/websocket/examples/echoserver.php @@ -0,0 +1,87 @@ +<?php + +/** + * This file is used for the tests, but can also serve as an example of a WebSocket\Server. + * Run in console: php examples/echoserver.php + * + * Console options: + * --port <int> : The port to listen to, default 8000 + * --timeout <int> : Timeout in seconds, default 200 seconds + * --debug : Output log data (if logger is available) + */ + +namespace WebSocket; + +require __DIR__ . '/../vendor/autoload.php'; + +error_reporting(-1); + +echo "> Random server\n"; + +// Server options specified or random +$options = array_merge([ + 'port' => 8000, + 'timeout' => 200, + 'filter' => ['text', 'binary', 'ping', 'pong'], +], getopt('', ['port:', 'timeout:', 'debug'])); + +// If debug mode and logger is available +if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) { + $logger = new EchoLog(); + $options['logger'] = $logger; + echo "> Using logger\n"; +} + +// Setting timeout to 200 seconds to make time for all tests and manual runs. +try { + $server = new Server($options); +} catch (ConnectionException $e) { + echo "> ERROR: {$e->getMessage()}\n"; + die(); +} + +echo "> Listening to port {$server->getPort()}\n"; + +// Force quit to close server +while (true) { + try { + while ($server->accept()) { + echo "> Accepted on port {$server->getPort()}\n"; + while (true) { + $message = $server->receive(); + $opcode = $server->getLastOpcode(); + if (is_null($message)) { + echo "> Closing connection\n"; + continue 2; + } + echo "> Got '{$message}' [opcode: {$opcode}]\n"; + if (in_array($opcode, ['ping', 'pong'])) { + $server->send($message); + continue; + } + // Allow certain string to trigger server action + switch ($message) { + case 'exit': + echo "> Client told me to quit. Bye bye.\n"; + $server->close(); + echo "> Close status: {$server->getCloseStatus()}\n"; + exit; + case 'headers': + $server->text(implode("\r\n", $server->getRequest())); + break; + case 'ping': + $server->ping($message); + break; + case 'auth': + $auth = $server->getHeader('Authorization'); + $server->text("{$auth} - {$message}"); + break; + default: + $server->text($message); + } + } + } + } catch (ConnectionException $e) { + echo "> ERROR: {$e->getMessage()}\n"; + } +} diff --git a/vendor/textalk/websocket/examples/random_client.php b/vendor/textalk/websocket/examples/random_client.php new file mode 100644 index 0000000..b23bd6b --- /dev/null +++ b/vendor/textalk/websocket/examples/random_client.php @@ -0,0 +1,94 @@ +<?php + +/** + * Websocket client that read/write random data. + * Run in console: php examples/random_client.php + * + * Console options: + * --uri <uri> : The URI to connect to, default ws://localhost:8000 + * --timeout <int> : Timeout in seconds, random default + * --fragment_size <int> : Fragment size as bytes, random default + * --debug : Output log data (if logger is available) + */ + +namespace WebSocket; + +require __DIR__ . '/../vendor/autoload.php'; + +error_reporting(-1); + +$randStr = function (int $maxlength = 4096) { + $string = ''; + $length = rand(1, $maxlength); + for ($i = 0; $i < $length; $i++) { + $string .= chr(rand(33, 126)); + } + return $string; +}; + +echo "> Random client\n"; + +// Server options specified or random +$options = array_merge([ + 'uri' => 'ws://localhost:8000', + 'timeout' => rand(1, 60), + 'fragment_size' => rand(1, 4096) * 8, +], getopt('', ['uri:', 'timeout:', 'fragment_size:', 'debug'])); + +// If debug mode and logger is available +if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) { + $logger = new EchoLog(); + $options['logger'] = $logger; + echo "> Using logger\n"; +} + +// Main loop +while (true) { + try { + $client = new Client($options['uri'], $options); + $info = json_encode([ + 'uri' => $options['uri'], + 'timeout' => $options['timeout'], + 'framgemt_size' => $client->getFragmentSize(), + ]); + echo "> Creating client {$info}\n"; + + try { + while (true) { + // Random actions + switch (rand(1, 10)) { + case 1: + echo "> Sending text\n"; + $client->text("Text message {$randStr()}"); + break; + case 2: + echo "> Sending binary\n"; + $client->binary("Binary message {$randStr()}"); + break; + case 3: + echo "> Sending close\n"; + $client->close(rand(1000, 2000), "Close message {$randStr(8)}"); + break; + case 4: + echo "> Sending ping\n"; + $client->ping("Ping message {$randStr(8)}"); + break; + case 5: + echo "> Sending pong\n"; + $client->pong("Pong message {$randStr(8)}"); + break; + default: + echo "> Receiving\n"; + $received = $client->receive(); + echo "> Received {$client->getLastOpcode()}: {$received}\n"; + } + sleep(rand(1, 5)); + } + } catch (\Throwable $e) { + echo "ERROR I/O: {$e->getMessage()} [{$e->getCode()}]\n"; + } + } catch (\Throwable $e) { + echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n"; + } + sleep(rand(1, 5)); +} diff --git a/vendor/textalk/websocket/examples/random_server.php b/vendor/textalk/websocket/examples/random_server.php new file mode 100644 index 0000000..0b0849c --- /dev/null +++ b/vendor/textalk/websocket/examples/random_server.php @@ -0,0 +1,93 @@ +<?php + +/** + * Websocket server that read/write random data. + * Run in console: php examples/random_server.php + * + * Console options: + * --port <int> : The port to listen to, default 8000 + * --timeout <int> : Timeout in seconds, random default + * --fragment_size <int> : Fragment size as bytes, random default + * --debug : Output log data (if logger is available) + */ + +namespace WebSocket; + +require __DIR__ . '/../vendor/autoload.php'; + +error_reporting(-1); + +$randStr = function (int $maxlength = 4096) { + $string = ''; + $length = rand(1, $maxlength); + for ($i = 0; $i < $length; $i++) { + $string .= chr(rand(33, 126)); + } + return $string; +}; + +echo "> Random server\n"; + +// Server options specified or random +$options = array_merge([ + 'port' => 8000, + 'timeout' => rand(1, 60), + 'fragment_size' => rand(1, 4096) * 8, +], getopt('', ['port:', 'timeout:', 'fragment_size:', 'debug'])); + +// If debug mode and logger is available +if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) { + $logger = new EchoLog(); + $options['logger'] = $logger; + echo "> Using logger\n"; +} + +// Force quit to close server +while (true) { + try { + // Setup server + $server = new Server($options); + $info = json_encode([ + 'port' => $server->getPort(), + 'timeout' => $options['timeout'], + 'framgemt_size' => $server->getFragmentSize(), + ]); + echo "> Creating server {$info}\n"; + + while ($server->accept()) { + while (true) { + // Random actions + switch (rand(1, 10)) { + case 1: + echo "> Sending text\n"; + $server->text("Text message {$randStr()}"); + break; + case 2: + echo "> Sending binary\n"; + $server->binary("Binary message {$randStr()}"); + break; + case 3: + echo "> Sending close\n"; + $server->close(rand(1000, 2000), "Close message {$randStr(8)}"); + break; + case 4: + echo "> Sending ping\n"; + $server->ping("Ping message {$randStr(8)}"); + break; + case 5: + echo "> Sending pong\n"; + $server->pong("Pong message {$randStr(8)}"); + break; + default: + echo "> Receiving\n"; + $received = $server->receive(); + echo "> Received {$server->getLastOpcode()}: {$received}\n"; + } + sleep(rand(1, 5)); + } + } + } catch (\Throwable $e) { + echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n"; + } + sleep(rand(1, 5)); +} diff --git a/vendor/textalk/websocket/examples/send.php b/vendor/textalk/websocket/examples/send.php new file mode 100644 index 0000000..30e48e0 --- /dev/null +++ b/vendor/textalk/websocket/examples/send.php @@ -0,0 +1,51 @@ +<?php + +/** + * Simple send & receive client for test purpose. + * Run in console: php examples/send.php <options> <message> + * + * Console options: + * --uri <uri> : The URI to connect to, default ws://localhost:8000 + * --opcode <string> : Opcode to send, default 'text' + * --debug : Output log data (if logger is available) + */ + +namespace WebSocket; + +require __DIR__ . '/../vendor/autoload.php'; + +error_reporting(-1); + +echo "> Send client\n"; + +// Server options specified or random +$options = array_merge([ + 'uri' => 'ws://localhost:8000', + 'opcode' => 'text', +], getopt('', ['uri:', 'opcode:', 'debug'])); +$message = array_pop($argv); + +// If debug mode and logger is available +if (isset($options['debug']) && class_exists('WebSocket\EchoLog')) { + $logger = new EchoLog(); + $options['logger'] = $logger; + echo "> Using logger\n"; +} + +try { + // Create client, send and recevie + $client = new Client($options['uri'], $options); + $client->send($message, $options['opcode']); + echo "> Sent '{$message}' [opcode: {$options['opcode']}]\n"; + if (in_array($options['opcode'], ['text', 'binary'])) { + $message = $client->receive(); + $opcode = $client->getLastOpcode(); + if (!is_null($message)) { + echo "> Got '{$message}' [opcode: {$opcode}]\n"; + } + } + $client->close(); + echo "> Closing client\n"; +} catch (\Throwable $e) { + echo "ERROR: {$e->getMessage()} [{$e->getCode()}]\n"; +} diff --git a/vendor/textalk/websocket/lib/BadOpcodeException.php b/vendor/textalk/websocket/lib/BadOpcodeException.php new file mode 100644 index 0000000..a518715 --- /dev/null +++ b/vendor/textalk/websocket/lib/BadOpcodeException.php @@ -0,0 +1,7 @@ +<?php + +namespace WebSocket; + +class BadOpcodeException extends Exception +{ +} diff --git a/vendor/textalk/websocket/lib/BadUriException.php b/vendor/textalk/websocket/lib/BadUriException.php new file mode 100644 index 0000000..39e975d --- /dev/null +++ b/vendor/textalk/websocket/lib/BadUriException.php @@ -0,0 +1,7 @@ +<?php + +namespace WebSocket; + +class BadUriException extends Exception +{ +} diff --git a/vendor/textalk/websocket/lib/Base.php b/vendor/textalk/websocket/lib/Base.php new file mode 100644 index 0000000..f460289 --- /dev/null +++ b/vendor/textalk/websocket/lib/Base.php @@ -0,0 +1,486 @@ +<?php + +/** + * Copyright (C) 2014-2020 Textalk/Abicart and contributors. + * + * This file is part of Websocket PHP and is free software under the ISC License. + * License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING + */ + +namespace WebSocket; + +use Psr\Log\{LoggerAwareInterface, LoggerInterface, NullLogger}; +use WebSocket\Message\Factory; + +class Base implements LoggerAwareInterface +{ + protected $socket; + protected $options = []; + protected $is_closing = false; + protected $last_opcode = null; + protected $close_status = null; + protected $logger; + private $read_buffer; + + protected static $opcodes = [ + 'continuation' => 0, + 'text' => 1, + 'binary' => 2, + 'close' => 8, + 'ping' => 9, + 'pong' => 10, + ]; + + public function getLastOpcode(): ?string + { + return $this->last_opcode; + } + + public function getCloseStatus(): ?int + { + return $this->close_status; + } + + public function isConnected(): bool + { + return $this->socket && + (get_resource_type($this->socket) == 'stream' || + get_resource_type($this->socket) == 'persistent stream'); + } + + public function setTimeout(int $timeout): void + { + $this->options['timeout'] = $timeout; + + if ($this->isConnected()) { + stream_set_timeout($this->socket, $timeout); + } + } + + public function setFragmentSize(int $fragment_size): self + { + $this->options['fragment_size'] = $fragment_size; + return $this; + } + + public function getFragmentSize(): int + { + return $this->options['fragment_size']; + } + + public function setLogger(LoggerInterface $logger = null): void + { + $this->logger = $logger ?: new NullLogger(); + } + + public function send(string $payload, string $opcode = 'text', bool $masked = true): void + { + if (!$this->isConnected()) { + $this->connect(); + } + + if (!in_array($opcode, array_keys(self::$opcodes))) { + $warning = "Bad opcode '{$opcode}'. Try 'text' or 'binary'."; + $this->logger->warning($warning); + throw new BadOpcodeException($warning); + } + + $payload_chunks = str_split($payload, $this->options['fragment_size']); + $frame_opcode = $opcode; + + for ($index = 0; $index < count($payload_chunks); ++$index) { + $chunk = $payload_chunks[$index]; + $final = $index == count($payload_chunks) - 1; + + $this->sendFragment($final, $chunk, $frame_opcode, $masked); + + // all fragments after the first will be marked a continuation + $frame_opcode = 'continuation'; + } + + $this->logger->info("Sent '{$opcode}' message", [ + 'opcode' => $opcode, + 'content-length' => strlen($payload), + 'frames' => count($payload_chunks), + ]); + } + + /** + * Convenience method to send text message + * @param string $payload Content as string + */ + public function text(string $payload): void + { + $this->send($payload); + } + + /** + * Convenience method to send binary message + * @param string $payload Content as binary string + */ + public function binary(string $payload): void + { + $this->send($payload, 'binary'); + } + + /** + * Convenience method to send ping + * @param string $payload Optional text as string + */ + public function ping(string $payload = ''): void + { + $this->send($payload, 'ping'); + } + + /** + * Convenience method to send unsolicited pong + * @param string $payload Optional text as string + */ + public function pong(string $payload = ''): void + { + $this->send($payload, 'pong'); + } + + /** + * Get name of local socket, or null if not connected + * @return string|null + */ + public function getName(): ?string + { + return $this->isConnected() ? stream_socket_get_name($this->socket, false) : null; + } + + /** + * Get name of remote socket, or null if not connected + * @return string|null + */ + public function getPier(): ?string + { + return $this->isConnected() ? stream_socket_get_name($this->socket, true) : null; + } + + /** + * Get string representation of instance + * @return string String representation + */ + public function __toString(): string + { + return sprintf( + "%s(%s)", + get_class($this), + $this->getName() ?: 'closed' + ); + } + + /** + * Receive one message. + * Will continue reading until read message match filter settings. + * Return Message instance or string according to settings. + */ + protected function sendFragment(bool $final, string $payload, string $opcode, bool $masked): void + { + $data = ''; + + $byte_1 = $final ? 0b10000000 : 0b00000000; // Final fragment marker. + $byte_1 |= self::$opcodes[$opcode]; // Set opcode. + $data .= pack('C', $byte_1); + + $byte_2 = $masked ? 0b10000000 : 0b00000000; // Masking bit marker. + + // 7 bits of payload length... + $payload_length = strlen($payload); + if ($payload_length > 65535) { + $data .= pack('C', $byte_2 | 0b01111111); + $data .= pack('J', $payload_length); + } elseif ($payload_length > 125) { + $data .= pack('C', $byte_2 | 0b01111110); + $data .= pack('n', $payload_length); + } else { + $data .= pack('C', $byte_2 | $payload_length); + } + + // Handle masking + if ($masked) { + // generate a random mask: + $mask = ''; + for ($i = 0; $i < 4; $i++) { + $mask .= chr(rand(0, 255)); + } + $data .= $mask; + + // Append payload to frame: + for ($i = 0; $i < $payload_length; $i++) { + $data .= $payload[$i] ^ $mask[$i % 4]; + } + } else { + $data .= $payload; + } + + $this->write($data); + $this->logger->debug("Sent '{$opcode}' frame", [ + 'opcode' => $opcode, + 'final' => $final, + 'content-length' => strlen($payload), + ]); + } + + public function receive() + { + $filter = $this->options['filter']; + if (!$this->isConnected()) { + $this->connect(); + } + + do { + $response = $this->receiveFragment(); + list ($payload, $final, $opcode) = $response; + + // Continuation and factual opcode + $continuation = ($opcode == 'continuation'); + $payload_opcode = $continuation ? $this->read_buffer['opcode'] : $opcode; + + // Filter frames + if (!in_array($payload_opcode, $filter)) { + if ($payload_opcode == 'close') { + return null; // Always abort receive on close + } + $final = false; + continue; // Continue reading + } + + // First continuation frame, create buffer + if (!$final && !$continuation) { + $this->read_buffer = ['opcode' => $opcode, 'payload' => $payload, 'frames' => 1]; + continue; // Continue reading + } + + // Subsequent continuation frames, add to buffer + if ($continuation) { + $this->read_buffer['payload'] .= $payload; + $this->read_buffer['frames']++; + } + } while (!$final); + + // Final, return payload + $frames = 1; + if ($continuation) { + $payload = $this->read_buffer['payload']; + $frames = $this->read_buffer['frames']; + $this->read_buffer = null; + } + $this->logger->info("Received '{opcode}' message", [ + 'opcode' => $payload_opcode, + 'content-length' => strlen($payload), + 'frames' => $frames, + ]); + + $this->last_opcode = $payload_opcode; + $factory = new Factory(); + return $this->options['return_obj'] + ? $factory->create($payload_opcode, $payload) + : $payload; + } + + protected function receiveFragment(): array + { + // Read the fragment "header" first, two bytes. + $data = $this->read(2); + list ($byte_1, $byte_2) = array_values(unpack('C*', $data)); + + $final = (bool)($byte_1 & 0b10000000); // Final fragment marker. + $rsv = $byte_1 & 0b01110000; // Unused bits, ignore + + // Parse opcode + $opcode_int = $byte_1 & 0b00001111; + $opcode_ints = array_flip(self::$opcodes); + if (!array_key_exists($opcode_int, $opcode_ints)) { + $warning = "Bad opcode in websocket frame: {$opcode_int}"; + $this->logger->warning($warning); + throw new ConnectionException($warning, ConnectionException::BAD_OPCODE); + } + $opcode = $opcode_ints[$opcode_int]; + + // Masking bit + $mask = (bool)($byte_2 & 0b10000000); + + $payload = ''; + + // Payload length + $payload_length = $byte_2 & 0b01111111; + + if ($payload_length > 125) { + if ($payload_length === 126) { + $data = $this->read(2); // 126: Payload is a 16-bit unsigned int + $payload_length = current(unpack('n', $data)); + } else { + $data = $this->read(8); // 127: Payload is a 64-bit unsigned int + $payload_length = current(unpack('J', $data)); + } + } + + // Get masking key. + if ($mask) { + $masking_key = $this->read(4); + } + + // Get the actual payload, if any (might not be for e.g. close frames. + if ($payload_length > 0) { + $data = $this->read($payload_length); + + if ($mask) { + // Unmask payload. + for ($i = 0; $i < $payload_length; $i++) { + $payload .= ($data[$i] ^ $masking_key[$i % 4]); + } + } else { + $payload = $data; + } + } + + $this->logger->debug("Read '{opcode}' frame", [ + 'opcode' => $opcode, + 'final' => $final, + 'content-length' => strlen($payload), + ]); + + // if we received a ping, send a pong and wait for the next message + if ($opcode === 'ping') { + $this->logger->debug("Received 'ping', sending 'pong'."); + $this->send($payload, 'pong', true); + return [$payload, true, $opcode]; + } + + // if we received a pong, wait for the next message + if ($opcode === 'pong') { + $this->logger->debug("Received 'pong'."); + return [$payload, true, $opcode]; + } + + if ($opcode === 'close') { + $status_bin = ''; + $status = ''; + // Get the close status. + $status_bin = ''; + $status = ''; + if ($payload_length > 0) { + $status_bin = $payload[0] . $payload[1]; + $status = current(unpack('n', $payload)); + $this->close_status = $status; + } + // Get additional close message + if ($payload_length >= 2) { + $payload = substr($payload, 2); + } + + $this->logger->debug("Received 'close', status: {$this->close_status}."); + + if ($this->is_closing) { + $this->is_closing = false; // A close response, all done. + } else { + $this->send($status_bin . 'Close acknowledged: ' . $status, 'close', true); // Respond. + } + + // Close the socket. + fclose($this->socket); + + // Closing should not return message. + return [$payload, true, $opcode]; + } + + return [$payload, $final, $opcode]; + } + + /** + * Tell the socket to close. + * + * @param integer $status http://tools.ietf.org/html/rfc6455#section-7.4 + * @param string $message A closing message, max 125 bytes. + */ + public function close(int $status = 1000, string $message = 'ttfn'): void + { + if (!$this->isConnected()) { + return; + } + $status_binstr = sprintf('%016b', $status); + $status_str = ''; + foreach (str_split($status_binstr, 8) as $binstr) { + $status_str .= chr(bindec($binstr)); + } + $this->send($status_str . $message, 'close', true); + $this->logger->debug("Closing with status: {$status_str}."); + + $this->is_closing = true; + $this->receive(); // Receiving a close frame will close the socket now. + } + + /** + * Disconnect from client/server. + */ + public function disconnect(): void + { + if ($this->isConnected()) { + fclose($this->socket); + $this->socket = null; + } + } + + protected function write(string $data): void + { + $length = strlen($data); + $written = @fwrite($this->socket, $data); + if ($written === false) { + $this->throwException("Failed to write {$length} bytes."); + } + if ($written < strlen($data)) { + $this->throwException("Could only write {$written} out of {$length} bytes."); + } + $this->logger->debug("Wrote {$written} of {$length} bytes."); + } + + protected function read(string $length): string + { + $data = ''; + while (strlen($data) < $length) { + $buffer = @fread($this->socket, $length - strlen($data)); + + if (!$buffer) { + $meta = stream_get_meta_data($this->socket); + if (!empty($meta['timed_out'])) { + $message = 'Client read timeout'; + $this->logger->error($message, $meta); + throw new TimeoutException($message, ConnectionException::TIMED_OUT, $meta); + } + } + if ($buffer === false) { + $read = strlen($data); + $this->throwException("Broken frame, read {$read} of stated {$length} bytes."); + } + if ($buffer === '') { + $this->throwException("Empty read; connection dead?"); + } + $data .= $buffer; + $read = strlen($data); + $this->logger->debug("Read {$read} of {$length} bytes."); + } + return $data; + } + + protected function throwException(string $message, int $code = 0): void + { + $meta = ['closed' => true]; + if ($this->isConnected()) { + $meta = stream_get_meta_data($this->socket); + fclose($this->socket); + $this->socket = null; + } + if (!empty($meta['timed_out'])) { + $this->logger->error($message, $meta); + throw new TimeoutException($message, ConnectionException::TIMED_OUT, $meta); + } + if (!empty($meta['eof'])) { + $code = ConnectionException::EOF; + } + $this->logger->error($message, $meta); + throw new ConnectionException($message, $code, $meta); + } +} diff --git a/vendor/textalk/websocket/lib/Client.php b/vendor/textalk/websocket/lib/Client.php new file mode 100644 index 0000000..8cefaaa --- /dev/null +++ b/vendor/textalk/websocket/lib/Client.php @@ -0,0 +1,216 @@ +<?php + +/** + * Copyright (C) 2014-2020 Textalk/Abicart and contributors. + * + * This file is part of Websocket PHP and is free software under the ISC License. + * License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING + */ + +namespace WebSocket; + +class Client extends Base +{ + // Default options + protected static $default_options = [ + 'context' => null, + 'filter' => ['text', 'binary'], + 'fragment_size' => 4096, + 'headers' => null, + 'logger' => null, + 'origin' => null, // @deprecated + 'persistent' => false, + 'return_obj' => false, + 'timeout' => 5, + ]; + + protected $socket_uri; + + /** + * @param string $uri A ws/wss-URI + * @param array $options + * Associative array containing: + * - context: Set the stream context. Default: empty context + * - timeout: Set the socket timeout in seconds. Default: 5 + * - fragment_size: Set framgemnt size. Default: 4096 + * - headers: Associative array of headers to set/override. + */ + public function __construct(string $uri, array $options = []) + { + $this->options = array_merge(self::$default_options, $options); + $this->socket_uri = $uri; + $this->setLogger($this->options['logger']); + } + + public function __destruct() + { + if ($this->isConnected() && get_resource_type($this->socket) !== 'persistent stream') { + fclose($this->socket); + } + $this->socket = null; + } + + /** + * Perform WebSocket handshake + */ + protected function connect(): void + { + $url_parts = parse_url($this->socket_uri); + if (empty($url_parts) || empty($url_parts['scheme']) || empty($url_parts['host'])) { + $error = "Invalid url '{$this->socket_uri}' provided."; + $this->logger->error($error); + throw new BadUriException($error); + } + $scheme = $url_parts['scheme']; + $host = $url_parts['host']; + $user = isset($url_parts['user']) ? $url_parts['user'] : ''; + $pass = isset($url_parts['pass']) ? $url_parts['pass'] : ''; + $port = isset($url_parts['port']) ? $url_parts['port'] : ($scheme === 'wss' ? 443 : 80); + $path = isset($url_parts['path']) ? $url_parts['path'] : '/'; + $query = isset($url_parts['query']) ? $url_parts['query'] : ''; + $fragment = isset($url_parts['fragment']) ? $url_parts['fragment'] : ''; + + $path_with_query = $path; + if (!empty($query)) { + $path_with_query .= '?' . $query; + } + if (!empty($fragment)) { + $path_with_query .= '#' . $fragment; + } + + if (!in_array($scheme, ['ws', 'wss'])) { + $error = "Url should have scheme ws or wss, not '{$scheme}' from URI '{$this->socket_uri}'."; + $this->logger->error($error); + throw new BadUriException($error); + } + + $host_uri = ($scheme === 'wss' ? 'ssl' : 'tcp') . '://' . $host; + + // Set the stream context options if they're already set in the config + if (isset($this->options['context'])) { + // Suppress the error since we'll catch it below + if (@get_resource_type($this->options['context']) === 'stream-context') { + $context = $this->options['context']; + } else { + $error = "Stream context in \$options['context'] isn't a valid context."; + $this->logger->error($error); + throw new \InvalidArgumentException($error); + } + } else { + $context = stream_context_create(); + } + + $persistent = $this->options['persistent'] === true; + $flags = STREAM_CLIENT_CONNECT; + $flags = $persistent ? $flags | STREAM_CLIENT_PERSISTENT : $flags; + + $error = $errno = $errstr = null; + set_error_handler(function (int $severity, string $message, string $file, int $line) use (&$error) { + $this->logger->warning($message, ['severity' => $severity]); + $error = $message; + }, E_ALL); + + // Open the socket. + $this->socket = stream_socket_client( + "{$host_uri}:{$port}", + $errno, + $errstr, + $this->options['timeout'], + $flags, + $context + ); + + restore_error_handler(); + + if (!$this->isConnected()) { + $error = "Could not open socket to \"{$host}:{$port}\": {$errstr} ({$errno}) {$error}."; + $this->logger->error($error); + throw new ConnectionException($error); + } + + $address = "{$scheme}://{$host}{$path_with_query}"; + + if (!$persistent || ftell($this->socket) == 0) { + // Set timeout on the stream as well. + stream_set_timeout($this->socket, $this->options['timeout']); + + // Generate the WebSocket key. + $key = self::generateKey(); + + // Default headers + $headers = [ + 'Host' => $host . ":" . $port, + 'User-Agent' => 'websocket-client-php', + 'Connection' => 'Upgrade', + 'Upgrade' => 'websocket', + 'Sec-WebSocket-Key' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + + // Handle basic authentication. + if ($user || $pass) { + $headers['authorization'] = 'Basic ' . base64_encode($user . ':' . $pass); + } + + // Deprecated way of adding origin (use headers instead). + if (isset($this->options['origin'])) { + $headers['origin'] = $this->options['origin']; + } + + // Add and override with headers from options. + if (isset($this->options['headers'])) { + $headers = array_merge($headers, $this->options['headers']); + } + + $header = "GET " . $path_with_query . " HTTP/1.1\r\n" . implode( + "\r\n", + array_map( + function ($key, $value) { + return "$key: $value"; + }, + array_keys($headers), + $headers + ) + ) . "\r\n\r\n"; + + // Send headers. + $this->write($header); + + // Get server response header (terminated with double CR+LF). + $response = stream_get_line($this->socket, 1024, "\r\n\r\n"); + + // Validate response. + if (!preg_match('#Sec-WebSocket-Accept:\s(.*)$#mUi', $response, $matches)) { + $error = "Connection to '{$address}' failed: Server sent invalid upgrade response: {$response}"; + $this->logger->error($error); + throw new ConnectionException($error); + } + + $keyAccept = trim($matches[1]); + $expectedResonse + = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); + + if ($keyAccept !== $expectedResonse) { + $error = 'Server sent bad upgrade response.'; + $this->logger->error($error); + throw new ConnectionException($error); + } + } + + $this->logger->info("Client connected to {$address}"); + } + + /** + * Generate a random string for WebSocket key. + * + * @return string Random string + */ + protected static function generateKey(): string + { + $key = ''; + for ($i = 0; $i < 16; $i++) { + $key .= chr(rand(33, 126)); + } + return base64_encode($key); + } +} diff --git a/vendor/textalk/websocket/lib/ConnectionException.php b/vendor/textalk/websocket/lib/ConnectionException.php new file mode 100644 index 0000000..7e1ecbf --- /dev/null +++ b/vendor/textalk/websocket/lib/ConnectionException.php @@ -0,0 +1,26 @@ +<?php + +namespace WebSocket; + +use Throwable; + +class ConnectionException extends Exception +{ + // Native codes in interval 0-106 + public const TIMED_OUT = 1024; + public const EOF = 1025; + public const BAD_OPCODE = 1026; + + private $data; + + public function __construct(string $message, int $code = 0, array $data = [], Throwable $prev = null) + { + parent::__construct($message, $code, $prev); + $this->data = $data; + } + + public function getData(): array + { + return $this->data; + } +} diff --git a/vendor/textalk/websocket/lib/Exception.php b/vendor/textalk/websocket/lib/Exception.php new file mode 100644 index 0000000..6482b7e --- /dev/null +++ b/vendor/textalk/websocket/lib/Exception.php @@ -0,0 +1,7 @@ +<?php + +namespace WebSocket; + +class Exception extends \Exception +{ +} diff --git a/vendor/textalk/websocket/lib/Message/Binary.php b/vendor/textalk/websocket/lib/Message/Binary.php new file mode 100644 index 0000000..84d27f5 --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Binary.php @@ -0,0 +1,8 @@ +<?php + +namespace WebSocket\Message; + +class Binary extends Message +{ + protected $opcode = 'binary'; +} diff --git a/vendor/textalk/websocket/lib/Message/Close.php b/vendor/textalk/websocket/lib/Message/Close.php new file mode 100644 index 0000000..b759fe9 --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Close.php @@ -0,0 +1,8 @@ +<?php + +namespace WebSocket\Message; + +class Close extends Message +{ + protected $opcode = 'close'; +} diff --git a/vendor/textalk/websocket/lib/Message/Factory.php b/vendor/textalk/websocket/lib/Message/Factory.php new file mode 100644 index 0000000..31df89c --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Factory.php @@ -0,0 +1,25 @@ +<?php + +namespace WebSocket\Message; + +use WebSocket\BadOpcodeException; + +class Factory +{ + public function create(string $opcode, string $payload = ''): Message + { + switch ($opcode) { + case 'text': + return new Text($payload); + case 'binary': + return new Binary($payload); + case 'ping': + return new Ping($payload); + case 'pong': + return new Pong($payload); + case 'close': + return new Close($payload); + } + throw new BadOpcodeException("Invalid opcode '{$opcode}' provided"); + } +} diff --git a/vendor/textalk/websocket/lib/Message/Message.php b/vendor/textalk/websocket/lib/Message/Message.php new file mode 100644 index 0000000..998caa1 --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Message.php @@ -0,0 +1,53 @@ +<?php + +namespace WebSocket\Message; + +use DateTime; + +abstract class Message +{ + protected $opcode; + protected $payload; + protected $timestamp; + + public function __construct(string $payload = '') + { + $this->payload = $payload; + $this->timestamp = new DateTime(); + } + + public function getOpcode(): string + { + return $this->opcode; + } + + public function getLength(): int + { + return strlen($this->payload); + } + + public function getTimestamp(): DateTime + { + return $this->timestamp; + } + + public function getContent(): string + { + return $this->payload; + } + + public function setContent(string $payload = ''): void + { + $this->payload = $payload; + } + + public function hasContent(): bool + { + return $this->payload != ''; + } + + public function __toString(): string + { + return get_class($this); + } +} diff --git a/vendor/textalk/websocket/lib/Message/Ping.php b/vendor/textalk/websocket/lib/Message/Ping.php new file mode 100644 index 0000000..908d233 --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Ping.php @@ -0,0 +1,8 @@ +<?php + +namespace WebSocket\Message; + +class Ping extends Message +{ + protected $opcode = 'ping'; +} diff --git a/vendor/textalk/websocket/lib/Message/Pong.php b/vendor/textalk/websocket/lib/Message/Pong.php new file mode 100644 index 0000000..76a8f9e --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Pong.php @@ -0,0 +1,8 @@ +<?php + +namespace WebSocket\Message; + +class Pong extends Message +{ + protected $opcode = 'pong'; +} diff --git a/vendor/textalk/websocket/lib/Message/Text.php b/vendor/textalk/websocket/lib/Message/Text.php new file mode 100644 index 0000000..88a22f7 --- /dev/null +++ b/vendor/textalk/websocket/lib/Message/Text.php @@ -0,0 +1,8 @@ +<?php + +namespace WebSocket\Message; + +class Text extends Message +{ + protected $opcode = 'text'; +} diff --git a/vendor/textalk/websocket/lib/Server.php b/vendor/textalk/websocket/lib/Server.php new file mode 100644 index 0000000..ae9325f --- /dev/null +++ b/vendor/textalk/websocket/lib/Server.php @@ -0,0 +1,176 @@ +<?php + +/** + * Copyright (C) 2014-2020 Textalk/Abicart and contributors. + * + * This file is part of Websocket PHP and is free software under the ISC License. + * License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING + */ + +namespace WebSocket; + +class Server extends Base +{ + // Default options + protected static $default_options = [ + 'filter' => ['text', 'binary'], + 'fragment_size' => 4096, + 'logger' => null, + 'port' => 8000, + 'return_obj' => false, + 'timeout' => null, + ]; + + protected $addr; + protected $port; + protected $listening; + protected $request; + protected $request_path; + + /** + * @param array $options + * Associative array containing: + * - timeout: Set the socket timeout in seconds. + * - fragment_size: Set framgemnt size. Default: 4096 + * - port: Chose port for listening. Default 8000. + */ + public function __construct(array $options = []) + { + $this->options = array_merge(self::$default_options, $options); + $this->port = $this->options['port']; + $this->setLogger($this->options['logger']); + + $error = $errno = $errstr = null; + set_error_handler(function (int $severity, string $message, string $file, int $line) use (&$error) { + $this->logger->warning($message, ['severity' => $severity]); + $error = $message; + }, E_ALL); + + do { + $this->listening = stream_socket_server("tcp://0.0.0.0:$this->port", $errno, $errstr); + } while ($this->listening === false && $this->port++ < 10000); + + restore_error_handler(); + + if (!$this->listening) { + $error = "Could not open listening socket: {$errstr} ({$errno}) {$error}"; + $this->logger->error($error); + throw new ConnectionException($error, (int)$errno); + } + + $this->logger->info("Server listening to port {$this->port}"); + } + + public function __destruct() + { + if ($this->isConnected()) { + fclose($this->socket); + } + $this->socket = null; + } + + public function getPort(): int + { + return $this->port; + } + + public function getPath(): string + { + return $this->request_path; + } + + public function getRequest(): array + { + return $this->request; + } + + public function getHeader($header): ?string + { + foreach ($this->request as $row) { + if (stripos($row, $header) !== false) { + list($headername, $headervalue) = explode(":", $row); + return trim($headervalue); + } + } + return null; + } + + public function accept(): bool + { + $this->socket = null; + return (bool)$this->listening; + } + + protected function connect(): void + { + + $error = null; + set_error_handler(function (int $severity, string $message, string $file, int $line) use (&$error) { + $this->logger->warning($message, ['severity' => $severity]); + $error = $message; + }, E_ALL); + + if (isset($this->options['timeout'])) { + $this->socket = stream_socket_accept($this->listening, $this->options['timeout']); + } else { + $this->socket = stream_socket_accept($this->listening); + } + + restore_error_handler(); + + if (!$this->socket) { + $this->throwException("Server failed to connect. {$error}"); + } + if (isset($this->options['timeout'])) { + stream_set_timeout($this->socket, $this->options['timeout']); + } + + $this->logger->info("Client has connected to port {port}", [ + 'port' => $this->port, + 'pier' => stream_socket_get_name($this->socket, true), + ]); + $this->performHandshake(); + } + + protected function performHandshake(): void + { + $request = ''; + do { + $buffer = stream_get_line($this->socket, 1024, "\r\n"); + $request .= $buffer . "\n"; + $metadata = stream_get_meta_data($this->socket); + } while (!feof($this->socket) && $metadata['unread_bytes'] > 0); + + if (!preg_match('/GET (.*) HTTP\//mUi', $request, $matches)) { + $error = "No GET in request: {$request}"; + $this->logger->error($error); + throw new ConnectionException($error); + } + $get_uri = trim($matches[1]); + $uri_parts = parse_url($get_uri); + + $this->request = explode("\n", $request); + $this->request_path = $uri_parts['path']; + /// @todo Get query and fragment as well. + + if (!preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $request, $matches)) { + $error = "Client had no Key in upgrade request: {$request}"; + $this->logger->error($error); + throw new ConnectionException($error); + } + + $key = trim($matches[1]); + + /// @todo Validate key length and base 64... + $response_key = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); + + $header = "HTTP/1.1 101 Switching Protocols\r\n" + . "Upgrade: websocket\r\n" + . "Connection: Upgrade\r\n" + . "Sec-WebSocket-Accept: $response_key\r\n" + . "\r\n"; + + $this->write($header); + $this->logger->debug("Handshake on {$get_uri}"); + } +} diff --git a/vendor/textalk/websocket/lib/TimeoutException.php b/vendor/textalk/websocket/lib/TimeoutException.php new file mode 100644 index 0000000..d20e622 --- /dev/null +++ b/vendor/textalk/websocket/lib/TimeoutException.php @@ -0,0 +1,7 @@ +<?php + +namespace WebSocket; + +class TimeoutException extends ConnectionException +{ +} diff --git a/vendor/textalk/websocket/phpunit.xml.dist b/vendor/textalk/websocket/phpunit.xml.dist new file mode 100644 index 0000000..893ab65 --- /dev/null +++ b/vendor/textalk/websocket/phpunit.xml.dist @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit bootstrap="tests/bootstrap.php" colors="true"> + <testsuites> + <testsuite name="Unit tests"> + <directory suffix=".php">tests</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">lib/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/vendor/textalk/websocket/tests/ClientTest.php b/vendor/textalk/websocket/tests/ClientTest.php new file mode 100644 index 0000000..c7bdc16 --- /dev/null +++ b/vendor/textalk/websocket/tests/ClientTest.php @@ -0,0 +1,449 @@ +<?php + +/** + * Test case for Client. + * Note that this test is performed by mocking socket/stream calls. + */ + +declare(strict_types=1); + +namespace WebSocket; + +use PHPUnit\Framework\TestCase; + +class ClientTest extends TestCase +{ + + public function setUp(): void + { + error_reporting(-1); + } + + public function testClientMasked(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertEquals(4096, $client->getFragmentSize()); + + MockSocket::initialize('send-receive', $this); + $client->send('Sending a message'); + $message = $client->receive(); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertEquals('text', $client->getLastOpcode()); + + MockSocket::initialize('client.close', $this); + $this->assertTrue($client->isConnected()); + $this->assertNull($client->getCloseStatus()); + + $client->close(); + $this->assertFalse($client->isConnected()); + $this->assertEquals(1000, $client->getCloseStatus()); + + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testDestruct(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('client.destruct', $this); + } + + public function testClienExtendedUrl(): void + { + MockSocket::initialize('client.connect-extended', $this); + $client = new Client('ws://localhost:8000/my/mock/path?my_query=yes#my_fragment'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testClientWithTimeout(): void + { + MockSocket::initialize('client.connect-timeout', $this); + $client = new Client('ws://localhost:8000/my/mock/path', ['timeout' => 300]); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testClientWithContext(): void + { + MockSocket::initialize('client.connect-context', $this); + $client = new Client('ws://localhost:8000/my/mock/path', ['context' => '@mock-stream-context']); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testClientAuthed(): void + { + MockSocket::initialize('client.connect-authed', $this); + $client = new Client('wss://usename:password@localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testWithHeaders(): void + { + MockSocket::initialize('client.connect-headers', $this); + $client = new Client('ws://localhost:8000/my/mock/path', [ + 'origin' => 'Origin header', + 'headers' => ['Generic header' => 'Generic content'], + ]); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPayload128(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + $payload = file_get_contents(__DIR__ . '/mock/payload.128.txt'); + + MockSocket::initialize('send-receive-128', $this); + $client->send($payload, 'text', false); + $message = $client->receive(); + $this->assertEquals($payload, $message); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPayload65536(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + $payload = file_get_contents(__DIR__ . '/mock/payload.65536.txt'); + $client->setFragmentSize(65540); + + MockSocket::initialize('send-receive-65536', $this); + $client->send($payload, 'text', false); + $message = $client->receive(); + $this->assertEquals($payload, $message); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertEquals(65540, $client->getFragmentSize()); + } + + public function testMultiFragment(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('send-receive-multi-fragment', $this); + $client->setFragmentSize(8); + $client->send('Multi fragment test'); + $message = $client->receive(); + $this->assertEquals('Multi fragment test', $message); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertEquals(8, $client->getFragmentSize()); + } + + public function testPingPong(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('ping-pong', $this); + $client->send('Server ping', 'ping'); + $client->send('', 'ping'); + $message = $client->receive(); + $this->assertEquals('Receiving a message', $message); + $this->assertEquals('text', $client->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testRemoteClose(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('close-remote', $this); + + $message = $client->receive(); + $this->assertNull($message); + + $this->assertFalse($client->isConnected()); + $this->assertEquals(17260, $client->getCloseStatus()); + $this->assertNull($client->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testSetTimeout(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('config-timeout', $this); + $client->setTimeout(300); + $this->assertTrue($client->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testReconnect(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('client.close', $this); + $this->assertTrue($client->isConnected()); + $this->assertNull($client->getCloseStatus()); + $client->close(); + $this->assertFalse($client->isConnected()); + $this->assertEquals(1000, $client->getCloseStatus()); + $this->assertNull($client->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('client.reconnect', $this); + $message = $client->receive(); + $this->assertTrue($client->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPersistentConnection(): void + { + MockSocket::initialize('client.connect-persistent', $this); + $client = new Client('ws://localhost:8000/my/mock/path', ['persistent' => true]); + $client->send('Connect'); + $client->disconnect(); + $this->assertFalse($client->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testBadScheme(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('bad://localhost:8000/my/mock/path'); + $this->expectException('WebSocket\BadUriException'); + $this->expectExceptionMessage('Url should have scheme ws or wss'); + $client->send('Connect'); + } + + public function testBadUrl(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('this is not an url'); + $this->expectException('WebSocket\BadUriException'); + $this->expectExceptionMessage('Invalid url \'this is not an url\' provided.'); + $client->send('Connect'); + } + + public function testBadStreamContext(): void + { + MockSocket::initialize('client.connect-bad-context', $this); + $client = new Client('ws://localhost:8000/my/mock/path', ['context' => 'BAD']); + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('Stream context in $options[\'context\'] isn\'t a valid context'); + $client->send('Connect'); + } + + public function testFailedConnection(): void + { + MockSocket::initialize('client.connect-failed', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Could not open socket to "localhost:8000"'); + $client->send('Connect'); + } + + public function testFailedConnectionWithError(): void + { + MockSocket::initialize('client.connect-error', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Could not open socket to "localhost:8000"'); + $client->send('Connect'); + } + + public function testInvalidUpgrade(): void + { + MockSocket::initialize('client.connect-invalid-upgrade', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Connection to \'ws://localhost/my/mock/path\' failed'); + $client->send('Connect'); + } + + public function testInvalidKey(): void + { + MockSocket::initialize('client.connect-invalid-key', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Server sent bad upgrade response'); + $client->send('Connect'); + } + + public function testSendBadOpcode(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + + MockSocket::initialize('send-bad-opcode', $this); + $this->expectException('WebSocket\BadOpcodeException'); + $this->expectExceptionMessage('Bad opcode \'bad\'. Try \'text\' or \'binary\'.'); + $client->send('Bad Opcode', 'bad'); + } + + public function testRecieveBadOpcode(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('receive-bad-opcode', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1026); + $this->expectExceptionMessage('Bad opcode in websocket frame: 12'); + $message = $client->receive(); + } + + public function testBrokenWrite(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('send-broken-write', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1025); + $this->expectExceptionMessage('Could only write 18 out of 22 bytes.'); + $client->send('Failing to write'); + } + + public function testFailedWrite(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('send-failed-write', $this); + $this->expectException('WebSocket\TimeoutException'); + $this->expectExceptionCode(1024); + $this->expectExceptionMessage('Failed to write 22 bytes.'); + $client->send('Failing to write'); + } + + public function testBrokenRead(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('receive-broken-read', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1025); + $this->expectExceptionMessage('Broken frame, read 0 of stated 2 bytes.'); + $client->receive(); + } + + public function testReadTimeout(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('receive-client-timeout', $this); + $this->expectException('WebSocket\TimeoutException'); + $this->expectExceptionCode(1024); + $this->expectExceptionMessage('Client read timeout'); + $client->receive(); + } + + public function testEmptyRead(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $client->send('Connect'); + MockSocket::initialize('receive-empty-read', $this); + $this->expectException('WebSocket\TimeoutException'); + $this->expectExceptionCode(1024); + $this->expectExceptionMessage('Empty read; connection dead?'); + $client->receive(); + } + + public function testFrameFragmentation(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client( + 'ws://localhost:8000/my/mock/path', + ['filter' => ['text', 'binary', 'pong', 'close']] + ); + $client->send('Connect'); + MockSocket::initialize('receive-fragmentation', $this); + $message = $client->receive(); + $this->assertEquals('Server ping', $message); + $this->assertEquals('pong', $client->getLastOpcode()); + $message = $client->receive(); + $this->assertEquals('Multi fragment test', $message); + $this->assertEquals('text', $client->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + MockSocket::initialize('close-remote', $this); + $message = $client->receive(); + $this->assertEquals('Closing', $message); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertFalse($client->isConnected()); + $this->assertEquals(17260, $client->getCloseStatus()); + $this->assertEquals('close', $client->getLastOpcode()); + } + + public function testMessageFragmentation(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client( + 'ws://localhost:8000/my/mock/path', + ['filter' => ['text', 'binary', 'pong', 'close'], 'return_obj' => true] + ); + $client->send('Connect'); + MockSocket::initialize('receive-fragmentation', $this); + $message = $client->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Pong', $message); + $this->assertEquals('Server ping', $message->getContent()); + $this->assertEquals('pong', $message->getOpcode()); + $message = $client->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Text', $message); + $this->assertEquals('Multi fragment test', $message->getContent()); + $this->assertEquals('text', $message->getOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + MockSocket::initialize('close-remote', $this); + $message = $client->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Close', $message); + $this->assertEquals('Closing', $message->getContent()); + $this->assertEquals('close', $message->getOpcode()); + } + + public function testConvenicanceMethods(): void + { + MockSocket::initialize('client.connect', $this); + $client = new Client('ws://localhost:8000/my/mock/path'); + $this->assertNull($client->getName()); + $this->assertNull($client->getPier()); + $this->assertEquals('WebSocket\Client(closed)', "{$client}"); + $client->text('Connect'); + MockSocket::initialize('send-convenicance', $this); + $client->binary(base64_encode('Binary content')); + $client->ping(); + $client->pong(); + $this->assertEquals('127.0.0.1:12345', $client->getName()); + $this->assertEquals('127.0.0.1:8000', $client->getPier()); + $this->assertEquals('WebSocket\Client(127.0.0.1:12345)', "{$client}"); + } +} diff --git a/vendor/textalk/websocket/tests/ExceptionTest.php b/vendor/textalk/websocket/tests/ExceptionTest.php new file mode 100644 index 0000000..84b939c --- /dev/null +++ b/vendor/textalk/websocket/tests/ExceptionTest.php @@ -0,0 +1,52 @@ +<?php + +/** + * Test case for Exceptions. + */ + +declare(strict_types=1); + +namespace WebSocket; + +use PHPUnit\Framework\TestCase; +use Throwable; + +class ExceptionTest extends TestCase +{ + + public function setUp(): void + { + error_reporting(-1); + } + + public function testConnectionException(): void + { + try { + throw new ConnectionException( + 'An error message', + ConnectionException::EOF, + ['test' => 'with data'], + new TimeoutException( + 'Nested exception', + ConnectionException::TIMED_OUT + ) + ); + } catch (Throwable $e) { + } + + $this->assertInstanceOf('WebSocket\ConnectionException', $e); + $this->assertInstanceOf('WebSocket\Exception', $e); + $this->assertInstanceOf('Exception', $e); + $this->assertInstanceOf('Throwable', $e); + $this->assertEquals('An error message', $e->getMessage()); + $this->assertEquals(1025, $e->getCode()); + $this->assertEquals(['test' => 'with data'], $e->getData()); + + $p = $e->getPrevious(); + $this->assertInstanceOf('WebSocket\TimeoutException', $p); + $this->assertInstanceOf('WebSocket\ConnectionException', $p); + $this->assertEquals('Nested exception', $p->getMessage()); + $this->assertEquals(1024, $p->getCode()); + $this->assertEquals([], $p->getData()); + } +} diff --git a/vendor/textalk/websocket/tests/MessageTest.php b/vendor/textalk/websocket/tests/MessageTest.php new file mode 100644 index 0000000..2d06ab7 --- /dev/null +++ b/vendor/textalk/websocket/tests/MessageTest.php @@ -0,0 +1,61 @@ +<?php + +/** + * Test case for Message subsection. + */ + +declare(strict_types=1); + +namespace WebSocket; + +use PHPUnit\Framework\TestCase; +use WebSocket\Message\Factory; +use WebSocket\Message\Text; + +class MessageTest extends TestCase +{ + + public function setUp(): void + { + error_reporting(-1); + } + + public function testFactory(): void + { + $factory = new Factory(); + $message = $factory->create('text', 'Some content'); + $this->assertInstanceOf('WebSocket\Message\Text', $message); + $message = $factory->create('binary', 'Some content'); + $this->assertInstanceOf('WebSocket\Message\Binary', $message); + $message = $factory->create('ping', 'Some content'); + $this->assertInstanceOf('WebSocket\Message\Ping', $message); + $message = $factory->create('pong', 'Some content'); + $this->assertInstanceOf('WebSocket\Message\Pong', $message); + $message = $factory->create('close', 'Some content'); + $this->assertInstanceOf('WebSocket\Message\Close', $message); + } + + public function testMessage() + { + $message = new Text('Some content'); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Text', $message); + $this->assertEquals('Some content', $message->getContent()); + $this->assertEquals('text', $message->getOpcode()); + $this->assertEquals(12, $message->getLength()); + $this->assertTrue($message->hasContent()); + $this->assertInstanceOf('DateTime', $message->getTimestamp()); + $message->setContent(''); + $this->assertEquals(0, $message->getLength()); + $this->assertFalse($message->hasContent()); + $this->assertEquals('WebSocket\Message\Text', "{$message}"); + } + + public function testBadOpcode() + { + $factory = new Factory(); + $this->expectException('WebSocket\BadOpcodeException'); + $this->expectExceptionMessage("Invalid opcode 'invalid' provided"); + $message = $factory->create('invalid', 'Some content'); + } +} diff --git a/vendor/textalk/websocket/tests/README.md b/vendor/textalk/websocket/tests/README.md new file mode 100644 index 0000000..b710a7e --- /dev/null +++ b/vendor/textalk/websocket/tests/README.md @@ -0,0 +1,29 @@ +# Testing + +Unit tests with [PHPUnit](https://phpunit.readthedocs.io/). + + +## How to run + +To run all test, run in console. + +``` +make test +``` + + +## Continuous integration + +GitHub Actions are run on PHP versions +`7.2`, `7.3`, `7.4` and `8.0`. + +Code coverage by [Coveralls](https://coveralls.io/github/Textalk/websocket-php). + + +## Test strategy + +Test set up overloads various stream and socket functions, +and use "scripts" to define and mock input/output of these functions. + +This set up negates the dependency on running servers, +and allow testing various errors that might occur. diff --git a/vendor/textalk/websocket/tests/ServerTest.php b/vendor/textalk/websocket/tests/ServerTest.php new file mode 100644 index 0000000..8294236 --- /dev/null +++ b/vendor/textalk/websocket/tests/ServerTest.php @@ -0,0 +1,448 @@ +<?php + +/** + * Test case for Server. + * Note that this test is performed by mocking socket/stream calls. + */ + +declare(strict_types=1); + +namespace WebSocket; + +use PHPUnit\Framework\TestCase; + +class ServerTest extends TestCase +{ + + public function setUp(): void + { + error_reporting(-1); + } + + public function testServerMasked(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertEquals(8000, $server->getPort()); + $this->assertEquals('/my/mock/path', $server->getPath()); + $this->assertTrue($server->isConnected()); + $this->assertEquals(4096, $server->getFragmentSize()); + $this->assertNull($server->getCloseStatus()); + $this->assertEquals([ + 'GET /my/mock/path HTTP/1.1', + 'host: localhost:8000', + 'user-agent: websocket-client-php', + 'connection: Upgrade', + 'upgrade: websocket', + 'sec-websocket-key: cktLWXhUdDQ2OXF0ZCFqOQ==', + 'sec-websocket-version: 13', + '', + '', + ], $server->getRequest()); + $this->assertEquals('websocket-client-php', $server->getHeader('USER-AGENT')); + $this->assertNull($server->getHeader('no such header')); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('send-receive', $this); + $server->send('Sending a message'); + $message = $server->receive(); + $this->assertEquals('Receiving a message', $message); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertNull($server->getCloseStatus()); + $this->assertEquals('text', $server->getLastOpcode()); + + MockSocket::initialize('server.close', $this); + $server->close(); + $this->assertFalse($server->isConnected()); + $this->assertEquals(1000, $server->getCloseStatus()); + $this->assertTrue(MockSocket::isEmpty()); + + $server->close(); // Already closed + } + + public function testDestruct(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + + MockSocket::initialize('server.accept-destruct', $this); + $server->accept(); + $message = $server->receive(); + } + + public function testServerWithTimeout(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(['timeout' => 300]); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept-timeout', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPayload128(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + $payload = file_get_contents(__DIR__ . '/mock/payload.128.txt'); + + MockSocket::initialize('send-receive-128', $this); + $server->send($payload, 'text', false); + $message = $server->receive(); + $this->assertEquals($payload, $message); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPayload65536(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + $payload = file_get_contents(__DIR__ . '/mock/payload.65536.txt'); + $server->setFragmentSize(65540); + + MockSocket::initialize('send-receive-65536', $this); + $server->send($payload, 'text', false); + $message = $server->receive(); + $this->assertEquals($payload, $message); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testMultiFragment(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('send-receive-multi-fragment', $this); + $server->setFragmentSize(8); + $server->send('Multi fragment test'); + $message = $server->receive(); + $this->assertEquals('Multi fragment test', $message); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testPingPong(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('ping-pong', $this); + $server->send('Server ping', 'ping'); + $server->send('', 'ping'); + $message = $server->receive(); + $this->assertEquals('Receiving a message', $message); + $this->assertEquals('text', $server->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testRemoteClose(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('close-remote', $this); + + $message = $server->receive(); + $this->assertEquals('', $message); + + $this->assertTrue(MockSocket::isEmpty()); + $this->assertFalse($server->isConnected()); + $this->assertEquals(17260, $server->getCloseStatus()); + $this->assertNull($server->getLastOpcode()); + } + + public function testSetTimeout(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + + MockSocket::initialize('config-timeout', $this); + $server->setTimeout(300); + $this->assertTrue($server->isConnected()); + $this->assertTrue(MockSocket::isEmpty()); + } + + public function testFailedSocketServer(): void + { + MockSocket::initialize('server.construct-failed-socket-server', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Could not open listening socket:'); + $server = new Server(['port' => 9999]); + } + + public function testFailedSocketServerWithError(): void + { + MockSocket::initialize('server.construct-error-socket-server', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Could not open listening socket:'); + $server = new Server(['port' => 9999]); + } + + public function testFailedConnect(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + + MockSocket::initialize('server.accept-failed-connect', $this); + $server->accept(); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Server failed to connect'); + $server->send('Connect'); + } + + public function testFailedConnectWithError(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + + MockSocket::initialize('server.accept-error-connect', $this); + $server->accept(); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Server failed to connect'); + $server->send('Connect'); + } + + public function testFailedConnectTimeout(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(['timeout' => 300]); + + MockSocket::initialize('server.accept-failed-connect', $this); + $server->accept(); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Server failed to connect'); + $server->send('Connect'); + } + + public function testFailedHttp(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept-failed-http', $this); + $server->accept(); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('No GET in request'); + $server->send('Connect'); + } + + public function testFailedWsKey(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept-failed-ws-key', $this); + $server->accept(); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Client had no Key in upgrade request'); + $server->send('Connect'); + } + + public function testSendBadOpcode(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + $this->expectException('WebSocket\BadOpcodeException'); + $this->expectExceptionCode(0); + $this->expectExceptionMessage('Bad opcode \'bad\'. Try \'text\' or \'binary\'.'); + $server->send('Bad Opcode', 'bad'); + } + + public function testRecieveBadOpcode(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('receive-bad-opcode', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1026); + $this->expectExceptionMessage('Bad opcode in websocket frame: 12'); + $message = $server->receive(); + } + + public function testBrokenWrite(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('send-broken-write', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1025); + $this->expectExceptionMessage('Could only write 18 out of 22 bytes.'); + $server->send('Failing to write'); + } + + public function testFailedWrite(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('send-failed-write', $this); + $this->expectException('WebSocket\TimeoutException'); + $this->expectExceptionCode(1024); + $this->expectExceptionMessage('Failed to write 22 bytes.'); + $server->send('Failing to write'); + } + + public function testBrokenRead(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('receive-broken-read', $this); + $this->expectException('WebSocket\ConnectionException'); + $this->expectExceptionCode(1025); + $this->expectExceptionMessage('Broken frame, read 0 of stated 2 bytes.'); + $server->receive(); + } + + public function testEmptyRead(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('receive-empty-read', $this); + $this->expectException('WebSocket\TimeoutException'); + $this->expectExceptionCode(1024); + $this->expectExceptionMessage('Empty read; connection dead?'); + $server->receive(); + } + + public function testFrameFragmentation(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(['filter' => ['text', 'binary', 'pong', 'close']]); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('receive-fragmentation', $this); + $message = $server->receive(); + $this->assertEquals('Server ping', $message); + $this->assertEquals('pong', $server->getLastOpcode()); + $message = $server->receive(); + $this->assertEquals('Multi fragment test', $message); + $this->assertEquals('text', $server->getLastOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + MockSocket::initialize('close-remote', $this); + $message = $server->receive(); + $this->assertEquals('Closing', $message); + $this->assertTrue(MockSocket::isEmpty()); + $this->assertFalse($server->isConnected()); + $this->assertEquals(17260, $server->getCloseStatus()); + $this->assertEquals('close', $server->getLastOpcode()); + } + + public function testMessageFragmentation(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(['filter' => ['text', 'binary', 'pong', 'close'], 'return_obj' => true]); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->send('Connect'); + MockSocket::initialize('receive-fragmentation', $this); + $message = $server->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Pong', $message); + $this->assertEquals('Server ping', $message->getContent()); + $this->assertEquals('pong', $message->getOpcode()); + $message = $server->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Text', $message); + $this->assertEquals('Multi fragment test', $message->getContent()); + $this->assertEquals('text', $message->getOpcode()); + $this->assertTrue(MockSocket::isEmpty()); + MockSocket::initialize('close-remote', $this); + $message = $server->receive(); + $this->assertInstanceOf('WebSocket\Message\Message', $message); + $this->assertInstanceOf('WebSocket\Message\Close', $message); + $this->assertEquals('Closing', $message->getContent()); + $this->assertEquals('close', $message->getOpcode()); + } + + public function testConvenicanceMethods(): void + { + MockSocket::initialize('server.construct', $this); + $server = new Server(); + $this->assertNull($server->getName()); + $this->assertNull($server->getPier()); + $this->assertEquals('WebSocket\Server(closed)', "{$server}"); + MockSocket::initialize('server.accept', $this); + $server->accept(); + $server->text('Connect'); + MockSocket::initialize('send-convenicance', $this); + $server->binary(base64_encode('Binary content')); + $server->ping(); + $server->pong(); + $this->assertEquals('127.0.0.1:12345', $server->getName()); + $this->assertEquals('127.0.0.1:8000', $server->getPier()); + $this->assertEquals('WebSocket\Server(127.0.0.1:12345)', "{$server}"); + $this->assertTrue(MockSocket::isEmpty()); + } +} diff --git a/vendor/textalk/websocket/tests/bootstrap.php b/vendor/textalk/websocket/tests/bootstrap.php new file mode 100644 index 0000000..5d6bdd0 --- /dev/null +++ b/vendor/textalk/websocket/tests/bootstrap.php @@ -0,0 +1,6 @@ +<?php + +namespace WebSocket; + +require dirname(__DIR__) . '/vendor/autoload.php'; +require __DIR__ . '/mock/mock-socket.php'; diff --git a/vendor/textalk/websocket/tests/mock/EchoLog.php b/vendor/textalk/websocket/tests/mock/EchoLog.php new file mode 100644 index 0000000..369131a --- /dev/null +++ b/vendor/textalk/websocket/tests/mock/EchoLog.php @@ -0,0 +1,34 @@ +<?php + +/** + * Simple echo logger (only available when running in dev environment) + */ + +namespace WebSocket; + +class EchoLog implements \Psr\Log\LoggerInterface +{ + use \Psr\Log\LoggerTrait; + + public function log($level, $message, array $context = []) + { + $message = $this->interpolate($message, $context); + $context_string = empty($context) ? '' : json_encode($context); + echo str_pad($level, 8) . " | {$message} {$context_string}\n"; + } + + public function interpolate($message, array $context = []) + { + // Build a replacement array with braces around the context keys + $replace = []; + foreach ($context as $key => $val) { + // Check that the value can be cast to string + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = $val; + } + } + + // Interpolate replacement values into the message and return + return strtr($message, $replace); + } +} diff --git a/vendor/textalk/websocket/tests/mock/MockSocket.php b/vendor/textalk/websocket/tests/mock/MockSocket.php new file mode 100644 index 0000000..e12d6ed --- /dev/null +++ b/vendor/textalk/websocket/tests/mock/MockSocket.php @@ -0,0 +1,79 @@ +<?php + +/** + * This class is used by tests to mock and track various socket/stream calls. + */ + +namespace WebSocket; + +class MockSocket +{ + + private static $queue = []; + private static $stored = []; + private static $asserter; + + // Handler called by function overloads in mock-socket.php + public static function handle($function, $params = []) + { + $current = array_shift(self::$queue); + if ($function == 'get_resource_type' && is_null($current)) { + return null; // Catch destructors + } + self::$asserter->assertEquals($current['function'], $function); + foreach ($current['params'] as $index => $param) { + self::$asserter->assertEquals($param, $params[$index], json_encode([$current, $params])); + } + if (isset($current['error'])) { + $map = array_merge(['msg' => 'Error', 'type' => E_USER_NOTICE], (array)$current['error']); + trigger_error($map['msg'], $map['type']); + } + if (isset($current['return-op'])) { + return self::op($current['return-op'], $params, $current['return']); + } + if (isset($current['return'])) { + return $current['return']; + } + return call_user_func_array($function, $params); + } + + // Check if all expected calls are performed + public static function isEmpty(): bool + { + return empty(self::$queue); + } + + // Initialize call queue + public static function initialize($op_file, $asserter): void + { + $file = dirname(__DIR__) . "/scripts/{$op_file}.json"; + self::$queue = json_decode(file_get_contents($file), true); + self::$asserter = $asserter; + } + + // Special output handling + private static function op($op, $params, $data) + { + switch ($op) { + case 'chr-array': + // Convert int array to string + $out = ''; + foreach ($data as $val) { + $out .= chr($val); + } + return $out; + case 'file': + $content = file_get_contents(__DIR__ . "/{$data[0]}"); + return substr($content, $data[1], $data[2]); + case 'key-save': + preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $params[1], $matches); + self::$stored['sec-websocket-key'] = trim($matches[1]); + return $data; + case 'key-respond': + $key = self::$stored['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + $encoded = base64_encode(pack('H*', sha1($key))); + return str_replace('{key}', $encoded, $data); + } + return $data; + } +} diff --git a/vendor/textalk/websocket/tests/mock/mock-socket.php b/vendor/textalk/websocket/tests/mock/mock-socket.php new file mode 100644 index 0000000..1d6889a --- /dev/null +++ b/vendor/textalk/websocket/tests/mock/mock-socket.php @@ -0,0 +1,78 @@ +<?php + +/** + * This file is used by tests to overload and mock various socket/stream calls. + */ + +namespace WebSocket; + +function stream_socket_server($local_socket, &$errno, &$errstr) +{ + $args = [$local_socket, $errno, $errstr]; + return MockSocket::handle('stream_socket_server', $args); +} +function stream_socket_accept() +{ + $args = func_get_args(); + return MockSocket::handle('stream_socket_accept', $args); +} +function stream_set_timeout() +{ + $args = func_get_args(); + return MockSocket::handle('stream_set_timeout', $args); +} +function stream_get_line() +{ + $args = func_get_args(); + return MockSocket::handle('stream_get_line', $args); +} +function stream_get_meta_data() +{ + $args = func_get_args(); + return MockSocket::handle('stream_get_meta_data', $args); +} +function feof() +{ + $args = func_get_args(); + return MockSocket::handle('feof', $args); +} +function ftell() +{ + $args = func_get_args(); + return MockSocket::handle('ftell', $args); +} +function fclose() +{ + $args = func_get_args(); + return MockSocket::handle('fclose', $args); +} +function fwrite() +{ + $args = func_get_args(); + return MockSocket::handle('fwrite', $args); +} +function fread() +{ + $args = func_get_args(); + return MockSocket::handle('fread', $args); +} +function stream_context_create() +{ + $args = func_get_args(); + return MockSocket::handle('stream_context_create', $args); +} +function stream_socket_client($remote_socket, &$errno, &$errstr, $timeout, $flags, $context) +{ + $args = [$remote_socket, $errno, $errstr, $timeout, $flags, $context]; + return MockSocket::handle('stream_socket_client', $args); +} +function get_resource_type() +{ + $args = func_get_args(); + return MockSocket::handle('get_resource_type', $args); +} +function stream_socket_get_name() +{ + $args = func_get_args(); + return MockSocket::handle('stream_socket_get_name', $args); +} diff --git a/vendor/textalk/websocket/tests/mock/payload.128.txt b/vendor/textalk/websocket/tests/mock/payload.128.txt new file mode 100644 index 0000000..4c078a8 --- /dev/null +++ b/vendor/textalk/websocket/tests/mock/payload.128.txt @@ -0,0 +1,5 @@ +128-chars +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +a
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/mock/payload.65536.txt b/vendor/textalk/websocket/tests/mock/payload.65536.txt new file mode 100644 index 0000000..284eadd --- /dev/null +++ b/vendor/textalk/websocket/tests/mock/payload.65536.txt @@ -0,0 +1,1682 @@ +65536-chars +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abscdefgheijklmnopqrstuvwxyz0123456789 +abcd
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.close.json b/vendor/textalk/websocket/tests/scripts/client.close.json new file mode 100644 index 0000000..d449c17 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.close.json @@ -0,0 +1,76 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [], + "return": 12 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return":[136, 154] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return":[98, 250, 210, 113] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 26 + ], + "return-op": "chr-array", + "return": [97, 18, 145, 29, 13, 137, 183, 81, 3, 153, 185, 31, 13, 141, 190, 20, 6, 157, 183, 21, 88, 218, 227, 65, 82, 202] + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return":true + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "Unknown" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-authed.json b/vendor/textalk/websocket/tests/scripts/client.connect-authed.json new file mode 100644 index 0000000..6f75b6b --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-authed.json @@ -0,0 +1,59 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "ssl:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 248 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-bad-context.json b/vendor/textalk/websocket/tests/scripts/client.connect-bad-context.json new file mode 100644 index 0000000..e084a84 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-bad-context.json @@ -0,0 +1,7 @@ +[ + { + "function": "get_resource_type", + "params": [], + "return": "@mock-bad-context" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-context.json b/vendor/textalk/websocket/tests/scripts/client.connect-context.json new file mode 100644 index 0000000..0fcc279 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-context.json @@ -0,0 +1,59 @@ +[ + { + "function": "get_resource_type", + "params": [], + "return": "stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-error.json b/vendor/textalk/websocket/tests/scripts/client.connect-error.json new file mode 100644 index 0000000..e6c523d --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-error.json @@ -0,0 +1,23 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "error": { + "msg": "A PHP error", + "type": 512 + }, + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-extended.json b/vendor/textalk/websocket/tests/scripts/client.connect-extended.json new file mode 100644 index 0000000..4659e3e --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-extended.json @@ -0,0 +1,59 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 224 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-failed.json b/vendor/textalk/websocket/tests/scripts/client.connect-failed.json new file mode 100644 index 0000000..47725c2 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-failed.json @@ -0,0 +1,19 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-headers.json b/vendor/textalk/websocket/tests/scripts/client.connect-headers.json new file mode 100644 index 0000000..69808ce --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-headers.json @@ -0,0 +1,59 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 255 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-invalid-key.json b/vendor/textalk/websocket/tests/scripts/client.connect-invalid-key.json new file mode 100644 index 0000000..6472317 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-invalid-key.json @@ -0,0 +1,50 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: BAD" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-invalid-upgrade.json b/vendor/textalk/websocket/tests/scripts/client.connect-invalid-upgrade.json new file mode 100644 index 0000000..28cbc7e --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-invalid-upgrade.json @@ -0,0 +1,50 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return": "Invalid upgrade response" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-persistent.json b/vendor/textalk/websocket/tests/scripts/client.connect-persistent.json new file mode 100644 index 0000000..d95af01 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-persistent.json @@ -0,0 +1,95 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 5, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "persistent stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "persistent stream" + }, + { + "function": "ftell", + "params": [ + "@mock-stream" + ], + "return": 0 + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 248 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "persistent stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "persistent stream" + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return":true + } +] + diff --git a/vendor/textalk/websocket/tests/scripts/client.connect-timeout.json b/vendor/textalk/websocket/tests/scripts/client.connect-timeout.json new file mode 100644 index 0000000..c1ae6b5 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect-timeout.json @@ -0,0 +1,59 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 300, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 300 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.connect.json b/vendor/textalk/websocket/tests/scripts/client.connect.json new file mode 100644 index 0000000..eb7ecd2 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.connect.json @@ -0,0 +1,59 @@ +[ + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.destruct.json b/vendor/textalk/websocket/tests/scripts/client.destruct.json new file mode 100644 index 0000000..c04755b --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.destruct.json @@ -0,0 +1,23 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return": true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/client.reconnect.json b/vendor/textalk/websocket/tests/scripts/client.reconnect.json new file mode 100644 index 0000000..9d505fe --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/client.reconnect.json @@ -0,0 +1,100 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "Unknown" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_context_create", + "params": [], + "return": "@mock-stream-context" + }, + { + "function": "stream_socket_client", + "params": [ + "tcp:\/\/localhost:8000", + null, + null, + 5, + 4, + "@mock-stream-context" + ], + "return": "@mock-stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 5 + ], + "return": true + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return-op": "key-save", + "return": 199 + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n\r\n" + ], + "return-op": "key-respond", + "return": "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {key}" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 147] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [33, 111, 149, 174] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 19 + ], + "return-op": "chr-array", + "return": [115, 10, 246, 203, 72, 25, 252, 192, 70, 79, 244, 142, 76, 10, 230, 221, 64, 8, 240] + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/close-remote.json b/vendor/textalk/websocket/tests/scripts/close-remote.json new file mode 100644 index 0000000..d2eea02 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/close-remote.json @@ -0,0 +1,55 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [136, 137] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [54, 79, 233, 244] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 9 + ], + "return-op": "chr-array", + "return": [117, 35, 170, 152, 89, 60, 128, 154, 81] + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [], + "return": 33 + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return": true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/config-timeout.json b/vendor/textalk/websocket/tests/scripts/config-timeout.json new file mode 100644 index 0000000..243ae22 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/config-timeout.json @@ -0,0 +1,24 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 300 + ], + "return": true + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/ping-pong.json b/vendor/textalk/websocket/tests/scripts/ping-pong.json new file mode 100644 index 0000000..b647e91 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/ping-pong.json @@ -0,0 +1,150 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 17 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 6 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [138, 139] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [1, 1, 1, 1] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 11 + ], + "return-op": "chr-array", + "return": [82, 100, 115, 119, 100, 115, 33, 113, 104, 111, 102] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [138, 128] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [1, 1, 1, 1] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [137, 139] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [180, 77, 192, 201] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 11 + ], + "return-op": "chr-array", + "return": [247, 33, 169, 172, 218, 57, 224, 185, 221, 35, 167] + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 17 + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 147] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [33, 111, 149, 174] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 19 + ], + "return-op": "chr-array", + "return": [115, 10, 246, 203, 72, 25, 252, 192, 70, 79, 244, 142, 76, 10, 230, 221, 64, 8, 240] + } +] diff --git a/vendor/textalk/websocket/tests/scripts/receive-bad-opcode.json b/vendor/textalk/websocket/tests/scripts/receive-bad-opcode.json new file mode 100644 index 0000000..27dd2ba --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/receive-bad-opcode.json @@ -0,0 +1,18 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [140, 115] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/receive-broken-read.json b/vendor/textalk/websocket/tests/scripts/receive-broken-read.json new file mode 100644 index 0000000..15e3fa9 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/receive-broken-read.json @@ -0,0 +1,58 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [], + "return": false + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": true, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": true, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return":true + } +] diff --git a/vendor/textalk/websocket/tests/scripts/receive-client-timeout.json b/vendor/textalk/websocket/tests/scripts/receive-client-timeout.json new file mode 100644 index 0000000..5a258b0 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/receive-client-timeout.json @@ -0,0 +1,50 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [], + "return": false + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": true, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return":true + } +] diff --git a/vendor/textalk/websocket/tests/scripts/receive-empty-read.json b/vendor/textalk/websocket/tests/scripts/receive-empty-read.json new file mode 100644 index 0000000..f752017 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/receive-empty-read.json @@ -0,0 +1,58 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": true, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return":true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/receive-fragmentation.json b/vendor/textalk/websocket/tests/scripts/receive-fragmentation.json new file mode 100644 index 0000000..5ae9572 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/receive-fragmentation.json @@ -0,0 +1,126 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [1, 136] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [105, 29, 187, 18] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8 + ], + "return-op": "chr-array", + "return": [36, 104, 215, 102, 0, 61, 221, 96] + }, + + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [138, 139] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [1, 1, 1, 1] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 11 + ], + "return-op": "chr-array", + "return": [82, 100, 115, 119, 100, 115, 33, 113, 104, 111, 102] + }, + + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [0, 136] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [221, 240, 46, 69] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8 + ], + "return-op": "chr-array", + "return": [188, 151, 67, 32, 179, 132, 14, 49] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [128, 131] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [9, 60, 117, 193] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 3 + ], + "return-op": "chr-array", + "return": [108, 79, 1] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-bad-opcode.json b/vendor/textalk/websocket/tests/scripts/send-bad-opcode.json new file mode 100644 index 0000000..c0859d1 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-bad-opcode.json @@ -0,0 +1,9 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-broken-write.json b/vendor/textalk/websocket/tests/scripts/send-broken-write.json new file mode 100644 index 0000000..838c826 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-broken-write.json @@ -0,0 +1,43 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 18 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": true, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "fclose", + "params": [], + "return": true + } +] diff --git a/vendor/textalk/websocket/tests/scripts/send-convenicance.json b/vendor/textalk/websocket/tests/scripts/send-convenicance.json new file mode 100644 index 0000000..194cb5a --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-convenicance.json @@ -0,0 +1,86 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 26 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 6 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 6 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:8000" + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-failed-write.json b/vendor/textalk/websocket/tests/scripts/send-failed-write.json new file mode 100644 index 0000000..ea3d46b --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-failed-write.json @@ -0,0 +1,43 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": true, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "fclose", + "params": [], + "return": true + } +] diff --git a/vendor/textalk/websocket/tests/scripts/send-receive-128.json b/vendor/textalk/websocket/tests/scripts/send-receive-128.json new file mode 100644 index 0000000..66abea7 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-receive-128.json @@ -0,0 +1,50 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 132 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 126] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [0, 128] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 128 + ], + "return-op": "file", + "return": ["payload.128.txt", 0, 132] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-receive-65536.json b/vendor/textalk/websocket/tests/scripts/send-receive-65536.json new file mode 100644 index 0000000..67604b5 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-receive-65536.json @@ -0,0 +1,113 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 65546 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 127] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8 + ], + "return-op": "chr-array", + "return": [0, 0, 0, 0, 0, 1, 0, 0] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 65536 + ], + "return-op": "file", + "return": ["payload.65536.txt", 0, 16374] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 49162 + ], + "return-op": "file", + "return": ["payload.65536.txt", 16374, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 40970 + ], + "return-op": "file", + "return": ["payload.65536.txt", 24566, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 32778 + ], + "return-op": "file", + "return": ["payload.65536.txt", 32758, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 24586 + ], + "return-op": "file", + "return": ["payload.65536.txt", 40950, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 16394 + ], + "return-op": "file", + "return": ["payload.65536.txt", 49142, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8202 + ], + "return-op": "file", + "return": ["payload.65536.txt", 57334, 8192] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 10 + ], + "return-op": "file", + "return": ["payload.65536.txt", 65526, 10] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-receive-multi-fragment.json b/vendor/textalk/websocket/tests/scripts/send-receive-multi-fragment.json new file mode 100644 index 0000000..426434e --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-receive-multi-fragment.json @@ -0,0 +1,112 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [], + "return": 14 + }, + { + "function": "fwrite", + "params": [], + "return": 14 + }, + { + "function": "fwrite", + "params": [], + "return": 9 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [1, 136] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [105, 29, 187, 18] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8 + ], + "return-op": "chr-array", + "return": [36, 104, 215, 102, 0, 61, 221, 96] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [0, 136] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [221, 240, 46, 69] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 8 + ], + "return-op": "chr-array", + "return": [188, 151, 67, 32, 179, 132, 14, 49] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [128, 131] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [9, 60, 117, 193] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 3 + ], + "return-op": "chr-array", + "return": [108, 79, 1] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/send-receive.json b/vendor/textalk/websocket/tests/scripts/send-receive.json new file mode 100644 index 0000000..7ad0350 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/send-receive.json @@ -0,0 +1,50 @@ +[ + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 23 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 147] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [33, 111, 149, 174] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 19 + ], + "return-op": "chr-array", + "return": [115, 10, 246, 203, 72, 25, 252, 192, 70, 79, 244, 142, 76, 10, 230, 221, 64, 8, 240] + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-destruct.json b/vendor/textalk/websocket/tests/scripts/server.accept-destruct.json new file mode 100644 index 0000000..970286b --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-destruct.json @@ -0,0 +1,315 @@ +[ + { + "function": "stream_socket_accept", + "params": [ + "@mock-socket" + ], + "return": "@mock-stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "GET \/my\/mock\/path HTTP\/1.1" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 171, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "host: localhost:8000" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 149, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "user-agent: websocket-client-php" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 115, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "connection: Upgrade" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 94, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "upgrade: websocket" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 74, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-key: cktLWXhUdDQ2OXF0ZCFqOQ==" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 29, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-version: 13" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } + , + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "fwrite", + "params": [ + "@mock-stream", + "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: YmysboNHNoWzWVeQpduY7xELjgU=\r\n\r\n" + ], + "return": 129 + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [129, 147] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [33, 111, 149, 174] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 19 + ], + "return-op": "chr-array", + "return": [115, 10, 246, 203, 72, 25, 252, 192, 70, 79, 244, 142, 76, 10, 230, 221, 64, 8, 240] + }, + + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return": true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-error-connect.json b/vendor/textalk/websocket/tests/scripts/server.accept-error-connect.json new file mode 100644 index 0000000..f0fef27 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-error-connect.json @@ -0,0 +1,18 @@ +[ + { + "function": "stream_socket_accept", + "params": [], + "error": { + "msg": "A PHP error", + "type": 512 + }, + "return": false + }, + { + "function": "fclose", + "params": [ + false + ], + "return": true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-failed-connect.json b/vendor/textalk/websocket/tests/scripts/server.accept-failed-connect.json new file mode 100644 index 0000000..24692db --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-failed-connect.json @@ -0,0 +1,14 @@ +[ + { + "function": "stream_socket_accept", + "params": [], + "return": false + }, + { + "function": "fclose", + "params": [ + false + ], + "return": true + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-failed-http.json b/vendor/textalk/websocket/tests/scripts/server.accept-failed-http.json new file mode 100644 index 0000000..ab66db4 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-failed-http.json @@ -0,0 +1,265 @@ +[ + { + "function": "stream_socket_accept", + "params": [ + "@mock-socket" + ], + "return": "@mock-stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "missing http header" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 171, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "host: localhost:8000" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 149, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "user-agent: websocket-client-php" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 115, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "connection: Upgrade" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 94, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "upgrade: websocket" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 74, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "no key in upgrade request" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 29, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-version: 13" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } + , + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-failed-ws-key.json b/vendor/textalk/websocket/tests/scripts/server.accept-failed-ws-key.json new file mode 100644 index 0000000..06bf4e6 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-failed-ws-key.json @@ -0,0 +1,265 @@ +[ + { + "function": "stream_socket_accept", + "params": [ + "@mock-socket" + ], + "return": "@mock-stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "GET \/my\/mock\/path HTTP\/1.1" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 171, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "host: localhost:8000" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 149, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "user-agent: websocket-client-php" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 115, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "connection: Upgrade" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 94, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "upgrade: websocket" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 74, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "no key in upgrade request" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 29, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-version: 13" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } + , + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept-timeout.json b/vendor/textalk/websocket/tests/scripts/server.accept-timeout.json new file mode 100644 index 0000000..17a5660 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept-timeout.json @@ -0,0 +1,289 @@ +[ + { + "function": "stream_socket_accept", + "params": [ + "@mock-socket", + 300 + ], + "return": "@mock-stream" + }, + { + "function": "stream_set_timeout", + "params": [ + "@mock-stream", + 300 + ], + "return": true + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "GET \/my\/mock\/path HTTP\/1.1" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 171, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "host: localhost:8000" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 149, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "user-agent: websocket-client-php" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 115, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "connection: Upgrade" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 94, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "upgrade: websocket" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 74, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-key: cktLWXhUdDQ2OXF0ZCFqOQ==" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 29, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-version: 13" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } + , + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "fwrite", + "params": [ + "@mock-stream", + "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: YmysboNHNoWzWVeQpduY7xELjgU=\r\n\r\n" + ], + "return": 129 + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.accept.json b/vendor/textalk/websocket/tests/scripts/server.accept.json new file mode 100644 index 0000000..a1463dc --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.accept.json @@ -0,0 +1,287 @@ +[ + { + "function": "stream_socket_accept", + "params": [ + "@mock-socket" + ], + "return": "@mock-stream" + }, + { + "function": "stream_socket_get_name", + "params": [ + "@mock-stream" + ], + "return": "127.0.0.1:12345" + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "GET \/my\/mock\/path HTTP\/1.1" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 171, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "host: localhost:8000" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 149, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "user-agent: websocket-client-php" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 115, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "connection: Upgrade" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 94, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "upgrade: websocket" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 74, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-key: cktLWXhUdDQ2OXF0ZCFqOQ==" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 29, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "sec-websocket-version: 13" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 2, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + } + , + { + "function": "stream_get_line", + "params": [ + "@mock-stream", + 1024, + "\r\n" + ], + "return": "" + }, + { + "function": "stream_get_meta_data", + "params": [ + "@mock-stream" + ], + "return": { + "timed_out": false, + "blocked": true, + "eof": false, + "stream_type": "tcp_socket\/ssl", + "mode": "r+", + "unread_bytes": 0, + "seekable": false + } + }, + { + "function": "feof", + "params": [ + "@mock-stream" + ], + "return": false + }, + { + "function": "fwrite", + "params": [ + "@mock-stream", + "HTTP\/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: YmysboNHNoWzWVeQpduY7xELjgU=\r\n\r\n" + ], + "return": 129 + }, + { + "function": "fwrite", + "params": [ + "@mock-stream" + ], + "return": 13 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.close.json b/vendor/textalk/websocket/tests/scripts/server.close.json new file mode 100644 index 0000000..b6e045c --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.close.json @@ -0,0 +1,70 @@ +[ + + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": true + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fwrite", + "params": [], + "return":12 + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "stream" + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 2 + ], + "return-op": "chr-array", + "return": [136,154] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 4 + ], + "return-op": "chr-array", + "return": [245,55,62,8] + }, + { + "function": "fread", + "params": [ + "@mock-stream", + 26 + ], + "return-op": "chr-array", + "return": [246,223,125,100,154,68,91,40,148,84,85,102,154,64,82,109,145,80,91,108,207,23,15,56,197,7] + }, + { + "function": "fclose", + "params": [ + "@mock-stream" + ], + "return": true + }, + { + "function": "get_resource_type", + "params": [ + "@mock-stream" + ], + "return": "Unknown" + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.construct-error-socket-server.json b/vendor/textalk/websocket/tests/scripts/server.construct-error-socket-server.json new file mode 100644 index 0000000..3f4909f --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.construct-error-socket-server.json @@ -0,0 +1,28 @@ +[ + { + "function": "stream_socket_server", + "params": [ + "tcp://0.0.0.0:9999", + null, + null + ], + "error": { + "msg": "A PHP error", + "type": 512 + }, + "return": false + }, + { + "function": "stream_socket_server", + "params": [ + "tcp://0.0.0.0:10000", + null, + null + ], + "error": { + "msg": "A PHP error", + "type": 512 + }, + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.construct-failed-socket-server.json b/vendor/textalk/websocket/tests/scripts/server.construct-failed-socket-server.json new file mode 100644 index 0000000..30d1ea6 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.construct-failed-socket-server.json @@ -0,0 +1,20 @@ +[ + { + "function": "stream_socket_server", + "params": [ + "tcp://0.0.0.0:9999", + null, + null + ], + "return": false + }, + { + "function": "stream_socket_server", + "params": [ + "tcp://0.0.0.0:10000", + null, + null + ], + "return": false + } +]
\ No newline at end of file diff --git a/vendor/textalk/websocket/tests/scripts/server.construct.json b/vendor/textalk/websocket/tests/scripts/server.construct.json new file mode 100644 index 0000000..c3181f3 --- /dev/null +++ b/vendor/textalk/websocket/tests/scripts/server.construct.json @@ -0,0 +1,11 @@ +[ + { + "function": "stream_socket_server", + "params": [ + "tcp://0.0.0.0:8000", + null, + null + ], + "return": "@mock-socket" + } +]
\ No newline at end of file |