Skip to content

Commit b9816ed

Browse files
authored
Fuzzy header parsing deflate compression (#144)
* Fuzzy header parsing deflate compression * Ensure valid range deflation window size
1 parent 44348ac commit b9816ed

File tree

8 files changed

+111
-5
lines changed

8 files changed

+111
-5
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Please provide a clear and concise description of the suspected issue.
1414
If possible, provide information - possibly including code snippets - on how to reproduce the issue.
1515

1616
**Logs**
17-
If possible, provide logs that indicate the issue. See https://github.com/Textalk/websocket-php/blob/master/docs/Examples.md#logger on how to use a logger.
17+
If possible, provide logs that indicate the issue. See [Examples: Logger](../../docs/Examples.md#logger) on how to use a logger.
1818

1919
**Versions**
2020
* Version of this library

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ and is maintained by Sören Jensen, who has been maintaining the original since
2929
* [Changelog](docs/Changelog.md) - The changelog of this repo
3030
* [Contributing](docs/Contributing.md) - Contributors and requirements
3131
* [Examples](docs/Examples.md) - Examples
32+
33+
## Migration
34+
35+
* [v1 -> v2](docs/Migrate_1_2.md) - How to migrate from v1 to v2
3236
* [v2 -> v3](docs/Migrate_2_3.md) - How to migrate from v2 to v3
3337

3438
## Installing

docs/Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
> PHP version `^8.1`
88
9+
### `3.6.2`
10+
11+
* Deflate compression fuzzy header parsing fix (@sirn-se)
12+
* More documentation (@sirn-se)
13+
914
### `3.6.1`
1015

1116
* Fix typos and broken links (@pieterocp)

docs/Contributing.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,5 @@ make stan
103103
* Dejan Levec
104104
* Pieter Oliver
105105
* Sebastian Hagens
106-
* Adrian Mihai
106+
* Adrian Mihai
107+
* Pieter Oliver

docs/Index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@
2222

2323
## Migration
2424

25+
* [v1 -> v2](Migrate_1_2.md) - How to migrate from v1 to v2
2526
* [v2 -> v3](Migrate_2_3.md) - How to migrate from v2 to v3

docs/Migrate_1_2.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
[Documentation](Index.md) / Migration v1 -> v2
2+
3+
# Websocket: Migration v1 -> v2
4+
5+
Version `2.x` has significant changes compared to previous version.
6+
Amongst other things, it introduces callback listeners, middleware support, and the Server now support multiple connections.
7+
8+
## Constructor and configuration
9+
10+
Where `1.x` was configured by using an array of options on the constructor,
11+
`2.x` provides a number of configuration methods instead.
12+
Replace configuration with a call to corresponding method instead.
13+
14+
```php
15+
new WebSocket\Client(string $uri)
16+
new WebSocket\Server(bool $ssl, int $port)
17+
18+
setLogger(Psr\Log\LoggerInterface $logger) // logger
19+
setTimeout(int $timeout) // timeout
20+
setFrameSize(int $frameSize) // fragment_size
21+
setPersistent(bool $persistent) // persistent
22+
setContext(array $context_as_array) // context
23+
addHeader(string $name, string $value) // headers
24+
```
25+
26+
The `filter` and `return_obj` are no longer valid.
27+
28+
## Middlewares
29+
30+
`2.x` introduces middlewares to extend functionality to Client and Server.
31+
Two of the middlewares cover functionality built-in in `1.x` and should be added unless you create your own code instead.
32+
33+
```php
34+
addMiddleware(new WebSocket\Middleware\CloseHandler())
35+
addMiddleware(new WebSocket\Middleware\PingResponder())
36+
```
37+
38+
## Receiving messages
39+
40+
The Client `receive()` method always return an instance of Message (Text, Binary, Ping, Pong or Close).
41+
This corresponds to setting `return_obj: true` in `1.x` configuration. By default `1.x` returned string.
42+
43+
You are encouraged to read incoming messages using [listener callback methods](https://github.com/sirn-se/websocket-php/blob/2.0.0/docs/Client.md#subscribe-operation) instead.
44+
45+
The Server no longer has the `receive()` method.
46+
Receiving, use [listener callback methods](https://github.com/sirn-se/websocket-php/blob/2.0.0/docs/Server.md#message-listeners).
47+
48+
## Sending messages
49+
50+
The `send(...)` method no longer accepts message and opcode as strings,
51+
but only accepts an instance of Message (Text, Binary, Ping, Pong or Close).
52+
However, there are convenience methods available as `text(...)`, `binary(...)`, `ping(...)`, `pong(...)` and `close(...)`.
53+
54+
## More?
55+
56+
As `2.x` has significant internal code changes, other classes and methods are likely to have changed.

src/Middleware/CompressionExtension/DeflateCompressor.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ public function getConfiguration(string $element, bool $isServer): object
149149
];
150150
foreach (explode(';', $element) as $parameter) {
151151
$parts = explode('=', $parameter);
152-
$key = trim($parts[0]);
152+
$key = trim(array_shift($parts));
153+
$value = array_shift($parts);
153154
// @todo: Error handling when parsing
154155
switch ($key) {
155156
case 'permessage-deflate':
@@ -162,11 +163,11 @@ public function getConfiguration(string $element, bool $isServer): object
162163
$configuration->clientNoContextTakeover = true;
163164
break;
164165
case 'server_max_window_bits':
165-
$bits = intval($parts[1] ?? self::MAX_WINDOW_SIZE);
166+
$bits = $this->intVal($value);
166167
$configuration->serverMaxWindowBits = min($bits, $this->serverMaxWindowBits);
167168
break;
168169
case 'client_max_window_bits':
169-
$bits = intval($parts[1] ?? self::MAX_WINDOW_SIZE);
170+
$bits = $this->intVal($value);
170171
$configuration->clientMaxWindowBits = min($bits, $this->clientMaxWindowBits);
171172
break;
172173
}
@@ -233,4 +234,17 @@ public function decompress(Binary|Text $message, object $configuration): Binary|
233234
$message->setPayload($inflated);
234235
return $message;
235236
}
237+
238+
private function intVal(string|null $input): int
239+
{
240+
if (is_null($input)) {
241+
return self::MAX_WINDOW_SIZE;
242+
}
243+
preg_match('/([1-9][0-9]*)/', $input, $matches);
244+
if (empty($matches)) {
245+
return self::MAX_WINDOW_SIZE;
246+
}
247+
$value = (int)array_shift($matches);
248+
return min(max($value, self::MIN_WINDOW_SIZE), self::MAX_WINDOW_SIZE);
249+
}
236250
}

tests/suites/middleware/DeflateCompressorTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,31 @@ public function testDeflateCompressorClientBitsToHigh(): void
712712
new DeflateCompressor(clientMaxWindowBits: 16);
713713
}
714714

715+
public function testHeaderParsing(): void
716+
{
717+
$compressor = new DeflateCompressor();
718+
719+
$header = 'client_max_window_bits=10;server_max_window_bits=11;';
720+
$configuration = $compressor->getConfiguration($header, false);
721+
$this->assertSame(10, $configuration->clientMaxWindowBits);
722+
$this->assertSame(11, $configuration->serverMaxWindowBits);
723+
724+
$header = ' client_max_window_bits = "10" ; server_max_window_bits=\'11\' ; ';
725+
$configuration = $compressor->getConfiguration($header, false);
726+
$this->assertSame(10, $configuration->clientMaxWindowBits);
727+
$this->assertSame(11, $configuration->serverMaxWindowBits);
728+
729+
$header = 'client_max_window_bits; server_max_window_bits=invalid;';
730+
$configuration = $compressor->getConfiguration($header, false);
731+
$this->assertSame(15, $configuration->clientMaxWindowBits);
732+
$this->assertSame(15, $configuration->serverMaxWindowBits);
733+
734+
$header = 'client_max_window_bits=8;server_max_window_bits=16;';
735+
$configuration = $compressor->getConfiguration($header, false);
736+
$this->assertSame(9, $configuration->clientMaxWindowBits);
737+
$this->assertSame(15, $configuration->serverMaxWindowBits);
738+
}
739+
715740

716741
/**
717742
* PhpStan cannot resolve otherwise

0 commit comments

Comments
 (0)