Skip to content

Commit

Permalink
Merge pull request #83 from Textalk/error-handling
Browse files Browse the repository at this point in the history
Error code in ConnectionException
  • Loading branch information
sirn-se authored Jun 7, 2020
2 parents 68bed3b + c64d855 commit fe34840
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test: vendor/bin/phpunit build
./vendor/bin/phpunit

cs-check: vendor/bin/phpunit
./vendor/bin/phpcs --standard=PSR1,PSR12 --encoding=UTF-8 --report=full --colors lib tests
./vendor/bin/phpcs --standard=codestandard.xml lib tests

coverage: vendor/bin/phpunit build
./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,15 @@ Ignas Bernotas, Mark Herhold, Andreas Palm, Sören Jensen, pmaasz, Alexey Stavro

## Changelog

1.3.1

* Allow control messages without payload (@Logioniz)
* Error code in ConnectionException (@sirn-se)

1.3.0

* Implements ping/pong frames (@pmccarren)
* Correct ping-pong behaviour (@Logioniz)
* Correct close behaviour (@sirn-se)
* 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)

Expand Down
12 changes: 12 additions & 0 deletions codestandard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?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">
<exclude name="PSR12.Properties.ConstantVisibility"/>
</rule>
</ruleset>
39 changes: 25 additions & 14 deletions lib/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,10 @@ protected function receiveFragment()
$opcode_int = ord($data[0]) & 31; // Bits 4-7
$opcode_ints = array_flip(self::$opcodes);
if (!array_key_exists($opcode_int, $opcode_ints)) {
throw new ConnectionException("Bad opcode in websocket frame: $opcode_int");
throw new ConnectionException(
"Bad opcode in websocket frame: $opcode_int",
ConnectionException::BAD_OPCODE
);
}
$opcode = $opcode_ints[$opcode_int];

Expand Down Expand Up @@ -272,11 +275,14 @@ public function close($status = 1000, $message = 'ttfn')
protected function write($data)
{
$written = fwrite($this->socket, $data);
if ($written === false) {
$length = strlen($data);
$this->throwException("Failed to write $length bytes.");
}

if ($written < strlen($data)) {
throw new ConnectionException(
"Could only write $written out of " . strlen($data) . " bytes."
);
$length = strlen($data);
$this->throwException("Could only write $written out of $length bytes.");
}
}

Expand All @@ -286,24 +292,29 @@ protected function read($length)
while (strlen($data) < $length) {
$buffer = fread($this->socket, $length - strlen($data));
if ($buffer === false) {
$metadata = stream_get_meta_data($this->socket);
throw new ConnectionException(
'Broken frame, read ' . strlen($data) . ' of stated '
. $length . ' bytes. Stream state: '
. json_encode($metadata)
);
$read = strlen($data);
$this->throwException("Broken frame, read $read of stated $length bytes.");
}
if ($buffer === '') {
$metadata = stream_get_meta_data($this->socket);
throw new ConnectionException(
'Empty read; connection dead? Stream state: ' . json_encode($metadata)
);
$this->throwException("Empty read; connection dead?");
}
$data .= $buffer;
}
return $data;
}

protected function throwException($message, $code = 0)
{
$meta = stream_get_meta_data($this->socket);
if (!empty($meta['timed_out'])) {
$code = ConnectionException::TIMED_OUT;
}
if (!empty($meta['eof'])) {
$code = ConnectionException::EOF;
}
$json_meta = json_encode($meta);
throw new ConnectionException("$message Stream state: $json_meta", $code);
}

/**
* Helper to convert a binary to a string of '0' and '1'.
Expand Down
4 changes: 4 additions & 0 deletions lib/ConnectionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@

class ConnectionException extends Exception
{
// Native codes in interval 0-106
const TIMED_OUT = 1024;
const EOF = 1025;
const BAD_OPCODE = 1026;
}
21 changes: 21 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ public function testBadStreamContext()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Could not open socket to "localhost:8000"
*/
public function testFailedConnection()
Expand All @@ -272,6 +273,7 @@ public function testFailedConnection()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Connection to 'ws://localhost/my/mock/path' failed
*/
public function testInvalidUpgrade()
Expand All @@ -283,6 +285,7 @@ public function testInvalidUpgrade()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Server sent bad upgrade response
*/
public function testInvalidKey()
Expand All @@ -308,6 +311,7 @@ public function testSendBadOpcode()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1026
* @expectedExceptionMessage Bad opcode in websocket frame: 12
*/
public function testRecieveBadOpcode()
Expand All @@ -321,6 +325,7 @@ public function testRecieveBadOpcode()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1025
* @expectedExceptionMessage Could only write 18 out of 22 bytes.
*/
public function testBrokenWrite()
Expand All @@ -334,6 +339,21 @@ public function testBrokenWrite()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1024
* @expectedExceptionMessage Failed to write 22 bytes.
*/
public function testFailedWrite()
{
MockSocket::initialize('client.connect', $this);
$client = new Client('ws://localhost:8000/my/mock/path');
$client->send('Connect');
MockSocket::initialize('send-failed-write', $this);
$client->send('Failing to write');
}

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1025
* @expectedExceptionMessage Broken frame, read 0 of stated 2 bytes.
*/
public function testBrokenRead()
Expand All @@ -347,6 +367,7 @@ public function testBrokenRead()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1024
* @expectedExceptionMessage Empty read; connection dead?
*/
public function testEmptyRead()
Expand Down
26 changes: 26 additions & 0 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ public function testSetTimeout()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Could not open listening socket:
*/
public function testFailedSocketServer()
Expand All @@ -230,6 +231,7 @@ public function testFailedSocketServer()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Server failed to connect
*/
public function testFailedConnect()
Expand All @@ -244,6 +246,7 @@ public function testFailedConnect()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Server failed to connect
*/
public function testFailedConnectTimeout()
Expand All @@ -258,6 +261,7 @@ public function testFailedConnectTimeout()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage No GET in request
*/
public function testFailedHttp()
Expand All @@ -271,6 +275,7 @@ public function testFailedHttp()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 0
* @expectedExceptionMessage Client had no Key in upgrade request
*/
public function testFailedWsKey()
Expand All @@ -284,6 +289,7 @@ public function testFailedWsKey()

/**
* @expectedException WebSocket\BadOpcodeException
* @expectedExceptionCode 0
* @expectedExceptionMessage Bad opcode 'bad'. Try 'text' or 'binary'.
*/
public function testSendBadOpcode()
Expand All @@ -298,6 +304,7 @@ public function testSendBadOpcode()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1026
* @expectedExceptionMessage Bad opcode in websocket frame: 12
*/
public function testRecieveBadOpcode()
Expand All @@ -313,6 +320,7 @@ public function testRecieveBadOpcode()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1025
* @expectedExceptionMessage Could only write 18 out of 22 bytes.
*/
public function testBrokenWrite()
Expand All @@ -328,6 +336,23 @@ public function testBrokenWrite()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1024
* @expectedExceptionMessage Failed to write 22 bytes.
*/
public function testFailedWrite()
{
MockSocket::initialize('server.construct', $this);
$server = new Server();
MockSocket::initialize('server.accept', $this);
$server->accept();
$server->send('Connect');
MockSocket::initialize('send-failed-write', $this);
$server->send('Failing to write');
}

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1025
* @expectedExceptionMessage Broken frame, read 0 of stated 2 bytes.
*/
public function testBrokenRead()
Expand All @@ -343,6 +368,7 @@ public function testBrokenRead()

/**
* @expectedException WebSocket\ConnectionException
* @expectedExceptionCode 1024
* @expectedExceptionMessage Empty read; connection dead?
*/
public function testEmptyRead()
Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/receive-broken-read.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"return": {
"timed_out": false,
"blocked": true,
"eof": false,
"eof": true,
"stream_type": "tcp_socket\/ssl",
"mode": "r+",
"unread_bytes": 2,
Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/receive-empty-read.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@mock-stream"
],
"return": {
"timed_out": false,
"timed_out": true,
"blocked": true,
"eof": false,
"stream_type": "tcp_socket\/ssl",
Expand Down
15 changes: 15 additions & 0 deletions tests/scripts/send-broken-write.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,20 @@
"@mock-stream"
],
"return": 18
},
{
"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
}
}
]
31 changes: 31 additions & 0 deletions tests/scripts/send-failed-write.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[
{
"function": "get_resource_type",
"params": [
"@mock-stream"
],
"return": "stream"
},
{
"function": "fwrite",
"params": [
"@mock-stream"
],
"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": 2,
"seekable": false
}
}
]

0 comments on commit fe34840

Please sign in to comment.