Skip to content

Commit 082c59b

Browse files
committed
Merge pull request #2 from clue/add-end
Add end() method
2 parents 4bb2ba3 + 48244c3 commit 082c59b

File tree

8 files changed

+193
-61
lines changed

8 files changed

+193
-61
lines changed

Datagram/Buffer.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Buffer extends EventEmitter
1212
private $socket;
1313
private $listening = false;
1414
private $outgoing = array();
15+
private $writable = true;
1516

1617
public function __construct(LoopInterface $loop, $socket)
1718
{
@@ -21,7 +22,7 @@ public function __construct(LoopInterface $loop, $socket)
2122

2223
public function send($data, $remoteAddress = null)
2324
{
24-
if ($this->socket === false) {
25+
if ($this->writable === false) {
2526
return;
2627
}
2728

@@ -54,6 +55,10 @@ public function handleWrite()
5455
if (!$this->outgoing) {
5556
$this->loop->removeWriteStream($this->socket);
5657
$this->listening = false;
58+
59+
if (!$this->writable) {
60+
$this->close();
61+
}
5762
}
5863
}
5964

@@ -70,8 +75,22 @@ public function close()
7075
$this->listening = false;
7176
}
7277

78+
$this->writable = false;
7379
$this->socket = false;
7480
$this->outgoing = array();
7581
$this->removeAllListeners();
7682
}
83+
84+
public function end()
85+
{
86+
if ($this->writable === false) {
87+
return;
88+
}
89+
90+
$this->writable = false;
91+
92+
if (!$this->listening) {
93+
$this->close();
94+
}
95+
}
7796
}

Datagram/Socket.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ public function close()
9999
$this->removeAllListeners();
100100
}
101101

102+
public function end()
103+
{
104+
$this->buffer->end();
105+
}
106+
102107
private function sanitizeAddress($address)
103108
{
104109
// doc comment suggests IPv6 address is not enclosed in square brackets?

Datagram/SocketInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public function send($data, $remoteAddress = null);
1717

1818
public function close();
1919

20+
public function end();
21+
2022
public function resume();
2123

2224
public function pause();

example/client.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,21 @@
2121
});
2222

2323
$n = 0;
24-
$loop->addPeriodicTimer(2.0, function() use ($client, &$n) {
24+
$tid = $loop->addPeriodicTimer(2.0, function() use ($client, &$n) {
2525
$client->send('tick' . ++$n);
2626
});
2727

2828
// read input from STDIN and forward everything to server
29-
$loop->addReadStream(STDIN, function () use ($client) {
30-
$client->send(trim(fgets(STDIN, 2000)));
29+
$loop->addReadStream(STDIN, function () use ($client, $loop, $tid) {
30+
$msg = fgets(STDIN, 2000);
31+
if ($msg === false) {
32+
// EOF => flush client and stop perodic sending and waiting for input
33+
$client->end();
34+
$loop->cancelTimer($tid);
35+
$loop->removeReadStream(STDIN);
36+
} else {
37+
$client->send(trim($msg));
38+
}
3139
});
3240
}, function($error) {
3341
echo 'ERROR: ' . $error->getMessage() . PHP_EOL;

phpunit.xml.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

3-
<phpunit colors="true">
3+
<phpunit colors="true" bootstrap="./tests/bootstrap.php">
44
<testsuites>
55
<testsuite name="Datagram Test Suite">
66
<directory>./tests/</directory>

tests/FactoryTest.php

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
<?php
22

33
use Datagram\Socket;
4-
54
use React\Promise\When;
6-
75
use React\Promise\PromiseInterface;
86

9-
require __DIR__.'/../vendor/autoload.php';
10-
11-
class FactoryTest extends PHPUnit_Framework_TestCase
7+
class FactoryTest extends TestCase
128
{
139
private $factory;
1410

@@ -57,55 +53,4 @@ public function testCreateServer()
5753

5854
$capturedServer->close();
5955
}
60-
61-
protected function getValueFromResolvedPromise($promise)
62-
{
63-
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
64-
65-
$loop = $this->loop;
66-
$capturedValue = null;
67-
$promise->then(function ($value) use (&$capturedValue, $loop) {
68-
$capturedValue = $value;
69-
$loop->stop();
70-
}, $this->expectCallableNever());
71-
72-
// future-turn resolutions are not enforced, so the value MAY be known here already
73-
if ($capturedValue === null) {
74-
$loop->run();
75-
}
76-
77-
return $capturedValue;
78-
}
79-
80-
protected function expectCallableOnce()
81-
{
82-
$mock = $this->createCallableMock();
83-
$mock
84-
->expects($this->once())
85-
->method('__invoke');
86-
87-
return $mock;
88-
}
89-
90-
protected function expectCallableNever()
91-
{
92-
$mock = $this->createCallableMock();
93-
$mock
94-
->expects($this->never())
95-
->method('__invoke');
96-
97-
return $mock;
98-
}
99-
100-
protected function createCallableMock()
101-
{
102-
return $this->getMock('React\Tests\Socket\Stub\CallableStub');
103-
}
104-
105-
private function createResolverMock()
106-
{
107-
return $this->getMockBuilder('React\Dns\Resolver\Resolver')
108-
->disableOriginalConstructor()
109-
->getMock();
110-
}
11156
}

tests/SocketTest.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
use Datagram\Socket;
4+
use React\Promise\When;
5+
use React\Promise\PromiseInterface;
6+
7+
class SocketTest extends TestCase
8+
{
9+
private $factory;
10+
11+
public function setUp()
12+
{
13+
$this->loop = React\EventLoop\Factory::create();
14+
$this->factory = new Datagram\Factory($this->loop, $this->createResolverMock());
15+
}
16+
17+
public function testCreateClientCloseWillNotBlock()
18+
{
19+
$promise = $this->factory->createClient('127.0.0.1', 12345);
20+
$client = $this->getValueFromResolvedPromise($promise);
21+
22+
$client->send('test');
23+
$client->close();
24+
25+
$this->loop->run();
26+
27+
return $client;
28+
}
29+
30+
/**
31+
*
32+
* @param Socket $client
33+
* @depends testCreateClientCloseWillNotBlock
34+
*/
35+
public function testClientCloseAgainWillNotBlock(Socket $client)
36+
{
37+
$client->close();
38+
$this->loop->run();
39+
}
40+
41+
public function testCreateClientEndWillNotBlock()
42+
{
43+
$promise = $this->factory->createClient('127.0.0.1', 12345);
44+
$client = $this->getValueFromResolvedPromise($promise);
45+
46+
$client->send('test');
47+
$client->end();
48+
49+
$this->loop->run();
50+
51+
return $client;
52+
}
53+
54+
/**
55+
*
56+
* @param Socket $client
57+
* @depends testCreateClientEndWillNotBlock
58+
*/
59+
public function testClientEndAgainWillNotBlock(Socket $client)
60+
{
61+
$client->end();
62+
$this->loop->run();
63+
}
64+
65+
public function testCreatePair()
66+
{
67+
$promise = $this->factory->createServer(0, '127.0.0.1');
68+
$server = $this->getValueFromResolvedPromise($promise);
69+
70+
$promise = $this->factory->createClient('127.0.0.1', $server->getPort());
71+
$client = $this->getValueFromResolvedPromise($promise);
72+
73+
$that = $this;
74+
$server->on('message', function ($message, $remote, $server) use ($that) {
75+
$that->assertEquals('test', $message);
76+
77+
// once the server receives a message, send it pack to client and stop server
78+
$server->send('response:' . $message, $remote);
79+
$server->end();
80+
});
81+
82+
$client->on('message', function ($message, $remote, $client) use ($that) {
83+
$that->assertEquals('response:test', $message);
84+
85+
// once the client receives a message, stop client
86+
$client->end();
87+
});
88+
89+
$client->send('test');
90+
91+
$this->loop->run();
92+
}
93+
}

tests/bootstrap.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
use React\Promise\When;
4+
use React\Promise\PromiseInterface;
5+
6+
require __DIR__.'/../vendor/autoload.php';
7+
8+
abstract class TestCase extends PHPUnit_Framework_TestCase
9+
{
10+
protected function getValueFromResolvedPromise($promise)
11+
{
12+
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
13+
14+
$loop = $this->loop;
15+
$capturedValue = null;
16+
$promise->then(function ($value) use (&$capturedValue, $loop) {
17+
$capturedValue = $value;
18+
$loop->stop();
19+
}, $this->expectCallableNever());
20+
21+
// future-turn resolutions are not enforced, so the value MAY be known here already
22+
if ($capturedValue === null) {
23+
$loop->run();
24+
}
25+
26+
return $capturedValue;
27+
}
28+
29+
protected function expectCallableOnce()
30+
{
31+
$mock = $this->createCallableMock();
32+
$mock
33+
->expects($this->once())
34+
->method('__invoke');
35+
36+
return $mock;
37+
}
38+
39+
protected function expectCallableNever()
40+
{
41+
$mock = $this->createCallableMock();
42+
$mock
43+
->expects($this->never())
44+
->method('__invoke');
45+
46+
return $mock;
47+
}
48+
49+
protected function createCallableMock()
50+
{
51+
return $this->getMock('React\Tests\Socket\Stub\CallableStub');
52+
}
53+
54+
protected function createResolverMock()
55+
{
56+
return $this->getMockBuilder('React\Dns\Resolver\Resolver')
57+
->disableOriginalConstructor()
58+
->getMock();
59+
}
60+
}

0 commit comments

Comments
 (0)