• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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