Completed
Pull Request — master (#100)
by Maxime
02:42
created

Request::setKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * This file is a part of a nekland library
5
 *
6
 * (c) Nekland <[email protected]>
7
 *
8
 * For the full license, take a look to the LICENSE file
9
 * on the root directory of this project
10
 */
11
12
namespace Nekland\Woketo\Http;
13
14
use Nekland\Woketo\Exception\HttpException;
15
use Nekland\Woketo\Meta;
16
17
class Request extends AbstractHttpMessage
18
{
19
    /**
20
     * @var string
21
     */
22
    private $method;
23
24
    /**
25
     * @var string
26
     */
27
    private $uri;
28
29
    /**
30
     * @var string
31
     */
32
    private $host;
33
34
    private function __construct() {}
35
36
    /**
37
     * @return Request
38
     */
39 8
    private function setMethod($method) : Request
40
    {
41 8
        $this->method = $method;
42
43 8
        return $this;
44
    }
45
46
    /**
47
     * @param string $uri
48
     * @return Request
49
     */
50 8
    private function setUri(string $uri) : Request
51
    {
52 8
        $this->uri = $uri;
53
54 8
        return $this;
55
    }
56
57
    /**
58
     * @return string
59
     */
60 6
    public function getUri() : string
61
    {
62 6
        return $this->uri;
63
    }
64
65
    /**
66
     * @return string
67
     */
68 4
    public function getMethod() : string
69
    {
70 4
        return $this->method;
71
    }
72
73
    /**
74
     * @return int
75
     */
76 1
    public function getVersion() : int
77
    {
78 1
        return (int) $this->getHeaders()->get('Sec-WebSocket-Version');
79
    }
80
81
    /**
82
     * @param int $version
83
     * @return Request
84
     */
85 1
    public function setVersion(int $version) : Request
86
    {
87 1
        $this->addHeader('Sec-WebSocket-Version', $version);
88
89 1
        return $this;
90
    }
91
92
    /**
93
     * @param string $key
94
     * @return Request
95
     */
96 1
    public function setKey(string $key) : Request
97
    {
98 1
        $this->addHeader('Sec-WebSocket-Key', $key);
99
100 1
        return $this;
101
    }
102
103
    /**
104
     * @param string $host
105
     * @return self
106
     */
107 1
    private function setHost(string $host) : Request
108
    {
109 1
        $this->host = $host;
110
111 1
        return $this;
112
    }
113
114
    /**
115
     * @return array
116
     */
117 2
    public function getExtensions() : array
118
    {
119 2
        $originalHeaders = $this->getHeaders()->get('Sec-WebSocket-Extensions');
120 2
        if (!\is_array($originalHeaders)) {
121 1
            $originalHeaders = [$originalHeaders];
122
        }
123
124 2
        $extensionHeaders = [];
125 2
        $extensions = [];
126
127 2
        foreach ($originalHeaders as $extensionHeader) {
128 2
            $extensionHeaders = \array_merge($extensionHeaders, \array_map('trim', \explode(',', $extensionHeader)));
129
        }
130
131 2
        foreach ($extensionHeaders as $extension) {
132 2
            $explodingHeader = \explode(';', $extension);
133 2
            $extensionName = \trim($explodingHeader[0]);
134 2
            $extensions[$extensionName] = [];
135
136 2
            if (\count($explodingHeader)) {
137 2
                unset($explodingHeader[0]); // removing the name of the extension
138 2
                foreach($explodingHeader as $variable) {
139 1
                    $explodeVariable = \explode('=', $variable);
140
141
                    // The value can be with or without quote. We need to remove extra quotes.
142 1
                    $value = \preg_replace('/^"(.+)"$/', '$1', trim($explodeVariable[1]));
143 1
                    $value = \str_replace('\\"', '"', $value);
144
145 2
                    $extensions[$extensionName][\trim($explodeVariable[0])] = $value;
146
                }
147
            }
148
        }
149
150 2
        return $extensions;
151
    }
152
153
    /**
154
     * @return string
155
     */
156 1
    public function getRequestAsString() : string
157
    {
158 1
        $request = mb_strtoupper($this->method) . ' ' . $this->uri . " HTTP/1.1\r\n";
159 1
        $request .= 'Host: ' . $this->host . "\r\n";
160 1
        $request .= 'User-Agent: Woketo/' . Meta::VERSION . "\r\n";
161 1
        $request .= "Upgrade: websocket\r\n";
162 1
        $request .= "Connection: Upgrade\r\n";
163
164 1
        foreach ($this->getHeaders() as $key => $header) {
165 1
            $request .= $key . ': ' . $header . "\r\n";
166
        }
167
168 1
        return $request;
169
    }
170
171
    /**
172
     * @return string
173
     */
174
    public function __toString()
175
    {
176
        return $this->getRequestAsString();
177
    }
178
179
    /**
180
     * @param string $requestString
181
     * @return Request
182
     * @throws HttpException
183
     */
184 10
    public static function create(string $requestString)
185
    {
186 10
        $request = new Request;
187
188 10
        $lines = \explode("\r\n", $requestString);
189 10
        Request::initRequest($lines[0], $request);
190
191 7
        unset($lines[0]);
192 7
        Request::initHeaders($lines, $request);
193
194 7
        if (empty($request->getHeaders()->get('Sec-WebSocket-Key')) || empty($request->getHeaders()->get('Upgrade')) || \strtolower($request->getHeaders()->get('Upgrade')) !== 'websocket') {
195
            throw new HttpException(sprintf("The request is not a websocket upgrade request, received:\n%s", $requestString));
196
        }
197
198 7
        return $request;
199
    }
200
201
    /**
202
     * @param string $uri
203
     * @param string $host
204
     * @return Request
205
     */
206 1
    public static function createClientRequest(string $uri, string $host)
207
    {
208 1
        $request = new Request();
209
210 1
        $request->setMethod('GET');
211 1
        $request->setUri($uri);
212 1
        $request->setHttpVersion('1.1');
213 1
        $request->setHost($host);
214
215 1
        return $request;
216
    }
217
218
    /**
219
     * @param string  $firstLine
220
     * @param Request $request
221
     * @throws HttpException
222
     */
223 10
    protected static function initRequest(string $firstLine, Request $request)
224
    {
225 10
        $httpElements = \explode(' ', $firstLine);
226
227 10
        if (\count($httpElements) < 3) {
228 1
            throw Request::createNotHttpException($firstLine);
229
        }
230
231 9
        $httpElements[2] = \trim($httpElements[2]);
232 9
        if (!\preg_match('/HTTP\/.+/', $httpElements[2])) {
233 1
            throw Request::createNotHttpException($firstLine);
234
        }
235 8
        $request->setHttpVersion($httpElements[2]);
236
237 8
        if (!\in_array($httpElements[0], ['POST', 'GET', 'PUT', 'DELETE'])) {
238 1
            throw new HttpException(
239 1
                \sprintf('Request not supported, only POST, GET, PUT, and DELETE are supported. "%s" received.', $httpElements[0])
240
            );
241
        }
242
243 7
        $request->setMethod($httpElements[0]);
244 7
        $request->setUri($httpElements[1]);
245 7
    }
246
247 2
    private static function createNotHttpException($line)
248
    {
249 2
        return new HttpException(
250 2
            \sprintf('The request is not an http request. "%s" received.', $line)
251
        );
252
    }
253
254 7
    protected static function initHeaders(array $headers, Request $request)
255
    {
256 7
        foreach ($headers as $header) {
257 7
            $cuttedHeader = \explode(':', $header);
258 7
            $request->addHeader(\trim($cuttedHeader[0]), trim(str_replace($cuttedHeader[0] . ':', '', $header)));
259
        }
260 7
    }
261
}
262