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

Client::isAuthorized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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...
98
        $this->tunnelClients = new TunnelClientCollection();
99
    }
100
101
    /**
102
     * Checks whether the client is authorized
103
     * @return bool
104
     */
105
    public function isAuthorized()
106
    {
107
        return !empty($this->id);
108
    }
109
110
    /**
111
     * Creates array of tunnels
112
     * @param array $data
113
     * @return TunnelInterface[]
114
     */
115
    protected function createTunnels($data)
116
    {
117
        $tunnels = [];
118
        foreach ($data as $info) {
119
            $tunnel = TunnelFactory::fromArray($info);
120
            $tunnels[] = $tunnel;
121
        }
122
        return $tunnels;
123
    }
124
125
    /**
126
     * Run the client
127
     */
128
    public function run()
129
    {
130
        $this->connector->connect($this->serverAddress)->then(function(ConnectionInterface $connection){
131
            //Emit the event
132
            $this->dispatcher->dispatch(new Event(EventStore::CONNECT_TO_SERVER, $this, [
133
                'connection' => $connection
134
            ]));
135
            $this->controlConnection = $connection;
136
            $this->requestAuth($connection);
137
            $this->handleControlConnection($connection);
138
        });
139
        $this->dispatcher->dispatch(EventStore::CLIENT_RUN);
140
        foreach ($this->getDefaultTimers() as $timer) {
141
            $this->addTimer($timer);
142
        }
143
        $this->loop->run();
144
    }
145
146
    /**
147
     * Close the client
148
     */
149
    public function close()
150
    {
151
        foreach ($this->timers as $timer) {
152
            $timer->cancel();
153
        }
154
        foreach ($this->tunnelClients as $tunnelClient) {
155
            $tunnelClient->close();
156
        }
157
        $this->controlConnection->end();
158
    }
159
160
    /**
161
     * Handles the control connection
162
     * @param ConnectionInterface $connection
163
     */
164
    protected function handleControlConnection(ConnectionInterface $connection)
165
    {
166
        $parser = new SpikeParser();
167
        $connection->on('data', function($data) use($parser, $connection){
168
            $parser->pushIncoming($data);
169
            $messages = $parser->parse();
170
            foreach ($messages as $message) {
171
                $message = Spike::fromString($message);
172
                $this->dispatcher->dispatch(new Event(EventStore::RECEIVE_MESSAGE, $this, [
173
                    'message' => $message,
174
                    'connection' => $connection
175
                ]));
176
                $this->createMessageHandler($message, $connection)->handle($message);
177
            }
178
        });
179
        $connection->on('close', function(){
180
            $this->close();
181
        });
182
    }
183
184
    /**
185
     * Request for auth
186
     * @param ConnectionInterface $connection
187
     */
188
    protected function requestAuth(ConnectionInterface $connection)
189
    {
190
        $authInfo = array_replace([
191
            'os' => PHP_OS,
192
            'version' => '',
193
        ], $this->auth);
194
        $connection->write(new Spike('auth', $authInfo));
195
    }
196
197
    /**
198
     * Sets the client id
199
     * @param string $id
200
     */
201
    public function setClientId($id)
202
    {
203
        $this->id = $id;
204
        Spike::setGlobalHeader('Client-ID', $id);
205
        $this->addTimer(new Heartbeat($this));
206
    }
207
208
    /**
209
     * Gets all tunnel clients
210
     * @return TunnelClientCollection
211
     */
212
    public function getTunnelClients()
213
    {
214
        return $this->tunnelClients;
215
    }
216
217
    /**
218
     * Gets all tunnels
219
     * @return TunnelInterface[]
220
     */
221
    public function getTunnels()
222
    {
223
        return $this->tunnels;
224
    }
225
226
    /**
227
     * Gets the dispatcher
228
     * @return Dispatcher
229
     */
230
    public function getDispatcher()
231
    {
232
        return $this->dispatcher;
233
    }
234
235
    /**
236
     * @return ConnectionInterface
237
     */
238
    public function getControlConnection()
239
    {
240
        return $this->controlConnection;
241
    }
242
243
    /**
244
     * Sets a logger
245
     * @param Logger $logger
246
     */
247
    public function setLogger($logger)
248
    {
249
        $this->logger = $logger;
250
    }
251
252
    /**
253
     * Gets the logger
254
     * @return Logger
255
     */
256
    public function getLogger()
257
    {
258
        return $this->logger;
259
    }
260
261
    /**
262
     * Gets the loop instance
263
     * @return LoopInterface
264
     */
265
    public function getLoop()
266
    {
267
        return $this->loop;
268
    }
269
270
    /**
271
     * Finds the matching tunnel
272
     * @param array $tunnelInfo
273
     * @throws RuntimeException
274
     * @return false|TunnelInterface
275
     */
276
    public function findTunnel($tunnelInfo)
277
    {
278
        foreach ($this->tunnels as $tunnel) {
0 ignored issues
show
Bug introduced by
The expression $this->tunnels of type object<Spike\Tunnel\TunnelInterface> is not traversable.
Loading history...
279
            if ($tunnel->match($tunnelInfo)) {
280
                return $tunnel;
281
            }
282
        }
283
        return false;
284
    }
285
286
    /**
287
     * Creates a tunnel client to process proxy connection
288
     * @param TunnelInterface $tunnel
289
     * @param string $proxyConnectionId
290
     * @return TunnelClientInterface
291
     */
292
    public function createTunnelClient(TunnelInterface $tunnel, $proxyConnectionId)
293
    {
294
        if ($tunnel instanceof HttpTunnel) {
295
            $tunnelClient = new TunnelClient\HttpTunnelClient($this, $tunnel, $proxyConnectionId, $this->serverAddress, $this->loop);
296
        } else {
297
            $tunnelClient = new TunnelClient\TcpTunnelClient($this, $tunnel, $proxyConnectionId, $this->serverAddress, $this->loop);
298
        }
299
        $tunnelClient->run();
300
        $this->tunnelClients->add($tunnelClient);
301
        return $tunnelClient;
302
    }
303
304
    /**
305
     * Creates the handler for the received message
306
     * @param SpikeInterface $message
307
     * @param ConnectionInterface $connection
308
     * @return Handler\HandlerInterface
309
     */
310
    protected function createMessageHandler(SpikeInterface $message, ConnectionInterface $connection)
311
    {
312
        switch ($message->getAction()) {
313
            case 'auth_response':
314
                $handler = new Handler\AuthResponseHandler($this, $connection);
315
                break;
316
            case 'register_tunnel_response':
317
                $handler = new Handler\RegisterTunnelResponseHandler($this, $connection);
318
                break;
319
            case 'request_proxy':
320
                $handler = new Handler\RequestProxyHandler($this, $connection);
321
                break;
322
            default:
323
                throw new InvalidArgumentException(sprintf('Cannot find handler for the message: "%s"',
324
                    $message->getAction()
325
                ));
326
        }
327
        return $handler;
328
    }
329
330
    /**
331
     * Creates default timers
332
     * @return TimerInterface[]
333
     */
334
    protected function getDefaultTimers()
335
    {
336
        return [
337
            new MemoryWatcher($this->logger)
338
        ];
339
    }
340
}