普通文本  |  900行  |  34.83 KB

// Copyright 2013 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.
//
// Tests for WebSocketBasicStream. Note that we do not attempt to verify that
// frame parsing itself functions correctly, as that is covered by the
// WebSocketFrameParser tests.

#include "net/websockets/websocket_basic_stream.h"

#include <string.h>  // for memcpy() and memset().

#include <string>

#include "base/basictypes.h"
#include "base/port.h"
#include "net/base/big_endian.h"
#include "net/base/capturing_net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {
namespace {

#define WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(name, value) \
  const char k##name[] = value;                                  \
  const size_t k##name##Size = arraysize(k##name) - 1;

WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(SampleFrame, "\x81\x06Sample");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(
    PartialLargeFrame,
    "\x81\x7F\x00\x00\x00\x00\x7F\xFF\xFF\xFF"
    "chromiunum ad pasco per loca insanis pullum manducat frumenti");
const size_t kLargeFrameHeaderSize = 10;
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MultipleFrames,
                                            "\x81\x01X\x81\x01Y\x81\x01Z");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFirstFrame, "\x01\x00");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyMiddleFrame, "\x00\x00");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalTextFrame, "\x81\x00");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalContinuationFrame,
                                            "\x80\x00");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(ValidPong, "\x8A\x00");
// This frame encodes a payload length of 7 in two bytes, which is always
// invalid.
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(InvalidFrame,
                                            "\x81\x7E\x00\x07Invalid");
// Control frames must have the FIN bit set. This one does not.
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(PingFrameWithoutFin, "\x09\x00");
// Control frames must have a payload of 125 bytes or less. This one has
// a payload of 126 bytes.
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(
    126BytePong,
    "\x8a\x7e\x00\x7eZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
    "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(CloseFrame,
                                            "\x88\x09\x03\xe8occludo");
WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(WriteFrame,
                                            "\x81\x85\x00\x00\x00\x00Write");
const WebSocketMaskingKey kNulMaskingKey = {{'\0', '\0', '\0', '\0'}};
const WebSocketMaskingKey kNonNulMaskingKey = {
    {'\x0d', '\x1b', '\x06', '\x17'}};

// A masking key generator function which generates the identity mask,
// ie. "\0\0\0\0".
WebSocketMaskingKey GenerateNulMaskingKey() { return kNulMaskingKey; }

// A masking key generation function which generates a fixed masking key with no
// nul characters.
WebSocketMaskingKey GenerateNonNulMaskingKey() { return kNonNulMaskingKey; }

// Base class for WebSocketBasicStream test fixtures.
class WebSocketBasicStreamTest : public ::testing::Test {
 protected:
  scoped_ptr<WebSocketBasicStream> stream_;
  CapturingNetLog net_log_;
};

// A subclass of StaticSocketDataProvider modified to require that all data
// expected to be read or written actually is.
class StrictStaticSocketDataProvider : public StaticSocketDataProvider {
 public:
  StrictStaticSocketDataProvider(MockRead* reads,
                                 size_t reads_count,
                                 MockWrite* writes,
                                 size_t writes_count,
                                 bool strict_mode)
      : StaticSocketDataProvider(reads, reads_count, writes, writes_count),
        strict_mode_(strict_mode) {}

  virtual ~StrictStaticSocketDataProvider() {
    if (strict_mode_) {
      EXPECT_EQ(read_count(), read_index());
      EXPECT_EQ(write_count(), write_index());
    }
  }

 private:
  const bool strict_mode_;
};

// A fixture for tests which only perform normal socket operations.
class WebSocketBasicStreamSocketTest : public WebSocketBasicStreamTest {
 protected:
  WebSocketBasicStreamSocketTest()
      : histograms_("a"),
        pool_(1, 1, &histograms_, &factory_),
        generator_(&GenerateNulMaskingKey),
        expect_all_io_to_complete_(true) {}

  virtual ~WebSocketBasicStreamSocketTest() {
    // stream_ has a reference to socket_data_ (via MockTCPClientSocket) and so
    // should be destroyed first.
    stream_.reset();
  }

  scoped_ptr<ClientSocketHandle> MakeTransportSocket(MockRead reads[],
                                                     size_t reads_count,
                                                     MockWrite writes[],
                                                     size_t writes_count) {
    socket_data_.reset(new StrictStaticSocketDataProvider(
        reads, reads_count, writes, writes_count, expect_all_io_to_complete_));
    socket_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
    factory_.AddSocketDataProvider(socket_data_.get());

    scoped_ptr<ClientSocketHandle> transport_socket(new ClientSocketHandle);
    scoped_refptr<MockTransportSocketParams> params;
    transport_socket->Init("a",
                           params,
                           MEDIUM,
                           CompletionCallback(),
                           &pool_,
                           bound_net_log_.bound());
    return transport_socket.Pass();
  }

  void SetHttpReadBuffer(const char* data, size_t size) {
    http_read_buffer_ = new GrowableIOBuffer;
    http_read_buffer_->SetCapacity(size);
    memcpy(http_read_buffer_->data(), data, size);
    http_read_buffer_->set_offset(size);
  }

  void CreateStream(MockRead reads[],
                    size_t reads_count,
                    MockWrite writes[],
                    size_t writes_count) {
    stream_ = WebSocketBasicStream::CreateWebSocketBasicStreamForTesting(
        MakeTransportSocket(reads, reads_count, writes, writes_count),
        http_read_buffer_,
        sub_protocol_,
        extensions_,
        generator_);
  }

  template <size_t N>
  void CreateReadOnly(MockRead (&reads)[N]) {
    CreateStream(reads, N, NULL, 0);
  }

  void CreateNullStream() { CreateStream(NULL, 0, NULL, 0); }

  scoped_ptr<SocketDataProvider> socket_data_;
  MockClientSocketFactory factory_;
  ClientSocketPoolHistograms histograms_;
  MockTransportClientSocketPool pool_;
  CapturingBoundNetLog(bound_net_log_);
  ScopedVector<WebSocketFrame> frames_;
  TestCompletionCallback cb_;
  scoped_refptr<GrowableIOBuffer> http_read_buffer_;
  std::string sub_protocol_;
  std::string extensions_;
  WebSocketBasicStream::WebSocketMaskingKeyGeneratorFunction generator_;
  bool expect_all_io_to_complete_;
};

// A test fixture for the common case of tests that only perform a single read.
class WebSocketBasicStreamSocketSingleReadTest
    : public WebSocketBasicStreamSocketTest {
 protected:
  void CreateRead(const MockRead& read) {
    reads_[0] = read;
    CreateStream(reads_, 1U, NULL, 0);
  }

  MockRead reads_[1];
};

// A test fixture for tests that perform chunked reads.
class WebSocketBasicStreamSocketChunkedReadTest
    : public WebSocketBasicStreamSocketTest {
 protected:
  // Specify the behaviour if there aren't enough chunks to use all the data. If
  // LAST_FRAME_BIG is specified, then the rest of the data will be
  // put in the last chunk. If LAST_FRAME_NOT_BIG is specified, then the last
  // frame will be no bigger than the rest of the frames (but it can be smaller,
  // if not enough data remains).
  enum LastFrameBehaviour {
    LAST_FRAME_BIG,
    LAST_FRAME_NOT_BIG
  };

  // Prepares a read from |data| of |data_size|, split into |number_of_chunks|,
  // each of |chunk_size| (except that the last chunk may be larger or
  // smaller). All reads must be either SYNCHRONOUS or ASYNC (not a mixture),
  // and errors cannot be simulated. Once data is exhausted, further reads will
  // return 0 (ie. connection closed).
  void CreateChunkedRead(IoMode mode,
                         const char data[],
                         size_t data_size,
                         int chunk_size,
                         int number_of_chunks,
                         LastFrameBehaviour last_frame_behaviour) {
    reads_.reset(new MockRead[number_of_chunks]);
    const char* start = data;
    for (int i = 0; i < number_of_chunks; ++i) {
      int len = chunk_size;
      const bool is_last_chunk = (i == number_of_chunks - 1);
      if ((last_frame_behaviour == LAST_FRAME_BIG && is_last_chunk) ||
          static_cast<int>(data + data_size - start) < len) {
        len = static_cast<int>(data + data_size - start);
      }
      reads_[i] = MockRead(mode, start, len);
      start += len;
    }
    CreateStream(reads_.get(), number_of_chunks, NULL, 0);
  }

  scoped_ptr<MockRead[]> reads_;
};

// Test fixture for write tests.
class WebSocketBasicStreamSocketWriteTest
    : public WebSocketBasicStreamSocketTest {
 protected:
  // All write tests use the same frame, so it is easiest to create it during
  // test creation.
  virtual void SetUp() OVERRIDE { PrepareWriteFrame(); }

  // Creates a WebSocketFrame with a wire format matching kWriteFrame and adds
  // it to |frames_|.
  void PrepareWriteFrame() {
    scoped_ptr<WebSocketFrame> frame(
        new WebSocketFrame(WebSocketFrameHeader::kOpCodeText));
    const size_t payload_size =
        kWriteFrameSize - (WebSocketFrameHeader::kBaseHeaderSize +
                           WebSocketFrameHeader::kMaskingKeyLength);
    frame->data = new IOBuffer(payload_size);
    memcpy(frame->data->data(),
           kWriteFrame + kWriteFrameSize - payload_size,
           payload_size);
    WebSocketFrameHeader& header = frame->header;
    header.final = true;
    header.masked = true;
    header.payload_length = payload_size;
    frames_.push_back(frame.release());
  }

  // Creates a stream that expects the listed writes.
  template <size_t N>
  void CreateWriteOnly(MockWrite (&writes)[N]) {
    CreateStream(NULL, 0, writes, N);
  }
};

TEST_F(WebSocketBasicStreamSocketTest, ConstructionWorks) {
  CreateNullStream();
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncReadWorks) {
  CreateRead(MockRead(SYNCHRONOUS, kSampleFrame, kSampleFrameSize));
  int result = stream_->ReadFrames(&frames_, cb_.callback());
  EXPECT_EQ(OK, result);
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
  EXPECT_TRUE(frames_[0]->header.final);
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncReadWorks) {
  CreateRead(MockRead(ASYNC, kSampleFrame, kSampleFrameSize));
  int result = stream_->ReadFrames(&frames_, cb_.callback());
  ASSERT_EQ(ERR_IO_PENDING, result);
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
  // Don't repeat all the tests from SyncReadWorks; just enough to be sure the
  // frame was really read.
}

// ReadFrames will not return a frame whose header has not been wholly received.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedSync) {
  CreateChunkedRead(
      SYNCHRONOUS, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG);
  int result = stream_->ReadFrames(&frames_, cb_.callback());
  EXPECT_EQ(OK, result);
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
}

// The same behaviour applies to asynchronous reads.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedAsync) {
  CreateChunkedRead(
      ASYNC, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG);
  int result = stream_->ReadFrames(&frames_, cb_.callback());
  ASSERT_EQ(ERR_IO_PENDING, result);
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
}

// If it receives an incomplete header in a synchronous call, then has to wait
// for the rest of the frame, ReadFrames will return ERR_IO_PENDING.
TEST_F(WebSocketBasicStreamSocketTest, HeaderFragmentedSyncAsync) {
  MockRead reads[] = {MockRead(SYNCHRONOUS, kSampleFrame, 1),
                      MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1)};
  CreateReadOnly(reads);
  int result = stream_->ReadFrames(&frames_, cb_.callback());
  ASSERT_EQ(ERR_IO_PENDING, result);
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
}

// An extended header should also return ERR_IO_PENDING if it is not completely
// received.
TEST_F(WebSocketBasicStreamSocketTest, FragmentedLargeHeader) {
  MockRead reads[] = {
      MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize - 1),
      MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
  CreateReadOnly(reads);
  EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
}

// A frame that does not arrive in a single read should be broken into separate
// frames.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, LargeFrameFirstChunk) {
  CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kPartialLargeFrameSize));
  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_FALSE(frames_[0]->header.final);
  EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize,
            static_cast<size_t>(frames_[0]->header.payload_length));
}

// If only the header of a data frame arrives, we should receive a frame with a
// zero-size payload.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, HeaderOnlyChunk) {
  CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize));

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(NULL, frames_[0]->data.get());
  EXPECT_EQ(0U, frames_[0]->header.payload_length);
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
}

// If the header and the body of a data frame arrive seperately, we should see
// them as separate frames.
TEST_F(WebSocketBasicStreamSocketTest, HeaderBodySeparated) {
  MockRead reads[] = {
      MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize),
      MockRead(ASYNC,
               kPartialLargeFrame + kLargeFrameHeaderSize,
               kPartialLargeFrameSize - kLargeFrameHeaderSize)};
  CreateReadOnly(reads);
  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(NULL, frames_[0]->data.get());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
  frames_.clear();
  EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize,
            frames_[0]->header.payload_length);
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames_[0]->header.opcode);
}

// Every frame has a header with a correct payload_length field.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, LargeFrameTwoChunks) {
  const size_t kChunkSize = 16;
  CreateChunkedRead(ASYNC,
                    kPartialLargeFrame,
                    kPartialLargeFrameSize,
                    kChunkSize,
                    2,
                    LAST_FRAME_NOT_BIG);
  TestCompletionCallback cb[2];

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
  EXPECT_EQ(OK, cb[0].WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(kChunkSize - kLargeFrameHeaderSize,
            frames_[0]->header.payload_length);

  frames_.clear();
  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
  EXPECT_EQ(OK, cb[1].WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(kChunkSize, frames_[0]->header.payload_length);
}

// Only the final frame of a fragmented message has |final| bit set.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OnlyFinalChunkIsFinal) {
  static const size_t kFirstChunkSize = 4;
  CreateChunkedRead(ASYNC,
                    kSampleFrame,
                    kSampleFrameSize,
                    kFirstChunkSize,
                    2,
                    LAST_FRAME_BIG);
  TestCompletionCallback cb[2];

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
  EXPECT_EQ(OK, cb[0].WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  ASSERT_FALSE(frames_[0]->header.final);

  frames_.clear();
  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
  EXPECT_EQ(OK, cb[1].WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  ASSERT_TRUE(frames_[0]->header.final);
}

// All frames after the first have their opcode changed to Continuation.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ContinuationOpCodeUsed) {
  const size_t kFirstChunkSize = 3;
  const int kChunkCount = 3;
  // The input data is one frame with opcode Text, which arrives in three
  // separate chunks.
  CreateChunkedRead(ASYNC,
                    kSampleFrame,
                    kSampleFrameSize,
                    kFirstChunkSize,
                    kChunkCount,
                    LAST_FRAME_BIG);
  TestCompletionCallback cb[kChunkCount];

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
  EXPECT_EQ(OK, cb[0].WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);

  // This test uses a loop to verify that the opcode for every frames generated
  // after the first is converted to Continuation.
  for (int i = 1; i < kChunkCount; ++i) {
    frames_.clear();
    ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[i].callback()));
    EXPECT_EQ(OK, cb[i].WaitForResult());
    ASSERT_EQ(1U, frames_.size());
    EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
              frames_[0]->header.opcode);
  }
}

// Multiple frames that arrive together should be parsed correctly.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, ThreeFramesTogether) {
  CreateRead(MockRead(SYNCHRONOUS, kMultipleFrames, kMultipleFramesSize));

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(3U, frames_.size());
  EXPECT_TRUE(frames_[0]->header.final);
  EXPECT_TRUE(frames_[1]->header.final);
  EXPECT_TRUE(frames_[2]->header.final);
}

// ERR_CONNECTION_CLOSED must be returned on close.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncClose) {
  CreateRead(MockRead(SYNCHRONOUS, "", 0));

  EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncClose) {
  CreateRead(MockRead(ASYNC, "", 0));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
}

// The result should be the same if the socket returns
// ERR_CONNECTION_CLOSED. This is not expected to happen on an established
// connection; a Read of size 0 is the expected behaviour. The key point of this
// test is to confirm that ReadFrames() behaviour is identical in both cases.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncCloseWithErr) {
  CreateRead(MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED));

  EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncCloseWithErr) {
  CreateRead(MockRead(ASYNC, ERR_CONNECTION_CLOSED));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncErrorsPassedThrough) {
  // ERR_INSUFFICIENT_RESOURCES here represents an arbitrary error that
  // WebSocketBasicStream gives no special handling to.
  CreateRead(MockRead(SYNCHRONOUS, ERR_INSUFFICIENT_RESOURCES));

  EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES,
            stream_->ReadFrames(&frames_, cb_.callback()));
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncErrorsPassedThrough) {
  CreateRead(MockRead(ASYNC, ERR_INSUFFICIENT_RESOURCES));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES, cb_.WaitForResult());
}

// If we get a frame followed by a close, we should receive them separately.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, CloseAfterFrame) {
  // The chunk size equals the data size, so the second chunk is 0 size, closing
  // the connection.
  CreateChunkedRead(SYNCHRONOUS,
                    kSampleFrame,
                    kSampleFrameSize,
                    kSampleFrameSize,
                    2,
                    LAST_FRAME_NOT_BIG);

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(1U, frames_.size());
  frames_.clear();
  EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
}

// Synchronous close after an async frame header is handled by a different code
// path.
TEST_F(WebSocketBasicStreamSocketTest, AsyncCloseAfterIncompleteHeader) {
  MockRead reads[] = {MockRead(ASYNC, kSampleFrame, 1U),
                      MockRead(SYNCHRONOUS, "", 0)};
  CreateReadOnly(reads);

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
}

// When Stream::Read returns ERR_CONNECTION_CLOSED we get the same result via a
// slightly different code path.
TEST_F(WebSocketBasicStreamSocketTest, AsyncErrCloseAfterIncompleteHeader) {
  MockRead reads[] = {MockRead(ASYNC, kSampleFrame, 1U),
                      MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED)};
  CreateReadOnly(reads);

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
}

// An empty first frame is not ignored.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFirstFrame) {
  CreateRead(MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize));

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(NULL, frames_[0]->data.get());
  EXPECT_EQ(0U, frames_[0]->header.payload_length);
}

// An empty frame in the middle of a message is ignored.
TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrame) {
  MockRead reads[] = {
      MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
      MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
      MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
  CreateReadOnly(reads);

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(1U, frames_.size());
  frames_.clear();
  EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
}

// An empty frame in the middle of a message that arrives separately is still
// ignored.
TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrameAsync) {
  MockRead reads[] = {
      MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
      MockRead(ASYNC, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
      // We include a pong message to verify the middle frame was actually
      // processed.
      MockRead(ASYNC, kValidPong, kValidPongSize)};
  CreateReadOnly(reads);

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(1U, frames_.size());
  frames_.clear();
  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodePong, frames_[0]->header.opcode);
}

// An empty final frame is not ignored.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFinalFrame) {
  CreateRead(
      MockRead(SYNCHRONOUS, kEmptyFinalTextFrame, kEmptyFinalTextFrameSize));

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(NULL, frames_[0]->data.get());
  EXPECT_EQ(0U, frames_[0]->header.payload_length);
}

// An empty middle frame is ignored with a final frame present.
TEST_F(WebSocketBasicStreamSocketTest, ThreeFrameEmptyMessage) {
  MockRead reads[] = {
      MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
      MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
      MockRead(SYNCHRONOUS,
               kEmptyFinalContinuationFrame,
               kEmptyFinalContinuationFrameSize)};
  CreateReadOnly(reads);

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
  frames_.clear();
  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_TRUE(frames_[0]->header.final);
}

// If there was a frame read at the same time as the response headers (and the
// handshake succeeded), then we should parse it.
TEST_F(WebSocketBasicStreamSocketTest, HttpReadBufferIsUsed) {
  SetHttpReadBuffer(kSampleFrame, kSampleFrameSize);
  CreateNullStream();

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  ASSERT_TRUE(frames_[0]->data);
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
}

// Check that a frame whose header partially arrived at the end of the response
// headers works correctly.
TEST_F(WebSocketBasicStreamSocketSingleReadTest,
       PartialFrameHeaderInHttpResponse) {
  SetHttpReadBuffer(kSampleFrame, 1);
  CreateRead(MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  ASSERT_TRUE(frames_[0]->data);
  EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length);
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
}

// Check that a control frame which partially arrives at the end of the response
// headers works correctly.
TEST_F(WebSocketBasicStreamSocketSingleReadTest,
       PartialControlFrameInHttpResponse) {
  const size_t kPartialFrameBytes = 3;
  SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes);
  CreateRead(MockRead(ASYNC,
                      kCloseFrame + kPartialFrameBytes,
                      kCloseFrameSize - kPartialFrameBytes));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
  EXPECT_EQ(kCloseFrameSize - 2, frames_[0]->header.payload_length);
  EXPECT_EQ(
      0,
      memcmp(frames_[0]->data->data(), kCloseFrame + 2, kCloseFrameSize - 2));
}

// Check that a control frame which partially arrives at the end of the response
// headers works correctly. Synchronous version (unlikely in practice).
TEST_F(WebSocketBasicStreamSocketSingleReadTest,
       PartialControlFrameInHttpResponseSync) {
  const size_t kPartialFrameBytes = 3;
  SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes);
  CreateRead(MockRead(SYNCHRONOUS,
                      kCloseFrame + kPartialFrameBytes,
                      kCloseFrameSize - kPartialFrameBytes));

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
}

// Check that an invalid frame results in an error.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncInvalidFrame) {
  CreateRead(MockRead(SYNCHRONOUS, kInvalidFrame, kInvalidFrameSize));

  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
}

TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncInvalidFrame) {
  CreateRead(MockRead(ASYNC, kInvalidFrame, kInvalidFrameSize));

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult());
}

// A control frame without a FIN flag is invalid and should not be passed
// through to higher layers. RFC6455 5.5 "All control frames ... MUST NOT be
// fragmented."
TEST_F(WebSocketBasicStreamSocketSingleReadTest, ControlFrameWithoutFin) {
  CreateRead(
      MockRead(SYNCHRONOUS, kPingFrameWithoutFin, kPingFrameWithoutFinSize));

  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_TRUE(frames_.empty());
}

// A control frame over 125 characters is invalid. RFC6455 5.5 "All control
// frames MUST have a payload length of 125 bytes or less". Since we use a
// 125-byte buffer to assemble fragmented control frames, we need to detect this
// error before attempting to assemble the fragments.
TEST_F(WebSocketBasicStreamSocketSingleReadTest, OverlongControlFrame) {
  CreateRead(MockRead(SYNCHRONOUS, k126BytePong, k126BytePongSize));

  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_TRUE(frames_.empty());
}

// A control frame over 125 characters should still be rejected if it is split
// into multiple chunks.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SplitOverlongControlFrame) {
  const size_t kFirstChunkSize = 16;
  expect_all_io_to_complete_ = false;
  CreateChunkedRead(SYNCHRONOUS,
                    k126BytePong,
                    k126BytePongSize,
                    kFirstChunkSize,
                    2,
                    LAST_FRAME_BIG);

  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_TRUE(frames_.empty());
}

TEST_F(WebSocketBasicStreamSocketChunkedReadTest,
       AsyncSplitOverlongControlFrame) {
  const size_t kFirstChunkSize = 16;
  expect_all_io_to_complete_ = false;
  CreateChunkedRead(ASYNC,
                    k126BytePong,
                    k126BytePongSize,
                    kFirstChunkSize,
                    2,
                    LAST_FRAME_BIG);

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult());
  // The caller should not call ReadFrames() again after receiving an error
  // other than ERR_IO_PENDING.
  EXPECT_TRUE(frames_.empty());
}

// In the synchronous case, ReadFrames assembles the whole control frame before
// returning.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SyncControlFrameAssembly) {
  const size_t kChunkSize = 3;
  CreateChunkedRead(
      SYNCHRONOUS, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG);

  EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
}

// In the asynchronous case, the callback is not called until the control frame
// has been completely assembled.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, AsyncControlFrameAssembly) {
  const size_t kChunkSize = 3;
  CreateChunkedRead(
      ASYNC, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG);

  ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
  ASSERT_EQ(1U, frames_.size());
  EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
}

// A frame with a 1MB payload that has to be read in chunks.
TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OneMegFrame) {
  // This should be equal to the definition of kReadBufferSize in
  // websocket_basic_stream.cc.
  const int kReadBufferSize = 32 * 1024;
  const uint64 kPayloadSize = 1 << 20;
  const size_t kWireSize = kPayloadSize + kLargeFrameHeaderSize;
  const size_t kExpectedFrameCount =
      (kWireSize + kReadBufferSize - 1) / kReadBufferSize;
  scoped_ptr<char[]> big_frame(new char[kWireSize]);
  memcpy(big_frame.get(), "\x81\x7F", 2);
  WriteBigEndian(big_frame.get() + 2, kPayloadSize);
  memset(big_frame.get() + kLargeFrameHeaderSize, 'A', kPayloadSize);

  CreateChunkedRead(ASYNC,
                    big_frame.get(),
                    kWireSize,
                    kReadBufferSize,
                    kExpectedFrameCount,
                    LAST_FRAME_BIG);

  for (size_t frame = 0; frame < kExpectedFrameCount; ++frame) {
    frames_.clear();
    ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
    EXPECT_EQ(OK, cb_.WaitForResult());
    ASSERT_EQ(1U, frames_.size());
    size_t expected_payload_size = kReadBufferSize;
    if (frame == 0) {
      expected_payload_size = kReadBufferSize - kLargeFrameHeaderSize;
    } else if (frame == kExpectedFrameCount - 1) {
      expected_payload_size = kLargeFrameHeaderSize;
    }
    EXPECT_EQ(expected_payload_size, frames_[0]->header.payload_length);
  }
}

// Check that writing a frame all at once works.
TEST_F(WebSocketBasicStreamSocketWriteTest, WriteAtOnce) {
  MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, kWriteFrameSize)};
  CreateWriteOnly(writes);

  EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback()));
}

// Check that completely async writing works.
TEST_F(WebSocketBasicStreamSocketWriteTest, AsyncWriteAtOnce) {
  MockWrite writes[] = {MockWrite(ASYNC, kWriteFrame, kWriteFrameSize)};
  CreateWriteOnly(writes);

  ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
}

// Check that writing a frame to an extremely full kernel buffer (so that it
// ends up being sent in bits) works. The WriteFrames() callback should not be
// called until all parts have been written.
TEST_F(WebSocketBasicStreamSocketWriteTest, WriteInBits) {
  MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, 4),
                        MockWrite(ASYNC, kWriteFrame + 4, 4),
                        MockWrite(ASYNC, kWriteFrame + 8, kWriteFrameSize - 8)};
  CreateWriteOnly(writes);

  ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback()));
  EXPECT_EQ(OK, cb_.WaitForResult());
}

// Check that writing with a non-NULL mask works correctly.
TEST_F(WebSocketBasicStreamSocketTest, WriteNonNulMask) {
  std::string masked_frame = std::string("\x81\x88");
  masked_frame += std::string(kNonNulMaskingKey.key, 4);
  masked_frame += "jiggered";
  MockWrite writes[] = {
      MockWrite(SYNCHRONOUS, masked_frame.data(), masked_frame.size())};
  generator_ = &GenerateNonNulMaskingKey;
  CreateStream(NULL, 0, writes, arraysize(writes));

  scoped_ptr<WebSocketFrame> frame(
      new WebSocketFrame(WebSocketFrameHeader::kOpCodeText));
  const std::string unmasked_payload = "graphics";
  const size_t payload_size = unmasked_payload.size();
  frame->data = new IOBuffer(payload_size);
  memcpy(frame->data->data(), unmasked_payload.data(), payload_size);
  WebSocketFrameHeader& header = frame->header;
  header.final = true;
  header.masked = true;
  header.payload_length = payload_size;
  frames_.push_back(frame.release());

  EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback()));
}

TEST_F(WebSocketBasicStreamSocketTest, GetExtensionsWorks) {
  extensions_ = "inflate-uuencode";
  CreateNullStream();

  EXPECT_EQ("inflate-uuencode", stream_->GetExtensions());
}

TEST_F(WebSocketBasicStreamSocketTest, GetSubProtocolWorks) {
  sub_protocol_ = "cyberchat";
  CreateNullStream();

  EXPECT_EQ("cyberchat", stream_->GetSubProtocol());
}

}  // namespace
}  // namespace net