Completed
Push — master ( 390e4e...b0ec12 )
by Taosikai
12:57
created

Server::closeClient()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 2
eloc 10
nc 2
nop 1
1
<?php
2
/**
3
 * Spike library
4
 * @author Tao <[email protected]>
5
 */
6
namespace Spike\Server;
7
8
use React\EventLoop\LoopInterface;
9
use React\EventLoop\Factory as LoopFactory;
10
use React\Socket\ConnectionInterface;
11
use React\Socket\Server as Socket;
12
use Slince\Event\Dispatcher;
13
use Slince\Event\Event;
14
use Spike\Authentication\AuthenticationInterface;
15
use Spike\Exception\InvalidArgumentException;
16
use Spike\Exception\RuntimeException;
17
use Spike\Logger\Logger;
18
use Spike\Parser\SpikeParser;
19
use Spike\Protocol\Spike;
20
use Spike\Protocol\SpikeInterface;
21
use Spike\Server\Handler\HandlerInterface;
22
use Spike\Server\Timer\ReviewClient;
23
use Spike\Server\Timer\SummaryWatcher;
24
use Spike\Timer\MemoryWatcher;
25
use Spike\Timer\TimerInterface;
26
use Spike\Timer\UseTimerTrait;
27
use Spike\Tunnel\HttpTunnel;
28
use Spike\Tunnel\TunnelInterface;
29
use Spike\Server\TunnelServer\TunnelServerInterface;
30
use Spike\Utility;
31
32
class Server
33
{
34
    use UseTimerTrait;
35
36
    /**
37
     * The server host
38
     * @var string
39
     */
40
    protected $host;
41
42
    /**
43
     * The server port
44
     * @var int
45
     */
46
    protected $port;
47
48
    /**
49
     * The auth information
50
     * @var AuthenticationInterface
51
     */
52
    protected $authentication;
53
54
    /**
55
     * @var LoopInterface
56
     */
57
    protected $loop;
58
59
    /**
60
     * @var Dispatcher
61
     */
62
    protected $dispatcher;
63
64
    /**
65
     * @var Socket
66
     */
67
    protected $socket;
68
69
    /**
70
     * @var TunnelServerInterface[]
71
     */
72
    protected $tunnelServers = [];
73
74
    /**
75
     * @var ClientCollection
76
     */
77
    protected $clients;
78
79
    /**
80
     * @var Logger
81
     */
82
    protected $logger;
83
84
    public function __construct(
85
        $address,
86
        AuthenticationInterface $authentication,
87
        LoopInterface $loop = null,
88
        Dispatcher $dispatcher = null
89
    ) {
90
        list($this->host, $this->port) = Utility::parseAddress($address);
91
        $this->authentication = $authentication;
92
        $this->loop = $loop ?: LoopFactory::create();
93
        $this->socket = new Socket($address, $this->loop);
94
        $this->dispatcher = $dispatcher ?: new Dispatcher();
95
        $this->clients = new ClientCollection();
96
        $this->tunnelServers = new TunnelServerCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Spike\Server\TunnelServerCollection() of type object<Spike\Server\TunnelServerCollection> is incompatible with the declared type array<integer,object<Spi...TunnelServerInterface>> of property $tunnelServers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
97
    }
98
99
    /**
100
     * Run the server
101
     */
102
    public function run()
103
    {
104
        $this->socket->on('connection', function(ConnectionInterface $connection){
105
            //Emit the event
106
            $this->dispatcher->dispatch(new Event(EventStore::ACCEPT_CONNECTION, $this, [
107
                'connection' => $connection
108
            ]));
109
            $this->handleControlConnection($connection);
110
        });
111
        $this->socket->on('error', function($exception){
112
            $this->dispatcher->dispatch(new Event(EventStore::SOCKET_ERROR, $this, [
113
                'exception' => $exception
114
            ]));
115
        });
116
        //Emit the event
117
        $this->dispatcher->dispatch(EventStore::SERVER_RUN);
118
        foreach ($this->getDefaultTimers() as $timer) {
119
            $this->addTimer($timer);
120
        }
121
        $this->loop->run();
122
    }
123
124
    /**
125
     * Handles control connection
126
     * @param ConnectionInterface $connection
127
     */
128
    protected function handleControlConnection(ConnectionInterface $connection)
129
    {
130
        $parser = new SpikeParser();
131
        $connection->on('data', function($data) use($parser, $connection){
132
            $parser->pushIncoming($data);
133
            try {
134
                $messages = $parser->parse();
135
                foreach ($messages as $message) {
136
                    $message = Spike::fromString($message);
137
                    $this->dispatcher->dispatch(new Event(EventStore::RECEIVE_MESSAGE, $this, [
138
                        'message' => $message,
139
                        'connection' => $connection
140
                    ]));
141
                    $this->createMessageHandler($message, $connection)->handle($message);
142
                }
143
            } catch (RuntimeException $exception) {
144
                $this->dispatcher->dispatch(new Event(EventStore::CONNECTION_ERROR, $this, [
145
                    'connection' => $connection,
146
                    'exception' => $exception
147
                ]));
148
                $connection->end('Bad message');
149
            }
150
        });
151
        //When client be closed
152
        $connection->on('end', function() use ($connection){
153
            $client = $this->clients->findByConnection($connection);
154
            $this->closeClient($client);
155
        });
156
    }
157
158
    /**
159
     * Close the given client
160
     * @param Client $client
161
     */
162
    public function closeClient(Client $client)
163
    {
164
        $tunnelServers = $this->tunnelServers->filterByControlConnection($client->getControlConnection());
0 ignored issues
show
Bug introduced by
The method filterByControlConnection cannot be called on $this->tunnelServers (of type array<integer,object<Spi...TunnelServerInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
165
        $this->dispatcher->dispatch(new Event(EventStore::CLIENT_CLOSE, $this, [
166
            'client' => $client,
167
            'tunnelServers' => $tunnelServers
168
        ]));
169
        foreach ($tunnelServers as $tunnelServer) {
170
            //Close the tunnel server and removes it
171
            $tunnelServer->close();
172
            $this->tunnelServers->removeElement($tunnelServer);
0 ignored issues
show
Bug introduced by
The method removeElement cannot be called on $this->tunnelServers (of type array<integer,object<Spi...TunnelServerInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
173
        }
174
        $client->close();
175
        $this->clients->removeElement($client); //Removes the client
176
    }
177
178
    /**
179
     * Creates a tunnel server for the tunnel
180
     * @param TunnelInterface $tunnel
181
     * @param ConnectionInterface $controlConnection
182
     */
183
    public function createTunnelServer(TunnelInterface $tunnel, ConnectionInterface $controlConnection)
184
    {
185
        if ($tunnel instanceof HttpTunnel) {
186
            $tunnelServer = new TunnelServer\HttpTunnelServer($this, $controlConnection, $tunnel, $this->loop);
187
        } else {
188
            $tunnelServer = new TunnelServer\TcpTunnelServer($this, $controlConnection, $tunnel, $this->loop);
189
        }
190
        $tunnelServer->run();
191
        $this->tunnelServers->add($tunnelServer);
0 ignored issues
show
Bug introduced by
The method add cannot be called on $this->tunnelServers (of type array<integer,object<Spi...TunnelServerInterface>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
192
    }
193
194
    /**
195
     * Gets the dispatcher
196
     * @return Dispatcher
197
     */
198
    public function getDispatcher()
199
    {
200
        return $this->dispatcher;
201
    }
202
203
    /**
204
     * Gets all clients
205
     * @return ClientCollection
206
     */
207
    public function getClients()
208
    {
209
        return $this->clients;
210
    }
211
212
    /**
213
     * Gets all tunnel server
214
     * @return TunnelServerCollection
215
     */
216
    public function getTunnelServers()
217
    {
218
        return $this->tunnelServers;
219
    }
220
221
    /**
222
     * Gets the server host
223
     * @return string
224
     */
225
    public function getHost()
226
    {
227
        return $this->host;
228
    }
229
230
    /**
231
     * Gets the loop instance
232
     * @return LoopInterface
233
     */
234
    public function getLoop()
235
    {
236
        return $this->loop;
237
    }
238
239
    /**
240
     * Sets a logger
241
     * @param Logger $logger
242
     */
243
    public function setLogger($logger)
244
    {
245
        $this->logger = $logger;
246
    }
247
248
    /**
249
     * Gets the logger
250
     * @return Logger
251
     */
252
    public function getLogger()
253
    {
254
        return $this->logger;
255
    }
256
257
    /**
258
     * Gets the server port
259
     * @return int
260
     */
261
    public function getPort()
262
    {
263
        return $this->port;
264
    }
265
266
    /**
267
     * Gets the authentication
268
     * @return AuthenticationInterface
269
     */
270
    public function getAuthentication()
271
    {
272
        return $this->authentication;
273
    }
274
275
    /**
276
     * Creates the handler for the received message
277
     * @param SpikeInterface $message
278
     * @param ConnectionInterface $connection
279
     * @return HandlerInterface
280
     */
281
    protected function createMessageHandler($message, $connection)
282
    {
283
        switch ($message->getAction()) {
284
            case 'auth':
285
                $handler = new Handler\AuthHandler($this, $connection);
286
                break;
287
            case 'ping':
288
                $handler = new Handler\PingHandler($this, $connection);
289
                break;
290
            case 'register_tunnel':
291
                $handler = new Handler\RegisterTunnelHandler($this, $connection);
292
                break;
293
            case 'register_proxy':
294
                $handler = new Handler\RegisterProxyHandler($this, $connection);
295
                break;
296
            default:
297
                throw new InvalidArgumentException(sprintf('Cannot find handler for message type: "%s"',
298
                    get_class($message)
299
                ));
300
        }
301
        return $handler;
302
    }
303
304
    /**
305
     * Creates default timers
306
     * @return TimerInterface[]
307
     */
308
    protected function getDefaultTimers()
309
    {
310
        return [
311
            new ReviewClient($this),
312
            new MemoryWatcher($this->logger),
313
            new SummaryWatcher($this)
314
        ];
315
    }
316
}