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

Response::getStatusCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/**
3
 * This file is a part of a nekland library
4
 *
5
 * (c) Nekland <[email protected]>
6
 *
7
 * For the full license, take a look to the LICENSE file
8
 * on the root directory of this project
9
 */
10
11
namespace Nekland\Woketo\Http;
12
13
14
use Nekland\Woketo\Exception\Http\HttpException;
15
use Nekland\Woketo\Exception\Http\IncompleteHttpMessageException;
16
use React\Socket\ConnectionInterface;
17
18
class Response extends AbstractHttpMessage
19
{
20
    const SWITCHING_PROTOCOLS = '101 Switching Protocols';
21
    const BAD_REQUEST = '400 Bad Request';
22
23
    /**
24
     * @var string For example "404 Not Found"
25
     */
26
    private $httpResponse;
27
28
    /**
29
     * @var int
30
     */
31
    private $statusCode;
32
33
    /**
34
     * @var string
35
     */
36
    private $reason;
37
38
    public function __construct()
39
    {
40
        $this->setHttpVersion('HTTP/1.1');
41
    }
42
43
    /**
44
     * @param string $httpResponse
45
     * @return Response
46
     */
47
    public function setHttpResponse($httpResponse)
48
    {
49
        $this->httpResponse = $httpResponse;
50
51
        return $this;
52
    }
53
54
    /**
55
     * @return int
56
     */
57
    public function getStatusCode(): int
58
    {
59
        return (int) $this->statusCode;
60
    }
61
62
    /**
63
     * @param int $statusCode
64
     */
65
    public function setStatusCode(int $statusCode)
66
    {
67
        $this->statusCode = $statusCode;
68
    }
69
70
    /**
71
     * @return string
72
     */
73
    public function getReason(): string
74
    {
75
        return $this->reason;
76
    }
77
78
    /**
79
     * @param string $reason
80
     */
81
    public function setReason(string $reason)
82
    {
83
        $this->reason = $reason;
84
    }
85
86
    /**
87
     * @return string
88
     */
89
    public function getAcceptKey()
90
    {
91
        return $this->getHeader('Sec-WebSocket-Accept');
92
    }
93
94
    /**
95
     * @param ConnectionInterface $stream
96
     */
97
    public function send(ConnectionInterface $stream)
98
    {
99
        $stringResponse = $this->getHttpVersion() . ' ' . $this->httpResponse . "\r\n";
100
101
        foreach ($this->getHeaders() as $name => $content) {
102
            $stringResponse .= $name . ': '. $content . "\r\n";
103
        }
104
105
        // No content to concatenate
106
        $stringResponse .= "\r\n";
107
108
        $stream->write($stringResponse);
109
    }
110
111
    public static function createSwitchProtocolResponse()
112
    {
113
        $response = new Response();
114
115
        $response->setHttpResponse(Response::SWITCHING_PROTOCOLS);
116
        $response->addHeader('Upgrade', 'websocket');
117
        $response->addHeader('Connection', 'Upgrade');
118
119
        return $response;
120
    }
121
122
    /**
123
     * @param string $data
124
     * @return Response
125
     * @throws HttpException
126
     */
127
    public static function create(string &$data) : Response
128
    {
129
        if (!\preg_match('/\\r\\n\\r\\n/', $data)) {
130
            throw new IncompleteHttpMessageException();
131
        }
132
133
        $exploded = explode("\r\n\r\n", $data);
134
        $responseString = '';
135
        if (count($exploded) > 1) {
136
            // Removing first line from data
137
            $responseString = $exploded[0];
138
            unset($exploded[0]);
139
            $data = implode("\r\n\r\n", $exploded);
140
        }
141
142
        $response = new Response();
143
144
        $lines = \explode("\r\n", $responseString);
145
        Response::initResponse($lines[0], $response);
146
147
        unset($lines[0]);
148
        Response::initHeaders($lines, $response);
149
150
        if (strtolower($response->getHeader('Upgrade')) !== 'websocket') {
151
            throw new HttpException('Missing or wrong upgrade header.');
152
        }
153
        if (strtolower($response->getHeader('Connection')) !== 'upgrade') {
154
            throw new HttpException('Missing upgrade header.');
155
        }
156
157
        return $response;
158
    }
159
160
    /**
161
     * @param string   $firstLine
162
     * @param Response $response
163
     * @throws HttpException
164
     */
165
    protected static function initResponse(string $firstLine, Response $response)
166
    {
167
        $httpElements = \explode(' ', $firstLine);
168
169
        if (!\preg_match('/HTTP\/[1-2\.]+/',$httpElements[0])) {
170
            throw Response::createNotHttpException($firstLine);
171
        }
172
        $response->setHttpVersion($httpElements[0]);
173
        unset($httpElements[0]);
174
175
        if ($httpElements[1] != 101) {
176
            throw new HttpException(
177
                sprintf('Attempted 101 response but got %s, message: %s', $httpElements[1], $firstLine)
178
            );
179
        }
180
        $response->setStatusCode($httpElements[1]);
181
        unset($httpElements[1]);
182
183
        $response->setReason(implode(' ', $httpElements));
184
    }
185
}
186