Completed
Push — master ( 124fed...6caa0a )
by Tomáš
12s
created

HttpServer::init()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 45
ccs 0
cts 36
cp 0
rs 8.439
c 0
b 0
f 0
cc 5
eloc 26
nc 1
nop 1
crap 30
1
<?php
2
3
/*
4
 * This file is a part of Sculpin.
5
 *
6
 * (c) Dragonfly Development Inc.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symplify\PHP7_Sculpin\HttpServer;
13
14
use Dflydev\ApacheMimeTypes\PhpRepository;
15
use React\EventLoop\StreamSelectLoop;
16
use React\Http\Request;
17
use React\Http\Response;
18
use React\Http\Server as ReactHttpServer;
19
use React\Socket\Server as ReactSocketServer;
20
use Symplify\PHP7_Sculpin\Io\ConsoleIo;
21
22
final class HttpServer
23
{
24
    /**
25
     * @var ConsoleIo
26
     */
27
    private $consoleIo;
28
29
    /**
30
     * @var string
31
     */
32
    private $outputDirectory;
33
34
    /**
35
     * @var PhpRepository
36
     */
37
    private $phpRepository;
38
39
    /**
40
     * @var StreamSelectLoop
41
     */
42
    private $streamSelectLoop;
43
44
    /**
45
     * @var ReactHttpServer
46
     */
47
    private $reactHttpServer;
48
49
    /**
50
     * @var int
51
     */
52
    private $port;
53
54
    /**
55
     * @var ReactSocketServer
56
     */
57
    private $reactSocketServer;
58
59
    public function __construct(
60
        string $outputDirectory,
61
        ConsoleIo $consoleIo,
62
        PhpRepository $phpRepository
63
    ) {
64
        $this->outputDirectory = $outputDirectory;
65
        $this->consoleIo = $consoleIo;
66
        $this->phpRepository = $phpRepository;
67
    }
68
69
    public function init(int $port = 8000)
70
    {
71
        $this->setupDependencies();
72
73
        $this->port = $port;
74
75
        $this->reactHttpServer->on('request', function (Request $request, Response $response) {
76
            $path = $this->outputDirectory.'/'.ltrim(rawurldecode($request->getPath()), '/');
77
            if (is_dir($path)) {
78
                $path .= '/index.html';
79
            }
80
81
            if (!file_exists($path)) {
82
                $this->logRequest(404, $request);
83
                $response->writeHead(404, [
84
                    'Content-Type' => 'text/html',
85
                ]);
86
87
                return $response->end(
88
                    '<h1>404</h1>'.
89
                    '<h2>Not Found</h2>'.
90
                    '<p>'.
91
                    'The embedded <a href="https://sculpin.io">Sculpin</a> web server could not find the requested resource.'.
92
                    '</p>'
93
                );
94
            }
95
96
            $type = 'application/octet-stream';
97
98
            if ('' !== $extension = pathinfo($path, PATHINFO_EXTENSION)) {
99
                if ($guessedType = $this->phpRepository->findType($extension)) {
100
                    $type = $guessedType;
101
                }
102
            }
103
104
            $this->logRequest(200, $request);
105
106
            $response->writeHead(200, [
107
                'Content-Type' => $type,
108
            ]);
109
            $response->end(file_get_contents($path));
110
        });
111
112
        $this->reactSocketServer->listen($this->port, '0.0.0.0');
113
    }
114
115
    public function addPeriodicTimer(int $interval, callable $callback)
116
    {
117
        $this->streamSelectLoop->addPeriodicTimer($interval, $callback);
118
    }
119
120
    public function run()
121
    {
122
        $this->consoleIo->write(sprintf(
123
            'Starting Sculpin at <info>http://%s:%s</info>',
124
            'localhost',
125
            $this->port
126
        ));
127
        $this->consoleIo->write('Quit the server with CONTROL-C.');
128
129
        $this->streamSelectLoop->run();
130
    }
131
132
    private function setupDependencies()
133
    {
134
        $this->streamSelectLoop = new StreamSelectLoop();
135
        $this->reactSocketServer = new ReactSocketServer($this->streamSelectLoop);
136
        $this->reactHttpServer = new ReactHttpServer($this->reactSocketServer);
137
    }
138
139
    private function logRequest(string $responseCode, Request $request)
140
    {
141
        $wrapOpen = '';
142
        $wrapClose = '';
143
        if ($responseCode >= 400) {
144
            $wrapOpen = '<error>';
145
            $wrapClose = '</error>';
146
        }
147
148
        $message = sprintf(
149
            '%s for path: "%s"',
150
            $responseCode,
151
            $request->getPath()
152
        );
153
154
        $this->consoleIo->write($wrapOpen.$message.$wrapClose);
155
    }
156
}
157