Skip to content

Commit 716f96d

Browse files
authored
Merge pull request #117 from sirn-se/v3.4-ready
Examples & Documentation
2 parents 0eb2335 + 296e39a commit 716f96d

File tree

10 files changed

+225
-43
lines changed

10 files changed

+225
-43
lines changed

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,8 @@
3939
"phrity/net-mock": "^2.2",
4040
"phrity/util-errorhandler": "^1.1",
4141
"squizlabs/php_codesniffer": "^3.5"
42+
},
43+
"suggests": {
44+
"ext-zlib": "Required for per-message deflate compression"
4245
}
4346
}

docs/Changelog.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
# Websocket: Changelog
44

5+
## `v3.4`
6+
7+
> PHP version `^8.1`
8+
9+
### `3.4.0`
10+
11+
* Compression Extension as optional middleware (@sirn-se)
12+
* Per-Message Deflate as Compression implementation (@sirn-se)
13+
* Example: Server using SSL certificate capture (@marki555)
14+
515
## `v3.3`
616

717
> PHP version `^8.1`
@@ -268,24 +278,24 @@
268278

269279
> PHP version `^7.1`
270280
271-
#### `1.4.3`
281+
### `1.4.3`
272282

273283
* Solve stream closure/get meta conflict (@sirn-se)
274284
* Examples and documentation overhaul (@sirn-se)
275285

276-
#### `1.4.2`
286+
### `1.4.2`
277287

278288
* Force stream close on read error (@sirn-se)
279289
* Authorization headers line feed (@sirn-se)
280290
* Documentation (@matias-pool, @sirn-se)
281291

282-
#### `1.4.1`
292+
### `1.4.1`
283293

284294
* Ping/Pong, handled internally to avoid breaking fragmented messages (@nshmyrev, @sirn-se)
285295
* Fix for persistent connections (@rmeisler)
286296
* Fix opcode bitmask (@peterjah)
287297

288-
#### `1.4.0`
298+
### `1.4.0`
289299

290300
* Dropped support of old PHP versions (@sirn-se)
291301
* Added PSR-3 Logging support (@sirn-se)
@@ -296,12 +306,12 @@
296306

297307
> PHP version `^5.4` and `^7.0`
298308
299-
#### `1.3.1`
309+
### `1.3.1`
300310

301311
* Allow control messages without payload (@Logioniz)
302312
* Error code in ConnectionException (@sirn-se)
303313

304-
#### `1.3.0`
314+
### `1.3.0`
305315

306316
* Implements ping/pong frames (@pmccarren @Logioniz)
307317
* Close behaviour (@sirn-se)
@@ -312,43 +322,43 @@
312322

313323
> PHP version `^5.4` and `^7.0`
314324
315-
#### `1.2.0`
325+
### `1.2.0`
316326

317327
* Adding stream context options (to set e.g. SSL `allow_self_signed`).
318328

319329
## `v1.1`
320330

321331
> PHP version `^5.4` and `^7.0`
322332
323-
#### `1.1.2`
333+
### `1.1.2`
324334

325335
* Fixed error message on broken frame.
326336

327-
#### `1.1.1`
337+
### `1.1.1`
328338

329339
* Adding license information.
330340

331-
#### `1.1.0`
341+
### `1.1.0`
332342

333343
* Supporting huge payloads.
334344

335345
## `v1.0`
336346

337347
> PHP version `^5.4` and `^7.0`
338348
339-
#### `1.0.3`
349+
### `1.0.3`
340350

341351
* Bugfix: Correcting address in error-message
342352

343-
#### `1.0.2`
353+
### `1.0.2`
344354

345355
* Bugfix: Add port in request-header.
346356

347-
#### `1.0.1`
357+
### `1.0.1`
348358

349359
* Fixing a bug from empty payloads.
350360

351-
#### `1.0.0`
361+
### `1.0.0`
352362

353363
* Release as production ready.
354364
* Adding option to set/override headers.

docs/Examples.md

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,27 @@ info | Sent 'close' message []
3535
info | Received 'close' message []
3636
```
3737

38-
## A self-resuming continuous subscription Client
38+
## Self-resuming continuous subscription Client
3939

4040
This setup will create Client that sends initial message to Server,
4141
and then subscribes to messages sent by Server.
4242
The `PingInterval` (possibly change interval) will keep connection open.
4343
If something goes wrong, it will in most cases be able to re-connect and resume subscription.
4444

4545
```php
46-
use Psr\Http\Message\ResponseInterface;
46+
use Psr\Http\Message\{
47+
ResponseInterface,
48+
RequestInterface,
49+
};
4750
use WebSocket\Client;
4851
use WebSocket\Connection;
49-
use WebSocket\Connection;
50-
use WebSocket\Exception\Exception;
51-
use WebSocket\Message\Message;
52-
use WebSocket\Middleware\CloseHandler;
53-
use WebSocket\Middleware\PingResponder;
54-
use WebSocket\Middleware\PingInterval;
52+
use WebSocket\Exception\ExceptionInterface;
53+
use WebSocket\Message\Text;
54+
use WebSocket\Middleware\{
55+
CloseHandler,
56+
PingResponder,
57+
PingInterval,
58+
};
5559

5660
// Create client
5761
$client = new Client("wss://echo.websocket.org/");
@@ -61,18 +65,20 @@ $client
6165
->addMiddleware(new PingResponder())
6266
// Add ping interval middleware as heartbeat to keep connection open
6367
->addMiddleware(new PingInterval(interval: 30))
68+
// Timeout should have same (or lower) value as interval
69+
->setTimeout(30)
6470
->onHandshake(function (Client $client, Connection $connection, RequestInterface $request, ResponseInterface $response) {
6571
// Initial message, typically some authorization or configuration
6672
// This will be called everytime the client connect or reconnect
6773
$client->text($initial_message);
6874
})
69-
->onText(function (Client $client, Connection $connection, Message $message) {
75+
->onText(function (Client $client, Connection $connection, Text $message) {
7076
// Act on incoming message
7177
$message->getContent();
7278
// Possibly respond to server
7379
$client->text($some_message);
7480
})
75-
->onError(function (Client $client, Connection|null $connection, Exception $exception) {
81+
->onError(function (Client $client, Connection|null $connection, ExceptionInterface $exception) {
7682
// Act on exception
7783
if (!$client->isRunning()) {
7884
// Re-start if not running - will reconnect if necessary
@@ -84,23 +90,86 @@ $client
8490
;
8591
```
8692

93+
## Server using SSL certificate capture
94+
95+
By setting `capture_peer_cert` the Server will capture SSL certificates.
96+
Example code set context on Server and verify the certificate during Handshake.
97+
98+
Example provided by [marki555](https://github.com/marki555).
99+
100+
```php
101+
use Psr\Http\Message\{
102+
ResponseInterface,
103+
RequestInterface,
104+
};
105+
use WebSocket\Connection;
106+
use WebSocket\Exception\CloseException;
107+
use WebSocket\Message\Text;
108+
use WebSocket\Middleware\{
109+
CloseHandler,
110+
PingResponder,
111+
};
112+
use WebSocket\Server;
113+
114+
$port = 443; // Port to use
115+
$logger = new Logger(); // PSR-3 Logger to use
116+
117+
// Create server
118+
$server = new Server(port: $port, ssl: true);
119+
$server
120+
// Set up SSL with CA certificate
121+
->setContext(['ssl' => [
122+
'local_cert' => 'certs/CA/acs-ws-server.crt',
123+
'local_pk' => 'certs/CA/acs-ws-server.key',
124+
'cafile' => 'certs/CA/ca.crt',
125+
'verify_peer' => true, // if false, accept SSL handshake without client certificate
126+
'verify_peer_name' => false,
127+
'allow_self_signed' => false,
128+
'capture_peer_cert' => true,
129+
]])
130+
// Add standard middlewares
131+
->addMiddleware(new CloseHandler())
132+
->addMiddleware(new PingResponder())
133+
// Set logger
134+
->setLogger($logger)
135+
// Resolve cerificate during Handshake
136+
->onHandshake(function (Server $server, Connection $connection, RequestInterface $request, ResponseInterface $response) use ($logger) {
137+
$context = $connection->getContext();
138+
$certificate = $context->getOption('ssl','peer_certificate');
139+
$cn = openssl_x509_parse($certificate)['subject']['CN'];
140+
$user = myUserVerification($cn); // Verify user using yout own code
141+
if (!$user) {
142+
$logger->warning("[{$conn->getRemoteName()}] Client authentication failed, unknown CN: {$cn}");
143+
throw new CloseException(1008, Client Auth failed'); // CLOSE_POLICY_VIOLATION
144+
}
145+
$logger->info("[{$conn->getRemoteName()}] Client authenticated as '{$user}'");
146+
$connection->setMeta('user', $user); // Store for later use
147+
})
148+
->onText(function (Server $server, Connection $connection, Text $message) use ($logger) {
149+
$logger->info("[{$conn->getMeta('user')}] Rcvd: {$message->getContent()}");
150+
})
151+
// Start listening to incoming traffic
152+
->start()
153+
;
154+
```
155+
87156
## The `send` client
88157

89-
Source: [examples/send.php](../examples/send.php)
158+
Source: [examples/send.php](https://github.com/sirn-se/websocket-php/blob/v3.4-main/examples/send.php)
90159

91160
A simple, single send/receive client.
92161

93162
Example use:
94163
```bash
95164
php examples/send.php --opcode text "A text message" # Send a text message to localhost
96165
php examples/send.php --opcode ping "ping it" # Send a ping message to localhost
97-
php examples/send.php --uri ws://echo.websocket.org "A text message" # Send a text message to echo.websocket.org
166+
php examples/send.php --uri wss://echo.websocket.org "A text message" # Send a text message to echo.websocket.org
98167
php examples/send.php --opcode text --debug "A text message" # Use runtime debugging
99168
```
100169

101170
## The `echoserver` server
102171

103-
Source: [examples/echoserver.php](../examples/echoserver.php)
172+
Source: [examples/echoserver.php](https://github.com/sirn-se/websocket-php/blob/v3.4-main/examples/echoserver.php)
104173

105174
A simple server that responds to received commands.
106175

@@ -125,20 +194,20 @@ These strings can be sent as message to trigger server to perform actions;
125194

126195
## The `random` client
127196

128-
Source: [examples/random_client.php](../examples/random_client.php)
197+
Source: [examples/random_client.php](https://github.com/sirn-se/websocket-php/blob/v3.4-main/examples/random_client.php)
129198

130199
The random client will use random options and continuously send/receive random messages.
131200

132201
Example use:
133202
```bash
134-
php examples/random_client.php --uri ws://echo.websocket.org # Connect to echo.websocket.org
203+
php examples/random_client.php --uri wss://echo.websocket.org # Connect to echo.websocket.org
135204
php examples/random_client.php --timeout 5 --framesize 16 # Specify settings
136205
php examples/random_client.php --debug # Use runtime debugging
137206
```
138207

139208
## The `random` server
140209

141-
Source: [examples/random_server.php](../examples/random_server.php)
210+
Source: [examples/random_server.php](https://github.com/sirn-se/websocket-php/blob/v3.4-main/examples/random_server.php)
142211

143212
The random server will use random options and continuously send/receive random messages.
144213

@@ -151,7 +220,7 @@ php examples/random_server.php --debug # Use runtime debugging
151220

152221
## The `delegating` server
153222

154-
Source: [examples/delegating_server.php](../examples/delegating_server.php)
223+
Source: [examples/delegating_server.php](https://github.com/sirn-se/websocket-php/blob/v3.4-main/examples/delegating_server.php)
155224

156225
By using context switching, the server will act as a proxy and forward incoming messages to a remote server.
157226
Please note that switching between listening context can appear slow.

docs/Middleware.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and should be added unless you write your own implementation of close and ping/p
3232
These middlewares are included in library and can be added to provide additional functionality.
3333

3434
* [Callback](Middleware/Callback.md) - Apply provided callback function on specified actions
35+
* [CompressionExtension](Middleware/CompressionExtension.md) - Support message compression (Per-Message Deflate)
3536
* [FollowRedirect](Middleware/FollowRedirect.md) - Follow redirect during handshake (Client only)
3637
* [PingInterval](Middleware/PingInterval.md) - Used to automatically send Ping messages at specified interval
3738
* [SubprotocolNegotiation](Middleware/SubprotocolNegotiation.md) - Helper middleware that negotiate subprotocol
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[Documentation](../Index.md) / [Middleware](../Middleware.md) / CompressionExtension
2+
3+
# Websocket: CompressionExtension middleware
4+
5+
This middlewares is included in library and can be added to provide additional functionality.
6+
7+
Add support for message compression.
8+
Each supported compression method is added as argument.
9+
Client will request compression method according to order added.
10+
Server will then respond with the first compression method it can match.
11+
If Client and Server can not agree on a compression method, messages will not be compressed.
12+
13+
## DeflateCompressor compression method
14+
15+
The [permessage-deflate](https://datatracker.ietf.org/doc/html/rfc7692#section-7) compression extension is available in library.
16+
Add it as argument to the CompressionExtension middleware to deflate compression support.
17+
18+
```php
19+
$client_or_server->addMiddleware(new WebSocket\Middleware\CompressionExtension(
20+
new WebSocket\Middleware\CompressionExtension\DeflateCompressor()
21+
));
22+
```
23+
24+
### Configuration
25+
26+
The compressor support four optional arguments specifying compression options.
27+
28+
```php
29+
$deflateCompressor = new WebSocket\Middleware\CompressionExtension\DeflateCompressor(
30+
serverNoContextTakeover: false, // bool - Server disables context takeover
31+
clientNoContextTakeover: false, // bool - Client disables context takeover
32+
serverMaxWindowBits: 15, // int<8, 15> - Maximum bits for LZ77 sliding window used by Server
33+
clientMaxWindowBits: 15, // int<8, 15> - Maximum bits for LZ77 sliding window used by Client
34+
);
35+
```
36+
37+
### Priority
38+
39+
It is possible to add multiple DeflateCompressor instances with different configuration.
40+
Client will request compression method according to order added.
41+
Server will then respond with the first compression method it can match.
42+
43+
```php
44+
$client->addMiddleware(new WebSocket\Middleware\CompressionExtension(
45+
new WebSocket\Middleware\CompressionExtension\DeflateCompressor(
46+
serverNoContextTakeover: true,
47+
clientNoContextTakeover: true,
48+
),
49+
new WebSocket\Middleware\CompressionExtension\DeflateCompressor(),
50+
));
51+
```
52+
53+
In the example, Client tells Server it prefers that context takeover is disabled.
54+
But if Server do not support this configuration, it will fallback to default settings.

0 commit comments

Comments
 (0)