Skip to content

Commit 061da83

Browse files
committed
Split payload handling logic out
So that it can be used client-side too
1 parent f97c155 commit 061da83

File tree

2 files changed

+121
-44
lines changed

2 files changed

+121
-44
lines changed

lib/Wrench/Connection.php

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Wrench;
44

5+
use Wrench\Payload\PayloadHandler;
6+
57
use Wrench\Protocol\Protocol;
68

79
use Wrench\Payload\Payload;
@@ -37,7 +39,7 @@ class Connection extends Configurable
3739
*
3840
* Wraps the client connection resource
3941
*
40-
* @var Socket
42+
* @var ServerClientSocket
4143
*/
4244
protected $socket;
4345

@@ -77,9 +79,9 @@ class Connection extends Configurable
7779
protected $id = null;
7880

7981
/**
80-
* The current payload
82+
* @var PayloadHandler
8183
*/
82-
protected $payload;
84+
protected $payloadHandler;
8385

8486
/**
8587
* Constructor
@@ -97,9 +99,11 @@ public function __construct(
9799
$this->manager = $manager;
98100
$this->socket = $socket;
99101

102+
100103
parent::__construct($options);
101104

102105
$this->configureClientInformation();
106+
$this->configurePayloadHandler();
103107

104108
$this->log('Connected');
105109
}
@@ -127,6 +131,14 @@ protected function configure(array $options)
127131
parent::configure($options);
128132
}
129133

134+
protected function configurePayloadHandler()
135+
{
136+
$this->payloadHandler = new PayloadHandler(
137+
array($this, 'handlePayload'),
138+
$this->options
139+
);
140+
}
141+
130142
/**
131143
* @throws RuntimeException
132144
*/
@@ -266,62 +278,27 @@ protected function export($data)
266278
* @todo An endpoint MUST be capable of handling control frames in the
267279
* middle of a fragmented message.
268280
* @param string $data
281+
* @return void
269282
*/
270283
public function handle($data)
271284
{
272-
if (!$this->payload) {
273-
$this->payload = $this->protocol->getPayload();
274-
}
275-
276-
while ($data) { // Each iteration pulls off a single payload chunk
277-
$size = strlen($data);
278-
$remaining = $this->payload->getRemainingData();
279-
280-
// If we don't yet know how much data is remaining, read data into
281-
// the payload in two byte chunks (the size of a WebSocket frame
282-
// header to get the initial length)
283-
//
284-
// Then re-loop. For extended lengths, this will happen once or four
285-
// times extra, as the extended length is read in.
286-
if ($remaining === null) {
287-
$chunk_size = 2;
288-
} elseif ($remaining > 0) {
289-
$chunk_size = $remaining;
290-
} elseif ($remaining === 0) {
291-
$chunk_size = 0;
292-
}
293-
294-
$chunk_size = min(strlen($data), $chunk_size);
295-
$chunk = substr($data, 0, $chunk_size);
296-
$data = substr($data, $chunk_size);
297-
298-
$this->payload->receiveData($chunk);
299-
300-
if ($remaining !== 0 && !$this->payload->isComplete()) {
301-
continue;
302-
}
303-
304-
if ($this->payload->isComplete()) {
305-
$this->handlePayload($this->payload);
306-
$this->payload = $this->protocol->getPayload();
307-
} else {
308-
throw new ConnectionException('Payload will not complete');
309-
}
310-
}
285+
$this->payloadHandler->handle($data);
311286
}
312287

313288
/**
314289
* Handle a complete payload received from the client
315290
*
291+
* Public because called from our PayloadHandler
292+
*
316293
* @param string $payload
317294
*/
318-
protected function handlePayload(Payload $payload)
295+
public function handlePayload(Payload $payload)
319296
{
320297
$app = $this->getClientApplication();
321298

322299
$this->log('Handling payload: ' . $payload->getPayload(), 'debug');
323300

324-
switch ($payload->getType()) {
301+
switch ($type = $payload->getType()) {
325302
case Protocol::TYPE_TEXT:
326303
if (method_exists($app, 'onData')) {
327304
$app->onData($payload, $this);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Wrench\Payload;
4+
5+
use Wrench\Exception\PayloadException;
6+
use Wrench\Exception\ConnectionException;
7+
use Wrench\Util\Configurable;
8+
use \InvalidArgumentException;
9+
10+
/**
11+
* Handles chunking and splitting of payloads into frames
12+
*/
13+
class PayloadHandler extends Configurable
14+
{
15+
/**
16+
* A callback that will be called when a complete payload is available
17+
*
18+
* @var callable
19+
*/
20+
protected $callback;
21+
22+
/**
23+
* The current payload
24+
*/
25+
protected $payload;
26+
27+
/**
28+
* @param callable $callback
29+
* @param array $options
30+
* @throws InvalidArgumentException
31+
*/
32+
public function __construct($callback, array $options = array())
33+
{
34+
parent::__construct($options);
35+
36+
if (!is_callable($callback)) {
37+
throw new InvalidArgumentException('You must supply a callback to PayloadHandler');
38+
}
39+
40+
$this->callback = $callback;
41+
}
42+
43+
/**
44+
* Handles the raw socket data given
45+
*
46+
* @param string $data
47+
*/
48+
public function handle($data)
49+
{
50+
if (!$this->payload) {
51+
$this->payload = $this->protocol->getPayload();
52+
}
53+
54+
while ($data) { // Each iteration pulls off a single payload chunk
55+
$size = strlen($data);
56+
$remaining = $this->payload->getRemainingData();
57+
58+
// If we don't yet know how much data is remaining, read data into
59+
// the payload in two byte chunks (the size of a WebSocket frame
60+
// header to get the initial length)
61+
//
62+
// Then re-loop. For extended lengths, this will happen once or four
63+
// times extra, as the extended length is read in.
64+
if ($remaining === null) {
65+
$chunk_size = 2;
66+
} elseif ($remaining > 0) {
67+
$chunk_size = $remaining;
68+
} elseif ($remaining === 0) {
69+
$chunk_size = 0;
70+
}
71+
72+
$chunk_size = min(strlen($data), $chunk_size);
73+
$chunk = substr($data, 0, $chunk_size);
74+
$data = substr($data, $chunk_size);
75+
76+
$this->payload->receiveData($chunk);
77+
78+
if ($remaining !== 0 && !$this->payload->isComplete()) {
79+
continue;
80+
}
81+
82+
if ($this->payload->isComplete()) {
83+
$this->emit($this->payload);
84+
$this->payload = $this->protocol->getPayload();
85+
} else {
86+
throw new PayloadException('Payload will not complete');
87+
}
88+
}
89+
}
90+
91+
/**
92+
* Emits a complete payload to the callback
93+
*
94+
* @param Payload $payload
95+
*/
96+
protected function emit(Payload $payload)
97+
{
98+
call_user_func($this->callback, $payload);
99+
}
100+
}

0 commit comments

Comments
 (0)