1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "net/websockets/websocket_frame.h"
11
12 #include <stdint.h>
13 #include <string.h>
14
15 #include <algorithm>
16 #include <iterator>
17 #include <string>
18 #include <string_view>
19 #include <vector>
20
21 #include "base/containers/span.h"
22 #include "base/memory/aligned_memory.h"
23 #include "base/ranges/algorithm.h"
24 #include "net/base/net_errors.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26
27 namespace net {
28
29 namespace {
30
TEST(WebSocketFrameHeaderTest,FrameLengths)31 TEST(WebSocketFrameHeaderTest, FrameLengths) {
32 struct TestCase {
33 const std::string_view frame_header;
34 uint64_t frame_length;
35 };
36 static constexpr TestCase kTests[] = {
37 {{"\x81\x00", 2}, UINT64_C(0)},
38 {{"\x81\x7D", 2}, UINT64_C(125)},
39 {{"\x81\x7E\x00\x7E", 4}, UINT64_C(126)},
40 {{"\x81\x7E\xFF\xFF", 4}, UINT64_C(0xFFFF)},
41 {{"\x81\x7F\x00\x00\x00\x00\x00\x01\x00\x00", 10}, UINT64_C(0x10000)},
42 {{"\x81\x7F\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 10},
43 UINT64_C(0x7FFFFFFFFFFFFFFF)}};
44
45 for (const auto& test : kTests) {
46 WebSocketFrameHeader header(WebSocketFrameHeader::kOpCodeText);
47 header.final = true;
48 header.payload_length = test.frame_length;
49
50 std::vector<char> expected_output(test.frame_header.begin(),
51 test.frame_header.end());
52 std::vector<char> output(expected_output.size());
53 EXPECT_EQ(static_cast<int>(expected_output.size()),
54 WriteWebSocketFrameHeader(header, nullptr,
55 base::as_writable_byte_span(output)));
56 EXPECT_EQ(expected_output, output);
57 }
58 }
59
TEST(WebSocketFrameHeaderTest,FrameLengthsWithMasking)60 TEST(WebSocketFrameHeaderTest, FrameLengthsWithMasking) {
61 static constexpr std::string_view kMaskingKey = "\xDE\xAD\xBE\xEF";
62 static_assert(kMaskingKey.size() == WebSocketFrameHeader::kMaskingKeyLength,
63 "incorrect masking key size");
64
65 struct TestCase {
66 const std::string_view frame_header;
67 uint64_t frame_length;
68 };
69 static constexpr TestCase kTests[] = {
70 {{"\x81\x80\xDE\xAD\xBE\xEF", 6}, UINT64_C(0)},
71 {{"\x81\xFD\xDE\xAD\xBE\xEF", 6}, UINT64_C(125)},
72 {{"\x81\xFE\x00\x7E\xDE\xAD\xBE\xEF", 8}, UINT64_C(126)},
73 {{"\x81\xFE\xFF\xFF\xDE\xAD\xBE\xEF", 8}, UINT64_C(0xFFFF)},
74 {{"\x81\xFF\x00\x00\x00\x00\x00\x01\x00\x00\xDE\xAD\xBE\xEF", 14},
75 UINT64_C(0x10000)},
76 {{"\x81\xFF\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xDE\xAD\xBE\xEF", 14},
77 UINT64_C(0x7FFFFFFFFFFFFFFF)}};
78
79 WebSocketMaskingKey masking_key;
80 base::as_writable_byte_span(masking_key.key)
81 .copy_from(base::as_byte_span(kMaskingKey));
82
83 for (const auto& test : kTests) {
84 WebSocketFrameHeader header(WebSocketFrameHeader::kOpCodeText);
85 header.final = true;
86 header.masked = true;
87 header.payload_length = test.frame_length;
88
89 std::vector<char> expected_output(test.frame_header.begin(),
90 test.frame_header.end());
91 std::vector<char> output(expected_output.size());
92 EXPECT_EQ(static_cast<int>(expected_output.size()),
93 WriteWebSocketFrameHeader(header, &masking_key,
94 base::as_writable_byte_span(output)));
95 EXPECT_EQ(expected_output, output);
96 }
97 }
98
TEST(WebSocketFrameHeaderTest,FrameOpCodes)99 TEST(WebSocketFrameHeaderTest, FrameOpCodes) {
100 struct TestCase {
101 const std::string_view frame_header;
102 WebSocketFrameHeader::OpCode opcode;
103 };
104 static constexpr TestCase kTests[] = {
105 {{"\x80\x00", 2}, WebSocketFrameHeader::kOpCodeContinuation},
106 {{"\x81\x00", 2}, WebSocketFrameHeader::kOpCodeText},
107 {{"\x82\x00", 2}, WebSocketFrameHeader::kOpCodeBinary},
108 {{"\x88\x00", 2}, WebSocketFrameHeader::kOpCodeClose},
109 {{"\x89\x00", 2}, WebSocketFrameHeader::kOpCodePing},
110 {{"\x8A\x00", 2}, WebSocketFrameHeader::kOpCodePong},
111 // These are undefined opcodes, but the builder should accept them anyway.
112 {{"\x83\x00", 2}, 0x3},
113 {{"\x84\x00", 2}, 0x4},
114 {{"\x85\x00", 2}, 0x5},
115 {{"\x86\x00", 2}, 0x6},
116 {{"\x87\x00", 2}, 0x7},
117 {{"\x8B\x00", 2}, 0xB},
118 {{"\x8C\x00", 2}, 0xC},
119 {{"\x8D\x00", 2}, 0xD},
120 {{"\x8E\x00", 2}, 0xE},
121 {{"\x8F\x00", 2}, 0xF}};
122
123 for (const auto& test : kTests) {
124 WebSocketFrameHeader header(test.opcode);
125 header.final = true;
126 header.payload_length = 0;
127
128 std::vector<char> expected_output(test.frame_header.begin(),
129 test.frame_header.end());
130 std::vector<char> output(expected_output.size());
131 EXPECT_EQ(static_cast<int>(expected_output.size()),
132 WriteWebSocketFrameHeader(header, nullptr,
133 base::as_writable_byte_span(output)));
134 EXPECT_EQ(expected_output, output);
135 }
136 }
137
TEST(WebSocketFrameHeaderTest,FinalBitAndReservedBits)138 TEST(WebSocketFrameHeaderTest, FinalBitAndReservedBits) {
139 struct TestCase {
140 const std::string_view frame_header;
141 bool final;
142 bool reserved1;
143 bool reserved2;
144 bool reserved3;
145 };
146 static constexpr TestCase kTests[] = {
147 {{"\x81\x00", 2}, true, false, false, false},
148 {{"\x01\x00", 2}, false, false, false, false},
149 {{"\xC1\x00", 2}, true, true, false, false},
150 {{"\xA1\x00", 2}, true, false, true, false},
151 {{"\x91\x00", 2}, true, false, false, true},
152 {{"\x71\x00", 2}, false, true, true, true},
153 {{"\xF1\x00", 2}, true, true, true, true}};
154
155 for (const auto& test : kTests) {
156 WebSocketFrameHeader header(WebSocketFrameHeader::kOpCodeText);
157 header.final = test.final;
158 header.reserved1 = test.reserved1;
159 header.reserved2 = test.reserved2;
160 header.reserved3 = test.reserved3;
161 header.payload_length = 0;
162
163 std::vector<char> expected_output(test.frame_header.begin(),
164 test.frame_header.end());
165 std::vector<char> output(expected_output.size());
166 EXPECT_EQ(static_cast<int>(expected_output.size()),
167 WriteWebSocketFrameHeader(header, nullptr,
168 base::as_writable_byte_span(output)));
169 EXPECT_EQ(expected_output, output);
170 }
171 }
172
TEST(WebSocketFrameHeaderTest,InsufficientBufferSize)173 TEST(WebSocketFrameHeaderTest, InsufficientBufferSize) {
174 struct TestCase {
175 uint64_t payload_length;
176 bool masked;
177 size_t expected_header_size;
178 };
179 static constexpr TestCase kTests[] = {
180 {UINT64_C(0), false, 2u},
181 {UINT64_C(125), false, 2u},
182 {UINT64_C(126), false, 4u},
183 {UINT64_C(0xFFFF), false, 4u},
184 {UINT64_C(0x10000), false, 10u},
185 {UINT64_C(0x7FFFFFFFFFFFFFFF), false, 10u},
186 {UINT64_C(0), true, 6u},
187 {UINT64_C(125), true, 6u},
188 {UINT64_C(126), true, 8u},
189 {UINT64_C(0xFFFF), true, 8u},
190 {UINT64_C(0x10000), true, 14u},
191 {UINT64_C(0x7FFFFFFFFFFFFFFF), true, 14u}};
192
193 for (const auto& test : kTests) {
194 WebSocketFrameHeader header(WebSocketFrameHeader::kOpCodeText);
195 header.final = true;
196 header.opcode = WebSocketFrameHeader::kOpCodeText;
197 header.masked = test.masked;
198 header.payload_length = test.payload_length;
199
200 std::array<uint8_t, 14> dummy_buffer;
201 // Set an insufficient size to |buffer_size|.
202 EXPECT_EQ(
203 ERR_INVALID_ARGUMENT,
204 WriteWebSocketFrameHeader(
205 header, nullptr,
206 base::span(dummy_buffer).first(test.expected_header_size - 1)));
207 }
208 }
209
TEST(WebSocketFrameTest,MaskPayload)210 TEST(WebSocketFrameTest, MaskPayload) {
211 struct TestCase {
212 const std::string_view masking_key;
213 uint64_t frame_offset;
214 const char* input;
215 const char* output;
216 size_t data_length;
217 };
218 static constexpr TestCase kTests[] = {
219 {"\xDE\xAD\xBE\xEF", 0, "FooBar", "\x98\xC2\xD1\xAD\xBF\xDF", 6},
220 {"\xDE\xAD\xBE\xEF", 1, "FooBar", "\xEB\xD1\x80\x9C\xCC\xCC", 6},
221 {"\xDE\xAD\xBE\xEF", 2, "FooBar", "\xF8\x80\xB1\xEF\xDF\x9D", 6},
222 {"\xDE\xAD\xBE\xEF", 3, "FooBar", "\xA9\xB1\xC2\xFC\x8E\xAC", 6},
223 {"\xDE\xAD\xBE\xEF", 4, "FooBar", "\x98\xC2\xD1\xAD\xBF\xDF", 6},
224 {"\xDE\xAD\xBE\xEF", 42, "FooBar", "\xF8\x80\xB1\xEF\xDF\x9D", 6},
225 {"\xDE\xAD\xBE\xEF", 0, "", "", 0},
226 {"\xDE\xAD\xBE\xEF", 0, "\xDE\xAD\xBE\xEF", "\x00\x00\x00\x00", 4},
227 {"\xDE\xAD\xBE\xEF", 0, "\x00\x00\x00\x00", "\xDE\xAD\xBE\xEF", 4},
228 {{"\x00\x00\x00\x00", WebSocketFrameHeader::kMaskingKeyLength},
229 0,
230 "FooBar",
231 "FooBar",
232 6},
233 {"\xFF\xFF\xFF\xFF", 0, "FooBar", "\xB9\x90\x90\xBD\x9E\x8D", 6},
234 };
235
236 for (const auto& test : kTests) {
237 WebSocketMaskingKey masking_key;
238 base::as_writable_byte_span(masking_key.key)
239 .copy_from(base::as_byte_span(test.masking_key));
240 std::vector<char> frame_data(test.input, test.input + test.data_length);
241 std::vector<char> expected_output(test.output,
242 test.output + test.data_length);
243 MaskWebSocketFramePayload(masking_key, test.frame_offset,
244 base::as_writable_byte_span(frame_data));
245 EXPECT_EQ(expected_output, frame_data);
246 }
247 }
248
249 // Check that all combinations of alignment, frame offset and chunk size work
250 // correctly for MaskWebSocketFramePayload(). This is mainly used to ensure that
251 // vectorisation optimisations don't break anything. We could take a "white box"
252 // approach and only test the edge cases, but since the exhaustive "black box"
253 // approach runs in acceptable time, we don't have to take the risk of being
254 // clever.
255 //
256 // This brute-force approach runs in O(N^3) time where N is the size of the
257 // maximum vector size we want to test again. This might need reconsidering if
258 // MaskWebSocketFramePayload() is ever optimised for a dedicated vector
259 // architecture.
TEST(WebSocketFrameTest,MaskPayloadAlignment)260 TEST(WebSocketFrameTest, MaskPayloadAlignment) {
261 // This reflects what might be implemented in the future, rather than
262 // the current implementation. FMA3 and FMA4 support 256-bit vector ops.
263 static constexpr size_t kMaxVectorSizeInBits = 256;
264 static constexpr size_t kMaxVectorSize = kMaxVectorSizeInBits / 8;
265 static constexpr size_t kMaxVectorAlignment = kMaxVectorSize;
266 static constexpr size_t kMaskingKeyLength =
267 WebSocketFrameHeader::kMaskingKeyLength;
268 static constexpr size_t kScratchBufferSize =
269 kMaxVectorAlignment + kMaxVectorSize * 2;
270 static constexpr std::string_view kTestMask = "\xd2\xba\x5a\xbe";
271 // We use 786 bits of random input to reduce the risk of correlated errors.
272 static constexpr char kTestInput[] = {
273 "\x3d\x77\x1d\x1b\x19\x8c\x48\xa3\x19\x6d\xf7\xcc\x39\xe7\x57\x0b"
274 "\x69\x8c\xda\x4b\xfc\xac\x2c\xd3\x49\x96\x6e\x8a\x7b\x5a\x32\x76"
275 "\xd0\x11\x43\xa0\x89\xfc\x76\x2b\x10\x2f\x4c\x7b\x4f\xa6\xdd\xe4"
276 "\xfc\x8e\xd8\x72\xcf\x7e\x37\xcd\x31\xcd\xc1\xc0\x89\x0c\xa7\x4c"
277 "\xda\xa8\x4b\x75\xa1\xcb\xa9\x77\x19\x4d\x6e\xdf\xc8\x08\x1c\xb6"
278 "\x6d\xfb\x38\x04\x44\xd5\xba\x57\x9f\x76\xb0\x2e\x07\x91\xe6\xa8"};
279 static constexpr size_t kTestInputSize = std::size(kTestInput) - 1;
280 static constexpr char kTestOutput[] = {
281 "\xef\xcd\x47\xa5\xcb\x36\x12\x1d\xcb\xd7\xad\x72\xeb\x5d\x0d\xb5"
282 "\xbb\x36\x80\xf5\x2e\x16\x76\x6d\x9b\x2c\x34\x34\xa9\xe0\x68\xc8"
283 "\x02\xab\x19\x1e\x5b\x46\x2c\x95\xc2\x95\x16\xc5\x9d\x1c\x87\x5a"
284 "\x2e\x34\x82\xcc\x1d\xc4\x6d\x73\xe3\x77\x9b\x7e\x5b\xb6\xfd\xf2"
285 "\x08\x12\x11\xcb\x73\x71\xf3\xc9\xcb\xf7\x34\x61\x1a\xb2\x46\x08"
286 "\xbf\x41\x62\xba\x96\x6f\xe0\xe9\x4d\xcc\xea\x90\xd5\x2b\xbc\x16"};
287 static_assert(std::size(kTestInput) == std::size(kTestOutput),
288 "output and input arrays should have the same length");
289 std::unique_ptr<char, base::AlignedFreeDeleter> scratch(static_cast<char*>(
290 base::AlignedAlloc(kScratchBufferSize, kMaxVectorAlignment)));
291 WebSocketMaskingKey masking_key;
292 base::as_writable_byte_span(masking_key.key)
293 .copy_from(base::as_byte_span(kTestMask));
294 for (size_t frame_offset = 0; frame_offset < kMaskingKeyLength;
295 ++frame_offset) {
296 for (size_t alignment = 0; alignment < kMaxVectorAlignment; ++alignment) {
297 char* const aligned_scratch = scratch.get() + alignment;
298 const size_t aligned_len = std::min(kScratchBufferSize - alignment,
299 kTestInputSize - frame_offset);
300 for (size_t chunk_size = 1; chunk_size < kMaxVectorSize; ++chunk_size) {
301 memcpy(aligned_scratch, kTestInput + frame_offset, aligned_len);
302 for (size_t chunk_start = 0; chunk_start < aligned_len;
303 chunk_start += chunk_size) {
304 const size_t this_chunk_size =
305 std::min(chunk_size, aligned_len - chunk_start);
306 MaskWebSocketFramePayload(
307 masking_key, frame_offset + chunk_start,
308 base::as_writable_bytes(
309 base::span(aligned_scratch + chunk_start, this_chunk_size)));
310 }
311 // Stop the test if it fails, since we don't want to spew thousands of
312 // failures.
313 ASSERT_TRUE(std::equal(aligned_scratch,
314 aligned_scratch + aligned_len,
315 kTestOutput + frame_offset))
316 << "Output failed to match for frame_offset=" << frame_offset
317 << ", alignment=" << alignment << ", chunk_size=" << chunk_size;
318 }
319 }
320 }
321 }
322
323 // "IsKnownDataOpCode" is currently implemented in an "obviously correct"
324 // manner, but we test is anyway in case it changes to a more complex
325 // implementation in future.
TEST(WebSocketFrameHeaderTest,IsKnownDataOpCode)326 TEST(WebSocketFrameHeaderTest, IsKnownDataOpCode) {
327 // Make the test less verbose.
328 using Frame = WebSocketFrameHeader;
329
330 // Known opcode, is used for data frames
331 EXPECT_TRUE(Frame::IsKnownDataOpCode(Frame::kOpCodeContinuation));
332 EXPECT_TRUE(Frame::IsKnownDataOpCode(Frame::kOpCodeText));
333 EXPECT_TRUE(Frame::IsKnownDataOpCode(Frame::kOpCodeBinary));
334
335 // Known opcode, is used for control frames
336 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeClose));
337 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodePing));
338 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodePong));
339
340 // Check that unused opcodes return false
341 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeDataUnused3));
342 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeDataUnused4));
343 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeDataUnused5));
344 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeDataUnused6));
345 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeDataUnused7));
346 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeControlUnusedB));
347 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeControlUnusedC));
348 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeControlUnusedD));
349 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeControlUnusedE));
350 EXPECT_FALSE(Frame::IsKnownDataOpCode(Frame::kOpCodeControlUnusedF));
351
352 // Check that out-of-range opcodes return false
353 EXPECT_FALSE(Frame::IsKnownDataOpCode(-1));
354 EXPECT_FALSE(Frame::IsKnownDataOpCode(0xFF));
355 }
356
357 // "IsKnownControlOpCode" is implemented in an "obviously correct" manner but
358 // might be optimised in future.
TEST(WebSocketFrameHeaderTest,IsKnownControlOpCode)359 TEST(WebSocketFrameHeaderTest, IsKnownControlOpCode) {
360 // Make the test less verbose.
361 using Frame = WebSocketFrameHeader;
362
363 // Known opcode, is used for data frames
364 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeContinuation));
365 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeText));
366 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeBinary));
367
368 // Known opcode, is used for control frames
369 EXPECT_TRUE(Frame::IsKnownControlOpCode(Frame::kOpCodeClose));
370 EXPECT_TRUE(Frame::IsKnownControlOpCode(Frame::kOpCodePing));
371 EXPECT_TRUE(Frame::IsKnownControlOpCode(Frame::kOpCodePong));
372
373 // Check that unused opcodes return false
374 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeDataUnused3));
375 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeDataUnused4));
376 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeDataUnused5));
377 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeDataUnused6));
378 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeDataUnused7));
379 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeControlUnusedB));
380 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeControlUnusedC));
381 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeControlUnusedD));
382 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeControlUnusedE));
383 EXPECT_FALSE(Frame::IsKnownControlOpCode(Frame::kOpCodeControlUnusedF));
384
385 // Check that out-of-range opcodes return false
386 EXPECT_FALSE(Frame::IsKnownControlOpCode(-1));
387 EXPECT_FALSE(Frame::IsKnownControlOpCode(0xFF));
388 }
389
390 // Test for reserved data opcodes.
TEST(WebSocketFrameHeaderTest,IsReservedDataOpCode)391 TEST(WebSocketFrameHeaderTest, IsReservedDataOpCode) {
392 using Frame = WebSocketFrameHeader;
393
394 // Known opcodes for data frames should not be reserved.
395 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodeContinuation));
396 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodeText));
397 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodeBinary));
398
399 // Unused opcodes in the data frame range should be considered reserved.
400 EXPECT_TRUE(Frame::IsReservedDataOpCode(Frame::kOpCodeDataUnused3));
401 EXPECT_TRUE(Frame::IsReservedDataOpCode(Frame::kOpCodeDataUnused4));
402 EXPECT_TRUE(Frame::IsReservedDataOpCode(Frame::kOpCodeDataUnused5));
403 EXPECT_TRUE(Frame::IsReservedDataOpCode(Frame::kOpCodeDataUnused6));
404 EXPECT_TRUE(Frame::IsReservedDataOpCode(Frame::kOpCodeDataUnused7));
405
406 // Known opcodes for control frames should not be considered reserved data
407 // opcodes.
408 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodeClose));
409 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodePing));
410 EXPECT_FALSE(Frame::IsReservedDataOpCode(Frame::kOpCodePong));
411
412 // Out-of-range opcodes should not be considered reserved data opcodes.
413 EXPECT_FALSE(Frame::IsReservedDataOpCode(-1));
414 EXPECT_FALSE(Frame::IsReservedDataOpCode(0xFF));
415 }
416
417 // Test for reserved control opcodes.
TEST(WebSocketFrameHeaderTest,IsReservedControlOpCode)418 TEST(WebSocketFrameHeaderTest, IsReservedControlOpCode) {
419 using Frame = WebSocketFrameHeader;
420
421 // Known opcodes for data frames should not be reserved control opcodes.
422 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodeContinuation));
423 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodeText));
424 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodeBinary));
425
426 // Known opcodes for control frames should not be reserved.
427 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodeClose));
428 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodePing));
429 EXPECT_FALSE(Frame::IsReservedControlOpCode(Frame::kOpCodePong));
430
431 // Unused opcodes in the control frame range should be considered reserved.
432 EXPECT_TRUE(Frame::IsReservedControlOpCode(Frame::kOpCodeControlUnusedB));
433 EXPECT_TRUE(Frame::IsReservedControlOpCode(Frame::kOpCodeControlUnusedC));
434 EXPECT_TRUE(Frame::IsReservedControlOpCode(Frame::kOpCodeControlUnusedD));
435 EXPECT_TRUE(Frame::IsReservedControlOpCode(Frame::kOpCodeControlUnusedE));
436 EXPECT_TRUE(Frame::IsReservedControlOpCode(Frame::kOpCodeControlUnusedF));
437
438 // Out-of-range opcodes should not be considered reserved control opcodes.
439 EXPECT_FALSE(Frame::IsReservedControlOpCode(-1));
440 EXPECT_FALSE(Frame::IsReservedControlOpCode(0xFF));
441 }
442
443 } // namespace
444
445 } // namespace net
446