1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BenTools\MercurePHP\Hub; |
4
|
|
|
|
5
|
|
|
use BenTools\MercurePHP\Configuration\Configuration; |
6
|
|
|
use BenTools\MercurePHP\Helpers\LoggerAwareTrait; |
7
|
|
|
use BenTools\MercurePHP\Metrics\MetricsHandlerInterface; |
8
|
|
|
use BenTools\MercurePHP\Security\CORS; |
9
|
|
|
use Psr\Http\Message\ResponseInterface; |
10
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
11
|
|
|
use Psr\Log\LoggerInterface; |
12
|
|
|
use Psr\Log\NullLogger; |
13
|
|
|
use React\EventLoop\LoopInterface; |
14
|
|
|
use React\Http; |
15
|
|
|
use React\Promise\PromiseInterface; |
16
|
|
|
use React\Socket; |
17
|
|
|
use React\Socket\ConnectionInterface; |
18
|
|
|
|
19
|
|
|
final class Hub |
20
|
|
|
{ |
21
|
|
|
use LoggerAwareTrait; |
22
|
|
|
|
23
|
|
|
private array $config; |
24
|
|
|
private LoopInterface $loop; |
25
|
|
|
private RequestHandler $requestHandler; |
26
|
|
|
private CORS $cors; |
27
|
|
|
private MetricsHandlerInterface $metricsHandler; |
28
|
|
|
private ?int $shutdownSignal; |
29
|
|
|
|
30
|
|
|
public function __construct( |
31
|
1 |
|
array $config, |
32
|
|
|
LoopInterface $loop, |
33
|
|
|
RequestHandler $requestHandler, |
34
|
|
|
MetricsHandlerInterface $metricsHandler, |
35
|
|
|
?LoggerInterface $logger = null |
36
|
|
|
) { |
37
|
1 |
|
$this->config = $config; |
38
|
1 |
|
$this->loop = $loop; |
39
|
1 |
|
$this->requestHandler = $requestHandler; |
40
|
1 |
|
$this->metricsHandler = $metricsHandler; |
41
|
1 |
|
$this->logger = $logger ?? new NullLogger(); |
|
|
|
|
42
|
1 |
|
$this->cors = new CORS($config); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
public function run(): void |
46
|
|
|
{ |
47
|
|
|
$localAddress = $this->config[Configuration::ADDR]; |
48
|
|
|
$this->shutdownSignal = null; |
49
|
|
|
$this->metricsHandler->resetUsers($localAddress); |
50
|
|
|
$this->loop->addSignal(SIGINT, function ($signal) { |
51
|
|
|
$this->stop($signal, $this->loop); |
52
|
|
|
}); |
53
|
|
|
$this->loop->addPeriodicTimer( |
54
|
|
|
15, |
55
|
|
|
fn() => $this->metricsHandler->getNbUsers()->then( |
56
|
|
|
function (int $nbUsers) { |
57
|
|
|
$memory = \memory_get_usage(true) / 1024 / 1024; |
58
|
|
|
$this->logger()->debug("Users: {$nbUsers} - Memory: {$memory}MB"); |
59
|
|
|
} |
60
|
|
|
) |
61
|
|
|
); |
62
|
|
|
|
63
|
|
|
$socket = $this->createSocketConnection($localAddress, $this->loop); |
64
|
|
|
$this->serve($localAddress, $socket, $this->loop); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
public function __invoke(ServerRequestInterface $request): PromiseInterface |
68
|
|
|
{ |
69
|
4 |
|
return $this->requestHandler->handle($request) |
70
|
|
|
->then(fn(ResponseInterface $response) => $this->cors->decorateResponse($request, $response)); |
71
|
4 |
|
} |
72
|
4 |
|
|
73
|
4 |
|
private function createSocketConnection(string $localAddress, LoopInterface $loop): Socket\Server |
74
|
|
|
{ |
75
|
|
|
$socket = new Socket\Server($localAddress, $loop); |
76
|
|
|
$socket->on('connection', function (ConnectionInterface $connection) use ($localAddress) { |
77
|
4 |
|
$this->metricsHandler->incrementUsers($localAddress); |
78
|
|
|
$connection->on('close', fn() => $this->metricsHandler->decrementUsers($localAddress)); |
79
|
4 |
|
}); |
80
|
|
|
|
81
|
|
|
return $socket; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
private function serve(string $localAddress, Socket\Server $socket, LoopInterface $loop): void |
85
|
|
|
{ |
86
|
|
|
$server = new Http\Server($loop, $this); |
87
|
|
|
$server->listen($socket); |
88
|
|
|
|
89
|
|
|
$this->logger()->info("Server running at http://" . $localAddress); |
90
|
|
|
$loop->run(); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
public function getShutdownSignal(): ?int |
94
|
|
|
{ |
95
|
|
|
return $this->shutdownSignal; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
private function stop(int $signal, LoopInterface $loop): void |
99
|
|
|
{ |
100
|
|
|
$this->shutdownSignal = $signal; |
101
|
|
|
$loop->futureTick(function () use ($loop) { |
102
|
|
|
$loop->stop(); |
103
|
|
|
}); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
This property has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.