Completed
Push — master ( c3a2f6...5765e4 )
by Aurimas
16:41
created

HttpServer::withRequestModifier()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 6
ccs 0
cts 5
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Thruster\Component\HttpServer;
4
5
use GuzzleHttp\Psr7\Response;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Thruster\Component\Promise\PromiseInterface;
9
use Thruster\Component\Socket\Server;
10
use Thruster\Component\Socket\Connection;
11
use Thruster\Component\Socket\ServerInterface;
12
use Thruster\Component\Socket\ConnectionInterface;
13
use Thruster\Component\HttpModifier\RequestModifierInterface;
14
use Thruster\Component\HttpModifier\ResponseModifierInterface;
15
use Thruster\Component\HttpModifier\RequestModifierCollection;
16
use Thruster\Component\HttpModifier\ResponseModifierCollection;
17
use Thruster\Component\HttpModifier\ServerRequestModifierCollection;
18
use Thruster\Component\HttpModifiers\AddServerTimeModifier;
19
use Thruster\Component\HttpModifiers\AddServerPoweredByModifier;
20
use Thruster\Component\ServerApplication\ServerApplicationInterface;
21
22
/**
23
 * Class HttpServer
24
 *
25
 * @package Thruster\Component\HttpServer
26
 * @author  Aurimas Niekis <[email protected]>
27
 */
28
class HttpServer
29
{
30
    const VERSION = '0.2';
31
32
    /**
33
     * @var ServerApplicationInterface
34
     */
35
    private $application;
36
37
    /**
38
     * @var Server[]
39
     */
40
    private $servers;
41
42
    /**
43
     * @var ResponseModifierCollection
44
     */
45
    private $responseModifiers;
46
47
    /**
48
     * @var ServerRequestModifierCollection
49
     */
50
    private $requestModifiers;
51
52
    /**
53
     * @var bool
54
     */
55
    private $debug;
56
57
    /**
58
     * @var int
59
     */
60
    private $servedRequests;
61
62
    /**
63
     * @var int
64
     */
65
    private $failedRequests;
66
67
    /**
68
     * @var int
69
     */
70
    private $upTime;
71
72
    private function __construct(ServerApplicationInterface $application)
73
    {
74
        $this->application    = $application;
75
        $this->servers        = [];
76
        $this->debug          = false;
77
        $this->servedRequests = 0;
78
        $this->failedRequests = 0;
79
        $this->upTime         = time();
80
81
        $this->requestModifiers  = new ServerRequestModifierCollection();
82
        $this->responseModifiers = new ResponseModifierCollection(
83
            [
84
                new AddServerTimeModifier(),
85
                new AddServerPoweredByModifier('Thruster/' . static::VERSION),
86
            ]
87
        );
88
    }
89
90
    public static function create(ServerApplicationInterface $application) : self
91
    {
92
        return new static($application);
93
    }
94
95
    public function attachTo(ServerInterface $server) : self
96
    {
97
        if (false !== array_search($server, $this->servers, true)) {
98
            return $this;
99
        }
100
101
        $this->servers[] = $server;
102
        $this->listenTo($server);
103
104
        return $this;
105
    }
106
107
    public function withRequestModifier(RequestModifierInterface $modifier) : self
108
    {
109
        $this->requestModifiers->add($modifier);
0 ignored issues
show
Documentation introduced by
$modifier is of type object<Thruster\Componen...questModifierInterface>, but the function expects a object<Thruster\Componen...questModifierInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
110
111
        return $this;
112
    }
113
114
    public function withResponseModifier(ResponseModifierInterface $modifier) : self
115
    {
116
        $this->responseModifiers->add($modifier);
117
118
        return $this;
119
    }
120
121
    public function enableDebug() : self
122
    {
123
        $this->debug = true;
124
125
        return $this;
126
    }
127
128
    public function getStatistics() : array
129
    {
130
        return [
131
            'served_requests' => $this->servedRequests,
132
            'failed_requests' => $this->failedRequests,
133
            'up_time'         => (time() - $this->upTime),
134
        ];
135
    }
136
137
    private function listenTo(ServerInterface $server, array $options = [])
138
    {
139
        $server->on(
140
            'connection',
141
            function (ConnectionInterface $connection) use ($options) {
142
                $request = new Request($connection, $options);
143
144
                $request->on(
145
                    'received_head',
146
                    function ($headers, $httpMethod, $uri, $protocolVersion) use ($request, $connection) {
147
                        $this->servedRequests++;
148
149
                        $response = $this->application->processHead($headers, $httpMethod, $uri, $protocolVersion);
150
151
                        if (null !== $response) {
152
                            $connection->removeListener('data', [$request, 'feed']);
153
154
                            $response = $this->responseModifiers->modify($response);
155
156
                            $request->sendResponse($response);
157
                        }
158
                    }
159
                );
160
161
                $request->on(
162
                    'request',
163
                    function (ServerRequestInterface $serverRequest) use ($request, $connection) {
164
165
                        $connection->removeListener('data', [$request, 'feed']);
166
167
                        $serverRequest = $this->requestModifiers->modify($serverRequest);
168
169
                        $fulfilled = function (ResponseInterface $response) use ($request) {
170
                            $response = $this->responseModifiers->modify($response);
171
                            $request->sendResponse($response);
172
                        };
173
174
                        $rejected = function (\Throwable $exception) use ($request) {
175
                            $this->failedRequests++;
176
177
                            $response = new Response(500);
178
                            if (true === $this->debug) {
179
                                $error = [
180
                                    'class'   => get_class($exception),
181
                                    'message' => $exception->getMessage(),
182
                                    'code'    => $exception->getCode(),
183
                                    'file'    => $exception->getFile(),
184
                                    'line'    => $exception->getLine(),
185
                                ];
186
187
                                $response = $response->withHeader('Content-Type', 'application/json');
188
                                $response->getBody()->write(json_encode($error));
189
                            }
190
191
                            $response = $this->responseModifiers->modify($response);
192
                            $request->sendResponse($response);
193
                        };
194
195
                        $this->application->processRequest($serverRequest)->done($fulfilled, $rejected);
196
                    }
197
                );
198
199
                $connection->on('data', [$request, 'feed']);
200
            }
201
        );
202
    }
203
}
204