1
|
|
|
<?php
|
2
|
|
|
|
3
|
|
|
/**
|
4
|
|
|
* This file is a part of Woketo package.
|
5
|
|
|
*
|
6
|
|
|
* (c) Nekland <[email protected]>
|
7
|
|
|
*
|
8
|
|
|
* For the full license, take a look to the LICENSE file
|
9
|
|
|
* on the root directory of this project
|
10
|
|
|
*/
|
11
|
|
|
|
12
|
|
|
namespace Nekland\Woketo\Core;
|
13
|
|
|
|
14
|
|
|
use Nekland\Woketo\Exception\NoHandlerException;
|
15
|
|
|
use Nekland\Woketo\Exception\WebsocketException;
|
16
|
|
|
use Nekland\Woketo\Message\MessageHandlerInterface;
|
17
|
|
|
use Nekland\Woketo\Rfc6455\Frame;
|
18
|
|
|
use Nekland\Woketo\Rfc6455\Handshake\HandshakeInterface;
|
19
|
|
|
use Nekland\Woketo\Rfc6455\Message;
|
20
|
|
|
use Nekland\Woketo\Rfc6455\MessageProcessor;
|
21
|
|
|
use Nekland\Woketo\Utils\SimpleLogger;
|
22
|
|
|
use Psr\Log\LoggerAwareTrait;
|
23
|
|
|
use Psr\Log\LoggerInterface;
|
24
|
|
|
use React\EventLoop\LoopInterface;
|
25
|
|
|
use React\EventLoop\Timer\TimerInterface;
|
26
|
|
|
use React\Socket\Connection;
|
27
|
|
|
use React\Socket\ConnectionInterface;
|
28
|
|
|
|
29
|
|
|
abstract class AbstractConnection
|
30
|
|
|
{
|
31
|
|
|
/**
|
32
|
|
|
* 5 seconds
|
33
|
|
|
*/
|
34
|
|
|
const DEFAULT_TIMEOUT = 5;
|
35
|
|
|
|
36
|
|
|
use LoggerAwareTrait;
|
37
|
|
|
|
38
|
|
|
/**
|
39
|
|
|
* @var ConnectionInterface
|
40
|
|
|
*/
|
41
|
|
|
protected $stream;
|
42
|
|
|
|
43
|
|
|
/**
|
44
|
|
|
* @var MessageProcessor
|
45
|
|
|
*/
|
46
|
|
|
protected $messageProcessor;
|
47
|
|
|
|
48
|
|
|
/**
|
49
|
|
|
* @var \Nekland\Woketo\Rfc6455\Handshake\ServerHandshake|\Nekland\Woketo\Rfc6455\Handshake\ClientHandshake
|
50
|
|
|
*/
|
51
|
|
|
protected $handshake;
|
52
|
|
|
|
53
|
|
|
/**
|
54
|
|
|
* @var bool
|
55
|
|
|
*/
|
56
|
|
|
protected $handshakeDone;
|
57
|
|
|
|
58
|
|
|
/**
|
59
|
|
|
* @var string
|
60
|
|
|
*/
|
61
|
|
|
protected $uri;
|
62
|
|
|
|
63
|
|
|
/**
|
64
|
|
|
* @var MessageHandlerInterface|\Closure
|
65
|
|
|
*/
|
66
|
|
|
protected $handler;
|
67
|
|
|
|
68
|
|
|
/**
|
69
|
|
|
* @var TimerInterface
|
70
|
|
|
*/
|
71
|
|
|
protected $timeout;
|
72
|
|
|
|
73
|
|
|
/**
|
74
|
|
|
* @var Message
|
75
|
|
|
*/
|
76
|
|
|
protected $currentMessage;
|
77
|
|
|
|
78
|
|
|
/**
|
79
|
|
|
* @var LoopInterface
|
80
|
|
|
*/
|
81
|
|
|
protected $loop;
|
82
|
|
|
|
83
|
8 |
|
public function __construct(MessageProcessor $messageProcessor, LoopInterface $loop, HandshakeInterface $handshake = null)
|
84
|
|
|
{
|
85
|
8 |
|
$this->handshake = $handshake;
|
|
|
|
|
86
|
8 |
|
$this->messageProcessor = $messageProcessor;
|
87
|
8 |
|
$this->loop = $loop;
|
88
|
8 |
|
}
|
89
|
|
|
|
90
|
|
|
/**
|
91
|
|
|
* Behavior on new raw data received.
|
92
|
|
|
*
|
93
|
|
|
* @param string $data
|
94
|
|
|
*/
|
95
|
|
|
protected function onMessage(string $data)
|
96
|
|
|
{
|
97
|
|
|
try {
|
98
|
|
|
if (!$this->handshakeDone) {
|
99
|
|
|
$this->processHandshake($data);
|
100
|
|
|
} else {
|
101
|
|
|
$this->processMessage($data);
|
102
|
|
|
}
|
103
|
|
|
|
104
|
|
|
return;
|
105
|
|
|
} catch (WebsocketException $e) {
|
106
|
|
|
$this->messageProcessor->close($this->stream);
|
107
|
|
|
$this->getLogger()->notice('Connection to ' . $this->getIp() . ' closed with error : ' . $e->getMessage());
|
108
|
|
|
$this->handler->onError($e, $this);
|
|
|
|
|
109
|
|
|
}
|
110
|
|
|
}
|
111
|
|
|
|
112
|
|
|
/**
|
113
|
|
|
* @param string $data
|
114
|
|
|
* @return void
|
115
|
|
|
*/
|
116
|
|
|
protected abstract function processHandshake(string $data);
|
|
|
|
|
117
|
|
|
|
118
|
|
|
/**
|
119
|
|
|
* @param string $data
|
120
|
|
|
* @return void
|
121
|
|
|
*/
|
122
|
|
|
protected abstract function processMessage(string $data);
|
|
|
|
|
123
|
|
|
|
124
|
|
|
/**
|
125
|
|
|
* May return ip or hostname
|
126
|
|
|
*
|
127
|
|
|
* @return string
|
128
|
|
|
*/
|
129
|
|
|
public abstract function getIp();
|
|
|
|
|
130
|
|
|
|
131
|
|
|
/**
|
132
|
|
|
* @return \Psr\Log\LoggerInterface
|
133
|
|
|
*/
|
134
|
1 |
|
public function getLogger()
|
135
|
|
|
{
|
136
|
1 |
|
return $this->logger ?: $this->logger = new SimpleLogger();
|
137
|
|
|
}
|
138
|
|
|
|
139
|
|
|
/**
|
140
|
|
|
* @return MessageHandlerInterface
|
141
|
|
|
* @throws NoHandlerException
|
142
|
|
|
*/
|
143
|
5 |
|
protected function getHandler() : MessageHandlerInterface
|
144
|
|
|
{
|
145
|
5 |
|
if ($this->handler instanceof \Closure) {
|
146
|
5 |
|
$handler = $this->handler;
|
147
|
5 |
|
$handler = $handler($this->uri, $this);
|
148
|
|
|
|
149
|
5 |
|
if (null === $handler) {
|
150
|
1 |
|
throw new NoHandlerException(sprintf('No handler for request URI %s.', $this->uri));
|
151
|
|
|
}
|
152
|
|
|
|
153
|
4 |
|
$this->handler = $handler;
|
154
|
|
|
}
|
155
|
|
|
|
156
|
4 |
|
return $this->handler;
|
157
|
|
|
}
|
158
|
|
|
|
159
|
|
|
/**
|
160
|
|
|
* Close the connection with normal close.
|
161
|
|
|
*/
|
162
|
1 |
|
public function close()
|
163
|
|
|
{
|
164
|
1 |
|
$this->messageProcessor->close($this->stream);
|
165
|
1 |
|
}
|
166
|
|
|
|
167
|
|
|
/**
|
168
|
|
|
* @return LoopInterface
|
169
|
|
|
*/
|
170
|
1 |
|
public function getLoop(): LoopInterface
|
171
|
|
|
{
|
172
|
1 |
|
return $this->loop;
|
173
|
|
|
}
|
174
|
|
|
|
175
|
|
|
/**
|
176
|
|
|
* @param string|Frame $frame
|
177
|
|
|
* @param int $opCode
|
178
|
|
|
* @throws \Nekland\Woketo\Exception\RuntimeException
|
179
|
|
|
*/
|
180
|
|
|
public abstract function write($frame, int $opCode = Frame::OP_TEXT);
|
|
|
|
|
181
|
|
|
}
|
182
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.