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\Test\Unit\Messaging;
4
 
5
use Ratchet\RFC6455\Messaging\CloseFrameChecker;
6
use Ratchet\RFC6455\Messaging\Frame;
7
use Ratchet\RFC6455\Messaging\Message;
8
use Ratchet\RFC6455\Messaging\MessageBuffer;
9
use React\EventLoop\Factory;
10
use PHPUnit\Framework\TestCase;
11
 
12
class MessageBufferTest extends TestCase
13
{
14
    /**
15
     * This is to test that MessageBuffer can handle a large receive
16
     * buffer with many many frames without blowing the stack (pre-v0.4 issue)
17
     */
18
    public function testProcessingLotsOfFramesInASingleChunk() {
19
        $frame = new Frame('a', true, Frame::OP_TEXT);
20
 
21
        $frameRaw = $frame->getContents();
22
 
23
        $data = str_repeat($frameRaw, 1000);
24
 
25
        $messageCount = 0;
26
 
27
        $messageBuffer = new MessageBuffer(
28
            new CloseFrameChecker(),
29
            function (Message $message) use (&$messageCount) {
30
                $messageCount++;
31
                $this->assertEquals('a', $message->getPayload());
32
            },
33
            null,
34
            false
35
        );
36
 
37
        $messageBuffer->onData($data);
38
 
39
        $this->assertEquals(1000, $messageCount);
40
    }
41
 
42
    public function testProcessingMessagesAsynchronouslyWhileBlockingInMessageHandler() {
43
        $loop = Factory::create();
44
 
45
        $frameA = new Frame('a', true, Frame::OP_TEXT);
46
        $frameB = new Frame('b', true, Frame::OP_TEXT);
47
 
48
        $bReceived = false;
49
 
50
        $messageBuffer = new MessageBuffer(
51
            new CloseFrameChecker(),
52
            function (Message $message) use (&$messageCount, &$bReceived, $loop) {
53
                $payload = $message->getPayload();
54
                $bReceived = $payload === 'b';
55
 
56
                if (!$bReceived) {
57
                    $loop->run();
58
                }
59
            },
60
            null,
61
            false
62
        );
63
 
64
        $loop->addPeriodicTimer(0.1, function () use ($messageBuffer, $frameB, $loop) {
65
            $loop->stop();
66
            $messageBuffer->onData($frameB->getContents());
67
        });
68
 
69
        $messageBuffer->onData($frameA->getContents());
70
 
71
        $this->assertTrue($bReceived);
72
    }
73
 
74
    public function testInvalidFrameLength() {
75
        $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
76
 
77
        $frameRaw = $frame->getContents();
78
 
79
        $frameRaw[1] = "\x7f"; // 127 in the first spot
80
 
81
        $frameRaw[2] = "\xff"; // this will unpack to -1
82
        $frameRaw[3] = "\xff";
83
        $frameRaw[4] = "\xff";
84
        $frameRaw[5] = "\xff";
85
        $frameRaw[6] = "\xff";
86
        $frameRaw[7] = "\xff";
87
        $frameRaw[8] = "\xff";
88
        $frameRaw[9] = "\xff";
89
 
90
        /** @var Frame $controlFrame */
91
        $controlFrame = null;
92
        $messageCount = 0;
93
 
94
        $messageBuffer = new MessageBuffer(
95
            new CloseFrameChecker(),
96
            function (Message $message) use (&$messageCount) {
97
                $messageCount++;
98
            },
99
            function (Frame $frame) use (&$controlFrame) {
100
                $this->assertNull($controlFrame);
101
                $controlFrame = $frame;
102
            },
103
            false,
104
            null,
105
            0,
106
            10
107
        );
108
 
109
        $messageBuffer->onData($frameRaw);
110
 
111
        $this->assertEquals(0, $messageCount);
112
        $this->assertTrue($controlFrame instanceof Frame);
113
        $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
114
        $this->assertEquals([Frame::CLOSE_PROTOCOL], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
115
 
116
    }
117
 
118
    public function testFrameLengthTooBig() {
119
        $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
120
 
121
        $frameRaw = $frame->getContents();
122
 
123
        $frameRaw[1] = "\x7f"; // 127 in the first spot
124
 
125
        $frameRaw[2] = "\x7f"; // this will unpack to -1
126
        $frameRaw[3] = "\xff";
127
        $frameRaw[4] = "\xff";
128
        $frameRaw[5] = "\xff";
129
        $frameRaw[6] = "\xff";
130
        $frameRaw[7] = "\xff";
131
        $frameRaw[8] = "\xff";
132
        $frameRaw[9] = "\xff";
133
 
134
        /** @var Frame $controlFrame */
135
        $controlFrame = null;
136
        $messageCount = 0;
137
 
138
        $messageBuffer = new MessageBuffer(
139
            new CloseFrameChecker(),
140
            function (Message $message) use (&$messageCount) {
141
                $messageCount++;
142
            },
143
            function (Frame $frame) use (&$controlFrame) {
144
                $this->assertNull($controlFrame);
145
                $controlFrame = $frame;
146
            },
147
            false,
148
            null,
149
            0,
150
            10
151
        );
152
 
153
        $messageBuffer->onData($frameRaw);
154
 
155
        $this->assertEquals(0, $messageCount);
156
        $this->assertTrue($controlFrame instanceof Frame);
157
        $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
158
        $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
159
    }
160
 
161
    public function testFrameLengthBiggerThanMaxMessagePayload() {
162
        $frame = new Frame(str_repeat('a', 200), true, Frame::OP_TEXT);
163
 
164
        $frameRaw = $frame->getContents();
165
 
166
        /** @var Frame $controlFrame */
167
        $controlFrame = null;
168
        $messageCount = 0;
169
 
170
        $messageBuffer = new MessageBuffer(
171
            new CloseFrameChecker(),
172
            function (Message $message) use (&$messageCount) {
173
                $messageCount++;
174
            },
175
            function (Frame $frame) use (&$controlFrame) {
176
                $this->assertNull($controlFrame);
177
                $controlFrame = $frame;
178
            },
179
            false,
180
            null,
181
            100,
182
 
183
        );
184
 
185
        $messageBuffer->onData($frameRaw);
186
 
187
        $this->assertEquals(0, $messageCount);
188
        $this->assertTrue($controlFrame instanceof Frame);
189
        $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
190
        $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
191
    }
192
 
193
    public function testSecondFrameLengthPushesPastMaxMessagePayload() {
194
        $frame = new Frame(str_repeat('a', 200), false, Frame::OP_TEXT);
195
        $firstFrameRaw = $frame->getContents();
196
        $frame = new Frame(str_repeat('b', 200), true, Frame::OP_TEXT);
197
        $secondFrameRaw = $frame->getContents();
198
 
199
        /** @var Frame $controlFrame */
200
        $controlFrame = null;
201
        $messageCount = 0;
202
 
203
        $messageBuffer = new MessageBuffer(
204
            new CloseFrameChecker(),
205
            function (Message $message) use (&$messageCount) {
206
                $messageCount++;
207
            },
208
            function (Frame $frame) use (&$controlFrame) {
209
                $this->assertNull($controlFrame);
210
                $controlFrame = $frame;
211
            },
212
            false,
213
            null,
214
            300,
215
 
216
        );
217
 
218
        $messageBuffer->onData($firstFrameRaw);
219
        // only put part of the second frame in to watch it fail fast
220
        $messageBuffer->onData(substr($secondFrameRaw, 0, 150));
221
 
222
        $this->assertEquals(0, $messageCount);
223
        $this->assertTrue($controlFrame instanceof Frame);
224
        $this->assertEquals(Frame::OP_CLOSE, $controlFrame->getOpcode());
225
        $this->assertEquals([Frame::CLOSE_TOO_BIG], array_merge(unpack('n*', substr($controlFrame->getPayload(), 0, 2))));
226
    }
227
 
228
    /**
229
     * Some test cases from memory limit inspired by https://github.com/BrandEmbassy/php-memory
230
     *
231
     * Here is the license for that project:
232
     * MIT License
233
     *
234
     * Copyright (c) 2018 Brand Embassy
235
     *
236
     * Permission is hereby granted, free of charge, to any person obtaining a copy
237
     * of this software and associated documentation files (the "Software"), to deal
238
     * in the Software without restriction, including without limitation the rights
239
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
240
     * copies of the Software, and to permit persons to whom the Software is
241
     * furnished to do so, subject to the following conditions:
242
     *
243
     * The above copyright notice and this permission notice shall be included in all
244
     * copies or substantial portions of the Software.
245
     *
246
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
247
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
248
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
249
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
250
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
251
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
252
     * SOFTWARE.
253
     */
254
 
255
    /**
256
     * @dataProvider phpConfigurationProvider
257
     *
258
     * @param string $phpConfigurationValue
259
     * @param int $expectedLimit
260
     */
261
    public function testMemoryLimits($phpConfigurationValue, $expectedLimit) {
262
        $method = new \ReflectionMethod('Ratchet\RFC6455\Messaging\MessageBuffer', 'getMemoryLimit');
263
        $method->setAccessible(true);
264
        $actualLimit = $method->invoke(null, $phpConfigurationValue);
265
 
266
        $this->assertSame($expectedLimit, $actualLimit);
267
    }
268
 
269
    public function phpConfigurationProvider() {
270
        return [
271
            'without unit type, just bytes' => ['500', 500],
272
            '1 GB with big "G"' => ['1G', 1 * 1024 * 1024 * 1024],
273
            '128 MB with big "M"' => ['128M', 128 * 1024 * 1024],
274
            '128 MB with small "m"' => ['128m', 128 * 1024 * 1024],
275
            '24 kB with small "k"' => ['24k', 24 * 1024],
276
            '2 GB with small "g"' => ['2g', 2 * 1024 * 1024 * 1024],
277
            'unlimited memory' => ['-1', 0],
278
            'invalid float value' => ['2.5M', 2 * 1024 * 1024],
279
            'empty value' => ['', 0],
280
            'invalid ini setting' => ['whatever it takes', 0]
281
        ];
282
    }
283
 
284
    /**
285
     * @expectedException \InvalidArgumentException
286
     */
287
    public function testInvalidMaxFramePayloadSizes() {
288
        $messageBuffer = new MessageBuffer(
289
            new CloseFrameChecker(),
290
            function (Message $message) {},
291
            function (Frame $frame) {},
292
            false,
293
            null,
294
            0,
295
            0x8000000000000000
296
        );
297
    }
298
 
299
    /**
300
     * @expectedException \InvalidArgumentException
301
     */
302
    public function testInvalidMaxMessagePayloadSizes() {
303
        $messageBuffer = new MessageBuffer(
304
            new CloseFrameChecker(),
305
            function (Message $message) {},
306
            function (Frame $frame) {},
307
            false,
308
            null,
309
            0x8000000000000000,
310
 
311
        );
312
    }
313
 
314
    /**
315
     * @dataProvider phpConfigurationProvider
316
     *
317
     * @param string $phpConfigurationValue
318
     * @param int $expectedLimit
319
     *
320
     * @runInSeparateProcess
321
     * @requires PHP 7.0
322
     */
323
    public function testIniSizes($phpConfigurationValue, $expectedLimit) {
324
        ini_set('memory_limit', $phpConfigurationValue);
325
        $messageBuffer = new MessageBuffer(
326
            new CloseFrameChecker(),
327
            function (Message $message) {},
328
            function (Frame $frame) {},
329
            false,
330
            null
331
        );
332
 
333
        if ($expectedLimit === -1) {
334
            $expectedLimit = 0;
335
        }
336
 
337
        $prop = new \ReflectionProperty($messageBuffer, 'maxMessagePayloadSize');
338
        $prop->setAccessible(true);
339
        $this->assertEquals($expectedLimit / 4, $prop->getValue($messageBuffer));
340
 
341
        $prop = new \ReflectionProperty($messageBuffer, 'maxFramePayloadSize');
342
        $prop->setAccessible(true);
343
        $this->assertEquals($expectedLimit / 4, $prop->getValue($messageBuffer));
344
    }
345
 
346
    /**
347
     * @runInSeparateProcess
348
     * @requires PHP 7.0
349
     */
350
    public function testInvalidIniSize() {
351
        ini_set('memory_limit', 'lots of memory');
352
        $messageBuffer = new MessageBuffer(
353
            new CloseFrameChecker(),
354
            function (Message $message) {},
355
            function (Frame $frame) {},
356
            false,
357
            null
358
        );
359
 
360
        $prop = new \ReflectionProperty($messageBuffer, 'maxMessagePayloadSize');
361
        $prop->setAccessible(true);
362
        $this->assertEquals(0, $prop->getValue($messageBuffer));
363
 
364
        $prop = new \ReflectionProperty($messageBuffer, 'maxFramePayloadSize');
365
        $prop->setAccessible(true);
366
        $this->assertEquals(0, $prop->getValue($messageBuffer));
367
    }
368
}