HttpServer   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 12
dl 0
loc 187
ccs 0
cts 104
cp 0
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A create() 0 4 1
A attachTo() 0 16 3
A withRequestModifier() 0 6 1
A withResponseModifier() 0 6 1
A enableDebug() 0 6 1
A getStatistics() 0 8 1
A listenTo() 0 66 3
1
<?php
2
3
namespace Thruster\Component\HttpServer;
4
5
use Psr\Http\Message\ResponseInterface;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Thruster\Component\Socket\Server;
8
use Thruster\Component\Socket\ServerInterface;
9
use Thruster\Component\Socket\ConnectionInterface;
10
use Thruster\Component\HttpMessage\Response;
11
use Thruster\Component\HttpModifier\ResponseModifierInterface;
12
use Thruster\Component\HttpModifier\ResponseModifierCollection;
13
use Thruster\Component\HttpModifier\ServerRequestModifierInterface;
14
use Thruster\Component\HttpModifier\ServerRequestModifierCollection;
15
use Thruster\Component\HttpModifiers\AddServerTimeModifier;
16
use Thruster\Component\HttpModifiers\AddServerPoweredByModifier;
17
use Thruster\Component\ServerApplication\ServerApplicationInterface;
18
19
/**
20
 * Class HttpServer
21
 *
22
 * @package Thruster\Component\HttpServer
23
 * @author  Aurimas Niekis <[email protected]>
24
 */
25
class HttpServer
26
{
27
    const VERSION = '0.2';
28
29
    /**
30
     * @var ServerApplicationInterface
31
     */
32
    private $application;
33
34
    /**
35
     * @var bool
36
     */
37
    private $applicationPreloaded;
38
39
    /**
40
     * @var Server[]
41
     */
42
    private $servers;
43
44
    /**
45
     * @var ResponseModifierCollection
46
     */
47
    private $responseModifiers;
48
49
    /**
50
     * @var ServerRequestModifierCollection
51
     */
52
    private $requestModifiers;
53
54
    /**
55
     * @var bool
56
     */
57
    private $debug;
58
59
    /**
60
     * @var int
61
     */
62
    private $servedRequests;
63
64
    /**
65
     * @var int
66
     */
67
    private $failedRequests;
68
69
    /**
70
     * @var int
71
     */
72
    private $upTime;
73
74
    private function __construct(ServerApplicationInterface $application)
75
    {
76
        $this->application          = $application;
77
        $this->applicationPreloaded = false;
78
        $this->servers              = [];
79
        $this->debug                = false;
80
        $this->servedRequests       = 0;
81
        $this->failedRequests       = 0;
82
        $this->upTime               = time();
83
84
        $this->requestModifiers  = new ServerRequestModifierCollection();
85
        $this->responseModifiers = new ResponseModifierCollection(
86
            [
87
                new AddServerTimeModifier(),
88
                new AddServerPoweredByModifier('Thruster/' . static::VERSION),
89
            ]
90
        );
91
    }
92
93
    public static function create(ServerApplicationInterface $application) : self
94
    {
95
        return new static($application);
96
    }
97
98
    public function attachTo(ServerInterface $server) : self
99
    {
100
        if (false !== array_search($server, $this->servers, true)) {
101
            return $this;
102
        }
103
104
        if (false === $this->applicationPreloaded) {
105
            $this->application->preloadApplication();
106
            $this->applicationPreloaded = true;
107
        }
108
109
        $this->servers[] = $server;
110
        $this->listenTo($server);
111
112
        return $this;
113
    }
114
115
    public function withRequestModifier(ServerRequestModifierInterface $modifier) : self
116
    {
117
        $this->requestModifiers->add($modifier);
118
119
        return $this;
120
    }
121
122
    public function withResponseModifier(ResponseModifierInterface $modifier) : self
123
    {
124
        $this->responseModifiers->add($modifier);
125
126
        return $this;
127
    }
128
129
    public function enableDebug() : self
130
    {
131
        $this->debug = true;
132
133
        return $this;
134
    }
135
136
    public function getStatistics() : array
137
    {
138
        return [
139
            'served_requests' => $this->servedRequests,
140
            'failed_requests' => $this->failedRequests,
141
            'up_time'         => (time() - $this->upTime),
142
        ];
143
    }
144
145
    private function listenTo(ServerInterface $server, array $options = [])
146
    {
147
        $server->on(
148
            'connection',
149
            function (ConnectionInterface $connection) use ($options) {
150
                $request = new Request($connection, $options);
151
152
                $request->on(
153
                    'received_head',
154
                    function ($headers, $httpMethod, $uri, $protocolVersion) use ($request, $connection) {
155
                        $this->servedRequests++;
156
157
                        $response = $this->application->processHead($headers, $httpMethod, $uri, $protocolVersion);
158
159
                        if (null !== $response) {
160
                            $connection->removeListener('data', [$request, 'feed']);
161
162
                            $response = $this->responseModifiers->modify($response);
163
164
                            $request->sendResponse($response);
165
                        }
166
                    }
167
                );
168
169
                $request->on(
170
                    'request',
171
                    function (ServerRequestInterface $serverRequest) use ($request, $connection) {
172
173
                        $connection->removeListener('data', [$request, 'feed']);
174
175
                        $serverRequest = $this->requestModifiers->modify($serverRequest);
176
177
                        $fulfilled = function (ResponseInterface $response) use ($request) {
178
                            $response = $this->responseModifiers->modify($response);
179
                            $request->sendResponse($response);
180
                        };
181
182
                        $rejected = function (\Throwable $exception) use ($request) {
183
                            $this->failedRequests++;
184
185
                            $response = new Response(500);
186
                            if (true === $this->debug) {
187
                                $error = [
188
                                    'class'   => get_class($exception),
189
                                    'message' => $exception->getMessage(),
190
                                    'code'    => $exception->getCode(),
191
                                    'file'    => $exception->getFile(),
192
                                    'line'    => $exception->getLine(),
193
                                ];
194
195
                                $response = $response->withHeader('Content-Type', 'application/json');
196
                                $response->getBody()->write(json_encode($error));
197
                            }
198
199
                            $response = $this->responseModifiers->modify($response);
0 ignored issues
show
Compatibility introduced by
$response of type object<Psr\Http\Message\MessageInterface> is not a sub-type of object<Psr\Http\Message\ResponseInterface>. It seems like you assume a child interface of the interface Psr\Http\Message\MessageInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
200
                            $request->sendResponse($response);
201
                        };
202
203
                        $this->application->processRequest($serverRequest)->done($fulfilled, $rejected);
204
                    }
205
                );
206
207
                $connection->on('data', [$request, 'feed']);
208
            }
209
        );
210
    }
211
}
212