Skip to content

Commit 75111ed

Browse files
authored
Merge pull request #1138 from joanhey/ws
Refactor Websocket class: simplify input, encode, decode, inflate, an…
2 parents 6c15038 + f957bd0 commit 75111ed

File tree

2 files changed

+166
-224
lines changed

2 files changed

+166
-224
lines changed

src/Protocols/Websocket.php

Lines changed: 102 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,10 @@ class Websocket
7575
*/
7676
const BINARY_TYPE_ARRAYBUFFER_DEFLATE = "\xc2";
7777

78-
/**
79-
* Check the integrity of the package.
80-
*
81-
* @param string $buffer
82-
* @param TcpConnection $connection
83-
* @return int
84-
*/
8578
public static function input(string $buffer, TcpConnection $connection): int
8679
{
87-
$connection->websocketOrigin = $connection->websocketOrigin ?? null;
88-
$connection->websocketClientProtocol = $connection->websocketClientProtocol ?? null;
80+
$connection->websocketOrigin ??= null;
81+
$connection->websocketClientProtocol ??= null;
8982
// Receive length.
9083
$recvLen = strlen($buffer);
9184
// We need more data.
@@ -159,8 +152,7 @@ public static function input(string $buffer, TcpConnection $connection): int
159152
if ($headLen > $recvLen) {
160153
return 0;
161154
}
162-
$pack = unpack('nn/ntotal_len', $buffer);
163-
$dataLen = $pack['total_len'];
155+
$dataLen = unpack('nn/ntotal_len', $buffer)['total_len'];
164156
} else {
165157
if ($dataLen === 127) {
166158
$headLen = 14;
@@ -255,22 +247,13 @@ public static function input(string $buffer, TcpConnection $connection): int
255247
return 0;
256248
}
257249

258-
/**
259-
* Websocket encode.
260-
*
261-
* @param mixed $buffer
262-
* @param TcpConnection $connection
263-
* @return string
264-
*/
265250
public static function encode(mixed $buffer, TcpConnection $connection): string
266251
{
267252
if (!is_scalar($buffer)) {
268253
$buffer = json_encode($buffer, JSON_UNESCAPED_UNICODE);
269254
}
270255

271-
if (empty($connection->websocketType)) {
272-
$connection->websocketType = static::BINARY_TYPE_BLOB;
273-
}
256+
$connection->websocketType ??= static::BINARY_TYPE_BLOB;
274257

275258
if (ord($connection->websocketType) & 64) {
276259
$buffer = static::deflate($connection, $buffer);
@@ -279,15 +262,11 @@ public static function encode(mixed $buffer, TcpConnection $connection): string
279262
$firstByte = $connection->websocketType;
280263
$len = strlen($buffer);
281264

282-
if ($len <= 125) {
283-
$encodeBuffer = $firstByte . chr($len) . $buffer;
284-
} else {
285-
if ($len <= 65535) {
286-
$encodeBuffer = $firstByte . chr(126) . pack("n", $len) . $buffer;
287-
} else {
288-
$encodeBuffer = $firstByte . chr(127) . pack("xxxxN", $len) . $buffer;
289-
}
290-
}
265+
$encodeBuffer = match(true) {
266+
$len <= 125 => $firstByte . chr($len) . $buffer,
267+
$len <= 65535 => $firstByte . chr(126) . pack("n", $len) . $buffer,
268+
default => $firstByte . chr(127) . pack("xxxxN", $len) . $buffer,
269+
};
291270

292271
// Handshake not completed so temporary buffer websocket data waiting for send.
293272
if (empty($connection->context->websocketHandshake)) {
@@ -321,13 +300,6 @@ public static function encode(mixed $buffer, TcpConnection $connection): string
321300
return $encodeBuffer;
322301
}
323302

324-
/**
325-
* Websocket decode.
326-
*
327-
* @param string $buffer
328-
* @param TcpConnection $connection
329-
* @return string
330-
*/
331303
public static function decode(string $buffer, TcpConnection $connection): string
332304
{
333305
$firstByte = ord($buffer[0]);
@@ -336,18 +308,12 @@ public static function decode(string $buffer, TcpConnection $connection): string
336308
$isFinFrame = (bool)($firstByte >> 7);
337309
$rsv1 = 64 === ($firstByte & 64);
338310

339-
if ($len === 126) {
340-
$masks = substr($buffer, 4, 4);
341-
$data = substr($buffer, 8);
342-
} else {
343-
if ($len === 127) {
344-
$masks = substr($buffer, 10, 4);
345-
$data = substr($buffer, 14);
346-
} else {
347-
$masks = substr($buffer, 2, 4);
348-
$data = substr($buffer, 6);
349-
}
350-
}
311+
[$masks, $data] = match(true) {
312+
$len === 126 => [substr($buffer, 4, 4), substr($buffer, 8)],
313+
$len === 127 => [substr($buffer, 10, 4), substr($buffer, 14)],
314+
default => [substr($buffer, 2, 4), substr($buffer, 6)],
315+
};
316+
351317
$dataLength = strlen($data);
352318
$masks = str_repeat($masks, (int)floor($dataLength / 4)) . substr($masks, 0, $dataLength % 4);
353319
$decoded = $data ^ $masks;
@@ -368,18 +334,10 @@ public static function decode(string $buffer, TcpConnection $connection): string
368334
return $decoded;
369335
}
370336

371-
/**
372-
* Inflate.
373-
*
374-
* @param TcpConnection $connection
375-
* @param string $buffer
376-
* @param bool $isFinFrame
377-
* @return false|string
378-
*/
379-
protected static function inflate(TcpConnection $connection, string $buffer, bool $isFinFrame): bool|string
337+
protected static function inflate(TcpConnection $connection, string $buffer, bool $isFinFrame): false|string
380338
{
381-
if (!isset($connection->context->inflator)) {
382-
$connection->context->inflator = inflate_init(
339+
$connection->context->inflator ??=
340+
inflate_init(
383341
ZLIB_ENCODING_RAW,
384342
[
385343
'level' => -1,
@@ -388,7 +346,7 @@ protected static function inflate(TcpConnection $connection, string $buffer, boo
388346
'strategy' => ZLIB_DEFAULT_STRATEGY
389347
]
390348
);
391-
}
349+
392350
if ($isFinFrame) {
393351
$buffer .= "\x00\x00\xff\xff";
394352
}
@@ -402,17 +360,11 @@ protected static function inflate(TcpConnection $connection, string $buffer, boo
402360
return $result;
403361
}
404362

405-
/**
406-
* Deflate.
407-
*
408-
* @param TcpConnection $connection
409-
* @param string $buffer
410-
* @return false|string
411-
*/
412-
protected static function deflate(TcpConnection $connection, string $buffer): bool|string
363+
protected static function deflate(TcpConnection $connection, string $buffer): false|string
413364
{
414-
if (!isset($connection->context->deflator)) {
415-
$connection->context->deflator = deflate_init(
365+
366+
$connection->context->deflator ??=
367+
deflate_init(
416368
ZLIB_ENCODING_RAW,
417369
[
418370
'level' => -1,
@@ -421,74 +373,75 @@ protected static function deflate(TcpConnection $connection, string $buffer): bo
421373
'strategy' => ZLIB_DEFAULT_STRATEGY
422374
]
423375
);
424-
}
376+
425377
return substr(deflate_add($connection->context->deflator, $buffer), 0, -4);
426378
}
427379

428380
/**
429381
* Websocket handshake.
430382
*
431-
* @param string $buffer
432-
* @param TcpConnection $connection
433-
* @return int
434383
*/
435384
public static function dealHandshake(string $buffer, TcpConnection $connection): int
436385
{
437386
// HTTP protocol.
438-
if (str_starts_with($buffer, 'GET')) {
439-
// Find \r\n\r\n.
440-
$headerEndPos = strpos($buffer, "\r\n\r\n");
441-
if (!$headerEndPos) {
442-
return 0;
443-
}
444-
$headerLength = $headerEndPos + 4;
387+
if (!str_starts_with($buffer, 'GET')) {
388+
// Bad websocket handshake request.
389+
$connection->close(
390+
"HTTP/1.1 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>400 Bad Request</h1><hr>workerman</div>", true);
391+
return 0;
392+
}
445393

446-
// Get Sec-WebSocket-Key.
447-
if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
448-
$SecWebSocketKey = $match[1];
449-
} else {
450-
$connection->close(
451-
"HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman</div>", true);
452-
return 0;
453-
}
454-
// Calculation websocket key.
455-
$newKey = base64_encode(sha1($SecWebSocketKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
456-
// Handshake response data.
457-
$handshakeMessage = "HTTP/1.1 101 Switching Protocol\r\n"
458-
. "Upgrade: websocket\r\n"
459-
. "Sec-WebSocket-Version: 13\r\n"
460-
. "Connection: Upgrade\r\n"
461-
. "Sec-WebSocket-Accept: " . $newKey . "\r\n";
462-
463-
// Websocket data buffer.
464-
$connection->context->websocketDataBuffer = '';
465-
// Current websocket frame length.
466-
$connection->context->websocketCurrentFrameLength = 0;
467-
// Current websocket frame data.
468-
$connection->context->websocketCurrentFrameBuffer = '';
469-
// Consume handshake data.
470-
$connection->consumeRecvBuffer($headerLength);
471-
// Request from buffer
472-
$request = new Request($buffer);
473-
474-
// Try to emit onWebSocketConnect callback.
475-
$onWebsocketConnect = $connection->onWebSocketConnect ?? $connection->worker->onWebSocketConnect ?? false;
476-
if ($onWebsocketConnect) {
477-
try {
478-
$onWebsocketConnect($connection, $request);
479-
} catch (Throwable $e) {
480-
Worker::stopAll(250, $e);
481-
}
482-
}
394+
// Find \r\n\r\n.
395+
$headerEndPos = strpos($buffer, "\r\n\r\n");
396+
if (!$headerEndPos) {
397+
return 0;
398+
}
399+
$headerLength = $headerEndPos + 4;
483400

484-
// blob or arraybuffer
485-
if (empty($connection->websocketType)) {
486-
$connection->websocketType = static::BINARY_TYPE_BLOB;
401+
// Get Sec-WebSocket-Key.
402+
if (preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/i", $buffer, $match)) {
403+
$SecWebSocketKey = $match[1];
404+
} else {
405+
$connection->close(
406+
"HTTP/1.1 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>WebSocket</h1><hr>workerman</div>", true);
407+
return 0;
408+
}
409+
// Calculation websocket key.
410+
$newKey = base64_encode(sha1($SecWebSocketKey . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
411+
// Handshake response data.
412+
$handshakeMessage = "HTTP/1.1 101 Switching Protocol\r\n"
413+
. "Upgrade: websocket\r\n"
414+
. "Sec-WebSocket-Version: 13\r\n"
415+
. "Connection: Upgrade\r\n"
416+
. "Sec-WebSocket-Accept: $newKey\r\n";
417+
418+
// Websocket data buffer.
419+
$connection->context->websocketDataBuffer = '';
420+
// Current websocket frame length.
421+
$connection->context->websocketCurrentFrameLength = 0;
422+
// Current websocket frame data.
423+
$connection->context->websocketCurrentFrameBuffer = '';
424+
// Consume handshake data.
425+
$connection->consumeRecvBuffer($headerLength);
426+
// Request from buffer
427+
$request = new Request($buffer);
428+
429+
// Try to emit onWebSocketConnect callback.
430+
$onWebsocketConnect = $connection->onWebSocketConnect ?? $connection->worker->onWebSocketConnect ?? false;
431+
if ($onWebsocketConnect) {
432+
try {
433+
$onWebsocketConnect($connection, $request);
434+
} catch (Throwable $e) {
435+
Worker::stopAll(250, $e);
487436
}
437+
}
488438

489-
$hasServerHeader = false;
439+
// blob or arraybuffer
440+
$connection->websocketType ??= static::BINARY_TYPE_BLOB;
490441

491-
if ($connection->headers) {
442+
$hasServerHeader = false;
443+
444+
if ($connection->headers) {
492445
foreach ($connection->headers as $header) {
493446
if (strpbrk($header, "\r\n") !== false) {
494447
continue;
@@ -498,39 +451,34 @@ public static function dealHandshake(string $buffer, TcpConnection $connection):
498451
}
499452
$handshakeMessage .= "$header\r\n";
500453
}
454+
}
455+
if (!$hasServerHeader) {
456+
$handshakeMessage .= "Server: workerman\r\n";
457+
}
458+
$handshakeMessage .= "\r\n";
459+
// Send handshake response.
460+
$connection->send($handshakeMessage, true);
461+
// Mark handshake complete.
462+
$connection->context->websocketHandshake = true;
463+
464+
// Try to emit onWebSocketConnected callback.
465+
$onWebsocketConnected = $connection->onWebSocketConnected ?? $connection->worker->onWebSocketConnected ?? false;
466+
if ($onWebsocketConnected) {
467+
try {
468+
$onWebsocketConnected($connection, $request);
469+
} catch (Throwable $e) {
470+
Worker::stopAll(250, $e);
501471
}
502-
if (!$hasServerHeader) {
503-
$handshakeMessage .= "Server: workerman\r\n";
504-
}
505-
$handshakeMessage .= "\r\n";
506-
// Send handshake response.
507-
$connection->send($handshakeMessage, true);
508-
// Mark handshake complete.
509-
$connection->context->websocketHandshake = true;
510-
511-
// Try to emit onWebSocketConnected callback.
512-
$onWebsocketConnected = $connection->onWebSocketConnected ?? $connection->worker->onWebSocketConnected ?? false;
513-
if ($onWebsocketConnected) {
514-
try {
515-
$onWebsocketConnected($connection, $request);
516-
} catch (Throwable $e) {
517-
Worker::stopAll(250, $e);
518-
}
519-
}
472+
}
520473

521-
// There are data waiting to be sent.
522-
if (!empty($connection->context->tmpWebsocketData)) {
523-
$connection->send($connection->context->tmpWebsocketData, true);
524-
$connection->context->tmpWebsocketData = '';
525-
}
526-
if (strlen($buffer) > $headerLength) {
527-
return static::input(substr($buffer, $headerLength), $connection);
528-
}
529-
return 0;
474+
// There are data waiting to be sent.
475+
if (!empty($connection->context->tmpWebsocketData)) {
476+
$connection->send($connection->context->tmpWebsocketData, true);
477+
$connection->context->tmpWebsocketData = '';
478+
}
479+
if (strlen($buffer) > $headerLength) {
480+
return static::input(substr($buffer, $headerLength), $connection);
530481
}
531-
// Bad websocket handshake request.
532-
$connection->close(
533-
"HTTP/1.0 400 Bad Request\r\nServer: workerman\r\n\r\n<div style=\"text-align:center\"><h1>400 Bad Request</h1><hr>workerman</div>", true);
534482
return 0;
535483
}
536484
}

0 commit comments

Comments
 (0)