| // 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 "base/basictypes.h" |
| #include "base/port.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 { |
| |
| // TODO(ricea): Add tests for |
| // - Empty frames (data & control) |
| // - Non-NULL masking key |
| // - A frame larger than kReadBufferSize; |
| |
| const char kSampleFrame[] = "\x81\x06Sample"; |
| const size_t kSampleFrameSize = arraysize(kSampleFrame) - 1; |
| const char kPartialLargeFrame[] = |
| "\x81\x7F\x00\x00\x00\x00\x7F\xFF\xFF\xFF" |
| "chromiunum ad pasco per loca insanis pullum manducat frumenti"; |
| const size_t kPartialLargeFrameSize = arraysize(kPartialLargeFrame) - 1; |
| const size_t kLargeFrameHeaderSize = 10; |
| const size_t kLargeFrameDeclaredPayloadSize = 0x7FFFFFFF; |
| const char kMultipleFrames[] = "\x81\x01X\x81\x01Y\x81\x01Z"; |
| const size_t kMultipleFramesSize = arraysize(kMultipleFrames) - 1; |
| // This frame encodes a payload length of 7 in two bytes, which is always |
| // invalid. |
| const char kInvalidFrame[] = "\x81\x7E\x00\x07Invalid"; |
| const size_t kInvalidFrameSize = arraysize(kInvalidFrame) - 1; |
| const char kWriteFrame[] = "\x81\x85\x00\x00\x00\x00Write"; |
| const size_t kWriteFrameSize = arraysize(kWriteFrame) - 1; |
| const WebSocketMaskingKey kNulMaskingKey = {{'\0', '\0', '\0', '\0'}}; |
| |
| // Generates a ScopedVector<WebSocketFrameChunk> which will have a wire format |
| // matching kWriteFrame. |
| ScopedVector<WebSocketFrameChunk> GenerateWriteFrame() { |
| scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); |
| const size_t payload_size = |
| kWriteFrameSize - (WebSocketFrameHeader::kBaseHeaderSize + |
| WebSocketFrameHeader::kMaskingKeyLength); |
| chunk->data = new IOBufferWithSize(payload_size); |
| memcpy(chunk->data->data(), |
| kWriteFrame + kWriteFrameSize - payload_size, |
| payload_size); |
| chunk->final_chunk = true; |
| scoped_ptr<WebSocketFrameHeader> header( |
| new WebSocketFrameHeader(WebSocketFrameHeader::kOpCodeText)); |
| header->final = true; |
| header->masked = true; |
| header->payload_length = payload_size; |
| chunk->header = header.Pass(); |
| ScopedVector<WebSocketFrameChunk> chunks; |
| chunks.push_back(chunk.release()); |
| return chunks.Pass(); |
| } |
| |
| // A masking key generator function which generates the identity mask, |
| // ie. "\0\0\0\0". |
| WebSocketMaskingKey GenerateNulMaskingKey() { return kNulMaskingKey; } |
| |
| // Base class for WebSocketBasicStream test fixtures. |
| class WebSocketBasicStreamTest : public ::testing::Test { |
| protected: |
| scoped_ptr<WebSocketBasicStream> stream_; |
| CapturingNetLog net_log_; |
| }; |
| |
| // A fixture for tests which only perform normal socket operations. |
| class WebSocketBasicStreamSocketTest : public WebSocketBasicStreamTest { |
| protected: |
| WebSocketBasicStreamSocketTest() |
| : histograms_("a"), pool_(1, 1, &histograms_, &factory_) {} |
| |
| 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 StaticSocketDataProvider(reads, reads_count, writes, writes_count)); |
| 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_, |
| &GenerateNulMaskingKey); |
| } |
| |
| template <size_t N> |
| void CreateReadOnly(MockRead (&reads)[N]) { |
| CreateStream(reads, N, NULL, 0); |
| } |
| |
| template <size_t N> |
| void CreateWriteOnly(MockWrite (&writes)[N]) { |
| CreateStream(NULL, 0, writes, N); |
| } |
| |
| void CreateNullStream() { CreateStream(NULL, 0, NULL, 0); } |
| |
| scoped_ptr<SocketDataProvider> socket_data_; |
| MockClientSocketFactory factory_; |
| ClientSocketPoolHistograms histograms_; |
| MockTransportClientSocketPool pool_; |
| CapturingBoundNetLog(bound_net_log_); |
| ScopedVector<WebSocketFrameChunk> frame_chunks_; |
| TestCompletionCallback cb_; |
| scoped_refptr<GrowableIOBuffer> http_read_buffer_; |
| std::string sub_protocol_; |
| std::string extensions_; |
| }; |
| |
| TEST_F(WebSocketBasicStreamSocketTest, ConstructionWorks) { |
| CreateNullStream(); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, SyncReadWorks) { |
| MockRead reads[] = {MockRead(SYNCHRONOUS, kSampleFrame, kSampleFrameSize)}; |
| CreateReadOnly(reads); |
| int result = stream_->ReadFrames(&frame_chunks_, cb_.callback()); |
| EXPECT_EQ(OK, result); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(GG_UINT64_C(6), frame_chunks_[0]->header->payload_length); |
| EXPECT_TRUE(frame_chunks_[0]->header->final); |
| EXPECT_TRUE(frame_chunks_[0]->final_chunk); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncReadWorks) { |
| MockRead reads[] = {MockRead(ASYNC, kSampleFrame, kSampleFrameSize)}; |
| CreateReadOnly(reads); |
| int result = stream_->ReadFrames(&frame_chunks_, cb_.callback()); |
| ASSERT_EQ(ERR_IO_PENDING, result); |
| EXPECT_EQ(OK, cb_.WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(GG_UINT64_C(6), frame_chunks_[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(WebSocketBasicStreamSocketTest, HeaderFragmentedSync) { |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, kSampleFrame, 1), |
| MockRead(SYNCHRONOUS, kSampleFrame + 1, kSampleFrameSize - 1)}; |
| CreateReadOnly(reads); |
| int result = stream_->ReadFrames(&frame_chunks_, cb_.callback()); |
| ASSERT_EQ(OK, result); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(GG_UINT64_C(6), frame_chunks_[0]->header->payload_length); |
| } |
| |
| // The same behaviour applies to asynchronous reads. |
| TEST_F(WebSocketBasicStreamSocketTest, HeaderFragmentedAsync) { |
| MockRead reads[] = {MockRead(ASYNC, kSampleFrame, 1), |
| MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1)}; |
| CreateReadOnly(reads); |
| int result = stream_->ReadFrames(&frame_chunks_, cb_.callback()); |
| ASSERT_EQ(ERR_IO_PENDING, result); |
| EXPECT_EQ(OK, cb_.WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(GG_UINT64_C(6), frame_chunks_[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(&frame_chunks_, cb_.callback()); |
| ASSERT_EQ(ERR_IO_PENDING, result); |
| EXPECT_EQ(OK, cb_.WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(GG_UINT64_C(6), frame_chunks_[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(&frame_chunks_, cb_.callback())); |
| } |
| |
| // A frame that does not arrive in a single read should arrive in chunks. |
| TEST_F(WebSocketBasicStreamSocketTest, LargeFrameFirstChunk) { |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, kPartialLargeFrame, kPartialLargeFrameSize)}; |
| CreateReadOnly(reads); |
| EXPECT_EQ(OK, stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(kLargeFrameDeclaredPayloadSize, |
| frame_chunks_[0]->header->payload_length); |
| EXPECT_TRUE(frame_chunks_[0]->header->final); |
| EXPECT_FALSE(frame_chunks_[0]->final_chunk); |
| EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize, |
| static_cast<size_t>(frame_chunks_[0]->data->size())); |
| } |
| |
| // If only the header arrives, we should get a zero-byte chunk. |
| TEST_F(WebSocketBasicStreamSocketTest, HeaderOnlyChunk) { |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize)}; |
| CreateReadOnly(reads); |
| EXPECT_EQ(OK, stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| EXPECT_FALSE(frame_chunks_[0]->final_chunk); |
| EXPECT_TRUE(frame_chunks_[0]->data.get() == NULL); |
| } |
| |
| // The second and subsequent chunks of a frame have no header. |
| TEST_F(WebSocketBasicStreamSocketTest, LargeFrameTwoChunks) { |
| static const size_t kChunkSize = 16; |
| MockRead reads[] = { |
| MockRead(ASYNC, kPartialLargeFrame, kChunkSize), |
| MockRead(ASYNC, kPartialLargeFrame + kChunkSize, kChunkSize)}; |
| CreateReadOnly(reads); |
| TestCompletionCallback cb[2]; |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb[0].callback())); |
| EXPECT_EQ(OK, cb[0].WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| |
| frame_chunks_.clear(); |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb[1].callback())); |
| EXPECT_EQ(OK, cb[1].WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_FALSE(frame_chunks_[0]->header); |
| } |
| |
| // Only the final chunk of a frame has final_chunk set. |
| TEST_F(WebSocketBasicStreamSocketTest, OnlyFinalChunkIsFinal) { |
| static const size_t kFirstChunkSize = 4; |
| MockRead reads[] = {MockRead(ASYNC, kSampleFrame, kFirstChunkSize), |
| MockRead(ASYNC, |
| kSampleFrame + kFirstChunkSize, |
| kSampleFrameSize - kFirstChunkSize)}; |
| CreateReadOnly(reads); |
| TestCompletionCallback cb[2]; |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb[0].callback())); |
| EXPECT_EQ(OK, cb[0].WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_FALSE(frame_chunks_[0]->final_chunk); |
| |
| frame_chunks_.clear(); |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb[1].callback())); |
| EXPECT_EQ(OK, cb[1].WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->final_chunk); |
| } |
| |
| // Multiple frames that arrive together should be parsed correctly. |
| TEST_F(WebSocketBasicStreamSocketTest, ThreeFramesTogether) { |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, kMultipleFrames, kMultipleFramesSize)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(OK, stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| ASSERT_EQ(3U, frame_chunks_.size()); |
| EXPECT_TRUE(frame_chunks_[0]->final_chunk); |
| EXPECT_TRUE(frame_chunks_[1]->final_chunk); |
| EXPECT_TRUE(frame_chunks_[2]->final_chunk); |
| } |
| |
| // ERR_CONNECTION_CLOSED must be returned on close. |
| TEST_F(WebSocketBasicStreamSocketTest, SyncClose) { |
| MockRead reads[] = {MockRead(SYNCHRONOUS, "", 0)}; |
| CreateReadOnly(reads); |
| |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncClose) { |
| MockRead reads[] = {MockRead(ASYNC, "", 0)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, 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(WebSocketBasicStreamSocketTest, SyncCloseWithErr) { |
| MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED)}; |
| CreateReadOnly(reads); |
| |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncCloseWithErr) { |
| MockRead reads[] = {MockRead(ASYNC, ERR_CONNECTION_CLOSED)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, SyncErrorsPassedThrough) { |
| // ERR_INSUFFICIENT_RESOURCES here represents an arbitrary error that |
| // WebSocketBasicStream gives no special handling to. |
| MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_INSUFFICIENT_RESOURCES)}; |
| CreateReadOnly(reads); |
| |
| EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncErrorsPassedThrough) { |
| MockRead reads[] = {MockRead(ASYNC, ERR_INSUFFICIENT_RESOURCES)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, 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(WebSocketBasicStreamSocketTest, CloseAfterFrame) { |
| MockRead reads[] = {MockRead(SYNCHRONOUS, kSampleFrame, kSampleFrameSize), |
| MockRead(SYNCHRONOUS, "", 0)}; |
| CreateReadOnly(reads); |
| |
| EXPECT_EQ(OK, stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| EXPECT_EQ(1U, frame_chunks_.size()); |
| frame_chunks_.clear(); |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, |
| stream_->ReadFrames(&frame_chunks_, 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(&frame_chunks_, cb_.callback())); |
| ASSERT_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(&frame_chunks_, cb_.callback())); |
| ASSERT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); |
| } |
| |
| // 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(&frame_chunks_, cb_.callback())); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->data); |
| EXPECT_EQ(6, frame_chunks_[0]->data->size()); |
| } |
| |
| // Check that a frame whose header partially arrived at the end of the response |
| // headers works correctly. |
| TEST_F(WebSocketBasicStreamSocketTest, PartialFrameHeaderInHttpResponse) { |
| SetHttpReadBuffer(kSampleFrame, 1); |
| MockRead reads[] = {MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| EXPECT_EQ(OK, cb_.WaitForResult()); |
| ASSERT_EQ(1U, frame_chunks_.size()); |
| ASSERT_TRUE(frame_chunks_[0]->data); |
| EXPECT_EQ(6, frame_chunks_[0]->data->size()); |
| ASSERT_TRUE(frame_chunks_[0]->header); |
| EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, |
| frame_chunks_[0]->header->opcode); |
| } |
| |
| // Check that an invalid frame results in an error. |
| TEST_F(WebSocketBasicStreamSocketTest, SyncInvalidFrame) { |
| MockRead reads[] = {MockRead(SYNCHRONOUS, kInvalidFrame, kInvalidFrameSize)}; |
| CreateReadOnly(reads); |
| |
| EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| } |
| |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncInvalidFrame) { |
| MockRead reads[] = {MockRead(ASYNC, kInvalidFrame, kInvalidFrameSize)}; |
| CreateReadOnly(reads); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->ReadFrames(&frame_chunks_, cb_.callback())); |
| EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult()); |
| } |
| |
| // Check that writing a frame all at once works. |
| TEST_F(WebSocketBasicStreamSocketTest, WriteAtOnce) { |
| MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, kWriteFrameSize)}; |
| CreateWriteOnly(writes); |
| frame_chunks_ = GenerateWriteFrame(); |
| |
| EXPECT_EQ(OK, stream_->WriteFrames(&frame_chunks_, cb_.callback())); |
| } |
| |
| // Check that completely async writing works. |
| TEST_F(WebSocketBasicStreamSocketTest, AsyncWriteAtOnce) { |
| MockWrite writes[] = {MockWrite(ASYNC, kWriteFrame, kWriteFrameSize)}; |
| CreateWriteOnly(writes); |
| frame_chunks_ = GenerateWriteFrame(); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->WriteFrames(&frame_chunks_, 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(WebSocketBasicStreamSocketTest, WriteInBits) { |
| MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, 4), |
| MockWrite(ASYNC, kWriteFrame + 4, 4), |
| MockWrite(ASYNC, kWriteFrame + 8, kWriteFrameSize - 8)}; |
| CreateWriteOnly(writes); |
| frame_chunks_ = GenerateWriteFrame(); |
| |
| ASSERT_EQ(ERR_IO_PENDING, |
| stream_->WriteFrames(&frame_chunks_, cb_.callback())); |
| EXPECT_EQ(OK, cb_.WaitForResult()); |
| } |
| |
| 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 |