Index: net/websockets/websocket_frame_builder_unittest.cc |
diff --git a/net/websockets/websocket_frame_builder_unittest.cc b/net/websockets/websocket_frame_builder_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..399f5390aa73f49e07cb535fda076c57bc5e1888 |
--- /dev/null |
+++ b/net/websockets/websocket_frame_builder_unittest.cc |
@@ -0,0 +1,663 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "net/websockets/websocket_frame_builder.h" |
+ |
+#include <algorithm> |
+ |
+#include "base/basictypes.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/scoped_vector.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "net/websockets/websocket_frame.h" |
+ |
+namespace { |
+ |
+const char kHello[] = "Hello, world!"; |
+const uint64 kHelloLength = arraysize(kHello) - 1; |
+ |
+struct ShortFrame { |
+ const char* payload; |
+ size_t payload_length; |
+ const char* expected_output; |
+ size_t expected_output_length; |
+}; |
+static const ShortFrame kShortFrames[] = { |
+ // Each output data is split into two string literals because C++ lexers |
+ // consume unlimited number of hex characters in a hex character escape |
+ // (e.g. "\x05F" is not treated as { '\x5', 'F', '\0' } but as |
+ // { '\x5F', '\0' }). |
+ { "First", 5, "\x81\x05" "First", 7 }, |
+ { "Second", 6, "\x81\x06" "Second", 8 }, |
+ { "Third", 5, "\x81\x05" "Third", 7 }, |
+ { "Fourth", 6, "\x81\x06" "Fourth", 8 }, |
+ { "Fifth", 5, "\x81\x05" "Fifth", 7 }, |
+ { "Sixth", 5, "\x81\x05" "Sixth", 7 }, |
+ { "Seventh", 7, "\x81\x07" "Seventh", 9 }, |
+ { "Eighth", 6, "\x81\x06" "Eighth", 8 }, |
+ { "Ninth", 5, "\x81\x05" "Ninth", 7 }, |
+ { "Tenth", 5, "\x81\x05" "Tenth", 7 } |
+}; |
+static const int kNumShortFrames = arraysize(kShortFrames); |
+ |
+scoped_ptr<net::WebSocketFrameChunk> CreateFrameChunkWithMessage( |
+ const char* message, |
+ size_t message_length) { |
+ scoped_ptr<net::WebSocketFrameHeader> frame_header( |
+ new net::WebSocketFrameHeader); |
+ frame_header->final = true; |
+ frame_header->reserved1 = false; |
+ frame_header->reserved2 = false; |
+ frame_header->reserved3 = false; |
+ frame_header->opcode = net::WebSocketFrameHeader::kOpCodeText; |
+ frame_header->masked = false; |
+ frame_header->payload_length = message_length; |
+ |
+ scoped_ptr<net::WebSocketFrameChunk> frame_chunk( |
+ new net::WebSocketFrameChunk); |
+ frame_chunk->header = frame_header.Pass(); |
+ frame_chunk->final_chunk = true; |
+ frame_chunk->data.assign(message, message + message_length); |
+ return frame_chunk.Pass(); |
+} |
+ |
+ScopedVector<net::WebSocketFrameChunk> CreateChunksVectorWithSingleMessage( |
+ const char* message, |
+ size_t message_length) { |
+ scoped_ptr<net::WebSocketFrameChunk> frame_chunk = |
+ CreateFrameChunkWithMessage(message, message_length); |
+ ScopedVector<net::WebSocketFrameChunk> frame_chunks; |
+ frame_chunks.push_back(frame_chunk.release()); |
+ return frame_chunks.Pass(); |
+} |
+ |
+void ExpectBuilderDiesOrFailsOnChunks( |
+ ScopedVector<net::WebSocketFrameChunk> frame_chunks) { |
+ // Note: EXPECT_DEBUG_DEATH does not work on Win32 accoring to comments in |
+ // spdy_protocol_test.cc. We don't run the test in that case. |
+#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) |
+ net::WebSocketFrameBuilder builder; |
+ std::vector<char> output; |
+#if !defined(DCHECK_ALWAYS_ON) |
+ EXPECT_DEBUG_DEATH({ |
+ EXPECT_FALSE(builder.Encode(frame_chunks.Pass(), &output)); |
+ EXPECT_TRUE(builder.failed()); |
+ }, ""); |
+#else |
+ EXPECT_DEATH({ |
+ EXPECT_FALSE(builder.Encode(frame_chunks.Pass(), &output)); |
+ EXPECT_TRUE(builder.failed()); |
+ }, ""); |
+#endif |
+#endif |
+} |
+ |
+} // Unnamed namespace. |
+ |
+namespace net { |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeNormalFrame) { |
+ static const char kHelloFrame[] = "\x81\x0DHello, world!"; |
+ static const uint64 kHelloFrameLength = arraysize(kHelloFrame) - 1; |
+ |
+ ScopedVector<WebSocketFrameChunk> frame_chunks = |
+ CreateChunksVectorWithSingleMessage(kHello, kHelloLength); |
+ |
+ WebSocketFrameBuilder builder; |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
+ std::vector<char> expected_output(kHelloFrame, |
+ kHelloFrame + kHelloFrameLength); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeMaskedFrame) { |
+ static const size_t kMaskingKeyLength = |
+ WebSocketFrameHeader::kMaskingKeyLength; |
+ static const char kMaskingKey[] = "\xDE\xAD\xBE\xEF"; |
+ COMPILE_ASSERT(arraysize(kMaskingKey) - 1 == kMaskingKeyLength, |
+ incorrect_masking_key_length); |
+ static const char kMaskedHelloFrame[] = |
+ "\x81\x8D\xDE\xAD\xBE\xEF" |
+ "\x96\xC8\xD2\x83\xB1\x81\x9E\x98\xB1\xDF\xD2\x8B\xFF"; |
+ static const uint64 kMaskedHelloFrameLength = |
+ arraysize(kMaskedHelloFrame) - 1; |
+ |
+ ScopedVector<WebSocketFrameChunk> frame_chunks = |
+ CreateChunksVectorWithSingleMessage(kHello, kHelloLength); |
+ frame_chunks[0]->header->masked = true; |
+ |
+ WebSocketFrameBuilder builder; |
+ builder.PinMaskingKeyForTesting(kMaskingKey); |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
+ std::vector<char> expected_output( |
+ kMaskedHelloFrame, kMaskedHelloFrame + kMaskedHelloFrameLength); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeMultipleFramesAtOnce) { |
+ ScopedVector<WebSocketFrameChunk> frame_chunks; |
+ std::vector<char> expected_output; |
+ for (int i = 0; i < kNumShortFrames; ++i) { |
+ scoped_ptr<WebSocketFrameChunk> frame_chunk = CreateFrameChunkWithMessage( |
+ kShortFrames[i].payload, kShortFrames[i].payload_length); |
+ frame_chunks.push_back(frame_chunk.release()); |
+ expected_output.insert( |
+ expected_output.end(), |
+ kShortFrames[i].expected_output, |
+ kShortFrames[i].expected_output + |
+ kShortFrames[i].expected_output_length); |
+ } |
+ |
+ WebSocketFrameBuilder builder; |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeMultipleFramesSerially) { |
+ WebSocketFrameBuilder builder; |
+ for (int i = 0; i < kNumShortFrames; ++i) { |
+ ScopedVector<WebSocketFrameChunk> frame_chunks = |
+ CreateChunksVectorWithSingleMessage(kShortFrames[i].payload, |
+ kShortFrames[i].payload_length); |
+ std::vector<char> expected_output( |
+ kShortFrames[i].expected_output, |
+ kShortFrames[i].expected_output + |
+ kShortFrames[i].expected_output_length); |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(frame_chunks.Pass(), &output)); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeChunkedFrames) { |
+ // Send two messages in three steps: |
+ // 1. { message1[:cutting_pos1] } |
+ // 2. { message1[cutting_pos1:], message2[:cutting_pos2] } |
+ // 3. { message2[cutting_pos2:] } |
+ // where a[x:y] is Python-style slice notation. |
+ struct Message { |
+ const char* payload; |
+ size_t payload_length; |
+ const char* expected_header; |
+ size_t expected_header_length; |
+ }; |
+ static const Message kMessage1 = { "Larry Page", 10, "\x81\x0A", 2 }; |
+ static const Message kMessage2 = { "Sergey Brin", 11, "\x81\x0B", 2 }; |
+ |
+ for (size_t cutting_pos1 = 0; cutting_pos1 <= kMessage1.payload_length; |
+ ++cutting_pos1) { |
+ for (size_t cutting_pos2 = 0; cutting_pos2 <= kMessage2.payload_length; |
+ ++cutting_pos2) { |
+ scoped_ptr<WebSocketFrameHeader> header1(new WebSocketFrameHeader); |
+ header1->final = true; |
+ header1->reserved1 = false; |
+ header1->reserved2 = false; |
+ header1->reserved3 = false; |
+ header1->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header1->masked = false; |
+ header1->payload_length = kMessage1.payload_length; |
+ |
+ scoped_ptr<WebSocketFrameHeader> header2(new WebSocketFrameHeader); |
+ header2->final = true; |
+ header2->reserved1 = false; |
+ header2->reserved2 = false; |
+ header2->reserved3 = false; |
+ header2->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header2->masked = false; |
+ header2->payload_length = kMessage2.payload_length; |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk11(new WebSocketFrameChunk); |
+ chunk11->header = header1.Pass(); |
+ chunk11->final_chunk = false; |
+ chunk11->data.assign(kMessage1.payload, kMessage1.payload + cutting_pos1); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk12(new WebSocketFrameChunk); |
+ chunk12->final_chunk = true; |
+ chunk12->data.assign(kMessage1.payload + cutting_pos1, |
+ kMessage1.payload + kMessage1.payload_length); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk21(new WebSocketFrameChunk); |
+ chunk21->header = header2.Pass(); |
+ chunk21->final_chunk = false; |
+ chunk21->data.assign(kMessage2.payload, kMessage2.payload + cutting_pos2); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk22(new WebSocketFrameChunk); |
+ chunk22->final_chunk = true; |
+ chunk22->data.assign(kMessage2.payload + cutting_pos2, |
+ kMessage2.payload + kMessage2.payload_length); |
+ |
+ WebSocketFrameBuilder builder; |
+ |
+ // Step 1: Encode chunk11. |
+ std::vector<char> expected_output1; |
+ expected_output1.insert( |
+ expected_output1.end(), |
+ kMessage1.expected_header, |
+ kMessage1.expected_header + kMessage1.expected_header_length); |
+ expected_output1.insert( |
+ expected_output1.end(), |
+ kMessage1.payload, |
+ kMessage1.payload + cutting_pos1); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks1; |
+ chunks1.push_back(chunk11.release()); |
+ std::vector<char> output1; |
+ EXPECT_TRUE(builder.Encode(chunks1.Pass(), &output1)); |
+ EXPECT_EQ(expected_output1, output1); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // Step 2: Encode chunk12 and chunk 21. |
+ std::vector<char> expected_output2; |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage1.payload + cutting_pos1, |
+ kMessage1.payload + kMessage1.payload_length); |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage2.expected_header, |
+ kMessage2.expected_header + kMessage2.expected_header_length); |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage2.payload, |
+ kMessage2.payload + cutting_pos2); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks2; |
+ chunks2.push_back(chunk12.release()); |
+ chunks2.push_back(chunk21.release()); |
+ std::vector<char> output2; |
+ EXPECT_TRUE(builder.Encode(chunks2.Pass(), &output2)); |
+ EXPECT_EQ(expected_output2, output2); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // Step 3: Encode chunk22. |
+ std::vector<char> expected_output3( |
+ kMessage2.payload + cutting_pos2, |
+ kMessage2.payload + kMessage2.payload_length); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks3; |
+ chunks3.push_back(chunk22.release()); |
+ std::vector<char> output3; |
+ EXPECT_TRUE(builder.Encode(chunks3.Pass(), &output3)); |
+ EXPECT_EQ(expected_output3, output3); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // If we have found any test failure, let's exit the test early |
+ // to avoid excessive console output. |
+ if (HasFailure()) { |
+ FAIL() << "Failed: cutting_pos1 = " << cutting_pos1 << ", " |
+ << "cutting_pos2 = " << cutting_pos2; |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeChunkedMaskedFrames) { |
+ // Same as above, but this time we mask frame payloads. |
+ struct Message { |
+ const char* masking_key; |
+ const char* payload; |
+ const char* masked_payload; |
+ size_t payload_length; |
+ const char* expected_header; |
+ size_t expected_header_length; |
+ }; |
+ |
+ static const Message kMessage1 = { |
+ "\xDE\xAD\xBE\xEF", |
+ "Larry Page", "\x92\xCC\xCC\x9D\xA7\x8D\xEE\x8E\xB9\xC8", 10, |
+ "\x81\x8A\xDE\xAD\xBE\xEF", 6 |
+ }; |
+ static const Message kMessage2 = { |
+ "\xBA\xAD\xCA\xFE", |
+ "Sergey Brin", "\xE9\xC8\xB8\x99\xDF\xD4\xEA\xBC\xC8\xC4\xA4", 11, |
+ "\x81\x8B\xBA\xAD\xCA\xFE", 6 |
+ }; |
+ |
+ for (size_t cutting_pos1 = 0; cutting_pos1 < kMessage1.payload_length; |
+ ++cutting_pos1) { |
+ for (size_t cutting_pos2 = 0; cutting_pos2 < kMessage2.payload_length; |
+ ++cutting_pos2) { |
+ scoped_ptr<WebSocketFrameHeader> header1(new WebSocketFrameHeader); |
+ header1->final = true; |
+ header1->reserved1 = false; |
+ header1->reserved2 = false; |
+ header1->reserved3 = false; |
+ header1->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header1->masked = true; |
+ header1->payload_length = kMessage1.payload_length; |
+ |
+ scoped_ptr<WebSocketFrameHeader> header2(new WebSocketFrameHeader); |
+ header2->final = true; |
+ header2->reserved1 = false; |
+ header2->reserved2 = false; |
+ header2->reserved3 = false; |
+ header2->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header2->masked = true; |
+ header2->payload_length = kMessage2.payload_length; |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk11(new WebSocketFrameChunk); |
+ chunk11->header = header1.Pass(); |
+ chunk11->final_chunk = false; |
+ chunk11->data.assign(kMessage1.payload, kMessage1.payload + cutting_pos1); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk12(new WebSocketFrameChunk); |
+ chunk12->final_chunk = true; |
+ chunk12->data.assign(kMessage1.payload + cutting_pos1, |
+ kMessage1.payload + kMessage1.payload_length); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk21(new WebSocketFrameChunk); |
+ chunk21->header = header2.Pass(); |
+ chunk21->final_chunk = false; |
+ chunk21->data.assign(kMessage2.payload, kMessage2.payload + cutting_pos2); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk22(new WebSocketFrameChunk); |
+ chunk22->final_chunk = true; |
+ chunk22->data.assign(kMessage2.payload + cutting_pos2, |
+ kMessage2.payload + kMessage2.payload_length); |
+ |
+ WebSocketFrameBuilder builder; |
+ |
+ // Step 1: Encode chunk11. |
+ std::vector<char> expected_output1; |
+ expected_output1.insert( |
+ expected_output1.end(), |
+ kMessage1.expected_header, |
+ kMessage1.expected_header + kMessage1.expected_header_length); |
+ expected_output1.insert( |
+ expected_output1.end(), |
+ kMessage1.masked_payload, |
+ kMessage1.masked_payload + cutting_pos1); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks1; |
+ chunks1.push_back(chunk11.release()); |
+ builder.PinMaskingKeyForTesting(kMessage1.masking_key); |
+ std::vector<char> output1; |
+ EXPECT_TRUE(builder.Encode(chunks1.Pass(), &output1)); |
+ EXPECT_EQ(expected_output1, output1); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // Step 2: Encode chunk12 and chunk 21. |
+ std::vector<char> expected_output2; |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage1.masked_payload + cutting_pos1, |
+ kMessage1.masked_payload + kMessage1.payload_length); |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage2.expected_header, |
+ kMessage2.expected_header + kMessage2.expected_header_length); |
+ expected_output2.insert( |
+ expected_output2.end(), |
+ kMessage2.masked_payload, |
+ kMessage2.masked_payload + cutting_pos2); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks2; |
+ chunks2.push_back(chunk12.release()); |
+ chunks2.push_back(chunk21.release()); |
+ builder.PinMaskingKeyForTesting(kMessage2.masking_key); |
+ std::vector<char> output2; |
+ EXPECT_TRUE(builder.Encode(chunks2.Pass(), &output2)); |
+ EXPECT_EQ(expected_output2, output2); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // Step 3: Encode chunk22. |
+ std::vector<char> expected_output3( |
+ kMessage2.masked_payload + cutting_pos2, |
+ kMessage2.masked_payload + kMessage2.payload_length); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks3; |
+ chunks3.push_back(chunk22.release()); |
+ std::vector<char> output3; |
+ EXPECT_TRUE(builder.Encode(chunks3.Pass(), &output3)); |
+ EXPECT_EQ(expected_output3, output3); |
+ EXPECT_FALSE(builder.failed()); |
+ |
+ // If we have found any test failure, let's exit the test early |
+ // to avoid excessive console output. |
+ if (HasFailure()) { |
+ FAIL() << "Failed: cutting_pos1 = " << cutting_pos1 << ", " |
+ << "cutting_pos2 = " << cutting_pos2; |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, FrameHeadersOfVariousLengths) { |
+ struct TestCase { |
+ const char* frame_header; |
+ size_t frame_header_length; |
+ uint64 frame_length; |
+ }; |
+ static const TestCase kTests[] = { |
+ { "\x81\x00", 2, GG_UINT64_C(0) }, |
+ { "\x81\x7D", 2, GG_UINT64_C(125) }, |
+ { "\x81\x7E\x00\x7E", 4, GG_UINT64_C(126) }, |
+ { "\x81\x7E\xFF\xFF", 4, GG_UINT64_C(0xFFFF) }, |
+ { "\x81\x7F\x00\x00\x00\x00\x00\x01\x00\x00", 10, GG_UINT64_C(0x10000) }, |
+ { "\x81\x7F\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 10, |
+ GG_UINT64_C(0x7FFFFFFFFFFFFFFF) } |
+ }; |
+ static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
+ |
+ for (size_t i = 0; i < kNumTests; ++i) { |
+ scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
+ header->final = true; |
+ header->reserved1 = false; |
+ header->reserved2 = false; |
+ header->reserved3 = false; |
+ header->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header->masked = false; |
+ header->payload_length = kTests[i].frame_length; |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
+ chunk->header = header.Pass(); |
+ chunk->final_chunk = false; // Let the chunk carry no data. |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ std::vector<char> expected_output( |
+ kTests[i].frame_header, |
+ kTests[i].frame_header + kTests[i].frame_header_length); |
+ |
+ WebSocketFrameBuilder builder; |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, EncodeTooLargeFrame) { |
+ scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
+ header->final = true; |
+ header->reserved1 = false; |
+ header->reserved2 = false; |
+ header->reserved3 = false; |
+ header->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header->masked = false; |
+ header->payload_length = GG_UINT64_C(0x8000000000000000); |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
+ chunk->header = header.Pass(); |
+ chunk->final_chunk = false; |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ WebSocketFrameBuilder builder; |
+ std::vector<char> output; |
+ EXPECT_FALSE(builder.Encode(chunks.Pass(), &output)); |
+ EXPECT_TRUE(builder.failed()); |
+ |
+ // Once the builder has failed, it won't accept any more chunks. |
+ chunks = CreateChunksVectorWithSingleMessage("Hello!", 6); |
+ EXPECT_FALSE(builder.Encode(chunks.Pass(), &output)); |
+ EXPECT_TRUE(builder.failed()); |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, FrameTypes) { |
+ struct TestCase { |
+ const char* frame_header; |
+ size_t frame_header_length; |
+ WebSocketFrameHeader::OpCode opcode; |
+ }; |
+ static const TestCase kTests[] = { |
+ { "\x80\x00", 2, WebSocketFrameHeader::kOpCodeContinuation }, |
+ { "\x81\x00", 2, WebSocketFrameHeader::kOpCodeText }, |
+ { "\x82\x00", 2, WebSocketFrameHeader::kOpCodeBinary }, |
+ { "\x88\x00", 2, WebSocketFrameHeader::kOpCodeClose }, |
+ { "\x89\x00", 2, WebSocketFrameHeader::kOpCodePing }, |
+ { "\x8A\x00", 2, WebSocketFrameHeader::kOpCodePong }, |
+ // These are undefined opcodes, but the builder should accept them anyway. |
+ { "\x83\x00", 2, 0x3 }, |
+ { "\x84\x00", 2, 0x4 }, |
+ { "\x85\x00", 2, 0x5 }, |
+ { "\x86\x00", 2, 0x6 }, |
+ { "\x87\x00", 2, 0x7 }, |
+ { "\x8B\x00", 2, 0xB }, |
+ { "\x8C\x00", 2, 0xC }, |
+ { "\x8D\x00", 2, 0xD }, |
+ { "\x8E\x00", 2, 0xE }, |
+ { "\x8F\x00", 2, 0xF } |
+ }; |
+ static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
+ |
+ WebSocketFrameBuilder builder; |
+ |
+ for (size_t i = 0; i < kNumTests; ++i) { |
+ scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
+ header->final = true; |
+ header->reserved1 = false; |
+ header->reserved2 = false; |
+ header->reserved3 = false; |
+ header->opcode = kTests[i].opcode; |
+ header->masked = false; |
+ header->payload_length = 0; |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
+ chunk->header = header.Pass(); |
+ chunk->final_chunk = true; |
+ // chunk->data is empty. |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ std::vector<char> expected_output( |
+ kTests[i].frame_header, |
+ kTests[i].frame_header + kTests[i].frame_header_length); |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderTest, FinalBitAndReservedBits) { |
+ struct TestCase { |
+ const char* frame_header; |
+ size_t frame_header_length; |
+ bool final; |
+ bool reserved1; |
+ bool reserved2; |
+ bool reserved3; |
+ }; |
+ static const TestCase kTests[] = { |
+ { "\x81\x00", 2, true, false, false, false }, |
+ { "\x01\x00", 2, false, false, false, false }, |
+ { "\xC1\x00", 2, true, true, false, false }, |
+ { "\xA1\x00", 2, true, false, true, false }, |
+ { "\x91\x00", 2, true, false, false, true }, |
+ { "\x71\x00", 2, false, true, true, true }, |
+ { "\xF1\x00", 2, true, true, true, true } |
+ }; |
+ static const size_t kNumTests = ARRAYSIZE_UNSAFE(kTests); |
+ |
+ WebSocketFrameBuilder builder; |
+ |
+ for (size_t i = 0; i < kNumTests; ++i) { |
+ scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader); |
+ header->final = kTests[i].final; |
+ header->reserved1 = kTests[i].reserved1; |
+ header->reserved2 = kTests[i].reserved2; |
+ header->reserved3 = kTests[i].reserved3; |
+ header->opcode = WebSocketFrameHeader::kOpCodeText; |
+ header->masked = false; |
+ header->payload_length = 0; |
+ |
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
+ chunk->header = header.Pass(); |
+ chunk->final_chunk = true; |
+ // chunk->data is empty. |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ std::vector<char> expected_output( |
+ kTests[i].frame_header, |
+ kTests[i].frame_header + kTests[i].frame_header_length); |
+ std::vector<char> output; |
+ EXPECT_TRUE(builder.Encode(chunks.Pass(), &output)); |
+ EXPECT_EQ(expected_output, output); |
+ EXPECT_FALSE(builder.failed()); |
+ } |
+} |
+ |
+TEST(WebSocketFrameBuilderDeathTest, NewChunkAfterUnfinishedChunk) { |
+ scoped_ptr<WebSocketFrameChunk> chunk1 = |
+ CreateFrameChunkWithMessage("Hello", 5); |
+ chunk1->final_chunk = false; |
+ scoped_ptr<WebSocketFrameChunk> chunk2 = |
+ CreateFrameChunkWithMessage("World", 5); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk1.release()); |
+ chunks.push_back(chunk2.release()); |
+ |
+ ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
+} |
+ |
+TEST(WebSocketFrameBuilderDeathTest, NoHeaderInFirstChunk) { |
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
+ chunk->final_chunk = true; |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
+} |
+ |
+TEST(WebSocketFrameBuilderDeathTest, InsufficientPayloadData) { |
+ scoped_ptr<WebSocketFrameChunk> chunk = |
+ CreateFrameChunkWithMessage("Hello", 5); |
+ chunk->data.erase(chunk->data.end() - 1); |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
+} |
+ |
+TEST(WebSocketFrameBuilderDeathTest, ExcessivePayloadData) { |
+ scoped_ptr<WebSocketFrameChunk> chunk = |
+ CreateFrameChunkWithMessage("Hello", 5); |
+ chunk->data.push_back('!'); |
+ chunk->final_chunk = false; |
+ |
+ ScopedVector<WebSocketFrameChunk> chunks; |
+ chunks.push_back(chunk.release()); |
+ |
+ ExpectBuilderDiesOrFailsOnChunks(chunks.Pass()); |
+} |
+ |
+} // namespace net |