Codebase list ratchet-rfc6455 / 4260178
New upstream snapshot. Debian Janitor 2 years ago
29 changed file(s) with 1478 addition(s) and 174 deletion(s). Raw diff Collapse all Expand all
00 language: php
11
2 services: docker
3
24 php:
3 - 5.4
4 - 5.5
55 - 5.6
66 - 7.0
77 - 7.1
88 - 7.2
9 - hhvm
9 - 7.3
10 - 7.4
11 - nightly
12
13 env:
14 - ABTEST=client
15 - ABTEST=server
16
17 matrix:
18 allow_failures:
19 - php: nightly
1020
1121 before_install:
12 - export PATH=$HOME/.local/bin:$PATH
13 - pip install --user autobahntestsuite
14 - pip list --user autobahntestsuite
22 - docker pull crossbario/autobahn-testsuite
1523
1624 before_script:
1725 - composer install
00 # RFC6455 - The WebSocket Protocol
11
22 [![Build Status](https://travis-ci.org/ratchetphp/RFC6455.svg?branch=master)](https://travis-ci.org/ratchetphp/RFC6455)
3 ![Autobahn Testsuite](https://img.shields.io/badge/Autobahn-passing-brightgreen.svg)
3 [![Autobahn Testsuite](https://img.shields.io/badge/Autobahn-passing-brightgreen.svg)](http://socketo.me/reports/rfc-server/index.html)
44
55 This library a protocol handler for the RFC6455 specification.
66 It contains components for both server and client side handshake and messaging protocol negotation.
77
8 Aspects that are left open to interpertation in the specification are also left open in this library.
9 It is up to the implementation to determine how those interpertations are to be dealt with.
8 Aspects that are left open to interpretation in the specification are also left open in this library.
9 It is up to the implementation to determine how those interpretations are to be dealt with.
1010
1111 This library is independent, framework agnostic, and does not deal with any I/O.
1212 HTTP upgrade negotiation integration points are handled with PSR-7 interfaces.
44 "keywords": ["WebSockets", "websocket", "RFC6455"],
55 "homepage": "http://socketo.me",
66 "license": "MIT",
7 "authors": [{
8 "name": "Chris Boden"
9 , "email": "cboden@gmail.com"
10 , "role": "Developer"
11 }],
7 "authors": [
8 {
9 "name": "Chris Boden"
10 , "email": "cboden@gmail.com"
11 , "role": "Developer"
12 },
13 {
14 "name": "Matt Bonneau",
15 "role": "Developer"
16 }
17 ],
1218 "support": {
13 "forum": "https://groups.google.com/forum/#!forum/ratchet-php"
14 , "issues": "https://github.com/ratchetphp/RFC6455/issues"
15 , "irc": "irc://irc.freenode.org/reactphp"
19 "issues": "https://github.com/ratchetphp/RFC6455/issues",
20 "chat": "https://gitter.im/reactphp/reactphp"
1621 },
1722 "autoload": {
1823 "psr-4": {
2429 "guzzlehttp/psr7": "^1.0"
2530 },
2631 "require-dev": {
27 "react/http": "^0.4.1",
28 "react/socket-client": "^0.4.3",
29 "phpunit/phpunit": "4.8.*"
32 "phpunit/phpunit": "5.7.*",
33 "react/socket": "^1.3"
34 },
35 "scripts": {
36 "abtest-client": "ABTEST=client && sh tests/ab/run_ab_tests.sh",
37 "abtest-server": "ABTEST=server && sh tests/ab/run_ab_tests.sh",
38 "phpunit": "phpunit --colors=always",
39 "test": [
40 "@abtest-client",
41 "@abtest-server",
42 "@phpunit"
43 ]
3044 }
3145 }
0 ratchet-rfc6455 (0.3+git20201211.1.fb8cf47-1) UNRELEASED; urgency=low
1
2 * New upstream snapshot.
3
4 -- Debian Janitor <janitor@jelmer.uk> Thu, 03 Jun 2021 03:10:09 -0000
5
06 ratchet-rfc6455 (0.2.4-2) unstable; urgency=medium
17
28 * Fix typo in my name in d/copyright.
1515 */
1616 private $defaultHeader;
1717
18 function __construct() {
18 function __construct(PermessageDeflateOptions $perMessageDeflateOptions = null) {
1919 $this->verifier = new ResponseVerifier;
2020
2121 $this->defaultHeader = new Request('GET', '', [
2424 , 'Sec-WebSocket-Version' => $this->getVersion()
2525 , 'User-Agent' => "Ratchet"
2626 ]);
27
28 if ($perMessageDeflateOptions === null) {
29 $perMessageDeflateOptions = PermessageDeflateOptions::createDisabled();
30 }
31
32 // https://bugs.php.net/bug.php?id=73373
33 // https://bugs.php.net/bug.php?id=74240 - need >=7.1.4 or >=7.0.18
34 if ($perMessageDeflateOptions->isEnabled() &&
35 !PermessageDeflateOptions::permessageDeflateSupported()) {
36 trigger_error('permessage-deflate is being disabled because it is not support by your PHP version.', E_USER_NOTICE);
37 $perMessageDeflateOptions = PermessageDeflateOptions::createDisabled();
38 }
39 if ($perMessageDeflateOptions->isEnabled() && !function_exists('deflate_add')) {
40 trigger_error('permessage-deflate is being disabled because you do not have the zlib extension.', E_USER_NOTICE);
41 $perMessageDeflateOptions = PermessageDeflateOptions::createDisabled();
42 }
43
44 $this->defaultHeader = $perMessageDeflateOptions->addHeaderToRequest($this->defaultHeader);
2745 }
2846
2947 public function generateRequest(UriInterface $uri) {
0 <?php
1
2 namespace Ratchet\RFC6455\Handshake;
3
4 class InvalidPermessageDeflateOptionsException extends \Exception
5 {
6 }
0 <?php
1
2 namespace Ratchet\RFC6455\Handshake;
3
4 use Psr\Http\Message\MessageInterface;
5 use Psr\Http\Message\RequestInterface;
6 use Psr\Http\Message\ResponseInterface;
7
8 final class PermessageDeflateOptions
9 {
10 const MAX_WINDOW_BITS = 15;
11 /* this is a private instead of const for 5.4 compatibility */
12 private static $VALID_BITS = ['8', '9', '10', '11', '12', '13', '14', '15'];
13
14 private $deflateEnabled = false;
15
16 private $server_no_context_takeover;
17 private $client_no_context_takeover;
18 private $server_max_window_bits;
19 private $client_max_window_bits;
20
21 private function __construct() { }
22
23 public static function createEnabled() {
24 $new = new static();
25 $new->deflateEnabled = true;
26 $new->client_max_window_bits = self::MAX_WINDOW_BITS;
27 $new->client_no_context_takeover = false;
28 $new->server_max_window_bits = self::MAX_WINDOW_BITS;
29 $new->server_no_context_takeover = false;
30
31 return $new;
32 }
33
34 public static function createDisabled() {
35 return new static();
36 }
37
38 public function withClientNoContextTakeover() {
39 $new = clone $this;
40 $new->client_no_context_takeover = true;
41 return $new;
42 }
43
44 public function withoutClientNoContextTakeover() {
45 $new = clone $this;
46 $new->client_no_context_takeover = false;
47 return $new;
48 }
49
50 public function withServerNoContextTakeover() {
51 $new = clone $this;
52 $new->server_no_context_takeover = true;
53 return $new;
54 }
55
56 public function withoutServerNoContextTakeover() {
57 $new = clone $this;
58 $new->server_no_context_takeover = false;
59 return $new;
60 }
61
62 public function withServerMaxWindowBits($bits = self::MAX_WINDOW_BITS) {
63 if (!in_array($bits, self::$VALID_BITS)) {
64 throw new \Exception('server_max_window_bits must have a value between 8 and 15.');
65 }
66 $new = clone $this;
67 $new->server_max_window_bits = $bits;
68 return $new;
69 }
70
71 public function withClientMaxWindowBits($bits = self::MAX_WINDOW_BITS) {
72 if (!in_array($bits, self::$VALID_BITS)) {
73 throw new \Exception('client_max_window_bits must have a value between 8 and 15.');
74 }
75 $new = clone $this;
76 $new->client_max_window_bits = $bits;
77 return $new;
78 }
79
80 /**
81 * https://tools.ietf.org/html/rfc6455#section-9.1
82 * https://tools.ietf.org/html/rfc7692#section-7
83 *
84 * @param MessageInterface $requestOrResponse
85 * @return PermessageDeflateOptions[]
86 * @throws \Exception
87 */
88 public static function fromRequestOrResponse(MessageInterface $requestOrResponse) {
89 $optionSets = [];
90
91 $extHeader = preg_replace('/\s+/', '', join(', ', $requestOrResponse->getHeader('Sec-Websocket-Extensions')));
92
93 $configurationRequests = explode(',', $extHeader);
94 foreach ($configurationRequests as $configurationRequest) {
95 $parts = explode(';', $configurationRequest);
96 if (count($parts) == 0) {
97 continue;
98 }
99
100 if ($parts[0] !== 'permessage-deflate') {
101 continue;
102 }
103
104 array_shift($parts);
105 $options = new static();
106 $options->deflateEnabled = true;
107 foreach ($parts as $part) {
108 $kv = explode('=', $part);
109 $key = $kv[0];
110 $value = count($kv) > 1 ? $kv[1] : null;
111
112 switch ($key) {
113 case "server_no_context_takeover":
114 case "client_no_context_takeover":
115 if ($value !== null) {
116 throw new InvalidPermessageDeflateOptionsException($key . ' must not have a value.');
117 }
118 $value = true;
119 break;
120 case "server_max_window_bits":
121 if (!in_array($value, self::$VALID_BITS)) {
122 throw new InvalidPermessageDeflateOptionsException($key . ' must have a value between 8 and 15.');
123 }
124 break;
125 case "client_max_window_bits":
126 if ($value === null) {
127 $value = '15';
128 }
129 if (!in_array($value, self::$VALID_BITS)) {
130 throw new InvalidPermessageDeflateOptionsException($key . ' must have no value or a value between 8 and 15.');
131 }
132 break;
133 default:
134 throw new InvalidPermessageDeflateOptionsException('Option "' . $key . '"is not valid for permessage deflate');
135 }
136
137 if ($options->$key !== null) {
138 throw new InvalidPermessageDeflateOptionsException($key . ' specified more than once. Connection must be declined.');
139 }
140
141 $options->$key = $value;
142 }
143
144 if ($options->getClientMaxWindowBits() === null) {
145 $options->client_max_window_bits = 15;
146 }
147
148 if ($options->getServerMaxWindowBits() === null) {
149 $options->server_max_window_bits = 15;
150 }
151
152 $optionSets[] = $options;
153 }
154
155 // always put a disabled on the end
156 $optionSets[] = new static();
157
158 return $optionSets;
159 }
160
161 /**
162 * @return mixed
163 */
164 public function getServerNoContextTakeover()
165 {
166 return $this->server_no_context_takeover;
167 }
168
169 /**
170 * @return mixed
171 */
172 public function getClientNoContextTakeover()
173 {
174 return $this->client_no_context_takeover;
175 }
176
177 /**
178 * @return mixed
179 */
180 public function getServerMaxWindowBits()
181 {
182 return $this->server_max_window_bits;
183 }
184
185 /**
186 * @return mixed
187 */
188 public function getClientMaxWindowBits()
189 {
190 return $this->client_max_window_bits;
191 }
192
193 /**
194 * @return bool
195 */
196 public function isEnabled()
197 {
198 return $this->deflateEnabled;
199 }
200
201 /**
202 * @param ResponseInterface $response
203 * @return ResponseInterface
204 */
205 public function addHeaderToResponse(ResponseInterface $response)
206 {
207 if (!$this->deflateEnabled) {
208 return $response;
209 }
210
211 $header = 'permessage-deflate';
212 if ($this->client_max_window_bits != 15) {
213 $header .= '; client_max_window_bits='. $this->client_max_window_bits;
214 }
215 if ($this->client_no_context_takeover) {
216 $header .= '; client_no_context_takeover';
217 }
218 if ($this->server_max_window_bits != 15) {
219 $header .= '; server_max_window_bits=' . $this->server_max_window_bits;
220 }
221 if ($this->server_no_context_takeover) {
222 $header .= '; server_no_context_takeover';
223 }
224
225 return $response->withAddedHeader('Sec-Websocket-Extensions', $header);
226 }
227
228 public function addHeaderToRequest(RequestInterface $request) {
229 if (!$this->deflateEnabled) {
230 return $request;
231 }
232
233 $header = 'permessage-deflate';
234 if ($this->server_no_context_takeover) {
235 $header .= '; server_no_context_takeover';
236 }
237 if ($this->client_no_context_takeover) {
238 $header .= '; client_no_context_takeover';
239 }
240 if ($this->server_max_window_bits != 15) {
241 $header .= '; server_max_window_bits=' . $this->server_max_window_bits;
242 }
243 $header .= '; client_max_window_bits';
244 if ($this->client_max_window_bits != 15) {
245 $header .= '='. $this->client_max_window_bits;
246 }
247
248 return $request->withAddedHeader('Sec-Websocket-Extensions', $header);
249 }
250
251 public static function permessageDeflateSupported($version = PHP_VERSION) {
252 if (!function_exists('deflate_init')) {
253 return false;
254 }
255 if (version_compare($version, '7.1.3', '>')) {
256 return true;
257 }
258 if (version_compare($version, '7.0.18', '>=')
259 && version_compare($version, '7.1.0', '<')) {
260 return true;
261 }
262
263 return false;
264 }
265 }
118118
119119 /**
120120 * Verify the version passed matches this RFC
121 * @param string|int $versionHeader MUST equal 13|"13"
121 * @param string[] $versionHeader MUST equal ["13"]
122122 * @return bool
123123 */
124 public function verifyVersion($versionHeader) {
124 public function verifyVersion(array $versionHeader) {
125125 return (1 === count($versionHeader) && static::VERSION === (int)$versionHeader[0]);
126126 }
127127
136136 */
137137 public function verifyExtensions($val) {
138138 }
139
140 public function getPermessageDeflateOptions(array $requestHeader, array $responseHeader) {
141 $deflate = true;
142 if (!isset($requestHeader['Sec-WebSocket-Extensions']) || count(array_filter($requestHeader['Sec-WebSocket-Extensions'], function ($val) {
143 return 'permessage-deflate' === substr($val, 0, strlen('permessage-deflate'));
144 })) === 0) {
145 $deflate = false;
146 }
147
148 if (!isset($responseHeader['Sec-WebSocket-Extensions']) || count(array_filter($responseHeader['Sec-WebSocket-Extensions'], function ($val) {
149 return 'permessage-deflate' === substr($val, 0, strlen('permessage-deflate'));
150 })) === 0) {
151 $deflate = false;
152 }
153
154 return [
155 'deflate' => $deflate,
156 'no_context_takeover' => false,
157 'max_window_bits' => null,
158 'request_no_context_takeover' => false,
159 'request_max_window_bits' => null
160 ];
161 }
139162 }
1717 $request->getHeader('Sec-WebSocket-Protocol')
1818 , $response->getHeader('Sec-WebSocket-Protocol')
1919 );
20 $passes += (int)$this->verifyExtensions(
21 $request->getHeader('Sec-WebSocket-Extensions')
22 , $response->getHeader('Sec-WebSocket-Extensions')
23 );
2024
21 return (5 === $passes);
25 return (6 === $passes);
2226 }
2327
2428 public function verifyStatus($status) {
4650 }
4751
4852 public function verifySubProtocol(array $requestHeader, array $responseHeader) {
49 return 0 === count($responseHeader) || count(array_intersect($responseHeader, $requestHeader)) > 0;
53 if (0 === count($responseHeader)) {
54 return true;
55 }
56
57 $requestedProtocols = array_map('trim', explode(',', implode(',', $requestHeader)));
58
59 return count($responseHeader) === 1 && count(array_intersect($responseHeader, $requestedProtocols)) === 1;
5060 }
51 }
61
62 public function verifyExtensions(array $requestHeader, array $responseHeader) {
63 if (in_array('permessage-deflate', $responseHeader)) {
64 return strpos(implode(',', $requestHeader), 'permessage-deflate') !== false ? 1 : 0;
65 }
66
67 return 1;
68 }
69 }
1616
1717 private $_strictSubProtocols = false;
1818
19 public function __construct(RequestVerifier $requestVerifier) {
19 private $enablePerMessageDeflate = false;
20
21 public function __construct(RequestVerifier $requestVerifier, $enablePerMessageDeflate = false) {
2022 $this->verifier = $requestVerifier;
23
24 // https://bugs.php.net/bug.php?id=73373
25 // https://bugs.php.net/bug.php?id=74240 - need >=7.1.4 or >=7.0.18
26 $supported = PermessageDeflateOptions::permessageDeflateSupported();
27 if ($enablePerMessageDeflate && !$supported) {
28 throw new \Exception('permessage-deflate is not supported by your PHP version (need >=7.1.4 or >=7.0.18).');
29 }
30 if ($enablePerMessageDeflate && !function_exists('deflate_add')) {
31 throw new \Exception('permessage-deflate is not supported because you do not have the zlib extension.');
32 }
33
34 $this->enablePerMessageDeflate = $enablePerMessageDeflate;
2135 }
2236
2337 /**
6074 'Sec-WebSocket-Version' => $this->getVersionNumber()
6175 ];
6276 if (count($this->_supportedSubProtocols) > 0) {
63 $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', $this->_supportedSubProtocols);
77 $upgradeSuggestion['Sec-WebSocket-Protocol'] = implode(', ', array_keys($this->_supportedSubProtocols));
6478 }
6579 if (true !== $this->verifier->verifyUpgradeRequest($request->getHeader('Upgrade'))) {
6680 return new Response(426, $upgradeSuggestion, null, '1.1', 'Upgrade header MUST be provided');
96110 }
97111 }
98112
99 return new Response(101, array_merge($headers, [
113 $response = new Response(101, array_merge($headers, [
100114 'Upgrade' => 'websocket'
101 , 'Connection' => 'Upgrade'
102 , 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')[0])
103 , 'X-Powered-By' => 'Ratchet'
115 , 'Connection' => 'Upgrade'
116 , 'Sec-WebSocket-Accept' => $this->sign((string)$request->getHeader('Sec-WebSocket-Key')[0])
117 , 'X-Powered-By' => 'Ratchet'
104118 ]));
119
120 try {
121 $perMessageDeflateRequest = PermessageDeflateOptions::fromRequestOrResponse($request)[0];
122 } catch (InvalidPermessageDeflateOptionsException $e) {
123 return new Response(400, [], null, '1.1', $e->getMessage());
124 }
125
126 if ($this->enablePerMessageDeflate && $perMessageDeflateRequest->isEnabled()) {
127 $response = $perMessageDeflateRequest->addHeaderToResponse($response);
128 }
129
130 return $response;
105131 }
106132
107133 /**
148148 return 128 === ($this->firstByte & 128);
149149 }
150150
151 public function setRsv1($value = true) {
152 if (strlen($this->data) == 0) {
153 throw new \UnderflowException("Cannot set Rsv1 because there is no data.");
154 }
155
156 $this->firstByte =
157 ($this->isFinal() ? 128 : 0)
158 + $this->getOpcode()
159 + ($value ? 64 : 0)
160 + ($this->getRsv2() ? 32 : 0)
161 + ($this->getRsv3() ? 16 : 0)
162 ;
163
164 $this->data[0] = chr($this->firstByte);
165 return $this;
166 }
167
151168 /**
152169 * @return boolean
153170 * @throws \UnderflowException
66 */
77 private $_frames;
88
9 /**
10 * @var int
11 */
12 private $len;
13
914 public function __construct() {
1015 $this->_frames = new \SplDoublyLinkedList;
16 $this->len = 0;
1117 }
1218
1319 public function getIterator() {
3844 * {@inheritdoc}
3945 */
4046 public function addFrame(FrameInterface $fragment) {
47 $this->len += $fragment->getPayloadLength();
4148 $this->_frames->push($fragment);
4249
4350 return $this;
5865 * {@inheritdoc}
5966 */
6067 public function getPayloadLength() {
61 $len = 0;
62
63 foreach ($this->_frames as $frame) {
64 try {
65 $len += $frame->getPayloadLength();
66 } catch (\UnderflowException $e) {
67 // Not an error, want the current amount buffered
68 }
69 }
70
71 return $len;
68 return $this->len;
7269 }
7370
7471 /**
119116
120117 return Frame::OP_BINARY === $this->_frames->bottom()->getOpcode();
121118 }
119
120 /**
121 * @return boolean
122 */
123 public function getRsv1() {
124 if ($this->_frames->isEmpty()) {
125 return false;
126 //throw new \UnderflowException('Not enough data has been received to determine if message is binary');
127 }
128
129 return $this->_frames->bottom()->getRsv1();
130 }
122131 }
00 <?php
11 namespace Ratchet\RFC6455\Messaging;
22
3 use Ratchet\RFC6455\Handshake\PermessageDeflateOptions;
4
35 class MessageBuffer {
46 /**
57 * @var \Ratchet\RFC6455\Messaging\CloseFrameChecker
3537 * @var bool
3638 */
3739 private $checkForMask;
40
41 /**
42 * @var callable
43 */
44 private $sender;
45
46 /**
47 * @var string
48 */
49 private $leftovers;
50
51 /**
52 * @var int
53 */
54 private $streamingMessageOpCode = -1;
55
56 /**
57 * @var PermessageDeflateOptions
58 */
59 private $permessageDeflateOptions;
60
61 /**
62 * @var bool
63 */
64 private $deflateEnabled = false;
65
66 /**
67 * @var int
68 */
69 private $maxMessagePayloadSize;
70
71 /**
72 * @var int
73 */
74 private $maxFramePayloadSize;
75
76 /**
77 * @var bool
78 */
79 private $compressedMessage;
3880
3981 function __construct(
4082 CloseFrameChecker $frameChecker,
4183 callable $onMessage,
4284 callable $onControl = null,
4385 $expectMask = true,
44 $exceptionFactory = null
86 $exceptionFactory = null,
87 $maxMessagePayloadSize = null, // null for default - zero for no limit
88 $maxFramePayloadSize = null, // null for default - zero for no limit
89 callable $sender = null,
90 PermessageDeflateOptions $permessageDeflateOptions = null
4591 ) {
4692 $this->closeFrameChecker = $frameChecker;
4793 $this->checkForMask = (bool)$expectMask;
4894
49 $this->exceptionFactory ?: $this->exceptionFactory = function($msg) {
95 $this->exceptionFactory ?: $exceptionFactory = function($msg) {
5096 return new \UnderflowException($msg);
5197 };
5298
5399 $this->onMessage = $onMessage;
54100 $this->onControl = $onControl ?: function() {};
101
102 $this->sender = $sender;
103
104 $this->permessageDeflateOptions = $permessageDeflateOptions ?: PermessageDeflateOptions::createDisabled();
105
106 $this->deflateEnabled = $this->permessageDeflateOptions->isEnabled();
107
108 if ($this->deflateEnabled && !is_callable($this->sender)) {
109 throw new \InvalidArgumentException('sender must be set when deflate is enabled');
110 }
111
112 $this->compressedMessage = false;
113
114 $this->leftovers = '';
115
116 $memory_limit_bytes = static::getMemoryLimit();
117
118 if ($maxMessagePayloadSize === null) {
119 $maxMessagePayloadSize = $memory_limit_bytes / 4;
120 }
121 if ($maxFramePayloadSize === null) {
122 $maxFramePayloadSize = $memory_limit_bytes / 4;
123 }
124
125 if (!is_int($maxFramePayloadSize) || $maxFramePayloadSize > 0x7FFFFFFFFFFFFFFF || $maxFramePayloadSize < 0) { // this should be interesting on non-64 bit systems
126 throw new \InvalidArgumentException($maxFramePayloadSize . ' is not a valid maxFramePayloadSize');
127 }
128 $this->maxFramePayloadSize = $maxFramePayloadSize;
129
130 if (!is_int($maxMessagePayloadSize) || $maxMessagePayloadSize > 0x7FFFFFFFFFFFFFFF || $maxMessagePayloadSize < 0) {
131 throw new \InvalidArgumentException($maxMessagePayloadSize . 'is not a valid maxMessagePayloadSize');
132 }
133 $this->maxMessagePayloadSize = $maxMessagePayloadSize;
55134 }
56135
57136 public function onData($data) {
58 while (strlen($data) > 0) {
59 $data = $this->processData($data);
60 }
137 $data = $this->leftovers . $data;
138 $dataLen = strlen($data);
139
140 if ($dataLen < 2) {
141 $this->leftovers = $data;
142
143 return;
144 }
145
146 $frameStart = 0;
147 while ($frameStart + 2 <= $dataLen) {
148 $headerSize = 2;
149 $payload_length = unpack('C', $data[$frameStart + 1] & "\x7f")[1];
150 $isMasked = ($data[$frameStart + 1] & "\x80") === "\x80";
151 $headerSize += $isMasked ? 4 : 0;
152 if ($payload_length > 125 && ($dataLen - $frameStart < $headerSize + 125)) {
153 // no point of checking - this frame is going to be bigger than the buffer is right now
154 break;
155 }
156 if ($payload_length > 125) {
157 $payloadLenBytes = $payload_length === 126 ? 2 : 8;
158 $headerSize += $payloadLenBytes;
159 $bytesToUpack = substr($data, $frameStart + 2, $payloadLenBytes);
160 $payload_length = $payload_length === 126
161 ? unpack('n', $bytesToUpack)[1]
162 : unpack('J', $bytesToUpack)[1];
163 }
164
165 $closeFrame = null;
166
167 if ($payload_length < 0) {
168 // this can happen when unpacking in php
169 $closeFrame = $this->newCloseFrame(Frame::CLOSE_PROTOCOL, 'Invalid frame length');
170 }
171
172 if (!$closeFrame && $this->maxFramePayloadSize > 1 && $payload_length > $this->maxFramePayloadSize) {
173 $closeFrame = $this->newCloseFrame(Frame::CLOSE_TOO_BIG, 'Maximum frame size exceeded');
174 }
175
176 if (!$closeFrame && $this->maxMessagePayloadSize > 0
177 && $payload_length + ($this->messageBuffer ? $this->messageBuffer->getPayloadLength() : 0) > $this->maxMessagePayloadSize) {
178 $closeFrame = $this->newCloseFrame(Frame::CLOSE_TOO_BIG, 'Maximum message size exceeded');
179 }
180
181 if ($closeFrame !== null) {
182 $onControl = $this->onControl;
183 $onControl($closeFrame);
184 $this->leftovers = '';
185
186 return;
187 }
188
189 $isCoalesced = $dataLen - $frameStart >= $payload_length + $headerSize;
190 if (!$isCoalesced) {
191 break;
192 }
193 $this->processData(substr($data, $frameStart, $payload_length + $headerSize));
194 $frameStart = $frameStart + $payload_length + $headerSize;
195 }
196
197 $this->leftovers = substr($data, $frameStart);
61198 }
62199
63200 /**
69206 $this->frameBuffer ?: $this->frameBuffer = $this->newFrame();
70207
71208 $this->frameBuffer->addBuffer($data);
72 if (!$this->frameBuffer->isCoalesced()) {
73 return '';
74 }
75209
76210 $onMessage = $this->onMessage;
77211 $onControl = $this->onControl;
78212
79213 $this->frameBuffer = $this->frameCheck($this->frameBuffer);
80214
81 $overflow = $this->frameBuffer->extractOverflow();
82215 $this->frameBuffer->unMaskPayload();
83216
84217 $opcode = $this->frameBuffer->getOpcode();
85218
86219 if ($opcode > 2) {
87 $onControl($this->frameBuffer);
220 $onControl($this->frameBuffer, $this);
88221
89222 if (Frame::OP_CLOSE === $opcode) {
90223 return '';
91224 }
92225 } else {
226 if ($this->messageBuffer->count() === 0 && $this->frameBuffer->getRsv1()) {
227 $this->compressedMessage = true;
228 }
229 if ($this->compressedMessage) {
230 $this->frameBuffer = $this->inflateFrame($this->frameBuffer);
231 }
232
93233 $this->messageBuffer->addFrame($this->frameBuffer);
94234 }
95235
97237
98238 if ($this->messageBuffer->isCoalesced()) {
99239 $msgCheck = $this->checkMessage($this->messageBuffer);
240
241 $msgBuffer = $this->messageBuffer;
242 $this->messageBuffer = null;
243
100244 if (true !== $msgCheck) {
101 $onControl($this->newCloseFrame($msgCheck, 'Ratchet detected an invalid UTF-8 payload'));
245 $onControl($this->newCloseFrame($msgCheck, 'Ratchet detected an invalid UTF-8 payload'), $this);
102246 } else {
103 $onMessage($this->messageBuffer);
247 $onMessage($msgBuffer, $this);
104248 }
105249
106250 $this->messageBuffer = null;
107 }
108
109 return $overflow;
251 $this->compressedMessage = false;
252
253 if ($this->permessageDeflateOptions->getServerNoContextTakeover()) {
254 $this->inflator = null;
255 }
256 }
110257 }
111258
112259 /**
115262 * @return \Ratchet\RFC6455\Messaging\FrameInterface|FrameInterface
116263 */
117264 public function frameCheck(FrameInterface $frame) {
118 if (false !== $frame->getRsv1() ||
265 if ((false !== $frame->getRsv1() && !$this->deflateEnabled) ||
119266 false !== $frame->getRsv2() ||
120267 false !== $frame->getRsv3()
121268 ) {
227374 public function newCloseFrame($code, $reason = '') {
228375 return $this->newFrame(pack('n', $code) . $reason, true, Frame::OP_CLOSE);
229376 }
377
378 public function sendFrame(Frame $frame) {
379 if ($this->sender === null) {
380 throw new \Exception('To send frames using the MessageBuffer, sender must be set.');
381 }
382
383 if ($this->deflateEnabled &&
384 ($frame->getOpcode() === Frame::OP_TEXT || $frame->getOpcode() === Frame::OP_BINARY)) {
385 $frame = $this->deflateFrame($frame);
386 }
387
388 if (!$this->checkForMask) {
389 $frame->maskPayload();
390 }
391
392 $sender = $this->sender;
393 $sender($frame->getContents());
394 }
395
396 public function sendMessage($messagePayload, $final = true, $isBinary = false) {
397 $opCode = $isBinary ? Frame::OP_BINARY : Frame::OP_TEXT;
398 if ($this->streamingMessageOpCode === -1) {
399 $this->streamingMessageOpCode = $opCode;
400 }
401
402 if ($this->streamingMessageOpCode !== $opCode) {
403 throw new \Exception('Binary and text message parts cannot be streamed together.');
404 }
405
406 $frame = $this->newFrame($messagePayload, $final, $opCode);
407
408 $this->sendFrame($frame);
409
410 if ($final) {
411 // reset deflator if client doesn't remember contexts
412 if ($this->getDeflateNoContextTakeover()) {
413 $this->deflator = null;
414 }
415 $this->streamingMessageOpCode = -1;
416 }
417 }
418
419 private $inflator;
420
421 private function getDeflateNoContextTakeover() {
422 return $this->checkForMask ?
423 $this->permessageDeflateOptions->getServerNoContextTakeover() :
424 $this->permessageDeflateOptions->getClientNoContextTakeover();
425 }
426
427 private function getDeflateWindowBits() {
428 return $this->checkForMask ? $this->permessageDeflateOptions->getServerMaxWindowBits() : $this->permessageDeflateOptions->getClientMaxWindowBits();
429 }
430
431 private function getInflateNoContextTakeover() {
432 return $this->checkForMask ?
433 $this->permessageDeflateOptions->getClientNoContextTakeover() :
434 $this->permessageDeflateOptions->getServerNoContextTakeover();
435 }
436
437 private function getInflateWindowBits() {
438 return $this->checkForMask ? $this->permessageDeflateOptions->getClientMaxWindowBits() : $this->permessageDeflateOptions->getServerMaxWindowBits();
439 }
440
441 private function inflateFrame(Frame $frame) {
442 if ($this->inflator === null) {
443 $this->inflator = inflate_init(
444 ZLIB_ENCODING_RAW,
445 [
446 'level' => -1,
447 'memory' => 8,
448 'window' => $this->getInflateWindowBits(),
449 'strategy' => ZLIB_DEFAULT_STRATEGY
450 ]
451 );
452 }
453
454 $terminator = '';
455 if ($frame->isFinal()) {
456 $terminator = "\x00\x00\xff\xff";
457 }
458
459 gc_collect_cycles(); // memory runs away if we don't collect ??
460
461 return new Frame(
462 inflate_add($this->inflator, $frame->getPayload() . $terminator),
463 $frame->isFinal(),
464 $frame->getOpcode()
465 );
466 }
467
468 private $deflator;
469
470 private function deflateFrame(Frame $frame)
471 {
472 if ($frame->getRsv1()) {
473 return $frame; // frame is already deflated
474 }
475
476 if ($this->deflator === null) {
477 $bits = (int)$this->getDeflateWindowBits();
478 if ($bits === 8) {
479 $bits = 9;
480 }
481 $this->deflator = deflate_init(
482 ZLIB_ENCODING_RAW,
483 [
484 'level' => -1,
485 'memory' => 8,
486 'window' => $bits,
487 'strategy' => ZLIB_DEFAULT_STRATEGY
488 ]
489 );
490 }
491
492 // there is an issue in the zlib extension for php where
493 // deflate_add does not check avail_out to see if the buffer filled
494 // this only seems to be an issue for payloads between 16 and 64 bytes
495 // This if statement is a hack fix to break the output up allowing us
496 // to call deflate_add twice which should clear the buffer issue
497 // if ($frame->getPayloadLength() >= 16 && $frame->getPayloadLength() <= 64) {
498 // // try processing in 8 byte chunks
499 // // https://bugs.php.net/bug.php?id=73373
500 // $payload = "";
501 // $orig = $frame->getPayload();
502 // $partSize = 8;
503 // while (strlen($orig) > 0) {
504 // $part = substr($orig, 0, $partSize);
505 // $orig = substr($orig, strlen($part));
506 // $flags = strlen($orig) > 0 ? ZLIB_PARTIAL_FLUSH : ZLIB_SYNC_FLUSH;
507 // $payload .= deflate_add($this->deflator, $part, $flags);
508 // }
509 // } else {
510 $payload = deflate_add(
511 $this->deflator,
512 $frame->getPayload(),
513 ZLIB_SYNC_FLUSH
514 );
515 // }
516
517 $deflatedFrame = new Frame(
518 substr($payload, 0, $frame->isFinal() ? -4 : strlen($payload)),
519 $frame->isFinal(),
520 $frame->getOpcode()
521 );
522
523 if ($frame->isFinal()) {
524 $deflatedFrame->setRsv1();
525 }
526
527 return $deflatedFrame;
528 }
529
530 /**
531 * This is a separate function for testing purposes
532 * $memory_limit is only used for testing
533 *
534 * @param null|string $memory_limit
535 * @return int
536 */
537 private static function getMemoryLimit($memory_limit = null) {
538 $memory_limit = $memory_limit === null ? \trim(\ini_get('memory_limit')) : $memory_limit;
539 $memory_limit_bytes = 0;
540 if ($memory_limit !== '') {
541 $shifty = ['k' => 0, 'm' => 10, 'g' => 20];
542 $multiplier = strlen($memory_limit) > 1 ? substr(strtolower($memory_limit), -1) : '';
543 $memory_limit = (int)$memory_limit;
544 $memory_limit_bytes = in_array($multiplier, array_keys($shifty), true) ? $memory_limit * 1024 << $shifty[$multiplier] : $memory_limit;
545 }
546
547 return $memory_limit_bytes < 0 ? 0 : $memory_limit_bytes;
548 }
230549 }
00 <?php
1
12 namespace Ratchet\RFC6455\Test;
3 use PHPUnit\Framework\TestCase;
24
3 class AbResultsTest extends \PHPUnit_Framework_TestCase {
5 class AbResultsTest extends TestCase {
46 private function verifyAutobahnResults($fileName) {
57 if (!file_exists($fileName)) {
68 return $this->markTestSkipped('Autobahn TestSuite results not found');
00 <?php
11 use GuzzleHttp\Psr7\Uri;
2 use Ratchet\RFC6455\Handshake\InvalidPermessageDeflateOptionsException;
3 use Ratchet\RFC6455\Handshake\PermessageDeflateOptions;
4 use Ratchet\RFC6455\Messaging\MessageBuffer;
5 use Ratchet\RFC6455\Handshake\ClientNegotiator;
6 use Ratchet\RFC6455\Messaging\CloseFrameChecker;
7 use Ratchet\RFC6455\Messaging\MessageInterface;
28 use React\Promise\Deferred;
39 use Ratchet\RFC6455\Messaging\Frame;
10 use React\Socket\ConnectionInterface;
11 use React\Socket\Connector;
412
513 require __DIR__ . '/../bootstrap.php';
614
7 define('AGENT', 'RatchetRFC/0.0.0');
15 define('AGENT', 'RatchetRFC/0.3');
816
917 $testServer = "127.0.0.1";
1018
1119 $loop = React\EventLoop\Factory::create();
1220
13 $dnsResolverFactory = new React\Dns\Resolver\Factory();
14 $dnsResolver = $dnsResolverFactory->createCached('8.8.8.8', $loop);
15
16 $factory = new \React\SocketClient\Connector($loop, $dnsResolver);
17
18 function echoStreamerFactory($conn)
21 $connector = new Connector($loop);
22
23 function echoStreamerFactory($conn, $permessageDeflateOptions = null)
1924 {
25 $permessageDeflateOptions = $permessageDeflateOptions ?: PermessageDeflateOptions::createDisabled();
26
2027 return new \Ratchet\RFC6455\Messaging\MessageBuffer(
2128 new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
22 function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($conn) {
23 /** @var Frame $frame */
24 foreach ($msg as $frame) {
25 $frame->maskPayload();
26 }
27 $conn->write($msg->getContents());
29 function (\Ratchet\RFC6455\Messaging\MessageInterface $msg, MessageBuffer $messageBuffer) use ($conn) {
30 $messageBuffer->sendMessage($msg->getPayload(), true, $msg->isBinary());
2831 },
29 function (\Ratchet\RFC6455\Messaging\FrameInterface $frame) use ($conn) {
32 function (\Ratchet\RFC6455\Messaging\FrameInterface $frame, MessageBuffer $messageBuffer) use ($conn) {
3033 switch ($frame->getOpcode()) {
3134 case Frame::OP_PING:
3235 return $conn->write((new Frame($frame->getPayload(), true, Frame::OP_PONG))->maskPayload()->getContents());
3639 break;
3740 }
3841 },
39 false
42 false,
43 null,
44 null,
45 null,
46 [$conn, 'write'],
47 $permessageDeflateOptions
4048 );
4149 }
4250
4351 function getTestCases() {
44 global $factory;
4552 global $testServer;
53 global $connector;
4654
4755 $deferred = new Deferred();
4856
49 $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
50 $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
57 $connector->connect($testServer . ':9001')->then(function (ConnectionInterface $connection) use ($deferred) {
58 $cn = new ClientNegotiator();
5159 $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001/getCaseCount'));
5260
5361 $rawResponse = "";
5462 $response = null;
5563
56 /** @var \Ratchet\RFC6455\Messaging\Streaming\MessageBuffer $ms */
64 /** @var MessageBuffer $ms */
5765 $ms = null;
5866
59 $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
67 $connection->on('data', function ($data) use ($connection, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
6068 if ($response === null) {
6169 $rawResponse .= $data;
6270 $pos = strpos($rawResponse, "\r\n\r\n");
6674 $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
6775
6876 if (!$cn->validateResponse($cnRequest, $response)) {
69 $stream->end();
77 $connection->end();
7078 $deferred->reject();
7179 } else {
72 $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer(
73 new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
74 function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) {
80 $ms = new MessageBuffer(
81 new CloseFrameChecker,
82 function (MessageInterface $msg) use ($deferred, $connection) {
7583 $deferred->resolve($msg->getPayload());
76 $stream->close();
84 $connection->close();
7785 },
7886 null,
79 false
87 false,
88 null,
89 null,
90 null,
91 function () {}
8092 );
8193 }
8294 }
88100 }
89101 });
90102
91 $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
103 $connection->write(\GuzzleHttp\Psr7\str($cnRequest));
92104 });
93105
94106 return $deferred->promise();
95107 }
108
109 $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator(
110 PermessageDeflateOptions::permessageDeflateSupported() ? PermessageDeflateOptions::createEnabled() : null);
96111
97112 function runTest($case)
98113 {
99 global $factory;
114 global $connector;
100115 global $testServer;
116 global $cn;
101117
102118 $casePath = "/runCase?case={$case}&agent=" . AGENT;
103119
104120 $deferred = new Deferred();
105121
106 $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred, $casePath, $case) {
107 $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
122 $connector->connect($testServer . ':9001')->then(function (ConnectionInterface $connection) use ($deferred, $casePath, $case) {
123 $cn = new ClientNegotiator(
124 PermessageDeflateOptions::permessageDeflateSupported() ? PermessageDeflateOptions::createEnabled() : null);
108125 $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $casePath));
109126
110127 $rawResponse = "";
112129
113130 $ms = null;
114131
115 $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
132 $connection->on('data', function ($data) use ($connection, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
116133 if ($response === null) {
117134 $rawResponse .= $data;
118135 $pos = strpos($rawResponse, "\r\n\r\n");
122139 $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
123140
124141 if (!$cn->validateResponse($cnRequest, $response)) {
125 $stream->end();
142 echo "Invalid response.\n";
143 $connection->end();
126144 $deferred->reject();
127145 } else {
128 $ms = echoStreamerFactory($stream);
146 try {
147 $permessageDeflateOptions = PermessageDeflateOptions::fromRequestOrResponse($response)[0];
148 $ms = echoStreamerFactory(
149 $connection,
150 $permessageDeflateOptions
151 );
152 } catch (InvalidPermessageDeflateOptionsException $e) {
153 $connection->end();
154 }
129155 }
130156 }
131157 }
136162 }
137163 });
138164
139 $stream->on('close', function () use ($deferred) {
165 $connection->on('close', function () use ($deferred) {
140166 $deferred->resolve();
141167 });
142168
143 $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
169 $connection->write(\GuzzleHttp\Psr7\str($cnRequest));
144170 });
145171
146172 return $deferred->promise();
147173 }
148174
149175 function createReport() {
150 global $factory;
176 global $connector;
151177 global $testServer;
152178
153179 $deferred = new Deferred();
154180
155 $factory->create($testServer, 9001)->then(function (\React\Stream\Stream $stream) use ($deferred) {
156 $reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
157 $cn = new \Ratchet\RFC6455\Handshake\ClientNegotiator();
181 $connector->connect($testServer . ':9001')->then(function (ConnectionInterface $connection) use ($deferred) {
182 // $reportPath = "/updateReports?agent=" . AGENT . "&shutdownOnComplete=true";
183 // we will stop it using docker now instead of just shutting down
184 $reportPath = "/updateReports?agent=" . AGENT;
185 $cn = new ClientNegotiator();
158186 $cnRequest = $cn->generateRequest(new Uri('ws://127.0.0.1:9001' . $reportPath));
159187
160188 $rawResponse = "";
161189 $response = null;
162190
163 /** @var \Ratchet\RFC6455\Messaging\MessageBuffer $ms */
191 /** @var MessageBuffer $ms */
164192 $ms = null;
165193
166 $stream->on('data', function ($data) use ($stream, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
194 $connection->on('data', function ($data) use ($connection, &$rawResponse, &$response, &$ms, $cn, $deferred, &$context, $cnRequest) {
167195 if ($response === null) {
168196 $rawResponse .= $data;
169197 $pos = strpos($rawResponse, "\r\n\r\n");
173201 $response = \GuzzleHttp\Psr7\parse_response($rawResponse);
174202
175203 if (!$cn->validateResponse($cnRequest, $response)) {
176 $stream->end();
204 $connection->end();
177205 $deferred->reject();
178206 } else {
179 $ms = new \Ratchet\RFC6455\Messaging\MessageBuffer(
180 new \Ratchet\RFC6455\Messaging\CloseFrameChecker,
181 function (\Ratchet\RFC6455\Messaging\MessageInterface $msg) use ($deferred, $stream) {
207 $ms = new MessageBuffer(
208 new CloseFrameChecker,
209 function (MessageInterface $msg) use ($deferred, $connection) {
182210 $deferred->resolve($msg->getPayload());
183 $stream->close();
211 $connection->close();
184212 },
185213 null,
186 false
214 false,
215 null,
216 null,
217 null,
218 function () {}
187219 );
188220 }
189221 }
195227 }
196228 });
197229
198 $stream->write(\GuzzleHttp\Psr7\str($cnRequest));
230 $connection->write(\GuzzleHttp\Psr7\str($cnRequest));
199231 });
200232
201233 return $deferred->promise();
213245 $allDeferred->resolve();
214246 return;
215247 }
216 runTest($i)->then($runNextCase);
248 echo "Running test $i/$count...";
249 $startTime = microtime(true);
250 runTest($i)
251 ->then(function () use ($startTime) {
252 echo " completed " . round((microtime(true) - $startTime) * 1000) . " ms\n";
253 })
254 ->then($runNextCase);
217255 };
218256
219257 $i = 0;
0 #!/bin/bash
1 set -x
2
3 echo "Running $0"
4
5 echo Adding "$1 host.ratchet.internal" to /etc/hosts file
6
7 echo $1 host.ratchet.internal >> /etc/hosts
8
9 echo /etc/hosts contains:
10 cat /etc/hosts
11 echo
11 "options": {
22 "failByDrop": false
33 }
4 , "outdir": "./reports/servers"
4 , "outdir": "/reports/servers"
55 , "servers": [{
6 "agent": "RatchetRFC/0.1.0"
7 , "url": "ws://localhost:9001"
6 "agent": "RatchetRFC/0.3"
7 , "url": "ws://host.ratchet.internal:9001"
88 , "options": {"version": 18}
99 }]
10 , "cases": ["*"]
11 , "exclude-cases": ["6.4.*", "12.*","13.*"]
10 , "cases": [
11 "*"
12 ]
13 , "exclude-cases": []
1214 , "exclude-agent-cases": {}
1315 }
0 {
1 "options": {
2 "failByDrop": false
3 }
4 , "outdir": "/reports/servers"
5 , "servers": [{
6 "agent": "RatchetRFC/0.3"
7 , "url": "ws://host.ratchet.internal:9001"
8 , "options": {"version": 18}
9 }]
10 , "cases": ["*"]
11 , "exclude-cases": ["12.*", "13.*"]
12 , "exclude-agent-cases": {}
13 }
33 "failByDrop": false
44 }
55 , "outdir": "./reports/clients"
6 , "cases": ["*"]
7 , "exclude-cases": ["6.4.*", "12.*", "13.*"]
6 , "cases": [
7 "*"
8 ]
9 , "exclude-cases": []
810 , "exclude-agent-cases": {}
911 }
0 {
1 "url": "ws://127.0.0.1:9001"
2 , "options": {
3 "failByDrop": false
4 }
5 , "outdir": "./reports/clients"
6 , "cases": ["*"]
7 , "exclude-cases": ["12.*", "13.*"]
8 , "exclude-agent-cases": {}
9 }
0 set -x
01 cd tests/ab
12
2 wstest -m fuzzingserver -s fuzzingserver.json &
3 sleep 5
4 php clientRunner.php
3 SKIP_DEFLATE=
4 if [ "$TRAVIS" = "true" ]; then
5 if [ $(phpenv version-name) = "hhvm" -o $(phpenv version-name) = "5.4" -o $(phpenv version-name) = "5.5" -o $(phpenv version-name) = "5.6" ]; then
6 echo "Skipping deflate autobahn tests for $(phpenv version-name)"
7 SKIP_DEFLATE=_skip_deflate
8 fi
9 fi
510
6 sleep 2
11 if [ "$ABTEST" = "client" ]; then
12 docker run --rm \
13 -d \
14 -v ${PWD}:/config \
15 -v ${PWD}/reports:/reports \
16 -p 9001:9001 \
17 --name fuzzingserver \
18 crossbario/autobahn-testsuite wstest -m fuzzingserver -s /config/fuzzingserver$SKIP_DEFLATE.json
19 sleep 5
20 if [ "$TRAVIS" != "true" ]; then
21 echo "Running tests vs Autobahn test client"
22 ###docker run -it --rm --name abpytest crossbario/autobahn-testsuite wstest --mode testeeclient -w ws://host.docker.internal:9001
23 fi
24 php -d memory_limit=256M clientRunner.php
725
8 php startServer.php &
9 sleep 3
10 wstest -m fuzzingclient -s fuzzingclient.json
26 docker ps -a
27
28 docker logs fuzzingserver
29
30 docker stop fuzzingserver
31
32 sleep 2
33 fi
34
35 if [ "$ABTEST" = "server" ]; then
36 php -d memory_limit=256M startServer.php &
37 sleep 3
38
39 if [ "$OSTYPE" = "linux-gnu" ]; then
40 IPADDR=`hostname -I | cut -f 1 -d ' '`
41 else
42 IPADDR=`ifconfig | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}' | head -1 | tr -d 'adr:'`
43 fi
44
45 docker run --rm \
46 -it \
47 -v ${PWD}:/config \
48 -v ${PWD}/reports:/reports \
49 --name fuzzingclient \
50 crossbario/autobahn-testsuite /bin/sh -c "sh /config/docker_bootstrap.sh $IPADDR; wstest -m fuzzingclient -s /config/fuzzingclient$SKIP_DEFLATE.json"
51 sleep 1
52
53 # send the shutdown command to the PHP echo server
54 wget -O - -q http://127.0.0.1:9001/shutdown
55 fi
56
57
00 <?php
1
2 use GuzzleHttp\Psr7\Response;
3 use Ratchet\RFC6455\Handshake\PermessageDeflateOptions;
4 use Ratchet\RFC6455\Messaging\MessageBuffer;
15 use Ratchet\RFC6455\Messaging\MessageInterface;
26 use Ratchet\RFC6455\Messaging\FrameInterface;
37 use Ratchet\RFC6455\Messaging\Frame;
610
711 $loop = \React\EventLoop\Factory::create();
812
9 $socket = new \React\Socket\Server($loop);
10 $server = new \React\Http\Server($socket);
13 $socket = new \React\Socket\Server('0.0.0.0:9001', $loop);
1114
1215 $closeFrameChecker = new \Ratchet\RFC6455\Messaging\CloseFrameChecker;
13 $negotiator = new \Ratchet\RFC6455\Handshake\ServerNegotiator(new \Ratchet\RFC6455\Handshake\RequestVerifier);
16 $negotiator = new \Ratchet\RFC6455\Handshake\ServerNegotiator(new \Ratchet\RFC6455\Handshake\RequestVerifier, PermessageDeflateOptions::permessageDeflateSupported());
1417
1518 $uException = new \UnderflowException;
1619
17 $server->on('request', function (\React\Http\Request $request, \React\Http\Response $response) use ($negotiator, $closeFrameChecker, $uException) {
18 $psrRequest = new \GuzzleHttp\Psr7\Request($request->getMethod(), $request->getPath(), $request->getHeaders());
1920
20 $negotiatorResponse = $negotiator->handshake($psrRequest);
21 $socket->on('connection', function (React\Socket\ConnectionInterface $connection) use ($negotiator, $closeFrameChecker, $uException, $socket) {
22 $headerComplete = false;
23 $buffer = '';
24 $parser = null;
25 $connection->on('data', function ($data) use ($connection, &$parser, &$headerComplete, &$buffer, $negotiator, $closeFrameChecker, $uException, $socket) {
26 if ($headerComplete) {
27 $parser->onData($data);
28 return;
29 }
2130
22 $response->writeHead(
23 $negotiatorResponse->getStatusCode(),
24 array_merge(
25 $negotiatorResponse->getHeaders(),
26 ["Content-Length" => "0"]
27 )
28 );
31 $buffer .= $data;
32 $parts = explode("\r\n\r\n", $buffer);
33 if (count($parts) < 2) {
34 return;
35 }
36 $headerComplete = true;
37 $psrRequest = \GuzzleHttp\Psr7\parse_request($parts[0] . "\r\n\r\n");
38 $negotiatorResponse = $negotiator->handshake($psrRequest);
2939
30 if ($negotiatorResponse->getStatusCode() !== 101) {
31 $response->end();
32 return;
33 }
40 $negotiatorResponse = $negotiatorResponse->withAddedHeader("Content-Length", "0");
3441
35 $parser = new \Ratchet\RFC6455\Messaging\MessageBuffer($closeFrameChecker, function(MessageInterface $message) use ($response) {
36 $response->write($message->getContents());
37 }, function(FrameInterface $frame) use ($response, &$parser) {
38 switch ($frame->getOpCode()) {
39 case Frame::OP_CLOSE:
40 $response->end($frame->getContents());
41 break;
42 case Frame::OP_PING:
43 $response->write($parser->newFrame($frame->getPayload(), true, Frame::OP_PONG)->getContents());
44 break;
42 if ($negotiatorResponse->getStatusCode() !== 101 && $psrRequest->getUri()->getPath() === '/shutdown') {
43 $connection->end(\GuzzleHttp\Psr7\str(new Response(200, [], 'Shutting down echo server.' . PHP_EOL)));
44 $socket->close();
45 return;
46 };
47
48 $connection->write(\GuzzleHttp\Psr7\str($negotiatorResponse));
49
50 if ($negotiatorResponse->getStatusCode() !== 101) {
51 $connection->end();
52 return;
4553 }
46 }, true, function() use ($uException) {
47 return $uException;
54
55 // there is no need to look through the client requests
56 // we support any valid permessage deflate
57 $deflateOptions = PermessageDeflateOptions::fromRequestOrResponse($psrRequest)[0];
58
59 $parser = new \Ratchet\RFC6455\Messaging\MessageBuffer($closeFrameChecker,
60 function (MessageInterface $message, MessageBuffer $messageBuffer) {
61 $messageBuffer->sendMessage($message->getPayload(), true, $message->isBinary());
62 }, function (FrameInterface $frame) use ($connection, &$parser) {
63 switch ($frame->getOpCode()) {
64 case Frame::OP_CLOSE:
65 $connection->end($frame->getContents());
66 break;
67 case Frame::OP_PING:
68 $connection->write($parser->newFrame($frame->getPayload(), true, Frame::OP_PONG)->getContents());
69 break;
70 }
71 }, true, function () use ($uException) {
72 return $uException;
73 },
74 null,
75 null,
76 [$connection, 'write'],
77 $deflateOptions);
78
79 array_shift($parts);
80 $parser->onData(implode("\r\n\r\n", $parts));
4881 });
49
50 $request->on('data', [$parser, 'onData']);
5182 });
5283
53 $socket->listen(9001, '0.0.0.0');
5484 $loop->run();
0 <?php
1
2 namespace Ratchet\RFC6455\Test\Unit\Handshake;
3
4 use Ratchet\RFC6455\Handshake\PermessageDeflateOptions;
5 use PHPUnit\Framework\TestCase;
6
7 class PermessageDeflateOptionsTest extends TestCase
8 {
9 public static function versionSupportProvider() {
10 return [
11 ['7.0.17', false],
12 ['7.0.18', true],
13 ['7.0.200', true],
14 ['5.6.0', false],
15 ['7.1.3', false],
16 ['7.1.4', true],
17 ['7.1.200', true],
18 ['10.0.0', true]
19 ];
20 }
21
22 /**
23 * @requires function deflate_init
24 * @dataProvider versionSupportProvider
25 */
26 public function testVersionSupport($version, $supported) {
27 $this->assertEquals($supported, PermessageDeflateOptions::permessageDeflateSupported($version));
28 }
29 }
00 <?php
1
12 namespace Ratchet\RFC6455\Test\Unit\Handshake;
3
24 use Ratchet\RFC6455\Handshake\RequestVerifier;
5 use PHPUnit\Framework\TestCase;
36
47 /**
58 * @covers Ratchet\RFC6455\Handshake\RequestVerifier
69 */
7 class RequestVerifierTest extends \PHPUnit_Framework_TestCase {
10 class RequestVerifierTest extends TestCase {
811 /**
912 * @var RequestVerifier
1013 */
00 <?php
1
12 namespace Ratchet\RFC6455\Test\Unit\Handshake;
3
24 use Ratchet\RFC6455\Handshake\ResponseVerifier;
5 use PHPUnit\Framework\TestCase;
36
47 /**
58 * @covers Ratchet\RFC6455\Handshake\ResponseVerifier
69 */
7 class ResponseVerifierTest extends \PHPUnit_Framework_TestCase {
10 class ResponseVerifierTest extends TestCase {
811 /**
912 * @var ResponseVerifier
1013 */
1720 public static function subProtocolsProvider() {
1821 return [
1922 [true, ['a'], ['a']]
20 , [true, ['b', 'a'], ['c', 'd', 'a']]
21 , [false, ['a', 'b', 'c'], ['d']]
23 , [true, ['c', 'd', 'a'], ['a']]
24 , [true, ['c, a', 'd'], ['a']]
2225 , [true, [], []]
2326 , [true, ['a', 'b'], []]
27 , [false, ['c', 'd', 'a'], ['b', 'a']]
28 , [false, ['a', 'b', 'c'], ['d']]
2429 ];
2530 }
2631
2732 /**
2833 * @dataProvider subProtocolsProvider
2934 */
30 public function testVerifySubProtocol($expected, $response, $request) {
31 $this->assertEquals($expected, $this->_v->verifySubProtocol($response, $request));
35 public function testVerifySubProtocol($expected, $request, $response) {
36 $this->assertEquals($expected, $this->_v->verifySubProtocol($request, $response));
3237 }
3338 }
33
44 use Ratchet\RFC6455\Handshake\RequestVerifier;
55 use Ratchet\RFC6455\Handshake\ServerNegotiator;
6
7 class ServerNegotiatorTest extends \PHPUnit_Framework_TestCase
6 use PHPUnit\Framework\TestCase;
7
8 class ServerNegotiatorTest extends TestCase
89 {
910 public function testNoUpgradeRequested() {
1011 $negotiator = new ServerNegotiator(new RequestVerifier());
1819 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
1920 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
2021 Accept-Encoding: gzip, deflate, sdch, br
21 Accept-Language: en-US,en;q=0.8';
22 Accept-Language: en-US,en;q=0.8
23
24 ';
2225
2326 $request = \GuzzleHttp\Psr7\parse_request($requestText);
2427
4548 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
4649 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
4750 Accept-Encoding: gzip, deflate, sdch, br
48 Accept-Language: en-US,en;q=0.8';
51 Accept-Language: en-US,en;q=0.8
52
53 ';
4954
5055 $request = \GuzzleHttp\Psr7\parse_request($requestText);
5156
7075 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
7176 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
7277 Accept-Encoding: gzip, deflate, sdch, br
73 Accept-Language: en-US,en;q=0.8';
78 Accept-Language: en-US,en;q=0.8
79
80 ';
7481
7582 $request = \GuzzleHttp\Psr7\parse_request($requestText);
7683
95102 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
96103 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
97104 Accept-Encoding: gzip, deflate, sdch, br
98 Accept-Language: en-US,en;q=0.8';
105 Accept-Language: en-US,en;q=0.8
106
107 ';
99108
100109 $request = \GuzzleHttp\Psr7\parse_request($requestText);
101110
127136 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
128137 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
129138 Accept-Encoding: gzip, deflate, sdch, br
130 Accept-Language: en-US,en;q=0.8';
139 Accept-Language: en-US,en;q=0.8
140
141 ';
131142
132143 $request = \GuzzleHttp\Psr7\parse_request($requestText);
133144
159170 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
160171 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
161172 Accept-Encoding: gzip, deflate, sdch, br
162 Accept-Language: en-US,en;q=0.8';
173 Accept-Language: en-US,en;q=0.8
174
175 ';
163176
164177 $request = \GuzzleHttp\Psr7\parse_request($requestText);
165178
171184 $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
172185 $this->assertFalse($response->hasHeader('Sec-WebSocket-Protocol'));
173186 }
174 }
187
188 public function testSuggestsAppropriateSubprotocol()
189 {
190 $negotiator = new ServerNegotiator(new RequestVerifier());
191 $negotiator->setStrictSubProtocolCheck(true);
192 $negotiator->setSupportedSubProtocols(['someproto']);
193
194 $requestText = 'GET / HTTP/1.1
195 Host: localhost:8080
196 Connection: Upgrade
197 Upgrade: websocket
198 Sec-WebSocket-Version: 13
199 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36
200 Accept-Encoding: gzip, deflate, br
201 Accept-Language: en-US,en;q=0.9
202 Sec-WebSocket-Key: HGt8eQax7nAOlXUw0/asPQ==
203 Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
204
205 ';
206
207 $request = \GuzzleHttp\Psr7\parse_request($requestText);
208
209 $response = $negotiator->handshake($request);
210
211 $this->assertEquals('1.1', $response->getProtocolVersion());
212 $this->assertEquals(426, $response->getStatusCode());
213 $this->assertEquals('Upgrade', $response->getHeaderLine('Connection'));
214 $this->assertEquals('websocket', $response->getHeaderLine('Upgrade'));
215 $this->assertEquals('someproto', $response->getHeaderLine('Sec-WebSocket-Protocol'));
216 }
217 }
00 <?php
1
12 namespace Ratchet\RFC6455\Test\Unit\Messaging;
3
24 use Ratchet\RFC6455\Messaging\Frame;
5 use PHPUnit\Framework\TestCase;
36
47 /**
58 * @covers Ratchet\RFC6455\Messaging\Frame
69 * @todo getMaskingKey, getPayloadStartingByte don't have tests yet
710 * @todo Could use some clean up in general, I had to rush to fix a bug for a deadline, sorry.
811 */
9 class FrameTest extends \PHPUnit_Framework_TestCase {
12 class FrameTest extends TestCase {
1013 protected $_firstByteFinText = '10000001';
1114
1215 protected $_secondByteMaskedSPL = '11111101';
55 use Ratchet\RFC6455\Messaging\Frame;
66 use Ratchet\RFC6455\Messaging\Message;
77 use Ratchet\RFC6455\Messaging\MessageBuffer;
8
9 class MessageBufferTest extends \PHPUnit_Framework_TestCase
8 use React\EventLoop\Factory;
9 use PHPUnit\Framework\TestCase;
10
11 class MessageBufferTest extends TestCase
1012 {
1113 /**
1214 * This is to test that MessageBuffer can handle a large receive
3537
3638 $this->assertEquals(1000, $messageCount);
3739 }
40
41 public function testProcessingMessagesAsynchronouslyWhileBlockingInMessageHandler() {
42 $loop = Factory::create();
43
44 $frameA = new Frame('a', true, Frame::OP_TEXT);
45 $frameB = new Frame('b', true, Frame::OP_TEXT);
46
47 $bReceived = false;
48
49 $messageBuffer = new MessageBuffer(
50 new CloseFrameChecker(),
51 function (Message $message) use (&$messageCount, &$bReceived, $loop) {
52 $payload = $message->getPayload();
53 $bReceived = $payload === 'b';
54
55 if (!$bReceived) {
56 $loop->run();
57 }
58 },
59 null,
60 false
61 );
62
63 $loop->addPeriodicTimer(0.1, function () use ($messageBuffer, $frameB, $loop) {
64 $loop->stop();
65 $messageBuffer->onData($frameB->getContents());
66 });
67
68 $messageBuffer->onData($frameA->getContents());
69
70 $this->assertTrue($bReceived);
71 }
72
73 public function testInvalidFrameLength() {
74 $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
75
76 $frameRaw = $frame->getContents();
77
78 $frameRaw[1] = "\x7f"; // 127 in the first spot
79
80 $frameRaw[2] = "\xff"; // this will unpack to -1
81 $frameRaw[3] = "\xff";
82 $frameRaw[4] = "\xff";
83 $frameRaw[5] = "\xff";
84 $frameRaw[6] = "\xff";
85 $frameRaw[7] = "\xff";
86 $frameRaw[8] = "\xff";
87 $frameRaw[9] = "\xff";
88
89 /** @var Frame $controlFrame */
90 $controlFrame = null;
91 $messageCount = 0;
92
93 $messageBuffer = new MessageBuffer(
94 new CloseFrameChecker(),
95 function (Message $message) use (&$messageCount) {
96 $messageCount++;
97 },
98 function (Frame $frame) use (&$controlFrame) {
99 $this->assertNull($controlFrame);
100 $controlFrame = $frame;
101 },
102 false,
103 null,
104 0,
105 10
106 );
107
108 $messageBuffer->onData($frameRaw);
109
110 $this->assertEquals(0, $messageCount);
111 $this->assertTrue($controlFrame instanceof Frame);
112 $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
113 $this->assertEquals([Frame::CLOSE_PROTOCOL], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
114
115 }
116
117 public function testFrameLengthTooBig() {
118 $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
119
120 $frameRaw = $frame->getContents();
121
122 $frameRaw[1] = "\x7f"; // 127 in the first spot
123
124 $frameRaw[2] = "\x7f"; // this will unpack to -1
125 $frameRaw[3] = "\xff";
126 $frameRaw[4] = "\xff";
127 $frameRaw[5] = "\xff";
128 $frameRaw[6] = "\xff";
129 $frameRaw[7] = "\xff";
130 $frameRaw[8] = "\xff";
131 $frameRaw[9] = "\xff";
132
133 /** @var Frame $controlFrame */
134 $controlFrame = null;
135 $messageCount = 0;
136
137 $messageBuffer = new MessageBuffer(
138 new CloseFrameChecker(),
139 function (Message $message) use (&$messageCount) {
140 $messageCount++;
141 },
142 function (Frame $frame) use (&$controlFrame) {
143 $this->assertNull($controlFrame);
144 $controlFrame = $frame;
145 },
146 false,
147 null,
148 0,
149 10
150 );
151
152 $messageBuffer->onData($frameRaw);
153
154 $this->assertEquals(0, $messageCount);
155 $this->assertTrue($controlFrame instanceof Frame);
156 $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
157 $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
158 }
159
160 public function testFrameLengthBiggerThanMaxMessagePayload() {
161 $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
162
163 $frameRaw = $frame->getContents();
164
165 /** @var Frame $controlFrame */
166 $controlFrame = null;
167 $messageCount = 0;
168
169 $messageBuffer = new MessageBuffer(
170 new CloseFrameChecker(),
171 function (Message $message) use (&$messageCount) {
172 $messageCount++;
173 },
174 function (Frame $frame) use (&$controlFrame) {
175 $this->assertNull($controlFrame);
176 $controlFrame = $frame;
177 },
178 false,
179 null,
180 100,
181 0
182 );
183
184 $messageBuffer->onData($frameRaw);
185
186 $this->assertEquals(0, $messageCount);
187 $this->assertTrue($controlFrame instanceof Frame);
188 $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
189 $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
190 }
191
192 public function testSecondFrameLengthPushesPastMaxMessagePayload() {
193 $frame = new Frame(str_repeat('a', 200), false, Frame::OP_TEXT);
194 $firstFrameRaw = $frame->getContents();
195 $frame = new Frame(str_repeat('b', 200), true, Frame::OP_TEXT);
196 $secondFrameRaw = $frame->getContents();
197
198 /** @var Frame $controlFrame */
199 $controlFrame = null;
200 $messageCount = 0;
201
202 $messageBuffer = new MessageBuffer(
203 new CloseFrameChecker(),
204 function (Message $message) use (&$messageCount) {
205 $messageCount++;
206 },
207 function (Frame $frame) use (&$controlFrame) {
208 $this->assertNull($controlFrame);
209 $controlFrame = $frame;
210 },
211 false,
212 null,
213 300,
214 0
215 );
216
217 $messageBuffer->onData($firstFrameRaw);
218 // only put part of the second frame in to watch it fail fast
219 $messageBuffer->onData(substr($secondFrameRaw, 0, 150));
220
221 $this->assertEquals(0, $messageCount);
222 $this->assertTrue($controlFrame instanceof Frame);
223 $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
224 $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
225 }
226
227 /**
228 * Some test cases from memory limit inspired by https://github.com/BrandEmbassy/php-memory
229 *
230 * Here is the license for that project:
231 * MIT License
232 *
233 * Copyright (c) 2018 Brand Embassy
234 *
235 * Permission is hereby granted, free of charge, to any person obtaining a copy
236 * of this software and associated documentation files (the "Software"), to deal
237 * in the Software without restriction, including without limitation the rights
238 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
239 * copies of the Software, and to permit persons to whom the Software is
240 * furnished to do so, subject to the following conditions:
241 *
242 * The above copyright notice and this permission notice shall be included in all
243 * copies or substantial portions of the Software.
244 *
245 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
246 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
247 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
248 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
249 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
250 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
251 * SOFTWARE.
252 */
253
254 /**
255 * @dataProvider phpConfigurationProvider
256 *
257 * @param string $phpConfigurationValue
258 * @param int $expectedLimit
259 */
260 public function testMemoryLimits($phpConfigurationValue, $expectedLimit) {
261 $method = new \ReflectionMethod('Ratchet\RFC6455\Messaging\MessageBuffer', 'getMemoryLimit');
262 $method->setAccessible(true);
263 $actualLimit = $method->invoke(null, $phpConfigurationValue);
264
265 $this->assertSame($expectedLimit, $actualLimit);
266 }
267
268 public function phpConfigurationProvider() {
269 return [
270 'without unit type, just bytes' => ['500', 500],
271 '1 GB with big "G"' => ['1G', 1 * 1024 * 1024 * 1024],
272 '128 MB with big "M"' => ['128M', 128 * 1024 * 1024],
273 '128 MB with small "m"' => ['128m', 128 * 1024 * 1024],
274 '24 kB with small "k"' => ['24k', 24 * 1024],
275 '2 GB with small "g"' => ['2g', 2 * 1024 * 1024 * 1024],
276 'unlimited memory' => ['-1', 0],
277 'invalid float value' => ['2.5M', 2 * 1024 * 1024],
278 'empty value' => ['', 0],
279 'invalid ini setting' => ['whatever it takes', 0]
280 ];
281 }
282
283 /**
284 * @expectedException \InvalidArgumentException
285 */
286 public function testInvalidMaxFramePayloadSizes() {
287 $messageBuffer = new MessageBuffer(
288 new CloseFrameChecker(),
289 function (Message $message) {},
290 function (Frame $frame) {},
291 false,
292 null,
293 0,
294 0x8000000000000000
295 );
296 }
297
298 /**
299 * @expectedException \InvalidArgumentException
300 */
301 public function testInvalidMaxMessagePayloadSizes() {
302 $messageBuffer = new MessageBuffer(
303 new CloseFrameChecker(),
304 function (Message $message) {},
305 function (Frame $frame) {},
306 false,
307 null,
308 0x8000000000000000,
309 0
310 );
311 }
312
313 /**
314 * @dataProvider phpConfigurationProvider
315 *
316 * @param string $phpConfigurationValue
317 * @param int $expectedLimit
318 *
319 * @runInSeparateProcess
320 * @requires PHP 7.0
321 */
322 public function testIniSizes($phpConfigurationValue, $expectedLimit) {
323 ini_set('memory_limit', $phpConfigurationValue);
324 $messageBuffer = new MessageBuffer(
325 new CloseFrameChecker(),
326 function (Message $message) {},
327 function (Frame $frame) {},
328 false,
329 null
330 );
331
332 if ($expectedLimit === -1) {
333 $expectedLimit = 0;
334 }
335
336 $prop = new \ReflectionProperty($messageBuffer, 'maxMessagePayloadSize');
337 $prop->setAccessible(true);
338 $this->assertEquals($expectedLimit / 4, $prop->getValue($messageBuffer));
339
340 $prop = new \ReflectionProperty($messageBuffer, 'maxFramePayloadSize');
341 $prop->setAccessible(true);
342 $this->assertEquals($expectedLimit / 4, $prop->getValue($messageBuffer));
343 }
344
345 /**
346 * @runInSeparateProcess
347 * @requires PHP 7.0
348 */
349 public function testInvalidIniSize() {
350 ini_set('memory_limit', 'lots of memory');
351 $messageBuffer = new MessageBuffer(
352 new CloseFrameChecker(),
353 function (Message $message) {},
354 function (Frame $frame) {},
355 false,
356 null
357 );
358
359 $prop = new \ReflectionProperty($messageBuffer, 'maxMessagePayloadSize');
360 $prop->setAccessible(true);
361 $this->assertEquals(0, $prop->getValue($messageBuffer));
362
363 $prop = new \ReflectionProperty($messageBuffer, 'maxFramePayloadSize');
364 $prop->setAccessible(true);
365 $this->assertEquals(0, $prop->getValue($messageBuffer));
366 }
38367 }
00 <?php
1
12 namespace Ratchet\RFC6455\Test\Unit\Messaging;
3
24 use Ratchet\RFC6455\Messaging\Frame;
35 use Ratchet\RFC6455\Messaging\Message;
6 use PHPUnit\Framework\TestCase;
47
58 /**
69 * @covers Ratchet\RFC6455\Messaging\Message
710 */
8 class MessageTest extends \PHPUnit_Framework_TestCase {
11 class MessageTest extends TestCase {
912 /** @var Message */
1013 protected $message;
1114