Completed
Push — master ( 8295bf...54c8d8 )
by Tomáš
04:39 queued 02:22
created

HttpServer   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 36.54%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 6
dl 0
loc 132
ccs 19
cts 52
cp 0.3654
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A init() 0 21 3
A addPeriodicTimer() 0 4 1
A run() 0 10 1
A setupDependencies() 0 6 1
A send404Response() 0 15 1
A logRequest() 0 10 1
A send200Response() 0 9 1
A prepareResponseMessage() 0 11 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Symplify
7
 * Copyright (c) 2016 Tomas Votruba (http://tomasvotruba.cz).
8
 */
9
10
namespace Symplify\PHP7_Sculpin\HttpServer;
11
12
use React\EventLoop\StreamSelectLoop;
13
use React\Http\Request;
14
use React\Http\Response;
15
use React\Http\Server as ReactHttpServer;
16
use React\Socket\Server as ReactSocketServer;
17
use Symfony\Component\Console\Output\OutputInterface;
18
19
final class HttpServer
20
{
21
    /**
22
     * @var string
23
     */
24
    private $outputDirectory;
25
26
    /**
27
     * @var StreamSelectLoop
28
     */
29
    private $streamSelectLoop;
30
31
    /**
32
     * @var ReactHttpServer
33
     */
34
    private $reactHttpServer;
35
36
    /**
37
     * @var int
38
     */
39
    private $port;
40
41
    /**
42
     * @var ReactSocketServer
43
     */
44
    private $reactSocketServer;
45
46
    /**
47
     * @var OutputInterface
48
     */
49
    private $output;
50
51 1
    public function __construct(string $outputDirectory, OutputInterface $output)
52
    {
53 1
        $this->outputDirectory = $outputDirectory;
54 1
        $this->output = $output;
55 1
    }
56
57 1
    public function init(int $port = 8000)
58
    {
59 1
        $this->setupDependencies();
60
61 1
        $this->port = $port;
62
63 1
        $this->reactHttpServer->on('request', function (Request $request, Response $response) {
64
            $path = $this->outputDirectory.'/'.ltrim(rawurldecode($request->getPath()), '/');
65
            if (is_dir($path)) {
66
                $path .= '/index.html';
67
            }
68
69
            if (!file_exists($path)) {
70
                $this->send404Response($request, $response);
71
            } else {
72
                $this->send200Response($request, $response, $path);
73
            }
74 1
        });
75
76 1
        $this->reactSocketServer->listen($this->port, '0.0.0.0');
77 1
    }
78
79 1
    public function addPeriodicTimer(int $interval, callable $callback)
80
    {
81 1
        $this->streamSelectLoop->addPeriodicTimer($interval, $callback);
82 1
    }
83
84
    public function run()
85
    {
86
        $this->output->writeln(sprintf(
87
            'Starting Sculpin at <info>http://localhost:%s</info>',
88
            $this->port
89
        ));
90
        $this->output->writeln('Quit the server with <info>CONTROL-C</info>.');
91
92
        $this->streamSelectLoop->run();
93
    }
94
95 1
    private function setupDependencies()
96
    {
97 1
        $this->streamSelectLoop = new StreamSelectLoop();
98 1
        $this->reactSocketServer = new ReactSocketServer($this->streamSelectLoop);
99 1
        $this->reactHttpServer = new ReactHttpServer($this->reactSocketServer);
100 1
    }
101
102
    private function send404Response(Request $request, Response $response)
103
    {
104
        $this->logRequest(404, $request);
105
        $response->writeHead(404, [
106
            'Content-Type' => 'text/html',
107
        ]);
108
109
        $response->end(
110
            '<h1>404</h1>'.
111
            '<h2>Not Found</h2>'.
112
            '<p>'.
113
            'The embedded <a href="https://sculpin.io">Sculpin</a> web server could not find the requested resource.'.
114
            '</p>'
115
        );
116
    }
117
118
    private function logRequest(int $responseCode, Request $request)
119
    {
120
        $message = sprintf(
121
            '%s for path: "%s"',
122
            $responseCode,
123
            $request->getPath()
124
        );
125
126
        $this->output->writeln($this->prepareResponseMessage($responseCode, $message));
127
    }
128
129
    private function send200Response(Request $request, Response $response, string $path)
130
    {
131
        $response->writeHead(200, [
132
            'Content-Type' => 'text/html',
133
        ]);
134
135
        $this->logRequest(200, $request);
136
        $response->end(file_get_contents($path));
137
    }
138
139
    private function prepareResponseMessage(int $responseCode, string $message) : string
140
    {
141
        $wrapOpen = '<info>';
142
        $wrapClose = '</info>';
143
        if ($responseCode >= 400) {
144
            $wrapOpen = '<error>';
145
            $wrapClose = '</error>';
146
        }
147
148
        return $wrapOpen.$message.$wrapClose;
149
    }
150
}
151