ReactPhpServer   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 47
c 3
b 0
f 0
dl 0
loc 92
rs 10
wmc 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A shutdown() 0 5 1
A initSocket() 0 21 4
A __construct() 0 5 1
A onRequest() 0 13 2
A listen() 0 23 2
1
<?php
2
/**
3
 * This file is part of Phiremock.
4
 *
5
 * Phiremock is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Lesser General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * Phiremock is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with Phiremock.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
namespace Mcustiel\Phiremock\Server\Http\Implementation;
20
21
use Mcustiel\Phiremock\Common\StringStream;
22
use Mcustiel\Phiremock\Server\Cli\Options\HostInterface;
23
use Mcustiel\Phiremock\Server\Cli\Options\Port;
24
use Mcustiel\Phiremock\Server\Cli\Options\SecureOptions;
25
use Mcustiel\Phiremock\Server\Http\RequestHandlerInterface;
26
use Mcustiel\Phiremock\Server\Http\ServerInterface;
27
use Psr\Http\Message\ResponseInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use Psr\Log\LoggerInterface;
30
use React\EventLoop\Factory as EventLoop;
31
use React\EventLoop\LoopInterface;
32
use React\Http\Middleware\RequestBodyBufferMiddleware;
33
use React\Http\Middleware\RequestBodyParserMiddleware;
34
use React\Http\Middleware\StreamingRequestMiddleware;
35
use React\Http\Server;
36
use React\Socket\Server as ReactSocket;
37
38
class ReactPhpServer implements ServerInterface
39
{
40
    /** @var RequestHandlerInterface */
41
    private $requestHandler;
42
43
    /** @var LoopInterface */
44
    private $loop;
45
46
    /** @var ReactSocket */
47
    private $socket;
48
49
    /** @var Server */
50
    private $http;
51
52
    /** @var LoggerInterface */
53
    private $logger;
54
55
    public function __construct(RequestHandlerInterface $requestHandler, LoggerInterface $logger)
56
    {
57
        $this->loop = EventLoop::create();
0 ignored issues
show
Deprecated Code introduced by
The function React\EventLoop\Factory::create() has been deprecated: 1.2.0 See Loop::get() instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

57
        $this->loop = /** @scrutinizer ignore-deprecated */ EventLoop::create();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
58
        $this->logger = $logger;
59
        $this->requestHandler = $requestHandler;
60
    }
61
62
    public function listen(HostInterface $host, Port $port, ?SecureOptions $secureOptions): void
63
    {
64
        $this->http = new Server(
0 ignored issues
show
Deprecated Code introduced by
The class React\Http\Server has been deprecated: 1.5.0 See HttpServer instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

64
        $this->http = /** @scrutinizer ignore-deprecated */ new Server(
Loading history...
65
            $this->loop,
66
            new StreamingRequestMiddleware(),
67
            new RequestBodyBufferMiddleware(),
68
            new RequestBodyParserMiddleware(),
69
            function (ServerRequestInterface $request) {
70
                return $this->onRequest($request);
71
            }
72
        );
73
74
        $listenConfig = "{$host->asString()}:{$port->asInt()}";
75
        $this->initSocket($listenConfig, $secureOptions);
76
        $this->http->listen($this->socket);
77
78
        // Dispatch pending signals periodically
79
        if (\function_exists('pcntl_signal_dispatch')) {
80
            $this->loop->addPeriodicTimer(0.5, function () {
81
                pcntl_signal_dispatch();
82
            });
83
        }
84
        $this->loop->run();
85
    }
86
87
    public function shutdown(): void
88
    {
89
        $this->http->removeAllListeners();
90
        $this->socket->close();
91
        $this->loop->stop();
92
    }
93
94
    private function onRequest(ServerRequestInterface $request): ResponseInterface
95
    {
96
        $start = microtime(true);
97
98
        // TODO: Remove this patch if ReactPHP is fixed
99
        if ($request->getParsedBody() !== null) {
100
            $request = $request->withBody(new StringStream(http_build_query($request->getParsedBody())));
101
        }
102
103
        $psrResponse = $this->requestHandler->dispatch(new ServerRequestWithCachedBody($request));
104
        $this->logger->debug('Processing took ' . number_format((microtime(true) - $start) * 1000, 3) . ' milliseconds');
105
106
        return $psrResponse;
107
    }
108
109
    private function initSocket(string $listenConfig, ?SecureOptions $secureOptions): void
110
    {
111
        $this->logger->info(
112
            sprintf(
113
                'Phiremock http server listening on %s over %s',
114
                $listenConfig,
115
                null === $secureOptions ? 'http' : 'https'
116
            )
117
        );
118
        $context = [];
119
        if ($secureOptions !== null) {
120
            $tlsContext = [];
121
            $listenConfig = sprintf('tls://%s', $listenConfig);
122
            $tlsContext['local_cert'] = $secureOptions->getCertificate()->asString();
123
            $tlsContext['local_pk'] = $secureOptions->getCertificateKey()->asString();
124
            if ($secureOptions->hasPassphrase()) {
125
                $tlsContext['passphrase'] = $secureOptions->getPassphrase()->asString();
126
            }
127
            $context['tls'] = $tlsContext;
128
        }
129
        $this->socket = new ReactSocket($listenConfig, $this->loop, $context);
0 ignored issues
show
Deprecated Code introduced by
The class React\Socket\Server has been deprecated: 1.9.0 See `SocketServer` instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

129
        $this->socket = /** @scrutinizer ignore-deprecated */ new ReactSocket($listenConfig, $this->loop, $context);
Loading history...
130
    }
131
}
132