Skip to content

Commit

Permalink
Added NetworkStream, plus some forward compatibility stuff.
Browse files Browse the repository at this point in the history
  • Loading branch information
boenrobot committed Oct 15, 2011
1 parent 70ba257 commit b7c234b
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 48 deletions.
7 changes: 5 additions & 2 deletions RELEASE-1.0.0a2
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
Lots of reorganization and initial test suite.

* Added test suite with nearly complete code coverage.
* Added FilterCollection, in turn allowing for the same filter to be applied more than once.
* Renamed all "Socket*Transmitter" classes to "Tcp*".
* Added test suite with nearly complete code coverage. Remaining coverage is due to forward compatibility issues.
* Added FilterCollection, in turn allowing for the same filter to be applied more than once.
* Added NetworkStream, and made Tpc* classes inherit from it. This new class contains a new method called shutdown().
* IPv6 addresses must now be written literally, without the surrounding "[" and "]".
* TcpServerConnection can now accept IPv6 connections.
* Merged sendStream() into send().
* Removed redundant error variables in TcpServerConnection.
* Removed the TcpServerConnection::isServer() function (can't tell apart server and client).
83 changes: 83 additions & 0 deletions src/PEAR2/Net/Transmitter/NetworkStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

/**
* ~~summary~~
*
* ~~description~~
*
* PHP version 5
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <[email protected]>
* @copyright 2011 Vasil Rangelov
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @version SVN: $WCREV$
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
/**
* The namespace declaration.
*/
namespace PEAR2\Net\Transmitter;

/**
* A network transmitter.
*
* This is a convinience wrapper for network streams. Used to ensure data
* integrity.
*
* @category Net
* @package PEAR2_Net_Transmitter
* @author Vasil Rangelov <[email protected]>
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class NetworkStream extends Stream
{

/**
* Checks whether there is data to be read from the socket.
*
* @return bool TRUE if there is data to be read, FALSE otherwise.
*/
public function isDataAwaiting()
{
if (parent::isDataAwaiting()) {
$meta = stream_get_meta_data($this->stream);
return!$meta['timed_out'] && !$meta['eof'];
}
return false;
}

public function setBuffer($size, $direction = self::DIRECTION_ALL)
{
$result = parent::setBuffer($size, $direction);
if (self::DIRECTION_SEND === $direction
&& function_exists('stream_set_chunk_size') && !$result
) {
return is_int(stream_set_chunk_size($this->stream, $size));
}
return $result;
}

/**
* Shutdown a full-duplex connection
*
* Shutdowns (partially or not) a full-duplex connection.
*
* @param string $direction The direction for which to disable further
* communications.
*
* @return bool TRUE on success, FALSE on failure.
*/
public function shutdown($direction = self::DIRECTION_ALL)
{
$directionMap = array(
self::DIRECTION_ALL => STREAM_SHUT_RDWR,
self::DIRECTION_SEND => STREAM_SHUT_WR,
self::DIRECTION_RECEIVE => STREAM_SHUT_RD
);
return array_key_exists($direction, $directionMap)
&& stream_socket_shutdown($this->stream, $directionMap[$direction]);
}
}
8 changes: 4 additions & 4 deletions src/PEAR2/Net/Transmitter/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ public function setBuffer($size, $direction = self::DIRECTION_ALL)
case self::DIRECTION_RECEIVE:
return stream_set_read_buffer($this->stream, $size) === 0;
case self::DIRECTION_ALL:
return stream_set_read_buffer($this->stream, $size) === 0
&& stream_set_write_buffer($this->stream, $size) === 0;
return $this->setBuffer($size, self::DIRECTION_RECEIVE)
&& $this->setBuffer($size, self::DIRECTION_SEND);
}
return false;
}
Expand Down Expand Up @@ -222,7 +222,7 @@ public function send($contents)
$bytes += $bytesNow;
} else {
throw $this->createException(
'Failed while sending stream.', 3
'Failed while sending stream.', 2
);
}
}
Expand All @@ -240,7 +240,7 @@ public function send($contents)
$bytes += $bytesNow;
} else {
throw $this->createException(
'Failed while sending string.', 2
'Failed while sending string.', 3
);
}
}
Expand Down
19 changes: 4 additions & 15 deletions src/PEAR2/Net/Transmitter/TcpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class TcpClient extends Stream
class TcpClient extends NetworkStream
{

/**
Expand Down Expand Up @@ -60,6 +60,9 @@ class TcpClient extends Stream
public function __construct($host, $port, $persist = false,
$timeout = null, $key = '', $context = null
) {
if (strpos($host, ':') !== false) {
$host = "[{$host}]";
}
$flags = STREAM_CLIENT_CONNECT;
if ($persist) {
$flags |= STREAM_CLIENT_PERSISTENT;
Expand Down Expand Up @@ -91,20 +94,6 @@ public function __construct($host, $port, $persist = false,
}
}

/**
* Checks whether there is data to be read from the socket.
*
* @return bool TRUE if there is data to be read, FALSE otherwise.
*/
public function isDataAwaiting()
{
if (parent::isDataAwaiting()) {
$meta = stream_get_meta_data($this->stream);
return!$meta['timed_out'] && !$meta['eof'];
}
return false;
}

/**
* Creates a new exception.
*
Expand Down
30 changes: 12 additions & 18 deletions src/PEAR2/Net/Transmitter/TcpServerConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear2.php.net/PEAR2_Net_Transmitter
*/
class TcpServerConnection extends Stream
class TcpServerConnection extends NetworkStream
{

/**
Expand Down Expand Up @@ -66,9 +66,17 @@ public function __construct($server, $timeout = null)
parent::__construct(
@stream_socket_accept($server, $timeout, $peername)
);
$hostPortCombo = explode(':', $peername);
$this->peerIP = $hostPortCombo[0];
$this->peerPort = (int) $hostPortCombo[1];
$portString = strrchr($peername, ':');
$this->peerPort = (int) substr($portString, 1);
$ipString = substr(
$peername, 0, strlen($peername) - strlen($portString)
);
if (strpos($ipString, '[') === 0
&& strpos(strrev($ipString), ']') === 0
) {
$ipString = substr($ipString, 1, strlen($ipString) - 2);
}
$this->peerIP = $ipString;
} catch (Exception $e) {
throw $this->createException('Failed to initialize connection.', 9);
}
Expand All @@ -94,20 +102,6 @@ public function getPeerPort()
return $this->peerPort;
}

/**
* Checks whether there is data to be read from the socket.
*
* @return bool TRUE if there is data to be read, FALSE otherwise.
*/
public function isDataAwaiting()
{
if (parent::isDataAwaiting()) {
$meta = stream_get_meta_data($this->stream);
return!$meta['timed_out'] && !$meta['eof'];
}
return false;
}

/**
* Creates a new exception.
*
Expand Down
22 changes: 18 additions & 4 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ public function testClientSendingIncompleteData()
$contents = str_repeat('7', $size);
try {
$this->client->send($contents);
$this->fail('Sending had to fail.');
} catch(SocketException $e) {
$this->assertEquals(2, $e->getCode(), 'Improper exception code.');
$this->assertEquals(3, $e->getCode(), 'Improper exception code.');
}
}

Expand All @@ -161,16 +162,16 @@ public function testClientSendingIncompleteDataStream()
rewind($stream);
try {
$this->client->send($stream);
$this->fail('Sending had to fail.');
} catch(SocketException $e) {
$this->assertEquals(3, $e->getCode(), 'Improper exception code.');
$this->assertEquals(2, $e->getCode(), 'Improper exception code.');
}
}

public function testClientTimingOut()
{
$this->assertEquals('999', $this->client->receive(3));
$this->client->setTimeout(2);
//$this->client->setChunk(1);
try {
$this->client->receive(30);
$this->fail('Second receiving had to fail.');
Expand All @@ -183,7 +184,6 @@ public function testClientTimingOutStream()
{
$this->assertEquals('aaa', $this->client->receive(3));
$this->client->setTimeout(2);
//$this->client->setChunk(1);
try {
$this->client->receiveStream(30);
$this->fail('Second receiving had to fail.');
Expand Down Expand Up @@ -262,4 +262,18 @@ public function testSetChunk()
$this->client->getChunk()
);
}

public function testShutdown()
{
$this->client->send('bbb');
$this->assertEquals('bbb', $this->client->receive(3));
$this->assertFalse($this->client->shutdown('undefined direction'));
$this->assertTrue($this->client->shutdown(Stream::DIRECTION_SEND));
try {
$this->client->send('b');
$this->fail('Sending had to fail.');
} catch(SocketException $e) {
$this->assertEquals(3, $e->getCode(), 'Improper exception code.');
}
}
}
18 changes: 17 additions & 1 deletion tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ class ServerTest extends \PHPUnit_Framework_TestCase

public static function setUpBeforeClass()
{
$hostname = strpos(LOCAL_HOSTNAME, ':') !== false
? '[' . LOCAL_HOSTNAME . ']' : LOCAL_HOSTNAME;
self::$server = stream_socket_server(
'tcp://' . LOCAL_HOSTNAME . ':' . LOCAL_PORT,
"tcp://{$hostname}:" . LOCAL_PORT,
self::$errorno, self::$errstr
);
}
Expand Down Expand Up @@ -260,4 +262,18 @@ public function testSetChunk()
$this->conn->getChunk()
);
}

public function testShutdown()
{
$this->assertEquals('bbb', $this->conn->receive(3));
$this->conn->send('bbb');
$this->assertFalse($this->conn->shutdown('undefined direction'));
$this->assertTrue($this->conn->shutdown(Stream::DIRECTION_RECEIVE));
try {
$this->conn->receive(1);
$this->fail('Receiving had to fail.');
} catch(SocketException $e) {
$this->assertEquals(4, $e->getCode(), 'Improper exception code.');
}
}
}
4 changes: 2 additions & 2 deletions tests/phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
<!--
The hostname and port of secondaryPeer.xml
-->
<const name="REMOTE_HOSTNAME" value="127.0.0.1" />
<const name="REMOTE_HOSTNAME" value="::1" />
<const name="REMOTE_PORT" value="6666" />
<!--
The hostname and port secondaryPeer.xml must use.
-->
<const name="LOCAL_HOSTNAME" value="127.0.0.1" />
<const name="LOCAL_HOSTNAME" value="::1" />
<const name="LOCAL_PORT" value="6667" />
<!--
A hostname which doesn't provide a serive on any port present here.
Expand Down
4 changes: 2 additions & 2 deletions tests/secondaryPeer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
<!--
The name and port of phpunit.xml
-->
<const name="REMOTE_HOSTNAME" value="127.0.0.1" />
<const name="REMOTE_HOSTNAME" value="::1" />
<const name="REMOTE_PORT" value="6667" />
<!--
The name and port phpunit.xml must use.
-->
<const name="LOCAL_HOSTNAME" value="127.0.0.1" />
<const name="LOCAL_HOSTNAME" value="::1" />
<const name="LOCAL_PORT" value="6666" />
</php>
<testsuites>
Expand Down

0 comments on commit b7c234b

Please sign in to comment.