summaryrefslogtreecommitdiffstats
path: root/vendor/textalk
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/textalk')
-rw-r--r--vendor/textalk/websocket/.github/ISSUE_TEMPLATE/bug_report.md21
-rw-r--r--vendor/textalk/websocket/.github/ISSUE_TEMPLATE/feature_request.md14
-rw-r--r--vendor/textalk/websocket/.github/ISSUE_TEMPLATE/other-issue.md10
-rw-r--r--vendor/textalk/websocket/.github/workflows/acceptance.yml97
-rw-r--r--vendor/textalk/websocket/.gitignore6
-rw-r--r--vendor/textalk/websocket/COPYING.md16
-rw-r--r--vendor/textalk/websocket/Makefile32
-rw-r--r--vendor/textalk/websocket/README.md67
-rw-r--r--vendor/textalk/websocket/codestandard.xml10
-rw-r--r--vendor/textalk/websocket/composer.json34
-rw-r--r--vendor/textalk/websocket/docs/Changelog.md130
-rw-r--r--vendor/textalk/websocket/docs/Client.md137
-rw-r--r--vendor/textalk/websocket/docs/Contributing.md44
-rw-r--r--vendor/textalk/websocket/docs/Examples.md98
-rw-r--r--vendor/textalk/websocket/docs/Message.md60
-rw-r--r--vendor/textalk/websocket/docs/Server.md136
-rw-r--r--vendor/textalk/websocket/examples/echoserver.php87
-rw-r--r--vendor/textalk/websocket/examples/random_client.php94
-rw-r--r--vendor/textalk/websocket/examples/random_server.php93
-rw-r--r--vendor/textalk/websocket/examples/send.php51
-rw-r--r--vendor/textalk/websocket/lib/BadOpcodeException.php7
-rw-r--r--vendor/textalk/websocket/lib/BadUriException.php7
-rw-r--r--vendor/textalk/websocket/lib/Base.php486
-rw-r--r--vendor/textalk/websocket/lib/Client.php216
-rw-r--r--vendor/textalk/websocket/lib/ConnectionException.php26
-rw-r--r--vendor/textalk/websocket/lib/Exception.php7
-rw-r--r--vendor/textalk/websocket/lib/Message/Binary.php8
-rw-r--r--vendor/textalk/websocket/lib/Message/Close.php8
-rw-r--r--vendor/textalk/websocket/lib/Message/Factory.php25
-rw-r--r--vendor/textalk/websocket/lib/Message/Message.php53
-rw-r--r--vendor/textalk/websocket/lib/Message/Ping.php8
-rw-r--r--vendor/textalk/websocket/lib/Message/Pong.php8
-rw-r--r--vendor/textalk/websocket/lib/Message/Text.php8
-rw-r--r--vendor/textalk/websocket/lib/Server.php176
-rw-r--r--vendor/textalk/websocket/lib/TimeoutException.php7
-rw-r--r--vendor/textalk/websocket/phpunit.xml.dist14
-rw-r--r--vendor/textalk/websocket/tests/ClientTest.php449
-rw-r--r--vendor/textalk/websocket/tests/ExceptionTest.php52
-rw-r--r--vendor/textalk/websocket/tests/MessageTest.php61
-rw-r--r--vendor/textalk/websocket/tests/README.md29
-rw-r--r--vendor/textalk/websocket/tests/ServerTest.php448
-rw-r--r--vendor/textalk/websocket/tests/bootstrap.php6
-rw-r--r--vendor/textalk/websocket/tests/mock/EchoLog.php34
-rw-r--r--vendor/textalk/websocket/tests/mock/MockSocket.php79
-rw-r--r--vendor/textalk/websocket/tests/mock/mock-socket.php78
-rw-r--r--vendor/textalk/websocket/tests/mock/payload.128.txt5
-rw-r--r--vendor/textalk/websocket/tests/mock/payload.65536.txt1682
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.close.json76
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-authed.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-bad-context.json7
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-context.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-error.json23
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-extended.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-failed.json19
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-headers.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-invalid-key.json50
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-invalid-upgrade.json50
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-persistent.json95
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect-timeout.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.connect.json59
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.destruct.json23
-rw-r--r--vendor/textalk/websocket/tests/scripts/client.reconnect.json100
-rw-r--r--vendor/textalk/websocket/tests/scripts/close-remote.json55
-rw-r--r--vendor/textalk/websocket/tests/scripts/config-timeout.json24
-rw-r--r--vendor/textalk/websocket/tests/scripts/ping-pong.json150
-rw-r--r--vendor/textalk/websocket/tests/scripts/receive-bad-opcode.json18
-rw-r--r--vendor/textalk/websocket/tests/scripts/receive-broken-read.json58
-rw-r--r--vendor/textalk/websocket/tests/scripts/receive-client-timeout.json50
-rw-r--r--vendor/textalk/websocket/tests/scripts/receive-empty-read.json58
-rw-r--r--vendor/textalk/websocket/tests/scripts/receive-fragmentation.json126
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-bad-opcode.json9
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-broken-write.json43
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-convenicance.json86
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-failed-write.json43
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-receive-128.json50
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-receive-65536.json113
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-receive-multi-fragment.json112
-rw-r--r--vendor/textalk/websocket/tests/scripts/send-receive.json50
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-destruct.json315
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-error-connect.json18
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-failed-connect.json14
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-failed-http.json265
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-failed-ws-key.json265
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept-timeout.json289
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.accept.json287
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.close.json70
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.construct-error-socket-server.json28
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.construct-failed-socket-server.json20
-rw-r--r--vendor/textalk/websocket/tests/scripts/server.construct.json11
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