Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace Ratchet\RFC6455\Handshake;
4
 
5
use Psr\Http\Message\MessageInterface;
6
use Psr\Http\Message\RequestInterface;
7
use Psr\Http\Message\ResponseInterface;
8
 
9
final class PermessageDeflateOptions
10
{
11
    const MAX_WINDOW_BITS = 15;
12
    /* this is a private instead of const for 5.4 compatibility */
13
    private static $VALID_BITS = ['8', '9', '10', '11', '12', '13', '14', '15'];
14
 
15
    private $deflateEnabled = false;
16
 
17
    private $server_no_context_takeover;
18
    private $client_no_context_takeover;
19
    private $server_max_window_bits;
20
    private $client_max_window_bits;
21
 
22
    private function __construct() { }
23
 
24
    public static function createEnabled() {
25
        $new                             = new static();
26
        $new->deflateEnabled             = true;
27
        $new->client_max_window_bits     = self::MAX_WINDOW_BITS;
28
        $new->client_no_context_takeover = false;
29
        $new->server_max_window_bits     = self::MAX_WINDOW_BITS;
30
        $new->server_no_context_takeover = false;
31
 
32
        return $new;
33
    }
34
 
35
    public static function createDisabled() {
36
        return new static();
37
    }
38
 
39
    public function withClientNoContextTakeover() {
40
        $new = clone $this;
41
        $new->client_no_context_takeover = true;
42
    }
43
 
44
    public function withoutClientNoContextTakeover() {
45
        $new = clone $this;
46
        $new->client_no_context_takeover = false;
47
    }
48
 
49
    public function withServerNoContextTakeover() {
50
        $new = clone $this;
51
        $new->server_no_context_takeover = true;
52
    }
53
 
54
    public function withoutServerNoContextTakeover() {
55
        $new = clone $this;
56
        $new->server_no_context_takeover = false;
57
    }
58
 
59
    public function withServerMaxWindowBits($bits = self::MAX_WINDOW_BITS) {
60
        if (!in_array($bits, self::$VALID_BITS)) {
61
            throw new \Exception('server_max_window_bits must have a value between 8 and 15.');
62
        }
63
        $new = clone $this;
64
        $new->server_max_window_bits = $bits;
65
    }
66
 
67
    public function withClientMaxWindowBits($bits = self::MAX_WINDOW_BITS) {
68
        if (!in_array($bits, self::$VALID_BITS)) {
69
            throw new \Exception('client_max_window_bits must have a value between 8 and 15.');
70
        }
71
        $new = clone $this;
72
        $new->client_max_window_bits = $bits;
73
    }
74
 
75
    /**
76
     * https://tools.ietf.org/html/rfc6455#section-9.1
77
     * https://tools.ietf.org/html/rfc7692#section-7
78
     *
79
     * @param MessageInterface $requestOrResponse
80
     * @return PermessageDeflateOptions[]
81
     * @throws \Exception
82
     */
83
    public static function fromRequestOrResponse(MessageInterface $requestOrResponse) {
84
        $optionSets = [];
85
 
86
        $extHeader = preg_replace('/\s+/', '', join(', ', $requestOrResponse->getHeader('Sec-Websocket-Extensions')));
87
 
88
        $configurationRequests = explode(',', $extHeader);
89
        foreach ($configurationRequests as $configurationRequest) {
90
            $parts = explode(';', $configurationRequest);
91
            if (count($parts) == 0) {
92
                continue;
93
            }
94
 
95
            if ($parts[0] !== 'permessage-deflate') {
96
                continue;
97
            }
98
 
99
            array_shift($parts);
100
            $options                 = new static();
101
            $options->deflateEnabled = true;
102
            foreach ($parts as $part) {
103
                $kv = explode('=', $part);
104
                $key = $kv[0];
105
                $value = count($kv) > 1 ? $kv[1] : null;
106
 
107
                switch ($key) {
108
                    case "server_no_context_takeover":
109
                    case "client_no_context_takeover":
110
                        if ($value !== null) {
111
                            throw new InvalidPermessageDeflateOptionsException($key . ' must not have a value.');
112
                        }
113
                        $value = true;
114
                        break;
115
                    case "server_max_window_bits":
116
                        if (!in_array($value, self::$VALID_BITS)) {
117
                            throw new InvalidPermessageDeflateOptionsException($key . ' must have a value between 8 and 15.');
118
                        }
119
                        break;
120
                    case "client_max_window_bits":
121
                        if ($value === null) {
122
                            $value = '15';
123
                        }
124
                        if (!in_array($value, self::$VALID_BITS)) {
125
                            throw new InvalidPermessageDeflateOptionsException($key . ' must have no value or a value between 8 and 15.');
126
                        }
127
                        break;
128
                    default:
129
                        throw new InvalidPermessageDeflateOptionsException('Option "' . $key . '"is not valid for permessage deflate');
130
                }
131
 
132
                if ($options->$key !== null) {
133
                    throw new InvalidPermessageDeflateOptionsException($key . ' specified more than once. Connection must be declined.');
134
                }
135
 
136
                $options->$key = $value;
137
            }
138
 
139
            if ($options->getClientMaxWindowBits() === null) {
140
                $options->client_max_window_bits = 15;
141
            }
142
 
143
            if ($options->getServerMaxWindowBits() === null) {
144
                $options->server_max_window_bits = 15;
145
            }
146
 
147
            $optionSets[] = $options;
148
        }
149
 
150
        // always put a disabled on the end
151
        $optionSets[] = new static();
152
 
153
        return $optionSets;
154
    }
155
 
156
    /**
157
     * @return mixed
158
     */
159
    public function getServerNoContextTakeover()
160
    {
161
        return $this->server_no_context_takeover;
162
    }
163
 
164
    /**
165
     * @return mixed
166
     */
167
    public function getClientNoContextTakeover()
168
    {
169
        return $this->client_no_context_takeover;
170
    }
171
 
172
    /**
173
     * @return mixed
174
     */
175
    public function getServerMaxWindowBits()
176
    {
177
        return $this->server_max_window_bits;
178
    }
179
 
180
    /**
181
     * @return mixed
182
     */
183
    public function getClientMaxWindowBits()
184
    {
185
        return $this->client_max_window_bits;
186
    }
187
 
188
    /**
189
     * @return bool
190
     */
191
    public function isEnabled()
192
    {
193
        return $this->deflateEnabled;
194
    }
195
 
196
    /**
197
     * @param ResponseInterface $response
198
     * @return ResponseInterface
199
     */
200
    public function addHeaderToResponse(ResponseInterface $response)
201
    {
202
        if (!$this->deflateEnabled) {
203
            return $response;
204
        }
205
 
206
        $header = 'permessage-deflate';
207
        if ($this->client_max_window_bits != 15) {
208
            $header .= '; client_max_window_bits='. $this->client_max_window_bits;
209
        }
210
        if ($this->client_no_context_takeover) {
211
            $header .= '; client_no_context_takeover';
212
        }
213
        if ($this->server_max_window_bits != 15) {
214
            $header .= '; server_max_window_bits=' . $this->server_max_window_bits;
215
        }
216
        if ($this->server_no_context_takeover) {
217
            $header .= '; server_no_context_takeover';
218
        }
219
 
220
        return $response->withAddedHeader('Sec-Websocket-Extensions', $header);
221
    }
222
 
223
    public function addHeaderToRequest(RequestInterface $request) {
224
        if (!$this->deflateEnabled) {
225
            return $request;
226
        }
227
 
228
        $header = 'permessage-deflate';
229
        if ($this->server_no_context_takeover) {
230
            $header .= '; server_no_context_takeover';
231
        }
232
        if ($this->client_no_context_takeover) {
233
            $header .= '; client_no_context_takeover';
234
        }
235
        if ($this->server_max_window_bits != 15) {
236
            $header .= '; server_max_window_bits=' . $this->server_max_window_bits;
237
        }
238
        $header .= '; client_max_window_bits';
239
        if ($this->client_max_window_bits != 15) {
240
            $header .= '='. $this->client_max_window_bits;
241
        }
242
 
243
        return $request->withAddedHeader('Sec-Websocket-Extensions', $header);
244
    }
245
 
246
    public static function permessageDeflateSupported($version = PHP_VERSION) {
247
        if (!function_exists('deflate_init')) {
248
            return false;
249
        }
250
        if (version_compare($version, '7.1.3', '>')) {
251
            return true;
252
        }
253
        if (version_compare($version, '7.0.18', '>=')
254
            && version_compare($version, '7.1.0', '<')) {
255
            return true;
256
        }
257
 
258
        return false;
259
    }
260
}