Skip to content

Commit 2a542a8

Browse files
authored
Fix size 65536 HybiFrame (#24)
* fix size 65536 HybiFrame + nitpicks off-by-1: previously, hybiframes with a 65536 bytes payload would generate a 16 bit size header signaling 0 bytes (pack("n", 65536) => "\x00\x00"), instead of using a 64bit size header. nit: composer.json php version had not been updated since 8.0.2 nit: instead of is_int && in_array, we can use in_array's strict mode. nit: $this->offset_mask and $this->offset_payload were initialized-to-null in constructor and encode, then written internally in getPayloadOffset, then written in encode() again. nit: use pack('J') instead of manually constructing big_endian_u64 (undocumented funfact, J is not available in 32bit php: php/doc-en#4644 , that may be why the original code did not use J) nit: use random_bytes instead of openssl_random_pseudo_bytes(), the original code was written for php5 where random_bytes did not exist. nit: getInitialLength() was calculating the result twice internally. * StyleCI * PR feedback https://github.com/chrome-php/wrench/pull/24/files#r2070000317 and https://github.com/chrome-php/wrench/pull/24/files#r2069999223
1 parent 10cd90c commit 2a542a8

1 file changed

Lines changed: 19 additions & 20 deletions

File tree

src/Frame/HybiFrame.php

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,58 +69,60 @@ class HybiFrame extends Frame
6969
*/
7070
public function encode(string $payload, int $type = Protocol::TYPE_TEXT, bool $masked = false): Frame
7171
{
72-
if (!\is_int($type) || !\in_array($type, Protocol::FRAME_TYPES)) {
72+
if (!\in_array($type, Protocol::FRAME_TYPES, true)) {
7373
throw new InvalidArgumentException('Invalid frame type');
7474
}
7575

7676
$this->type = $type;
7777
$this->masked = $masked;
7878
$this->payload = $payload;
7979
$this->length = \strlen($this->payload);
80-
$this->offset_mask = null;
81-
$this->offset_payload = null;
8280

8381
$this->buffer = "\x00\x00";
8482

83+
// FIN + opcode byte
8584
$this->buffer[self::BYTE_HEADER] = \chr(
8685
(self::BITFIELD_TYPE & $this->type)
87-
| (self::BITFIELD_FINAL & \PHP_INT_MAX)
86+
| self::BITFIELD_FINAL
8887
);
8988

90-
$masked_bit = (self::BITFIELD_MASKED & ($this->masked ? \PHP_INT_MAX : 0));
89+
$masked_bit = $this->masked ? self::BITFIELD_MASKED : 0;
9190

9291
if ($this->length <= 125) {
9392
$this->buffer[self::BYTE_INITIAL_LENGTH] = \chr(
94-
(self::BITFIELD_INITIAL_LENGTH & $this->length) | $masked_bit
93+
($this->length & self::BITFIELD_INITIAL_LENGTH)
94+
| $masked_bit
9595
);
96-
} elseif ($this->length <= 65536) {
96+
} elseif ($this->length <= 65535) {
9797
$this->buffer[self::BYTE_INITIAL_LENGTH] = \chr(
98-
(self::BITFIELD_INITIAL_LENGTH & 126) | $masked_bit
98+
(126 & self::BITFIELD_INITIAL_LENGTH)
99+
| $masked_bit
99100
);
100101
$this->buffer .= \pack('n', $this->length);
101102
} else {
102103
$this->buffer[self::BYTE_INITIAL_LENGTH] = \chr(
103-
(self::BITFIELD_INITIAL_LENGTH & 127) | $masked_bit
104+
(127 & self::BITFIELD_INITIAL_LENGTH)
105+
| $masked_bit
104106
);
105-
106-
if (\PHP_INT_MAX > 2147483647) {
107-
$this->buffer .= \pack('NN', $this->length >> 32, $this->length);
108-
} else {
107+
if (\PHP_INT_SIZE === 4) {
108+
// J is not available on 32-bit PHP
109109
$this->buffer .= \pack('NN', 0, $this->length);
110+
} else {
111+
$this->buffer .= \pack('J', $this->length);
110112
}
111113
}
112114

113115
if ($this->masked) {
114116
$this->mask = $this->generateMask();
117+
$this->offset_mask = \strlen($this->buffer);
115118
$this->buffer .= $this->mask;
119+
$this->offset_payload = \strlen($this->buffer);
116120
$this->buffer .= $this->mask($this->payload);
117121
} else {
122+
$this->offset_payload = \strlen($this->buffer);
118123
$this->buffer .= $this->payload;
119124
}
120125

121-
$this->offset_mask = $this->getMaskOffset();
122-
$this->offset_payload = $this->getPayloadOffset();
123-
124126
return $this;
125127
}
126128

@@ -196,7 +198,6 @@ protected function getMaskOffset(): int
196198
if (!isset($this->offset_mask)) {
197199
$offset = self::BYTE_INITIAL_LENGTH + 1;
198200
$offset += $this->getLengthSize();
199-
200201
$this->offset_mask = $offset;
201202
}
202203

@@ -233,7 +234,6 @@ protected function getInitialLength(): int
233234
if (!isset($this->buffer[self::BYTE_INITIAL_LENGTH])) {
234235
throw new FrameException('Cannot yet tell expected length');
235236
}
236-
$a = (int) (\ord($this->buffer[self::BYTE_INITIAL_LENGTH]) & self::BITFIELD_INITIAL_LENGTH);
237237

238238
return (int) (\ord($this->buffer[self::BYTE_INITIAL_LENGTH]) & self::BITFIELD_INITIAL_LENGTH);
239239
}
@@ -258,7 +258,6 @@ protected function getPayloadOffset(): int
258258
if (!isset($this->offset_payload)) {
259259
$offset = $this->getMaskOffset();
260260
$offset += $this->getMaskSize();
261-
262261
$this->offset_payload = $offset;
263262
}
264263

@@ -294,7 +293,7 @@ public function getType(): int
294293

295294
$type = (int) (\ord($this->buffer[self::BYTE_HEADER]) & self::BITFIELD_TYPE);
296295

297-
if (!\in_array($type, Protocol::FRAME_TYPES)) {
296+
if (!\in_array($type, Protocol::FRAME_TYPES, true)) {
298297
throw new FrameException('Invalid payload type');
299298
}
300299

0 commit comments

Comments
 (0)