Cleanup
Application stack working! Existing unit tests fixed Implemented HyBi-10 unframing
This commit is contained in:
parent
7514ce8e85
commit
51d0516aa3
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
class CloseConnection implements CommandInterface {
|
class CloseConnection implements CommandInterface {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
interface CommandInterface {
|
interface CommandInterface {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
class Null implements CommandInterface {
|
class Null implements CommandInterface {
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Ratchet\Server\Command;
|
namespace Ratchet\Command;
|
||||||
use Ratchet\SocketCollection;
|
use Ratchet\SocketCollection;
|
||||||
|
|
||||||
class SendMessage implements CommandInterface {
|
class SendMessage implements CommandInterface {
|
@ -8,6 +8,8 @@ use Ratchet\ReceiverInterface;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://ca.php.net/manual/en/ref.http.php
|
* @link http://ca.php.net/manual/en/ref.http.php
|
||||||
|
* @todo Make sure this works both ways (client/server) as stack needs to exist on client for framing
|
||||||
|
* @todo Clean up Client/Version stuff. This should be a factory making single instances of Version classes, implement chain of reponsibility for version - client should implement an interface?
|
||||||
*/
|
*/
|
||||||
class WebSocket implements ProtocolInterface {
|
class WebSocket implements ProtocolInterface {
|
||||||
/**
|
/**
|
||||||
@ -25,6 +27,11 @@ class WebSocket implements ProtocolInterface {
|
|||||||
*/
|
*/
|
||||||
protected $_app;
|
protected $_app;
|
||||||
|
|
||||||
|
protected $_versions = array(
|
||||||
|
'HyBi10' => null
|
||||||
|
, 'Hixie76' => null
|
||||||
|
);
|
||||||
|
|
||||||
public function __construct(ReceiverInterface $application) {
|
public function __construct(ReceiverInterface $application) {
|
||||||
$this->_lookup = new \SplObjectStorage;
|
$this->_lookup = new \SplObjectStorage;
|
||||||
$this->_app = $application;
|
$this->_app = $application;
|
||||||
@ -62,15 +69,29 @@ class WebSocket implements ProtocolInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onRecv(SocketInterface $from, $msg) {
|
public function onRecv(SocketInterface $from, $msg) {
|
||||||
$client = $this->_lookup[$from];
|
$client = $this->_lookup[$from];
|
||||||
if (true !== $client->isHandshakeComplete()) {
|
if (true !== $client->isHandshakeComplete()) {
|
||||||
$headers = $this->getHeaders($msg);
|
|
||||||
$header = $client->doHandshake($this->getVersion($headers));
|
// remove client, get protocol, do handshake, return, etc
|
||||||
|
|
||||||
|
$headers = $this->getHeaders($msg);
|
||||||
|
$response = $client->setVersion($this->getVersion($headers))->doHandshake($headers);
|
||||||
|
|
||||||
|
$header = '';
|
||||||
|
foreach ($response as $key => $val) {
|
||||||
|
if (!empty($key)) {
|
||||||
|
$header .= "{$key}: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$header .= "{$val}\r\n";
|
||||||
|
}
|
||||||
|
$header .= "\r\n";
|
||||||
|
// $header = implode("\r\n", $response) . "\r\n";
|
||||||
|
|
||||||
// $from->write($header, strlen($header));
|
// $from->write($header, strlen($header));
|
||||||
$to = new \Ratchet\SocketCollection;
|
$to = new \Ratchet\SocketCollection;
|
||||||
$to->enqueue($from);
|
$to->enqueue($from);
|
||||||
$cmd = new \Ratchet\Server\Command\SendMessage($to);
|
$cmd = new \Ratchet\Command\SendMessage($to);
|
||||||
$cmd->setMessage($header);
|
$cmd->setMessage($header);
|
||||||
|
|
||||||
// call my decorated onRecv()
|
// call my decorated onRecv()
|
||||||
@ -80,6 +101,20 @@ $this->_server->log('Returning handshake: ' . $header);
|
|||||||
return $cmd;
|
return $cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$msg = $client->getVersion()->unframe($msg);
|
||||||
|
if (is_array($msg)) { // temporary
|
||||||
|
$msg = $msg['payload'];
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\UnexpectedValueException $e) {
|
||||||
|
$to = new \Ratchet\SocketCollection;
|
||||||
|
$to->enqueue($from);
|
||||||
|
$cmd = new \Ratchet\Command\Close($to);
|
||||||
|
|
||||||
|
return $cmd;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->_app->onRecv($from, $msg);
|
return $this->_app->onRecv($from, $msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +133,7 @@ $this->_server->log('Returning handshake: ' . $header);
|
|||||||
/**
|
/**
|
||||||
* @param string
|
* @param string
|
||||||
* @return array
|
* @return array
|
||||||
|
* @todo Consider strtolower all the header keys...right now PHP Changes Sec-WebSocket-X to Sec-Websocket-X...this could change
|
||||||
*/
|
*/
|
||||||
protected function getHeaders($http_message) {
|
protected function getHeaders($http_message) {
|
||||||
return http_parse_headers($http_message);
|
return http_parse_headers($http_message);
|
||||||
@ -109,7 +145,11 @@ $this->_server->log('Returning handshake: ' . $header);
|
|||||||
protected function getVersion(array $headers) {
|
protected function getVersion(array $headers) {
|
||||||
if (isset($headers['Sec-Websocket-Version'])) { // HyBi
|
if (isset($headers['Sec-Websocket-Version'])) { // HyBi
|
||||||
if ($headers['Sec-Websocket-Version'] == '8') {
|
if ($headers['Sec-Websocket-Version'] == '8') {
|
||||||
return new Version\Hybi10($headers);
|
if (null === $this->_versions['HyBi10']) {
|
||||||
|
$this->_versions['HyBi10'] = new Version\Hybi10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_versions['HyBi10'];
|
||||||
}
|
}
|
||||||
} elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie
|
} elseif (isset($headers['Sec-Websocket-Key2'])) { // Hixie
|
||||||
}
|
}
|
||||||
|
14
lib/Ratchet/Protocol/WebSocket/AppInterface.php
Normal file
14
lib/Ratchet/Protocol/WebSocket/AppInterface.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
namespace Ratchet\Protocol\Websocket;
|
||||||
|
use Ratchet\ReceiverInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo App interfaces this (optionally) if is meant for WebSocket
|
||||||
|
* @todo WebSocket checks if instanceof AppInterface, if so uses getSubProtocol() when doing handshake
|
||||||
|
*/
|
||||||
|
interface AppInterface extends ReceiverInterface {
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getSubProtocol();
|
||||||
|
}
|
@ -13,13 +13,30 @@ class Client {
|
|||||||
*/
|
*/
|
||||||
protected $_hands_shook = false;
|
protected $_hands_shook = false;
|
||||||
|
|
||||||
public function doHandshake(VersionInterface $version) {
|
/**
|
||||||
$key = $version->sign();
|
* @param VersionInterface
|
||||||
// $tosend['Sec-WebSocket-Accept'] = $key;
|
* @return Client
|
||||||
|
*/
|
||||||
|
public function setVersion(VersionInterface $version) {
|
||||||
|
$this->_version = $version;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return VersionInterface
|
||||||
|
*/
|
||||||
|
public function getVersion() {
|
||||||
|
return $this->_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function doHandshake(array $headers) {
|
||||||
$this->_hands_shook = true;
|
$this->_hands_shook = true;
|
||||||
|
|
||||||
return "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {$key}\r\nSec-WebSocket-Protocol: test\r\n\r\n";
|
return $this->_version->handshake($headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,24 +2,23 @@
|
|||||||
namespace Ratchet\Protocol\WebSocket\Version;
|
namespace Ratchet\Protocol\WebSocket\Version;
|
||||||
|
|
||||||
class Hixie76 implements VersionInterface {
|
class Hixie76 implements VersionInterface {
|
||||||
protected $_headers = array();
|
public function handshake(array $headers) {
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct(array $headers) {
|
public function unframe($message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function frame($message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sign($key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* What was I doing here?
|
||||||
* @param Headers
|
* @param Headers
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function concatinateKeyString($headers) {
|
public function concatinateKeyString($headers) {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sign($key) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,17 +4,109 @@ namespace Ratchet\Protocol\WebSocket\Version;
|
|||||||
class Hybi10 implements VersionInterface {
|
class Hybi10 implements VersionInterface {
|
||||||
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||||
|
|
||||||
protected $_headers = array();
|
/**
|
||||||
|
*/
|
||||||
|
public function handshake(array $headers) {
|
||||||
|
$key = $this->sign($headers['Sec-Websocket-Key']);
|
||||||
|
|
||||||
public function __construct(array $headers) {
|
return array(
|
||||||
$this->_headers = $headers;
|
'' => 'HTTP/1.1 101 Switching Protocols'
|
||||||
|
, 'Upgrade' => 'websocket'
|
||||||
|
, 'Connection' => 'Upgrade'
|
||||||
|
, 'Sec-WebSocket-Accept' => $this->sign($headers['Sec-Websocket-Key'])
|
||||||
|
// , 'Sec-WebSocket-Protocol' => ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unframe a message received from the client
|
||||||
|
* Thanks to @lemmingzshadow for the code on decoding a HyBi-10 frame
|
||||||
|
* @link https://github.com/lemmingzshadow/php-websocket
|
||||||
|
* @param string
|
||||||
|
* @return string
|
||||||
|
* @throws UnexpectedValueException
|
||||||
|
*/
|
||||||
|
public function unframe($message) {
|
||||||
|
$data = $message;
|
||||||
|
$payloadLength = '';
|
||||||
|
$mask = '';
|
||||||
|
$unmaskedPayload = '';
|
||||||
|
$decodedData = array();
|
||||||
|
|
||||||
|
// estimate frame type:
|
||||||
|
$firstByteBinary = sprintf('%08b', ord($data[0]));
|
||||||
|
$secondByteBinary = sprintf('%08b', ord($data[1]));
|
||||||
|
$opcode = bindec(substr($firstByteBinary, 4, 4));
|
||||||
|
$isMasked = ($secondByteBinary[0] == '1') ? true : false;
|
||||||
|
$payloadLength = ord($data[1]) & 127;
|
||||||
|
|
||||||
|
// close connection if unmasked frame is received:
|
||||||
|
if($isMasked === false) {
|
||||||
|
throw new \UnexpectedValueException('Masked byte is false');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($opcode) {
|
||||||
|
// text frame:
|
||||||
|
case 1:
|
||||||
|
$decodedData['type'] = 'text';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// connection close frame:
|
||||||
|
case 8:
|
||||||
|
$decodedData['type'] = 'close';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ping frame:
|
||||||
|
case 9:
|
||||||
|
$decodedData['type'] = 'ping';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// pong frame:
|
||||||
|
case 10:
|
||||||
|
$decodedData['type'] = 'pong';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Close connection on unknown opcode:
|
||||||
|
throw new UnexpectedValueException('Unknown opcode');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($payloadLength === 126) {
|
||||||
|
$mask = substr($data, 4, 4);
|
||||||
|
$payloadOffset = 8;
|
||||||
|
} elseif($payloadLength === 127) {
|
||||||
|
$mask = substr($data, 10, 4);
|
||||||
|
$payloadOffset = 14;
|
||||||
|
} else {
|
||||||
|
$mask = substr($data, 2, 4);
|
||||||
|
$payloadOffset = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dataLength = strlen($data);
|
||||||
|
|
||||||
|
if($isMasked === true) {
|
||||||
|
for($i = $payloadOffset; $i < $dataLength; $i++) {
|
||||||
|
$j = $i - $payloadOffset;
|
||||||
|
$unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
|
||||||
|
}
|
||||||
|
$decodedData['payload'] = $unmaskedPayload;
|
||||||
|
} else {
|
||||||
|
$payloadOffset = $payloadOffset - 4;
|
||||||
|
$decodedData['payload'] = substr($data, $payloadOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decodedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sign($key = null) {
|
/**
|
||||||
if (null === $key) {
|
* @todo Complete this method
|
||||||
$key = $this->_headers['Sec-Websocket-Key'];
|
*/
|
||||||
}
|
public function frame($message) {
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sign($key) {
|
||||||
return base64_encode(sha1($key . static::GUID, 1));
|
return base64_encode(sha1($key . static::GUID, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,13 +3,31 @@ namespace Ratchet\Protocol\WebSocket\Version;
|
|||||||
|
|
||||||
interface VersionInterface {
|
interface VersionInterface {
|
||||||
/**
|
/**
|
||||||
|
* Perform the handshake and return the response headers
|
||||||
* @param array
|
* @param array
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
function __construct(array $headers);
|
function handshake(array $headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a framed message as per the protocol and return the decoded message
|
||||||
|
* @param string
|
||||||
|
* @return string
|
||||||
|
* @todo Return a frame object with message, type, masked?
|
||||||
|
*/
|
||||||
|
function unframe($message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string
|
* @param string
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function sign($header);
|
function frame($message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when doing the handshake to encode the key, verifying client/server are speaking the same language
|
||||||
|
* @param string
|
||||||
|
* @return string
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function sign($key);
|
||||||
}
|
}
|
@ -3,6 +3,9 @@ namespace Ratchet;
|
|||||||
use Ratchet\Server;
|
use Ratchet\Server;
|
||||||
use Ratchet\SocketObserver;
|
use Ratchet\SocketObserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Should probably move this into \Ratchet\Server namespace
|
||||||
|
*/
|
||||||
interface ReceiverInterface extends SocketObserver {
|
interface ReceiverInterface extends SocketObserver {
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -10,7 +10,7 @@ use Ratchet\Logging\NullLogger;
|
|||||||
* @todo Move SocketObserver methods to separate class, create, wrap class in __construct
|
* @todo Move SocketObserver methods to separate class, create, wrap class in __construct
|
||||||
* @todo Currently passing Socket object down the decorated chain - should be sending reference to it instead; Receivers do not interact with the Socket directly, they do so through the Command pattern
|
* @todo Currently passing Socket object down the decorated chain - should be sending reference to it instead; Receivers do not interact with the Socket directly, they do so through the Command pattern
|
||||||
*/
|
*/
|
||||||
class Server implements SocketObserver {
|
class Server implements SocketObserver, \IteratorAggregate {
|
||||||
/**
|
/**
|
||||||
* The master socket, receives all connections
|
* The master socket, receives all connections
|
||||||
* @type Socket
|
* @type Socket
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
namespace Ratchet\Tests\Protocol;
|
namespace Ratchet\Tests\Protocol;
|
||||||
use Ratchet\Protocol\WebSocket;
|
use Ratchet\Protocol\WebSocket;
|
||||||
use Ratchet\Tests\Mock\Socket;
|
use Ratchet\Tests\Mock\Socket;
|
||||||
|
use Ratchet\Tests\Mock\Application;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Protocol\WebSocket
|
* @covers Ratchet\Protocol\WebSocket
|
||||||
*/
|
*/
|
||||||
class ServerTest extends \PHPUnit_Framework_TestCase {
|
class WebSocketTest extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_ws;
|
protected $_ws;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_ws = new WebSocket();
|
$this->_ws = new WebSocket(new Application);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testServerImplementsServerInterface() {
|
public function testServerImplementsServerInterface() {
|
||||||
|
@ -11,10 +11,12 @@ use Ratchet\Tests\Mock\ArrayLogger;
|
|||||||
class ServerTest extends \PHPUnit_Framework_TestCase {
|
class ServerTest extends \PHPUnit_Framework_TestCase {
|
||||||
protected $_catalyst;
|
protected $_catalyst;
|
||||||
protected $_server;
|
protected $_server;
|
||||||
|
protected $_app;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
$this->_catalyst = new Socket;
|
$this->_catalyst = new Socket;
|
||||||
$this->_server = new Server($this->_catalyst, new TestApp);
|
$this->_app = new TestApp;
|
||||||
|
$this->_server = new Server($this->_catalyst, $this->_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getPrivateProperty($class, $name) {
|
protected function getPrivateProperty($class, $name) {
|
||||||
@ -36,7 +38,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase {
|
|||||||
|
|
||||||
public function testPassedLoggerIsSetInConstruct() {
|
public function testPassedLoggerIsSetInConstruct() {
|
||||||
$logger = new ArrayLogger;
|
$logger = new ArrayLogger;
|
||||||
$server = new Server(new Socket(), $logger);
|
$server = new Server(new Socket(), $this->_app, $logger);
|
||||||
|
|
||||||
$this->assertSame($logger, $this->getPrivateProperty($server, '_log'));
|
$this->assertSame($logger, $this->getPrivateProperty($server, '_log'));
|
||||||
}
|
}
|
||||||
@ -70,8 +72,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testBindToInvalidAddress() {
|
public function testBindToInvalidAddress() {
|
||||||
$this->markTestIncomplete();
|
return $this->markTestIncomplete();
|
||||||
return;
|
|
||||||
|
|
||||||
$app = new TestApp();
|
$app = new TestApp();
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ namespace Ratchet\Tests;
|
|||||||
use Ratchet\Tests\Mock\FakeSocket as Socket;
|
use Ratchet\Tests\Mock\FakeSocket as Socket;
|
||||||
use Ratchet\Socket as RealSocket;
|
use Ratchet\Socket as RealSocket;
|
||||||
use Ratchet\Tests\Mock\Protocol;
|
use Ratchet\Tests\Mock\Protocol;
|
||||||
|
use Ratchet\Tests\Mock\Application as TestApp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers Ratchet\Socket
|
* @covers Ratchet\Socket
|
||||||
@ -51,14 +52,14 @@ class SocketTest extends \PHPUnit_Framework_TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testConstructionFromProtocolInterfaceConfig() {
|
public function testConstructionFromProtocolInterfaceConfig() {
|
||||||
$protocol = new Protocol();
|
$protocol = new Protocol(new TestApp);
|
||||||
$socket = Socket::createFromConfig($protocol);
|
$socket = Socket::createFromConfig($protocol);
|
||||||
|
|
||||||
$this->assertInstanceOf('\\Ratchet\\Socket', $socket);
|
$this->assertInstanceOf('\\Ratchet\\Socket', $socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreationFromConfigOutputMatchesInput() {
|
public function testCreationFromConfigOutputMatchesInput() {
|
||||||
$protocol = new Protocol();
|
$protocol = new Protocol(new TestApp);
|
||||||
$socket = Socket::createFromConfig($protocol);
|
$socket = Socket::createFromConfig($protocol);
|
||||||
$config = $protocol::getDefaultConfig();
|
$config = $protocol::getDefaultConfig();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user