HttpProtocol::serveFile()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
1
<?php
2
namespace Fructify\Reload\Protocol;
3
4
use Fructify\Reload\Application\ServerApplication;
5
use React\Socket\Server as SocketServer;
6
use React\Socket\Connection as SocketConnection;
7
use Symfony\Component\HttpFoundation\Request;
8
use Fructify\Reload\Response\Response;
9
use Symfony\Component\Console\Output\OutputInterface;
10
11
class HttpProtocol
12
{
13
    protected $app;
14
15
    public function __construct(SocketServer $socket, ServerApplication $app)
16
    {
17
        $this->app = $app;
18
        $this->initEvent($socket);
19
    }
20
21
    protected function initEvent(SocketServer $socket)
22
    {
23
        $socket->on('connection', function(SocketConnection $conn){
24
            $this->onConnect($conn);
25
        });
26
    }
27
28
    protected function onConnect(SocketConnection $conn)
29
    {
30
        $conn->on('data', function($data) use($conn){
31
            $this->onData($conn, $data);
32
        });
33
    }
34
35
    protected function onData(SocketConnection $conn, $data)
36
    {
37
        $request = $this->doHttpHandshake($data);
38
        $this->handleRequest($conn, $request);
0 ignored issues
show
Security Bug introduced by
It seems like $request defined by $this->doHttpHandshake($data) on line 37 can also be of type false; however, Fructify\Reload\Protocol...otocol::handleRequest() does only seem to accept object<Symfony\Component\HttpFoundation\Request>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
39
    }
40
41
    protected function handleRequest(SocketConnection $conn, Request $request)
42
    {
43
        switch($request->getPathInfo()){
44
            case '/livereload':
45
                $this->initWebSocket($conn, $request);
46
                break;
47
            case '/livereload.js':
48
                $this->serveFile($conn, __DIR__.'/../../web/js/livereload.js');
49
                break;
50
            case '/changed':
51
                $this->notifyChanged($conn, $request);
52
                break;
53
            default:
54
                $this->serve404Error($conn);
55
        }
56
    }
57
58
    protected function initWebSocket(SocketConnection $conn, Request $request)
59
    {
60
        $conn->removeAllListeners('data');
61
        return new WebSocketProtocol($conn,  $this->app, $request);
62
    }
63
64
    protected function getRequestChangedFiles(Request $request)
65
    {
66
        if(($files = $request->query->get('files')) != null){
67
            return explode(',', $files);
68
        }
69
        $requestJson = json_decode($request->getContent(), true);
70
        return isset($requestJson['files'])?(is_array($requestJson['files'])?$requestJson['files']:[$requestJson['files']]):[];
71
    }
72
73
    protected function notifyChanged(SocketConnection $conn, Request $request)
74
    {
75
        foreach($this->getRequestChangedFiles($request) as $file){
76
            $this->app->getOutput()->writeln(strftime('%T')." - info - Receive request reload $file", OutputInterface::VERBOSITY_VERBOSE);
77
            $this->app->reloadFile($file);
78
        }
79
        $response = new Response(json_encode(array('status' => true)));
80
        $conn->write($response);
81
    }
82
83
    protected function serveFile(SocketConnection $conn, $file)
84
    {
85
        if(($path = realpath($file)) === null){
86
            return ;
87
        }
88
        $content = file_get_contents($file);
89
        $response = new Response($content);
90
        $response->setContentType('text/plain', 'utf-8');
91
        $conn->write($response);
92
    }
93
94
    protected function serve404Error(SocketConnection $conn)
95
    {
96
        $response = new Response('file not found.', Response::HTTP_NOT_FOUND);
97
        $conn->write($response);
98
        $conn->end();
99
    }
100
101
    protected function doHttpHandshake($data)
102
    {
103
        $pos = strpos($data, "\r\n\r\n");
104
        if($pos === false){
105
            return false;
106
        }
107
        $body = substr($data, $pos + 4);
108
        $rawHeaders = explode("\r\n", substr($data, 0, $pos));
109
        $requestLine = $this->parseRequest(array_shift($rawHeaders));
110
        $headers = $this->parseHeaders($rawHeaders);
111
        return Request::create($requestLine['uri'], $requestLine['method'], array(), array(), array(), $headers, $body);
112
    }
113
114
    protected function parseRequest($rawRequest)
115
    {
116
        return array_combine(array('method', 'uri', 'protocol'), explode(' ', $rawRequest));
117
    }
118
119
    protected function parseHeaders($rawHeaders)
120
    {
121
        $headers = array();
122
        foreach($rawHeaders as $headerLine){
123
            if(($pos = strpos($headerLine, ':')) === false){
124
                continue;
125
            }
126
            $headers['HTTP_'.str_replace('-', '_', trim(strtoupper(substr($headerLine, 0, $pos))))] = trim(substr($headerLine, $pos + 1));
127
        }
128
        return $headers;
129
    }
130
}
131