TcpChunkServer::findPublicConnectionById()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the slince/spike package.
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Spike\Server\ChunkServer;
13
14
use Doctrine\Common\Collections\ArrayCollection;
15
use Doctrine\Common\Collections\Collection;
16
use React\Socket\ConnectionInterface;
17
use React\Socket\Server as Socket;
18
use Slince\EventDispatcher\Event;
19
use Spike\Common\Exception\InvalidArgumentException;
20
use Spike\Common\Protocol\Spike;
21
use Spike\Common\Timer\TimersAware;
22
use Spike\Common\Tunnel\TcpTunnel;
23
use Spike\Common\Tunnel\TunnelInterface;
24
use Spike\Server\Client;
25
use Spike\Server\Event\Events;
26
use Spike\Server\Server;
27
use Spike\Server\ServerInterface;
28
29
class TcpChunkServer implements ChunkServerInterface
30
{
31
    use TimersAware;
32
33
    /**
34
     * @var Collection|PublicConnection[]
35
     */
36
    protected $publicConnections;
37
38
    /**
39
     * @var Socket
40
     */
41
    protected $socket;
42
43
    /**
44
     * @var TcpTunnel
45
     */
46
    protected $tunnel;
47
48
    /**
49
     * @var ServerInterface
50
     */
51
    protected $server;
52
53
    /**
54
     * @var Client
55
     */
56
    protected $client;
57
58
    public function __construct(Server $server, Client $client, TunnelInterface $tunnel)
59
    {
60
        $this->server = $server;
61
        $this->client = $client;
62
        $this->tunnel = $tunnel;
0 ignored issues
show
Documentation Bug introduced by
$tunnel is of type object<Spike\Common\Tunnel\TunnelInterface>, but the property $tunnel was declared to be of type object<Spike\Common\Tunnel\TcpTunnel>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
63
        $this->publicConnections = new ArrayCollection();
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function getTunnel()
70
    {
71
        return $this->tunnel;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function getServer()
78
    {
79
        return $this->server;
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     *
85
     * @codeCoverageIgnore
86
     */
87
    public function start()
88
    {
89
        $this->socket = new Socket($this->resolveListenAddress(), $this->server->getEventLoop());
90
        $this->socket->on('connection', function($connection){
91
            $publicConnection = new PublicConnection($connection);
92
            $this->publicConnections->add($publicConnection);
93
            $this->handlePublicConnection($publicConnection);
94
        });
95
        $this->addTimer(new Timer\PublicConnectionScanTimer($this));
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function getClient()
102
    {
103
        return $this->client;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function getPublicConnections()
110
    {
111
        return $this->publicConnections;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function stop()
118
    {
119
        //Close all public connection
120
        foreach ($this->publicConnections as $publicConnection) {
121
            $this->closePublicConnection($publicConnection);
122
        }
123
        $this->socket && $this->socket->close();
124
    }
125
126
    /**
127
     * Handles the public connection.
128
     *
129
     * @param PublicConnection $publicConnection
130
     * @codeCoverageIgnore
131
     */
132
    public function handlePublicConnection(PublicConnection $publicConnection)
133
    {
134
        //Request proxy to client
135
        $requestProxyMessage = new Spike('request_proxy', $this->tunnel->toArray(), [
136
            'public-connection-id' => $publicConnection->getId(),
137
        ]);
138
        $this->sendToClient($requestProxyMessage);
139
        //Fires 'request_proxy' event
140
        $this->server->getEventDispatcher()->dispatch(new Event(Events::REQUEST_PROXY, $this, [
141
            'message' => $requestProxyMessage,
142
        ]));
143
        //Pause the public connection
144
        $publicConnection->removeAllListeners();
145
        $publicConnection->pause();
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     *
151
     * @codeCoverageIgnore
152
     */
153
    public function setProxyConnection($publicConnectionId, ConnectionInterface $proxyConnection)
154
    {
155
        $publicConnection = $this->findPublicConnectionById($publicConnectionId);
156
        if (is_null($publicConnection)) {
157
            throw new InvalidArgumentException(sprintf('Cannot find the public connection "%s"', $publicConnectionId));
158
        }
159
        $startProxyMessage = new Spike('start_proxy');
160
        $proxyConnection->write($startProxyMessage);
161
        //Fires 'start_proxy' event
162
        $this->server->getEventDispatcher()->dispatch(new Event(Events::START_PROXY, $this, [
163
            'message' => $startProxyMessage,
164
        ]));
165
        //Resumes the public connection
166
        $publicConnection->resume();
167
        $publicConnection->pipe($proxyConnection);
168
        $proxyConnection->pipe($publicConnection->getConnection());
169
        $proxyConnection->write($publicConnection->getInitBuffer());
170
171
        //Handles public connection close
172
        $handlePublicConnectionClose = function() use ($proxyConnection, $publicConnection, &$handleProxyConnectionClose){
173
            $proxyConnection->removeListener('close', $handleProxyConnectionClose);
174
            $proxyConnection->removeListener('error', $handleProxyConnectionClose);
175
            $proxyConnection->end();
176
            //There will be a bug if the tunnel server is closed before public connection
177
            $this->publicConnections && $this->publicConnections->removeElement($publicConnection);
178
        };
179
        $publicConnection->on('close', $handlePublicConnectionClose);
180
        $publicConnection->on('error', $handlePublicConnectionClose);
181
182
        //Handles proxy connection close
183
        $handleProxyConnectionClose = function () use ($publicConnection, &$handlePublicConnectionClose) {
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $handleProxyConnectionClose, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
184
            $publicConnection->removeListener('close', $handlePublicConnectionClose);
185
            $publicConnection->removeListener('error', $handlePublicConnectionClose);
186
            $publicConnection->end();
187
        };
188
        $proxyConnection->on('close', $handleProxyConnectionClose);
189
        $proxyConnection->on('error', $handleProxyConnectionClose);
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195
    public function getEventLoop()
196
    {
197
        return $this->server->getEventLoop();
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203
    public function closePublicConnection(PublicConnection $publicConnection, $message = null)
204
    {
205
        $publicConnection->write($message ?: 'The chunk server is closed');
206
        $this->publicConnections->removeElement($publicConnection);
207
    }
208
209
    protected function sendToClient($data)
210
    {
211
        $this->client->getControlConnection()->write($data);
212
        $this->client->setActiveAt(new \DateTime());
213
    }
214
215
    /**
216
     * @param string $id
217
     *
218
     * @return null|PublicConnection
219
     */
220
    protected function findPublicConnectionById($id)
221
    {
222
        foreach ($this->publicConnections as $publicConnection) {
223
            if ($publicConnection->getId() === $id) {
224
                return $publicConnection;
225
            }
226
        }
227
228
        return null;
229
    }
230
231
    /**
232
     * Gets the server address to bind.
233
     *
234
     * @return string
235
     */
236
    protected function resolveListenAddress()
237
    {
238
        return "0.0.0.0:{$this->tunnel->getServerPort()}";
239
    }
240
}