This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace NokitaKaze\TestHTTPServer; |
||
4 | |||
5 | /** |
||
6 | * Class Server |
||
7 | * @package NokitaKaze\TestHTTPServer |
||
8 | * |
||
9 | * @doc https://tools.ietf.org/html/rfc7230 |
||
10 | * @doc http://httpwg.org/specs/rfc7230.html |
||
11 | */ |
||
12 | class Server { |
||
13 | const CHUNK_SIZE = 4096; |
||
14 | |||
15 | /** |
||
16 | * @var ServerSettings |
||
17 | */ |
||
18 | protected $_settings = null; |
||
19 | |||
20 | /** |
||
21 | * @var resource |
||
22 | */ |
||
23 | protected $_server_socket = null; |
||
24 | |||
25 | /** |
||
26 | * @var resource |
||
27 | */ |
||
28 | protected $_stream_context = null; |
||
29 | |||
30 | /** |
||
31 | * @var ClientDatum[] |
||
32 | */ |
||
33 | protected $_client_connects = []; |
||
34 | |||
35 | /** |
||
36 | * Server constructor |
||
37 | * |
||
38 | * @param ServerSettings|object|array $settings |
||
39 | * |
||
40 | * @throws Exception |
||
41 | */ |
||
42 | 613 | function __construct($settings) { |
|
43 | 613 | $settings = (object) $settings; |
|
44 | 613 | self::fix_incoming_settings($settings); |
|
45 | 613 | $this->_settings = $settings; |
|
46 | 613 | $this->init_ssl_settings(); |
|
47 | 601 | } |
|
48 | |||
49 | 601 | function __destruct() { |
|
50 | 601 | $this->shutdown(); |
|
51 | 601 | } |
|
52 | |||
53 | 601 | function shutdown() { |
|
54 | 601 | foreach ($this->_client_connects as &$connect) { |
|
55 | 268 | $this->close_connection($connect); |
|
56 | 301 | } |
|
57 | 601 | $this->_client_connects = []; |
|
58 | 601 | $this->close_connection_socket($this->_server_socket); |
|
59 | 601 | $this->_server_socket = null; |
|
60 | 601 | } |
|
61 | |||
62 | /** |
||
63 | * @param resource $connection |
||
64 | */ |
||
65 | 601 | function close_connection_socket($connection) { |
|
66 | 601 | if (is_resource($connection)) { |
|
67 | 597 | stream_socket_shutdown($connection, STREAM_SHUT_RDWR); |
|
68 | 597 | fclose($connection); |
|
69 | 299 | } |
|
70 | 601 | } |
|
71 | |||
72 | /** |
||
73 | * @param object|ClientDatum $connection |
||
74 | */ |
||
75 | 573 | function close_connection(&$connection) { |
|
76 | 573 | if (is_null($connection)) { |
|
77 | 245 | return; |
|
78 | } |
||
79 | 573 | $this->close_connection_socket($connection->client); |
|
80 | 573 | $connection->client = null; |
|
81 | 573 | $connection = null; |
|
82 | 573 | } |
|
83 | |||
84 | /** |
||
85 | * @return array |
||
86 | */ |
||
87 | 613 | static function get_default_settings() { |
|
88 | return [ |
||
89 | 613 | 'interface' => '127.0.0.1', |
|
90 | 613 | 'port' => 58080, |
|
91 | 613 | 'server_connect_waiting_time' => 0.02, |
|
92 | 307 | 'is_ssl' => false, |
|
93 | 'filterIncomingConnect' => function () { return true; }, |
||
94 | 307 | 'server_maximum_chunk' => 300 * 1024, |
|
95 | 613 | 'server_backlog' => min(255, SOMAXCONN), |
|
96 | |||
97 | 613 | 'time_wait_until_first_byte' => 60, |
|
98 | 307 | ]; |
|
99 | } |
||
100 | |||
101 | 613 | function init_ssl_settings() { |
|
102 | 613 | $this->_stream_context = stream_context_create(); |
|
103 | /** @url http://php.net/manual/ru/context.socket.php */ |
||
104 | 613 | stream_context_set_option($this->_stream_context, 'socket', 'backlog', $this->_settings->server_backlog); |
|
105 | 613 | if (isset($this->_settings->ssl_server_certificate_file)) { |
|
106 | // @hint Здесь специально не задаётся _settings->is_ssl |
||
107 | 412 | $this->stream_set_ssl_option('local_cert', $this->_settings->ssl_server_certificate_file); |
|
108 | 412 | $this->stream_set_ssl_option('allow_self_signed', true); |
|
109 | 412 | $this->stream_set_ssl_option('verify_peer', false); |
|
110 | 412 | if (isset($this->_settings->ssl_server_key_file)) { |
|
111 | 412 | $this->stream_set_ssl_option('local_pk', $this->_settings->ssl_server_key_file); |
|
112 | 206 | } |
|
113 | 412 | $this->stream_set_ssl_option('passphrase', |
|
114 | 412 | isset($this->_settings->ssl_server_key_password) ? $this->_settings->ssl_server_key_password : ''); |
|
115 | |||
116 | 412 | if (isset($this->_settings->ssl_client_certificate_file)) { |
|
117 | 48 | $this->stream_set_ssl_option('verify_peer', true); |
|
118 | 48 | $this->stream_set_ssl_option('capture_peer_cert', true); |
|
119 | 48 | $this->stream_set_ssl_option('capture_peer_cert_chain', true); |
|
120 | 139 | $this->stream_set_ssl_option('cafile', $this->_settings->ssl_client_certificate_file); |
|
121 | 24 | } |
|
122 | 307 | } elseif (isset($this->_settings->ssl_server_key_file)) { |
|
123 | 4 | throw new Exception('ssl_server_key_file is set, but ssl_server_certificate_file is missing'); |
|
124 | 99 | } elseif (isset($this->_settings->ssl_server_key_password)) { |
|
125 | 4 | throw new Exception('ssl_server_key_password is set, but ssl_server_certificate_file is missing'); |
|
126 | 97 | } elseif (isset($this->_settings->ssl_client_certificate_file)) { |
|
127 | 4 | throw new Exception('ssl_client_certificate_file is set, but ssl_server_certificate_file is missing'); |
|
128 | } |
||
129 | 601 | } |
|
130 | |||
131 | /** |
||
132 | * Server constructor. |
||
133 | * |
||
134 | * @param ServerSettings|object $settings |
||
135 | */ |
||
136 | 613 | protected static function fix_incoming_settings($settings) { |
|
137 | 613 | $default_settings = self::get_default_settings(); |
|
138 | 613 | foreach ($default_settings as $key => $value) { |
|
139 | 613 | if (!isset($settings->{$key})) { |
|
140 | 613 | $settings->{$key} = $value; |
|
141 | 307 | } |
|
142 | 307 | } |
|
143 | 613 | foreach (['ListenStart', 'Connect', 'Request', 'Disconnect', 'ListenStop', 'AnyIncomingData', 'HeadIncomingData', |
|
144 | 307 | 'BodyIncomingData', 'HeadReceived', 'HeadInvalidReceived'] as $event) { |
|
145 | 613 | if (!isset($settings->{'on'.$event})) { |
|
146 | 613 | $settings->{'on'.$event} = null; |
|
147 | 307 | } |
|
148 | 307 | } |
|
149 | 613 | } |
|
150 | |||
151 | /** |
||
152 | * @param callable|double $param |
||
153 | * @param callable|null $tick |
||
154 | */ |
||
155 | 34 | function listen($param, $tick = null) { |
|
156 | 34 | $this->init_listening(); |
|
157 | 34 | if (is_callable($param)) { |
|
158 | 34 | $closure = $param; |
|
159 | 18 | } else { |
|
160 | $closure = function () use ($param) { return (microtime(true) < $param); }; |
||
161 | } |
||
162 | /** @noinspection PhpMethodParametersCountMismatchInspection */ |
||
163 | 34 | while ($closure($this)) { |
|
164 | 34 | $this->listen_tick(); |
|
165 | 34 | if (!is_null($tick)) { |
|
166 | $tick($this); |
||
167 | } |
||
168 | 18 | } |
|
169 | 34 | } |
|
170 | |||
171 | 597 | function init_listening() { |
|
172 | 597 | if (!is_null($this->_server_socket)) { |
|
173 | 34 | return; |
|
174 | } |
||
175 | 597 | $this->_server_socket = stream_socket_server( |
|
176 | 597 | sprintf('%s://%s:%d', |
|
177 | 597 | $this->_settings->is_ssl ? 'ssl' : 'tcp', |
|
178 | 597 | is_null($this->_settings->interface) ? '0.0.0.0' : $this->_settings->interface, |
|
179 | 597 | $this->_settings->port |
|
180 | 299 | ), |
|
181 | 597 | $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, |
|
182 | 597 | $this->_stream_context); |
|
183 | 597 | if (!isset($this->_server_socket)) { |
|
184 | // @codeCoverageIgnoreStart |
||
185 | throw new Exception('Can not create socket ['.$errstr.']', 100 + $errno); |
||
186 | // @codeCoverageIgnoreEnd |
||
187 | } |
||
188 | |||
189 | 597 | $this->event_raise('ListenStart'); |
|
190 | 597 | } |
|
191 | |||
192 | 597 | function listen_tick() { |
|
193 | 597 | $write = null; |
|
194 | 597 | $except = null; |
|
195 | 597 | $tv_sec = (int) $this->_settings->server_connect_waiting_time; |
|
0 ignored issues
–
show
|
|||
196 | 597 | $tv_usec = ($this->_settings->server_connect_waiting_time - $tv_sec) * 1000000; |
|
0 ignored issues
–
show
Accessing
server_connect_waiting_time on the interface NokitaKaze\TestHTTPServer\ServerSettings suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
![]() |
|||
197 | 597 | while (true) { |
|
198 | 597 | $read = [$this->_server_socket]; |
|
199 | 597 | if (stream_select($read, $write, $except, $tv_sec, $tv_usec) === false) { |
|
200 | // @codeCoverageIgnoreStart |
||
201 | throw new Exception('Error on stream select'); |
||
202 | // @codeCoverageIgnoreEnd |
||
203 | } |
||
204 | 597 | if (empty($read)) { |
|
205 | 597 | $this->process_all_connected_clients(); |
|
206 | |||
207 | 597 | return; |
|
208 | } |
||
209 | |||
210 | 589 | $client = @stream_socket_accept($this->_server_socket); |
|
211 | 589 | if ($client === false) { |
|
212 | 30 | $this->event_raise('InvalidConnection'); |
|
213 | 30 | continue; |
|
214 | } |
||
215 | |||
216 | 573 | if (stream_set_blocking($client, 0) === false) { |
|
217 | // @codeCoverageIgnoreStart |
||
218 | throw new Exception('Can not set socket as non blocking'); |
||
219 | // @codeCoverageIgnoreEnd |
||
220 | } |
||
221 | 573 | $connect_time = microtime(true); |
|
222 | /** |
||
223 | * @var ClientDatum $datum |
||
224 | */ |
||
225 | 573 | $datum = new \stdClass(); |
|
226 | 573 | $datum->status = 0; |
|
227 | 573 | $datum->client = $client; |
|
228 | 573 | $datum->connection_time = $connect_time; |
|
229 | 573 | $datum->blob_request = ''; |
|
230 | 573 | $datum->request_head_params = []; |
|
231 | 573 | $datum->server = $this; |
|
232 | 573 | $datum->accepted_hosts = null; |
|
233 | 573 | $this->event_raise('Connect', $datum); |
|
234 | 573 | $this->_client_connects[] = $datum; |
|
235 | 287 | } |
|
236 | } |
||
237 | |||
238 | /** |
||
239 | * @param string $key |
||
240 | * @param mixed $value |
||
241 | * |
||
242 | * @url http://php.net/manual/ru/context.ssl.php |
||
243 | */ |
||
244 | 426 | function stream_set_ssl_option($key, $value) { |
|
245 | 426 | stream_context_set_option($this->_stream_context, 'ssl', $key, $value); |
|
246 | 426 | } |
|
247 | |||
248 | 17 | function set_option($key, $value) { |
|
249 | 17 | $this->_settings->{$key} = $value; |
|
250 | 17 | } |
|
251 | |||
252 | 7 | function get_option($key) { |
|
253 | 7 | return isset($this->_settings->{$key}) ? $this->_settings->{$key} : null; |
|
254 | } |
||
255 | |||
256 | /** |
||
257 | * Первый байт не пришёл |
||
258 | * |
||
259 | * @param object|ClientDatum $connection |
||
260 | * |
||
261 | * @return boolean |
||
262 | */ |
||
263 | 573 | protected function check_timeouts_on_connection_first_byte($connection) { |
|
264 | 573 | return isset($this->_settings->time_wait_until_first_byte) and |
|
265 | 573 | !isset($connection->first_byte_received_time) and |
|
266 | 573 | ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_first_byte); |
|
267 | } |
||
268 | |||
269 | /** |
||
270 | * Голова не пришла |
||
271 | * |
||
272 | * @param object|ClientDatum $connection |
||
273 | * |
||
274 | * @return boolean |
||
275 | */ |
||
276 | 572 | protected function check_timeouts_on_connection_head_received($connection) { |
|
277 | 572 | return isset($this->_settings->time_wait_until_head_received) and |
|
278 | 431 | !isset($connection->head_received_time) and |
|
279 | 572 | ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_head_received); |
|
280 | } |
||
281 | |||
282 | /** |
||
283 | * Запрос не пришёл |
||
284 | * |
||
285 | * @param object|ClientDatum $connection |
||
286 | * |
||
287 | * @return boolean |
||
288 | */ |
||
289 | 572 | protected function check_timeouts_on_connection_request_received($connection) { |
|
290 | 572 | return isset($this->_settings->time_wait_until_request_received) and |
|
291 | 431 | !isset($connection->full_request_received_time) and |
|
292 | 572 | ($connection->connection_time < microtime(true) - $this->_settings->time_wait_until_request_received); |
|
293 | } |
||
294 | |||
295 | /** |
||
296 | * Тело не пришло (голова пришла) |
||
297 | * |
||
298 | * @param object|ClientDatum $connection |
||
299 | * |
||
300 | * @return boolean |
||
301 | */ |
||
302 | 572 | protected function check_timeouts_on_connection_body_received_without_head($connection) { |
|
303 | 572 | return isset($this->_settings->time_between_head_and_body_received, $connection->head_received_time) and |
|
304 | 431 | !isset($connection->full_request_received_time) and |
|
305 | 572 | ($connection->head_received_time < microtime(true) - $this->_settings->time_between_head_and_body_received); |
|
306 | } |
||
307 | |||
308 | /** |
||
309 | * Проверяем слишком старые подключения и убиваем их |
||
310 | * |
||
311 | * @param object|ClientDatum $connection |
||
312 | * |
||
313 | * @return bool |
||
314 | */ |
||
315 | 573 | protected function check_timeouts_on_connection(&$connection) { |
|
316 | 573 | if ($this->check_timeouts_on_connection_first_byte($connection) or |
|
317 | 572 | $this->check_timeouts_on_connection_head_received($connection) or |
|
318 | 572 | $this->check_timeouts_on_connection_request_received($connection) or |
|
319 | 572 | $this->check_timeouts_on_connection_body_received_without_head($connection) |
|
320 | 287 | ) { |
|
321 | 25 | $this->close_connection($connection); |
|
322 | |||
323 | 25 | return false; |
|
324 | } |
||
325 | |||
326 | // @hint No Slowloris ( https://en.wikipedia.org/wiki/Slowloris_%28computer_security%29 ) test. |
||
327 | // This is not a real server |
||
328 | |||
329 | 572 | return true; |
|
330 | } |
||
331 | |||
332 | /** |
||
333 | * Processing all connected clients |
||
334 | */ |
||
335 | 597 | protected function process_all_connected_clients() { |
|
336 | 597 | if (empty($this->_client_connects)) { |
|
337 | 369 | return; |
|
338 | } |
||
339 | |||
340 | /** |
||
341 | * @var resource[] $read |
||
342 | * @var resource[] $write |
||
343 | * @var resource[] $except |
||
344 | */ |
||
345 | 573 | $read = []; |
|
346 | 573 | foreach ($this->_client_connects as $connected) { |
|
347 | 573 | if (is_null($connected)) { |
|
348 | 220 | continue; |
|
349 | } |
||
350 | 573 | if (is_null($connected->client)) { |
|
351 | 60 | $connected = null; |
|
352 | 60 | continue; |
|
353 | } |
||
354 | |||
355 | 573 | if (!is_resource($connected->client)) { |
|
356 | // @codeCoverageIgnoreStart |
||
357 | throw new Exception(sprintf('Connection became non resource: %s', (string) $connected->client)); |
||
358 | // @codeCoverageIgnoreEnd |
||
359 | } |
||
360 | |||
361 | // Проверяем слишком старые подключения и убиваем их |
||
362 | 573 | if (!$this->check_timeouts_on_connection($connected)) { |
|
363 | 25 | continue; |
|
364 | } |
||
365 | |||
366 | 572 | $read[] = $connected->client; |
|
367 | 287 | } |
|
368 | 573 | if (empty($read)) { |
|
369 | 305 | $this->_client_connects = []; |
|
370 | |||
371 | 305 | return; |
|
372 | } |
||
373 | 572 | $write = null; |
|
374 | 572 | $except = null; |
|
375 | 572 | if (stream_select($read, $write, $except, 0) === false) { |
|
376 | // @codeCoverageIgnoreStart |
||
377 | throw new Exception('Error on stream select'); |
||
378 | // @codeCoverageIgnoreEnd |
||
379 | } |
||
380 | 572 | unset($connected); |
|
381 | |||
382 | 572 | foreach ($read as &$socket_resource) { |
|
383 | 556 | foreach ($this->_client_connects as &$connected) { |
|
384 | 556 | if (!is_null($connected) and ($connected->client == $socket_resource)) { |
|
385 | 556 | $this->process_connected_client($connected); |
|
386 | 556 | break; |
|
387 | } |
||
388 | 278 | } |
|
389 | 286 | } |
|
390 | 572 | } |
|
391 | |||
392 | /** |
||
393 | * @param ClientDatum $connect |
||
394 | * @param double $time |
||
395 | * @param string $buf |
||
396 | * |
||
397 | * @return boolean |
||
398 | */ |
||
399 | 556 | protected function receive_data_from_connected_client($connect, $time, &$buf) { |
|
400 | 556 | $buf = @fread($connect->client, self::CHUNK_SIZE); |
|
401 | 556 | if (empty($buf)) { |
|
402 | 130 | $this->close_connection($connect); |
|
403 | |||
404 | 130 | return false; |
|
405 | } |
||
406 | 448 | $connect->last_byte_received_time = $time; |
|
407 | 448 | if (!isset($connect->first_byte_received_time)) { |
|
408 | 448 | $connect->first_byte_received_time = $time; |
|
409 | 224 | } |
|
410 | 448 | if (strlen($buf) >= self::CHUNK_SIZE) { |
|
411 | do { |
||
412 | 32 | $sub_buf = @fread($connect->client, self::CHUNK_SIZE); |
|
413 | 32 | $buf .= $sub_buf; |
|
414 | 32 | if (isset($this->_settings->server_maximum_chunk) and |
|
415 | 32 | (strlen($buf) >= $this->_settings->server_maximum_chunk) |
|
416 | 16 | ) { |
|
417 | // Слишком много пришло за один раз |
||
418 | 8 | break; |
|
419 | } |
||
420 | 24 | } while (strlen($sub_buf) >= self::CHUNK_SIZE); |
|
421 | 16 | } |
|
422 | |||
423 | 448 | return true; |
|
424 | } |
||
425 | |||
426 | /** |
||
427 | * Processing all connected clients |
||
428 | * |
||
429 | * @param ClientDatum $connect |
||
430 | */ |
||
431 | 556 | protected function process_connected_client(&$connect) { |
|
432 | 556 | $time = microtime(true); |
|
433 | 556 | $connect->context_options = stream_context_get_options($connect->client); |
|
434 | 556 | $connect->context_params = stream_context_get_params($connect->client); |
|
435 | 556 | if (!$this->receive_data_from_connected_client($connect, $time, $buf)) { |
|
436 | 130 | $this->close_connection($connect); |
|
437 | |||
438 | 130 | return; |
|
439 | } |
||
440 | 448 | $this->event_raise('AnyIncomingData', $connect, $buf); |
|
441 | 448 | if ($connect->status == 0) { |
|
442 | 448 | $this->event_raise('HeadIncomingData', $connect, $buf); |
|
443 | 258 | } elseif ($connect->status == 1) { |
|
444 | 68 | $this->event_raise('BodyIncomingData', $connect, $buf); |
|
445 | 34 | } |
|
446 | 448 | $connect->blob_request .= $buf; |
|
447 | 448 | if (strpos($connect->blob_request, "\r\n\r\n") === false) { |
|
448 | // Head не дошёл |
||
449 | 8 | return; |
|
450 | } |
||
451 | |||
452 | // Проверяем на голову |
||
453 | // Голова только-только дошла |
||
454 | 440 | if (($connect->status == 0) and !$this->process_connected_client_head($connect, $time)) { |
|
455 | 56 | return; |
|
456 | } |
||
457 | |||
458 | // Проверяем на body |
||
459 | 384 | if ($connect->status == 1) { |
|
460 | 96 | $this->process_connected_client_body($connect, $buf, $time); |
|
461 | 48 | } |
|
462 | 384 | if (is_null($connect) or ($connect->status != 2)) { |
|
463 | 92 | return; |
|
464 | } |
||
465 | |||
466 | // Проверяем, что Host в списке обрабатываемых |
||
467 | 360 | if (!$this->check_requested_host_in_accepted_list($connect)) { |
|
468 | 72 | return; |
|
469 | } |
||
470 | |||
471 | // filterIncomingConnect |
||
472 | 288 | $closure = $this->_settings->filterIncomingConnect; |
|
473 | 288 | if (!$closure($this, $connect)) { |
|
474 | 8 | $this->close_connection($connect); |
|
475 | |||
476 | 8 | return; |
|
477 | } |
||
478 | 280 | unset($closure, $buf); |
|
479 | |||
480 | // Request |
||
481 | 280 | $this->event_raise('Request', $connect); |
|
482 | |||
483 | 280 | if ($connect->status == 3) { |
|
484 | 277 | $this->close_connection($connect); |
|
485 | 139 | } |
|
486 | 280 | } |
|
487 | |||
488 | 360 | function check_requested_host_in_accepted_list($connect) { |
|
489 | 360 | if (!isset($this->_settings->accepted_hosts)) { |
|
490 | 220 | return true; |
|
491 | } |
||
492 | |||
493 | 140 | list($host) = explode(':', $connect->request_head_params['Host']); |
|
494 | 140 | if (!in_array(strtolower($host), $this->_settings->accepted_hosts)) { |
|
495 | 72 | if ($this->event_raise('HostNotFound', $connect) === false) { |
|
496 | 12 | $this->close_connection($connect); |
|
497 | |||
498 | 12 | return false; |
|
499 | } |
||
500 | |||
501 | 60 | $this->answer($connect, 404, 'Not Found', 'Host not found'); |
|
502 | 60 | $this->close_connection($connect); |
|
503 | |||
504 | 60 | return false; |
|
505 | } |
||
506 | |||
507 | 68 | return true; |
|
508 | } |
||
509 | |||
510 | /** |
||
511 | * If returns "false" connection closed |
||
512 | * |
||
513 | * @param ClientDatum $connect |
||
514 | * @param double $time |
||
515 | * |
||
516 | * @return boolean |
||
517 | */ |
||
518 | 440 | protected function process_connected_client_head(&$connect, $time) { |
|
519 | 440 | $connect->head_received_time = $time; |
|
520 | 440 | list($connect->blob_head) = explode("\r\n\r\n", $connect->blob_request, 2); |
|
521 | 440 | $a = explode("\r\n", $connect->blob_head, 2); |
|
522 | 440 | list($first_line, $other_lines) = (count($a) == 2) ? $a : [$a[0], '']; |
|
523 | 440 | if (!preg_match('_^([A-Z]+)\\s+(.+)\\s+HTTP/([0-9.]+)$_', $first_line, $a)) { |
|
524 | 16 | if ($this->event_raise('HeadInvalidReceived', $connect, 0) === false) { |
|
525 | // Подключение сдохло |
||
526 | 8 | $this->close_connection($connect); |
|
527 | |||
528 | 8 | return false; |
|
529 | } |
||
530 | |||
531 | 8 | $this->answer($connect, 400, 'Bad Request', "This is not a HTTP request"); |
|
532 | 8 | $this->close_connection($connect); |
|
533 | |||
534 | 8 | return false; |
|
535 | } |
||
536 | |||
537 | 424 | $connect->request_type = $a[1]; |
|
538 | 424 | $connect->request_url = $a[2]; |
|
539 | 424 | $connect->request_http_version = $a[3]; |
|
540 | 424 | if ($connect->request_type == 'POST') { |
|
541 | 96 | $connect->status = 1; |
|
542 | 376 | } elseif ($connect->request_type == 'GET') { |
|
543 | 312 | $connect->status = 2; |
|
544 | 312 | $connect->full_request_received_time = $time; |
|
545 | 156 | } else { |
|
546 | 16 | if ($this->event_raise('HeadInvalidReceived', $connect, 1) === false) { |
|
547 | // Подключение сдохло |
||
548 | 8 | $this->close_connection($connect); |
|
549 | |||
550 | 8 | return false; |
|
551 | } |
||
552 | |||
553 | 8 | $this->answer($connect, 400, 'Bad Request', "Can not process this request type"); |
|
554 | 8 | $this->close_connection($connect); |
|
555 | |||
556 | 8 | return false; |
|
557 | } |
||
558 | |||
559 | 408 | $connect->request_head_params = []; |
|
560 | 408 | foreach (explode("\r\n", $other_lines) as $other_line) { |
|
561 | 408 | if (empty($other_line) or !preg_match('_^([A-Za-z0-9-]+):\\s*(.*)$_', $other_line, $a)) { |
|
562 | 16 | if ($this->event_raise('HeadInvalidReceived', $connect, 2) === false) { |
|
563 | // Подключение сдохло |
||
564 | 8 | $this->close_connection($connect); |
|
565 | |||
566 | 8 | return false; |
|
567 | } |
||
568 | |||
569 | 8 | $this->answer($connect, 400, 'Bad Request', "Malformed head"); |
|
570 | 8 | $this->close_connection($connect); |
|
571 | |||
572 | 8 | return false; |
|
573 | } |
||
574 | |||
575 | 408 | $connect->request_head_params[$a[1]] = $a[2]; |
|
576 | 204 | } |
|
577 | |||
578 | 392 | if (!isset($connect->request_head_params['Host'])) { |
|
579 | 8 | $this->answer($connect, 400, 'Bad Request', "Field 'host' missed"); |
|
580 | 8 | $this->close_connection($connect); |
|
581 | |||
582 | 8 | return false; |
|
583 | } |
||
584 | |||
585 | // event |
||
586 | 384 | $this->event_raise('HeadReceived', $connect); |
|
587 | |||
588 | 384 | return true; |
|
589 | } |
||
590 | |||
591 | /** |
||
592 | * If returns "false" connection closed |
||
593 | * |
||
594 | * @param ClientDatum $connect |
||
595 | * @param string $buf |
||
596 | * @param double $time |
||
597 | */ |
||
598 | 96 | protected function process_connected_client_body(&$connect, $buf, $time) { |
|
599 | 96 | $head_end = strpos($connect->blob_request, "\r\n\r\n"); |
|
600 | 96 | if (!isset($connect->request_head_params['Content-Length'])) { |
|
601 | 16 | if ($this->event_raise('HeadInvalidReceived', $connect, 3) === false) { |
|
602 | 8 | $this->close_connection($connect); |
|
603 | |||
604 | 8 | return; |
|
605 | } |
||
606 | |||
607 | 8 | $this->answer($connect, 400, 'malformed request', 'Malformed request'); |
|
608 | 8 | $this->close_connection($connect); |
|
609 | |||
610 | 8 | return; |
|
611 | } |
||
612 | 80 | $requested_body_length = (int) $connect->request_head_params['Content-Length']; |
|
613 | 80 | if (strlen($connect->blob_request) >= $head_end + 4 + $requested_body_length) { |
|
614 | 72 | $connect->blob_body = substr($connect->blob_request, $head_end + 4, $requested_body_length); |
|
615 | 72 | $connect->status = 2; |
|
616 | 72 | $connect->body_received_time = $time; |
|
617 | 72 | $connect->full_request_received_time = $time; |
|
618 | // @hint Request raised in process_connected_client |
||
619 | 36 | } |
|
620 | |||
621 | 80 | $this->event_raise('BodyIncomingData', $connect, $buf); |
|
622 | 80 | } |
|
623 | |||
624 | /** |
||
625 | * @param string $event |
||
626 | * |
||
627 | * @return mixed|null |
||
628 | */ |
||
629 | 597 | function event_raise($event) { |
|
630 | 597 | $method_name = 'on'.$event; |
|
631 | 597 | if (!isset($this->_settings->{$method_name}) or !is_callable($this->_settings->{$method_name})) { |
|
632 | 597 | return null; |
|
633 | } |
||
634 | |||
635 | 349 | $args = func_get_args(); |
|
636 | 349 | $args[0] = $this; |
|
637 | |||
638 | 349 | return call_user_func_array($this->_settings->{$method_name}, $args); |
|
639 | } |
||
640 | |||
641 | /** |
||
642 | * @param ClientDatum $connect |
||
643 | * @param integer $code |
||
644 | * @param string $code_text |
||
645 | * @param string $body |
||
646 | * @param array $headers |
||
647 | */ |
||
648 | 429 | function answer($connect, $code, $code_text, $body, array $headers = []) { |
|
649 | 429 | $buf = sprintf("HTTP/1.0 %d %s\r\n", $code, $code_text); |
|
650 | 429 | $headers['Content-Length'] = strlen($body); |
|
651 | 429 | foreach ($headers as $key => &$value) { |
|
652 | 429 | $buf .= sprintf("%s: %s\r\n", $key, $value); |
|
653 | 215 | } |
|
654 | 429 | @fwrite($connect->client, "{$buf}\r\n{$body}"); |
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
655 | 429 | } |
|
656 | } |
||
657 | |||
658 | ?> |
If you access a property on an interface, you most likely code against a concrete implementation of the interface.
Available Fixes
Adding an additional type check:
Changing the type hint: