| 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
|
|
|