OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "net/websockets/websocket_frame_builder.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/basictypes.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/memory/scoped_vector.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "net/websockets/websocket_frame.h" |
| 14 |
| 15 namespace { |
| 16 |
| 17 const char kHello[] = "Hello, world!"; |
| 18 const uint64 kHelloLength = arraysize(kHello) - 1; |
| 19 |
| 20 struct ShortFrame { |
| 21 const char* payload; |
| 22 size_t payload_length; |
| 23 const char* expected_output; |
| 24 size_t expected_output_length; |
| 25 }; |
| 26 static const ShortFrame kShortFrames[] = { |
| 27 // Each output data is split into two string literals because C++ lexers |
| 28 // consume unlimited number of hex characters in a hex character escape |
| 29 // (e.g. "\x05F" is not treated as { '\x5', 'F', '\0' } but as |
| 30 // { '\x5F', '\0' }). |
| 31 { "First", 5, "\x81\x05" "First", 7 }, |
| 32 { "Second", 6, "\x81\x06" "Second", 8 }, |
| 33 { "Third", 5, "\x81\x05" "Third", 7 }, |
| 34 { "Fourth", 6, "\x81\x06" "Fourth", 8 }, |
| 35 { "Fifth", 5, "\x81\x05" "Fifth", 7 }, |
| 36 { "Sixth", 5, "\x81\x05" "Sixth", 7 }, |
| 37 { "Seventh", 7, "\x81\x07" "Seventh", 9 }, |
| 38 { "Eighth", 6, "\x81\x06" "Eighth", 8 }, |
| 39 { "Ninth", 5, "\x81\x05" "Ninth", 7 }, |
| 40 { "Tenth", 5, "\x81\x05" "Tenth", 7 } |
| 41 }; |
| 42 static const int kNumShortFrames = arraysize(kShortFrames); |
| 43 |
| 44 scoped_ptr<net::WebSocketFrameChunk> CreateFrameChunkWithMessage( |
| 45 const char* message, |
| 46 size_t message_length) { |
| 47 scoped_ptr<net::WebSocketFrameHeader> frame_header( |
| 48 new net::WebSocketFrameHeader); |
| 49 frame_header->final = true; |
| 50 frame_header->reserved1 = false; |
| 51 frame_header->reserved2 = false; |
| 52 frame_header->reserved3 = false; |
| 53 frame_header->opcode = net::WebSocketFrameHeader::kOpCodeText; |
| 54 frame_header->masked = false; |
| 55 frame_header->payload_length = message_length; |
| 56 |
| 57 scoped_ptr<net::WebSocketFrameChunk> frame_chunk( |
| 58 new net::WebSocketFrameChunk); |
| 59 frame_chunk->header = frame_header.Pass(); |
| 60 frame_chunk->final_chunk = true; |
| 61 frame_chunk->data.assign(message, message + message_length); |
| 62 return frame_chunk.Pass(); |
| 63 } |
| 64 |
| 65 ScopedVector<net::WebSocketFrameChunk> CreateChunksVectorWithSingleMessage( |
| 66 const char* message, |
| 67 size_t message_length) { |
| 68 scoped_ptr<net::WebSocketFrameChunk> frame_chunk = |
| 69 CreateFrameChunkWithMessage(message, message_length); |
| 70 ScopedVector<net::WebSocketFrameChunk> frame_chunks; |
| 71 frame_chunks.push_back(frame_chunk.release()); |
| 72 return frame_chunks.Pass(); |
| 73 } |
| 74 |
| 75 void ExpectBuilderDiesOrFailsOnChunks( |
| 76 ScopedVector<net::WebSocketFrameChunk> frame_chunks) { |
| 77 // Note: EXPECT_DEBUG_DEATH does not work on Win32 accoring to comments in |
| 78 // spdy_protocol_test.cc. We don't run the test in that case. |
| 79 #if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) |
| 80 net::WebSocketFrameBuilder builder; |
| 81 std::vector<char> output; |
| 82 #if !defined(DCHECK_ALWAYS_ON) |
| 83 EXPECT_DEBUG_DEATH({ |
| 84 EXPECT_FALSE(builder.Encode(frame_chunks.Pass(), &output)); |
| 85 EXPECT_TRUE(builder.failed()); |
| 86 }, ""); |
| 87 #else |
| 88 EXPECT_DEATH({ |
| 89 EXPECT_FALSE(builder.Encode(frame_chunks.Pass(), &output)); |
| 90 EXPECT_TRUE(builder.failed()); |
| 91 }, ""); |
| 92 #endif |
| 93 #endif |
| 94 } |
| 95 |
| 96 } // Unnamed namespace. |
| 97 |
| 98 namespace net { |
| 99 |
| 100 TEST(WebSocketFrameBuilderTest, EncodeNormalFrame) { |
| 101 static const char kHelloFrame[] = "\x81\x0DHello, world!"; |
| 102 static const uint64 kHelloFrameLength = arraysize(kHelloFrame) - 1; |
| 103 |
| 104 ScopedVector<WebSocketFrameChunk> frame_chunks = |
| 105 CreateChunksVectorWithSingleMessage(kHello, kHelloLength); |
| 106 |
| 107 WebSocketFrameBuilder builder; |
| 108 std::vector<char> output; |
| 109 EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
| 110 std::vector<char> expected_output(kHelloFrame, |
| 111 kHelloFrame + kHelloFrameLength); |
| 112 EXPECT_EQ(expected_output, output); |
| 113 EXPECT_FALSE(builder.failed()); |
| 114 } |
| 115 |
| 116 TEST(WebSocketFrameBuilderTest, EncodeMaskedFrame) { |
| 117 static const size_t kMaskingKeyLength = |
| 118 WebSocketFrameHeader::kMaskingKeyLength; |
| 119 static const char kMaskingKey[] = "\xDE\xAD\xBE\xEF"; |
| 120 COMPILE_ASSERT(arraysize(kMaskingKey) - 1 == kMaskingKeyLength, |
| 121 incorrect_masking_key_length); |
| 122 static const char kMaskedHelloFrame[] = |
| 123 "\x81\x8D\xDE\xAD\xBE\xEF" |
| 124 "\x96\xC8\xD2\x83\xB1\x81\x9E\x98\xB1\xDF\xD2\x8B\xFF"; |
| 125 static const uint64 kMaskedHelloFrameLength = |
| 126 arraysize(kMaskedHelloFrame) - 1; |
| 127 |
| 128 ScopedVector<WebSocketFrameChunk> frame_chunks = |
| 129 CreateChunksVectorWithSingleMessage(kHello, kHelloLength); |
| 130 frame_chunks[0]->header->masked = true; |
| 131 |
| 132 WebSocketFrameBuilder builder; |
| 133 builder.PinMaskingKeyForTesting(kMaskingKey); |
| 134 std::vector<char> output; |
| 135 EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
| 136 std::vector<char> expected_output( |
| 137 kMaskedHelloFrame, kMaskedHelloFrame + kMaskedHelloFrameLength); |
| 138 EXPECT_EQ(expected_output, output); |
| 139 EXPECT_FALSE(builder.failed()); |
| 140 } |
| 141 |
| 142 TEST(WebSocketFrameBuilderTest, EncodeMultipleFramesAtOnce) { |
| 143 ScopedVector<WebSocketFrameChunk> frame_chunks; |
| 144 std::vector<char> expected_output; |
| 145 for (int i = 0; i < kNumShortFrames; ++i) { |
| 146 scoped_ptr<WebSocketFrameChunk> frame_chunk = CreateFrameChunkWithMessage( |
| 147 kShortFrames[i].payload, kShortFrames[i].payload_length); |
| 148 frame_chunks.push_back(frame_chunk.release()); |
| 149 expected_output.insert( |
| 150 expected_output.end(), |
| 151 kShortFrames[i].expected_output, |
| 152 kShortFrames[i].expected_output + |
| 153 kShortFrames[i].expected_output_length); |
| 154 } |
| 155 |
| 156 WebSocketFrameBuilder builder; |
| 157 std::vector<char> output; |
| 158 EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
| 159 EXPECT_EQ(expected_output, output); |
| 160 EXPECT_FALSE(builder.failed()); |
| 161 } |
| 162 |
| 163 TEST(WebSocketFrameBuilderTest, EncodeMultipleFramesSerially) { |
| 164 WebSocketFrameBuilder builder; |
| 165 for (int i = 0; i < kNumShortFrames; ++i) { |
| 166 ScopedVector<WebSocketFrameChunk> frame_chunks = |
| 167 CreateChunksVectorWithSingleMessage(kShortFrames[i].payload, |
| 168 kShortFrames[i].payload_length); |
| 169 std::vector<char> expected_output( |
| 170 kShortFrames[i].expected_output, |
| 171 kShortFrames[i].expected_output + |
| 172 kShortFrames[i].expected_output_length); |
| 173 std::vector<char> output; |
| 174 EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
| 175 EXPECT_EQ(expected_output, output); |
| 176 EXPECT_FALSE(builder.failed()); |
| 177 } |
| 178 } |
| 179 |
| 180 TEST(WebSocketFrameBuilderTest, EncodeChunkedFrames) { |
| 181 // Send two messages in three steps: |
| 182 // 1. { message1[:cutting_pos1] } |
| 183 // 2. { message1[cutting_pos1:], message2[:cutting_pos2] } |
| 184 // 3. { message2[cutting_pos2:] } |
| 185 // where a[x:y] is Python-style slice notation. |
| 186 struct Message { |
| 187 const char* payload; |
| 188 size_t payload_length; |
| 189 const char* expected_header; |
| 190 size_t expected_header_length; |
| 191 }; |
| 192 static const Message kMessage1 = { "Larry Page", 10, "\x81\x0A", 2 }; |
| 193 static const Message kMessage2 = { "Sergey Brin", 11, "\x81\x0B", 2 }; |
| 194 |
| 195 for (size_t cutting_pos1 = 0; cutting_pos1 <= kMessage1.payload_length; |
| 196 ++cutting_pos1) { |
| 197 for (size_t cutting_pos2 = 0; cutting_pos2 <= kMessage2.payload_length; |
| 198 ++cutting_pos2) { |
| 199 scoped_ptr<WebSocketFrameHeader> header1(new WebSocketFrameHeader); |
| 200 header1->final = true; |
| 201 header1->reserved1 = false; |
| 202 header1->reserved2 = false; |
| 203 header1->reserved3 = false; |
| 204 header1->opcode = WebSocketFrameHeader::kOpCodeText; |
| 205 header1->masked = false; |
| 206 header1->payload_length = kMessage1.payload_length; |
| 207 |
| 208 scoped_ptr<WebSocketFrameHeader> header2(new WebSocketFrameHeader); |
| 209 header2->final = true; |
| 210 header2->reserved1 = false; |
| 211 header2->reserved2 = false; |
| 212 header2->reserved3 = false; |
| 213 header2->opcode = WebSocketFrameHeader::kOpCodeText; |
| 214 header2->masked = false; |
| 215 header2->payload_length = kMessage2.payload_length; |
| 216 |
| 217 scoped_ptr<WebSocketFrameChunk> chunk11(new WebSocketFrameChunk); |
| 218 chunk11->header = header1.Pass(); |
| 219 chunk11->final_chunk = false; |
| 220 chunk11->data.assign(kMessage1.payload, kMessage1.payload + cutting_pos1); |
| 221 |
| 222 scoped_ptr<WebSocketFrameChunk> chunk12(new WebSocketFrameChunk); |
| 223 chunk12->final_chunk = true; |
| 224 chunk12->data.assign(kMessage1.payload + cutting_pos1, |
| 225 kMessage1.payload + kMessage1.payload_length); |
| 226 |
| 227 scoped_ptr<WebSocketFrameChunk> chunk21(new WebSocketFrameChunk); |
| 228 chunk21->header = header2.Pass(); |
| 229 chunk21->final_chunk = false; |
| 230 chunk21->data.assign(kMessage2.payload, kMessage2.payload + cutting_pos2); |
| 231 |
| 232 scoped_ptr<WebSocketFrameChunk> chunk22(new WebSocketFrameChunk); |
| 233 chunk22->final_chunk = true; |
| 234 chunk22->data.assign(kMessage2.payload + cutting_pos2, |
| 235 kMessage2.payload + kMessage2.payload_length); |
| 236 |
| 237 WebSocketFrameBuilder builder; |
| 238 |
| 239 // Step 1: Encode chunk11. |
| 240 std::vector<char> expected_output1; |
| 241 expected_output1.insert( |
| 242 expected_output1.end(), |
| 243 kMessage1.expected_header, |
| 244 kMessage1.expected_header + kMessage1.expected_header_length); |
| 245 expected_output1.insert( |
| 246 expected_output1.end(), |
| 247 kMessage1.payload, |
| 248 kMessage1.payload + cutting_pos1); |
| 249 |
| 250 ScopedVector<WebSocketFrameChunk> chunks1; |
| 251 chunks1.push_back(chunk11.release()); |
| 252 std::vector<char> output1; |
| 253 EXPECT_TRUE(builder.Encode(chunks1.Pass(), &output1)); |
| 254 EXPECT_EQ(expected_output1, output1); |
| 255 EXPECT_FALSE(builder.failed()); |
| 256 |
| 257 // Step 2: Encode chunk12 and chunk 21. |
| 258 std::vector<char> expected_output2; |
| 259 expected_output2.insert( |
| 260 expected_output2.end(), |
| 261 kMessage1.payload + cutting_pos1, |
| 262 kMessage1.payload + kMessage1.payload_length); |
| 263 expected_output2.insert( |
| 264 expected_output2.end(), |
| 265 kMessage2.expected_header, |
| 266 kMessage2.expected_header + kMessage2.expected_header_length); |
| 267 expected_output2.insert( |
| 268 expected_output2.end(), |
| 269 kMessage2.payload, |
| 270 kMessage2.payload + cutting_pos2); |
| 271 |
| 272 ScopedVector<WebSocketFrameChunk> chunks2; |
| 273 chunks2.push_back(chunk12.release()); |
| 274 chunks2.push_back(chunk21.release()); |
| 275 std::vector<char> output2; |
| 276 EXPECT_TRUE(builder.Encode(chunks2.Pass(), &output2)); |
| 277 EXPECT_EQ(expected_output2, output2); |
| 278 EXPECT_FALSE(builder.failed()); |
| 279 |
| 280 // Step 3: Encode chunk22. |
| 281 std::vector<char> expected_output3( |
| 282 kMessage2.payload + cutting_pos2, |
| 283 kMessage2.payload + kMessage2.payload_length); |
| 284 |
| 285 ScopedVector<WebSocketFrameChunk> chunks3; |
| 286 chunks3.push_back(chunk22.release()); |
| 287 std::vector<char> output3; |
| 288 EXPECT_TRUE(builder.Encode(chunks3.Pass(), &output3)); |
| 289 EXPECT_EQ(expected_output3, output3); |
| 290 EXPECT_FALSE(builder.failed()); |
| 291 |
| 292 // If we have found any test failure, let's exit the test early |
| 293 // to avoid excessive console output. |
| 294 if (HasFailure()) { |
| 295 FAIL() << "Failed: cutting_pos1 = " << cutting_pos1 << ", " |
| 296 << "cutting_pos2 = " << cutting_pos2; |
| 297 return; |
| 298 } |
| 299 } |
| 300 } |
| 301 } |
| 302 |
| 303 TEST(WebSocketFrameBuilderTest, EncodeChunkedMaskedFrames) { |
| 304 // Same as above, but this time we mask frame payloads. |
| 305 struct Message { |
| 306 const char* masking_key; |
| 307 const char* payload; |
| 308 const char* masked_payload; |
| 309 size_t payload_length; |
| 310 const char* expected_header; |
| 311 size_t expected_header_length; |
| 312 }; |
| 313 |
| 314 static const Message kMessage1 = { |
| 315 "\xDE\xAD\xBE\xEF", |
| 316 "Larry Page", "\x92\xCC\xCC\x9D\xA7\x8D\xEE\x8E\xB9\xC8", 10, |
| 317 "\x81\x8A\xDE\xAD\xBE\xEF", 6 |
| 318 }; |
| 319 static const Message kMessage2 = { |
| 320 "\xBA\xAD\xCA\xFE", |
| 321 "Sergey Brin", "\xE9\xC8\xB8\x99\xDF\xD4\xEA\xBC\xC8\xC4\xA4", 11, |
| 322 "\x81\x8B\xBA\xAD\xCA\xFE", 6 |
| 323 }; |
| 324 |
| 325 for (size_t cutting_pos1 = 0; cutting_pos1 < kMessage1.payload_length; |
| 326 ++cutting_pos1) { |
| 327 for (size_t cutting_pos2 = 0; cutting_pos2 < kMessage2.payload_length; |
| 328 ++cutting_pos2) { |
| 329 scoped_ptr<WebSocketFrameHeader> header1(new WebSocketFrameHeader); |
| 330 header1->final = true; |
| 331 header1->reserved1 = false; |
| 332 header1->reserved2 = false; |
| 333 header1->reserved3 = false; |
| 334 header1->opcode = WebSocketFrameHeader::kOpCodeText; |
| 335 header1->masked = true; |
| 336 header1->payload_length = kMessage1.payload_length; |
| 337 |
| 338 scoped_ptr<WebSocketFrameHeader> header2(new WebSocketFrameHeader); |
| 339 header2->final = true; |
| 340 header2->reserved1 = false; |
| 341 header2->reserved2 = false; |
| 342 header2->reserved3 = false; |
| 343 header2->opcode = WebSocketFrameHeader::kOpCodeText; |
| 344 header2->masked = true; |
| 345 header2->payload_length = kMessage2.payload_length; |
| 346 |
| 347 scoped_ptr<WebSocketFrameChunk> chunk11(new WebSocketFrameChunk); |
| 348 chunk11->header = header1.Pass(); |
| 349 chunk11->final_chunk = false; |
| 350 chunk11->data.assign(kMessage1.payload, kMessage1.payload + cutting_pos1); |
| 351 |
| 352 scoped_ptr<WebSocketFrameChunk> chunk12(new WebSocketFrameChunk); |
| 353 chunk12->final_chunk = true; |
| 354 chunk12->data.assign(kMessage1.payload + cutting_pos1, |
| 355 kMessage1.payload + kMessage1.payload_length); |
| 356 |
| 357 scoped_ptr<WebSocketFrameChunk> chunk21(new WebSocketFrameChunk); |
| 358 chunk21->header = header2.Pass(); |
| 359 chunk21->final_chunk = false; |
| 360 chunk21->data.assign(kMessage2.payload, kMessage2.payload + cutting_pos2); |
| 361 |
| 362 scoped_ptr<WebSocketFrameChunk> chunk22(new WebSocketFrameChunk); |
| 363 chunk22->final_chunk = true; |
| 364 chunk22->data.assign(kMessage2.payload + cutting_pos2, |
| 365 kMessage2.payload + kMessage2.payload_length); |
| 366 |
| 367 WebSocketFrameBuilder builder; |
| 368 |
| 369 // Step 1: Encode chunk11. |
| 370 std::vector<char> expected_output1; |
| 371 expected_output1.insert( |
| 372 expected_output1.end(), |
| 373 kMessage1.expected_header, |
| 374 kMessage1.expected_header + kMessage1.expected_header_length); |
| 375 expected_output1.insert( |
| 376 expected_output1.end(), |
| 377 kMessage1.masked_payload, |
| 378 kMessage1.masked_payload + cutting_pos1); |
| 379 |
| 380 ScopedVector<WebSocketFrameChunk> chunks1; |
| 381 chunks1.push_back(chunk11.release()); |
| 382 builder.PinMaskingKeyForTesting(kMessage1.masking_key); |
| 383 std::vector<char> output1; |
| 384 EXPECT_TRUE(builder.Encode(chunks1.Pass(), &output1)); |
| 385 EXPECT_EQ(expected_output1, output1); |
| 386 EXPECT_FALSE(builder.failed()); |
| 387 |
| 388 // Step 2: Encode chunk12 and chunk 21. |
| 389 std::vector<char> expected_output2; |
| 390 expected_output2.insert( |
| 391 expected_output2.end(), |
| 392 kMessage1.masked_payload + cutting_pos1, |
| 393 kMessage1.masked_payload + kMessage1.payload_length); |
| 394 expected_output2.insert( |
| 395 expected_output2.end(), |
| 396 kMessage2.expected_header, |
| 397 kMessage2.expected_header + kMessage2.expected_header_length); |
| 398 expected_output2.insert( |
| 399 expected_output2.end(), |
| 400 kMessage2.masked_payload, |
| 401 kMessage2.masked_payload + cutting_pos2); |
| 402 |
| 403 ScopedVector<WebSocketFrameChunk> chunks2; |
| 404 chunks2.push_back(chunk12.release()); |
| 405 chunks2.push_back(chunk21.release()); |
| 406 builder.PinMaskingKeyForTesting(kMessage2.masking_key); |
| 407 std::vector<char> output2; |
| 408 EXPECT_TRUE(builder.Encode(chunks2.Pass(), &output2)); |
| 409 EXPECT_EQ(expected_output2, output2); |
| 410 EXPECT_FALSE(builder.failed()); |
| 411 |
| 412 // Step 3: Encode chunk22. |
| 413 std::vector<char> expected_output3( |
| 414 kMessage2.masked_payload + cutting_pos2, |
| 415 kMessage2.masked_payload + kMessage2.payload_length); |
| 416 |
| 417 ScopedVector<WebSocketFrameChunk> chunks3; |
| 418 chunks3.push_back(chunk22.release()); |
| 419 std::vector<char> output3; |
| 420 EXPECT_TRUE(builder.Encode(chunks3.Pass(), &output3)); |
| 421 EXPECT_EQ(expected_output3, output3); |
| 422 EXPECT_FALSE(builder.failed()); |
| 423 |
| 424 // If we have found any test failure, let's exit the test early |
| 425 // to avoid excessive console output. |
| 426 if (HasFailure()) { |
| 427 FAIL() << "Failed: cutting_pos1 = " << cutting_pos1 << ", " |
| 428 << "cutting_pos2 = " << cutting_pos2; |
| 429 return; |
| 430 } |
| 431 } |
| 432 } |
| 433 } |
| 434 |
| 435 TEST(WebSocketFrameBuilderTest, FrameHeadersOfVariousLengths) { |
| 436 struct TestCase { |
| 437 const char* frame_header; |
| 438 size_t frame_header_length; |
| 439 uint64 frame_length; |
| 440 }; |
| 441 static const TestCase kTests[] = { |
| 442 { "\x81\x00", 2, GG_UINT64_C(0) }, |
| 443 { "\x81\x7D", 2, GG_UINT64_C(125) }, |
| 444 { "\x81\x7E\x00\x7E", 4, GG_UINT64_C(126) }, |
| 445 { "\x81\x7E\xFF\xFF", 4, GG_UINT64_C(0xFFFF) }, |
| 446 { "\x81\x7F\x00\x00\x00\x00\x00\x01\x00\x00", 10, GG_UINT64_C(0x10000) }, |
| 447 { "\x81\x7F\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 10, |
| 448 GG_UINT64_C(0x7FFFFFFFFFFFFFFF) } |
| 449 }; |
| 450 static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
| 451 |
| 452 for (size_t i = 0; i < kNumTests; ++i) { |
| 453 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
| 454 header->final = true; |
| 455 header->reserved1 = false; |
| 456 header->reserved2 = false; |
| 457 header->reserved3 = false; |
| 458 header->opcode = WebSocketFrameHeader::kOpCodeText; |
| 459 header->masked = false; |
| 460 header->payload_length = kTests[i].frame_length; |
| 461 |
| 462 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| 463 chunk->header = header.Pass(); |
| 464 chunk->final_chunk = false; // Let the chunk carry no data. |
| 465 |
| 466 ScopedVector<WebSocketFrameChunk> chunks; |
| 467 chunks.push_back(chunk.release()); |
| 468 |
| 469 std::vector<char> expected_output( |
| 470 kTests[i].frame_header, |
| 471 kTests[i].frame_header + kTests[i].frame_header_length); |
| 472 |
| 473 WebSocketFrameBuilder builder; |
| 474 std::vector<char> output; |
| 475 EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
| 476 EXPECT_EQ(expected_output, output); |
| 477 EXPECT_FALSE(builder.failed()); |
| 478 } |
| 479 } |
| 480 |
| 481 TEST(WebSocketFrameBuilderTest, EncodeTooLargeFrame) { |
| 482 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
| 483 header->final = true; |
| 484 header->reserved1 = false; |
| 485 header->reserved2 = false; |
| 486 header->reserved3 = false; |
| 487 header->opcode = WebSocketFrameHeader::kOpCodeText; |
| 488 header->masked = false; |
| 489 header->payload_length = GG_UINT64_C(0x8000000000000000); |
| 490 |
| 491 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| 492 chunk->header = header.Pass(); |
| 493 chunk->final_chunk = false; |
| 494 |
| 495 ScopedVector<WebSocketFrameChunk> chunks; |
| 496 chunks.push_back(chunk.release()); |
| 497 |
| 498 WebSocketFrameBuilder builder; |
| 499 std::vector<char> output; |
| 500 EXPECT_FALSE(builder.Encode(chunks.Pass(), &output)); |
| 501 EXPECT_TRUE(builder.failed()); |
| 502 |
| 503 // Once the builder has failed, it won't accept any more chunks. |
| 504 chunks = CreateChunksVectorWithSingleMessage("Hello!", 6); |
| 505 EXPECT_FALSE(builder.Encode(chunks.Pass(), &output)); |
| 506 EXPECT_TRUE(builder.failed()); |
| 507 } |
| 508 |
| 509 TEST(WebSocketFrameBuilderTest, FrameTypes) { |
| 510 struct TestCase { |
| 511 const char* frame_header; |
| 512 size_t frame_header_length; |
| 513 WebSocketFrameHeader::OpCode opcode; |
| 514 }; |
| 515 static const TestCase kTests[] = { |
| 516 { "\x80\x00", 2, WebSocketFrameHeader::kOpCodeContinuation }, |
| 517 { "\x81\x00", 2, WebSocketFrameHeader::kOpCodeText }, |
| 518 { "\x82\x00", 2, WebSocketFrameHeader::kOpCodeBinary }, |
| 519 { "\x88\x00", 2, WebSocketFrameHeader::kOpCodeClose }, |
| 520 { "\x89\x00", 2, WebSocketFrameHeader::kOpCodePing }, |
| 521 { "\x8A\x00", 2, WebSocketFrameHeader::kOpCodePong }, |
| 522 // These are undefined opcodes, but the builder should accept them anyway. |
| 523 { "\x83\x00", 2, 0x3 }, |
| 524 { "\x84\x00", 2, 0x4 }, |
| 525 { "\x85\x00", 2, 0x5 }, |
| 526 { "\x86\x00", 2, 0x6 }, |
| 527 { "\x87\x00", 2, 0x7 }, |
| 528 { "\x8B\x00", 2, 0xB }, |
| 529 { "\x8C\x00", 2, 0xC }, |
| 530 { "\x8D\x00", 2, 0xD }, |
| 531 { "\x8E\x00", 2, 0xE }, |
| 532 { "\x8F\x00", 2, 0xF } |
| 533 }; |
| 534 static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
| 535 |
| 536 WebSocketFrameBuilder builder; |
| 537 |
| 538 for (size_t i = 0; i < kNumTests; ++i) { |
| 539 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
| 540 header->final = true; |
| 541 header->reserved1 = false; |
| 542 header->reserved2 = false; |
| 543 header->reserved3 = false; |
| 544 header->opcode = kTests[i].opcode; |
| 545 header->masked = false; |
| 546 header->payload_length = 0; |
| 547 |
| 548 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| 549 chunk->header = header.Pass(); |
| 550 chunk->final_chunk = true; |
| 551 // chunk->data is empty. |
| 552 |
| 553 ScopedVector<WebSocketFrameChunk> chunks; |
| 554 chunks.push_back(chunk.release()); |
| 555 |
| 556 std::vector<char> expected_output( |
| 557 kTests[i].frame_header, |
| 558 kTests[i].frame_header + kTests[i].frame_header_length); |
| 559 std::vector<char> output; |
| 560 EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
| 561 EXPECT_EQ(expected_output, output); |
| 562 EXPECT_FALSE(builder.failed()); |
| 563 } |
| 564 } |
| 565 |
| 566 TEST(WebSocketFrameBuilderTest, FinalBitAndReservedBits) { |
| 567 struct TestCase { |
| 568 const char* frame_header; |
| 569 size_t frame_header_length; |
| 570 bool final; |
| 571 bool reserved1; |
| 572 bool reserved2; |
| 573 bool reserved3; |
| 574 }; |
| 575 static const TestCase kTests[] = { |
| 576 { "\x81\x00", 2, true, false, false, false }, |
| 577 { "\x01\x00", 2, false, false, false, false }, |
| 578 { "\xC1\x00", 2, true, true, false, false }, |
| 579 { "\xA1\x00", 2, true, false, true, false }, |
| 580 { "\x91\x00", 2, true, false, false, true }, |
| 581 { "\x71\x00", 2, false, true, true, true }, |
| 582 { "\xF1\x00", 2, true, true, true, true } |
| 583 }; |
| 584 static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
| 585 |
| 586 WebSocketFrameBuilder builder; |
| 587 |
| 588 for (size_t i = 0; i < kNumTests; ++i) { |
| 589 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
| 590 header->final = kTests[i].final; |
| 591 header->reserved1 = kTests[i].reserved1; |
| 592 header->reserved2 = kTests[i].reserved2; |
| 593 header->reserved3 = kTests[i].reserved3; |
| 594 header->opcode = WebSocketFrameHeader::kOpCodeText; |
| 595 header->masked = false; |
| 596 header->payload_length = 0; |
| 597 |
| 598 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| 599 chunk->header = header.Pass(); |
| 600 chunk->final_chunk = true; |
| 601 // chunk->data is empty. |
| 602 |
| 603 ScopedVector<WebSocketFrameChunk> chunks; |
| 604 chunks.push_back(chunk.release()); |
| 605 |
| 606 std::vector<char> expected_output( |
| 607 kTests[i].frame_header, |
| 608 kTests[i].frame_header + kTests[i].frame_header_length); |
| 609 std::vector<char> output; |
| 610 EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
| 611 EXPECT_EQ(expected_output, output); |
| 612 EXPECT_FALSE(builder.failed()); |
| 613 } |
| 614 } |
| 615 |
| 616 TEST(WebSocketFrameBuilderDeathTest, NewChunkAfterUnfinishedChunk) { |
| 617 scoped_ptr<WebSocketFrameChunk> chunk1 = |
| 618 CreateFrameChunkWithMessage("Hello", 5); |
| 619 chunk1->final_chunk = false; |
| 620 scoped_ptr<WebSocketFrameChunk> chunk2 = |
| 621 CreateFrameChunkWithMessage("World", 5); |
| 622 |
| 623 ScopedVector<WebSocketFrameChunk> chunks; |
| 624 chunks.push_back(chunk1.release()); |
| 625 chunks.push_back(chunk2.release()); |
| 626 |
| 627 ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
| 628 } |
| 629 |
| 630 TEST(WebSocketFrameBuilderDeathTest, NoHeaderInFirstChunk) { |
| 631 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| 632 chunk->final_chunk = true; |
| 633 |
| 634 ScopedVector<WebSocketFrameChunk> chunks; |
| 635 chunks.push_back(chunk.release()); |
| 636 |
| 637 ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
| 638 } |
| 639 |
| 640 TEST(WebSocketFrameBuilderDeathTest, InsufficientPayloadData) { |
| 641 scoped_ptr<WebSocketFrameChunk> chunk = |
| 642 CreateFrameChunkWithMessage("Hello", 5); |
| 643 chunk->data.erase(chunk->data.end() - 1); |
| 644 |
| 645 ScopedVector<WebSocketFrameChunk> chunks; |
| 646 chunks.push_back(chunk.release()); |
| 647 |
| 648 ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
| 649 } |
| 650 |
| 651 TEST(WebSocketFrameBuilderDeathTest, ExcessivePayloadData) { |
| 652 scoped_ptr<WebSocketFrameChunk> chunk = |
| 653 CreateFrameChunkWithMessage("Hello", 5); |
| 654 chunk->data.push_back('!'); |
| 655 chunk->final_chunk = false; |
| 656 |
| 657 ScopedVector<WebSocketFrameChunk> chunks; |
| 658 chunks.push_back(chunk.release()); |
| 659 |
| 660 ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
| 661 } |
| 662 |
| 663 } // namespace net |
OLD | NEW |