// 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. #include "net/websockets/websocket_deflate_stream.h" #include <stdint.h> #include <deque> #include <string> #include "base/basictypes.h" #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/websockets/websocket_deflate_predictor.h" #include "net/websockets/websocket_deflater.h" #include "net/websockets/websocket_frame.h" #include "net/websockets/websocket_inflater.h" #include "net/websockets/websocket_stream.h" #include "net/websockets/websocket_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { typedef ::testing::MockFunction<void(int)> MockCallback; // NOLINT using ::testing::_; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Return; typedef uint32_t FrameFlag; const FrameFlag kNoFlag = 0; const FrameFlag kFinal = 1; const FrameFlag kReserved1 = 2; // We don't define values for other flags because we don't need them. // The value must equal to the value of the corresponding // constant in websocket_deflate_stream.cc const size_t kChunkSize = 4 * 1024; const int kWindowBits = 15; scoped_refptr<IOBuffer> ToIOBuffer(const std::string& s) { scoped_refptr<IOBuffer> buffer = new IOBuffer(s.size()); memcpy(buffer->data(), s.data(), s.size()); return buffer; } std::string ToString(IOBufferWithSize* buffer) { return std::string(buffer->data(), buffer->size()); } std::string ToString(const scoped_refptr<IOBufferWithSize>& buffer) { return ToString(buffer.get()); } std::string ToString(IOBuffer* buffer, size_t size) { return std::string(buffer->data(), size); } std::string ToString(const scoped_refptr<IOBuffer>& buffer, size_t size) { return ToString(buffer.get(), size); } std::string ToString(const WebSocketFrame* frame) { return frame->data ? ToString(frame->data, frame->header.payload_length) : ""; } void AppendTo(ScopedVector<WebSocketFrame>* frames, WebSocketFrameHeader::OpCode opcode, FrameFlag flag, const std::string& data) { scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(opcode)); frame->header.final = (flag & kFinal); frame->header.reserved1 = (flag & kReserved1); frame->data = ToIOBuffer(data); frame->header.payload_length = data.size(); frames->push_back(frame.release()); } void AppendTo(ScopedVector<WebSocketFrame>* frames, WebSocketFrameHeader::OpCode opcode, FrameFlag flag) { scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(opcode)); frame->header.final = (flag & kFinal); frame->header.reserved1 = (flag & kReserved1); frames->push_back(frame.release()); } class MockWebSocketStream : public WebSocketStream { public: MOCK_METHOD2(ReadFrames, int(ScopedVector<WebSocketFrame>*, const CompletionCallback&)); MOCK_METHOD2(WriteFrames, int(ScopedVector<WebSocketFrame>*, const CompletionCallback&)); MOCK_METHOD0(Close, void()); MOCK_CONST_METHOD0(GetSubProtocol, std::string()); MOCK_CONST_METHOD0(GetExtensions, std::string()); }; // This mock class relies on some assumptions. // - RecordInputDataFrame is called after the corresponding WriteFrames // call. // - RecordWrittenDataFrame is called before writing the frame. class WebSocketDeflatePredictorMock : public WebSocketDeflatePredictor { public: WebSocketDeflatePredictorMock() : result_(DEFLATE) {} virtual ~WebSocketDeflatePredictorMock() { // Verify whether all expectaions are consumed. if (!frames_to_be_input_.empty()) { ADD_FAILURE() << "There are missing frames to be input."; return; } if (!frames_written_.empty()) { ADD_FAILURE() << "There are extra written frames."; return; } } // WebSocketDeflatePredictor functions. virtual Result Predict(const ScopedVector<WebSocketFrame>& frames, size_t frame_index) OVERRIDE { return result_; } virtual void RecordInputDataFrame(const WebSocketFrame* frame) OVERRIDE { if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) { ADD_FAILURE() << "Control frames should not be recorded."; return; } if (frame->header.reserved1) { ADD_FAILURE() << "Input frame may not be compressed."; return; } if (frames_to_be_input_.empty()) { ADD_FAILURE() << "Unexpected input data frame"; return; } if (frame != frames_to_be_input_.front()) { ADD_FAILURE() << "Input data frame does not match the expectation."; return; } frames_to_be_input_.pop_front(); } virtual void RecordWrittenDataFrame(const WebSocketFrame* frame) OVERRIDE { if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) { ADD_FAILURE() << "Control frames should not be recorded."; return; } frames_written_.push_back(frame); } // Sets |result_| for the |Predict| return value. void set_result(Result result) { result_ = result; } // Adds |frame| as an expectation of future |RecordInputDataFrame| call. void AddFrameToBeInput(const WebSocketFrame* frame) { if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) return; frames_to_be_input_.push_back(frame); } // Verifies that |frame| is recorded in order. void VerifySentFrame(const WebSocketFrame* frame) { if (!WebSocketFrameHeader::IsKnownDataOpCode(frame->header.opcode)) return; if (frames_written_.empty()) { ADD_FAILURE() << "There are missing frames to be written."; return; } if (frame != frames_written_.front()) { ADD_FAILURE() << "Written data frame does not match the expectation."; return; } frames_written_.pop_front(); } void AddFramesToBeInput(const ScopedVector<WebSocketFrame>& frames) { for (size_t i = 0; i < frames.size(); ++i) AddFrameToBeInput(frames[i]); } void VerifySentFrames(const ScopedVector<WebSocketFrame>& frames) { for (size_t i = 0; i < frames.size(); ++i) VerifySentFrame(frames[i]); } // Call this method in order to disable checks in the destructor when // WriteFrames fails. void Clear() { frames_to_be_input_.clear(); frames_written_.clear(); } private: Result result_; // Data frames which will be recorded by |RecordInputFrames|. // Pushed by |AddFrameToBeInput| and popped and verified by // |RecordInputFrames|. std::deque<const WebSocketFrame*> frames_to_be_input_; // Data frames recorded by |RecordWrittenFrames|. // Pushed by |RecordWrittenFrames| and popped and verified by // |VerifySentFrame|. std::deque<const WebSocketFrame*> frames_written_; DISALLOW_COPY_AND_ASSIGN(WebSocketDeflatePredictorMock); }; class WebSocketDeflateStreamTest : public ::testing::Test { public: WebSocketDeflateStreamTest() : mock_stream_(NULL) { mock_stream_ = new testing::StrictMock<MockWebSocketStream>; predictor_ = new WebSocketDeflatePredictorMock; deflate_stream_.reset(new WebSocketDeflateStream( scoped_ptr<WebSocketStream>(mock_stream_), WebSocketDeflater::TAKE_OVER_CONTEXT, scoped_ptr<WebSocketDeflatePredictor>(predictor_))); } virtual ~WebSocketDeflateStreamTest() {} protected: scoped_ptr<WebSocketDeflateStream> deflate_stream_; // Owned by |deflate_stream_|. MockWebSocketStream* mock_stream_; // Owned by |deflate_stream_|. WebSocketDeflatePredictorMock* predictor_; }; // Since WebSocketDeflater with DoNotTakeOverContext is well tested at // websocket_deflater_test.cc, we have only a few tests for this configuration // here. class WebSocketDeflateStreamWithDoNotTakeOverContextTest : public ::testing::Test { public: WebSocketDeflateStreamWithDoNotTakeOverContextTest() : mock_stream_(NULL) { mock_stream_ = new testing::StrictMock<MockWebSocketStream>; predictor_ = new WebSocketDeflatePredictorMock; deflate_stream_.reset(new WebSocketDeflateStream( scoped_ptr<WebSocketStream>(mock_stream_), WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT, scoped_ptr<WebSocketDeflatePredictor>(predictor_))); } virtual ~WebSocketDeflateStreamWithDoNotTakeOverContextTest() {} protected: scoped_ptr<WebSocketDeflateStream> deflate_stream_; // |mock_stream_| will be deleted when |deflate_stream_| is destroyed. MockWebSocketStream* mock_stream_; // |predictor_| will be deleted when |deflate_stream_| is destroyed. WebSocketDeflatePredictorMock* predictor_; }; // ReadFrameStub is a stub for WebSocketStream::ReadFrames. // It returns |result_| and |frames_to_output_| to the caller and // saves parameters to |frames_passed_| and |callback_|. class ReadFramesStub { public: explicit ReadFramesStub(int result) : result_(result) {} ReadFramesStub(int result, ScopedVector<WebSocketFrame>* frames_to_output) : result_(result) { frames_to_output_.swap(*frames_to_output); } int Call(ScopedVector<WebSocketFrame>* frames, const CompletionCallback& callback) { DCHECK(frames->empty()); frames_passed_ = frames; callback_ = callback; frames->swap(frames_to_output_); return result_; } int result() const { return result_; } const CompletionCallback callback() const { return callback_; } ScopedVector<WebSocketFrame>* frames_passed() { return frames_passed_; } private: int result_; CompletionCallback callback_; ScopedVector<WebSocketFrame> frames_to_output_; ScopedVector<WebSocketFrame>* frames_passed_; }; // WriteFramesStub is a stub for WebSocketStream::WriteFrames. // It returns |result_| and |frames_| to the caller and // saves |callback| parameter to |callback_|. class WriteFramesStub { public: explicit WriteFramesStub(WebSocketDeflatePredictorMock* predictor, int result) : result_(result), predictor_(predictor) {} int Call(ScopedVector<WebSocketFrame>* frames, const CompletionCallback& callback) { frames_.insert(frames_.end(), frames->begin(), frames->end()); frames->weak_clear(); callback_ = callback; predictor_->VerifySentFrames(frames_); return result_; } int result() const { return result_; } const CompletionCallback callback() const { return callback_; } ScopedVector<WebSocketFrame>* frames() { return &frames_; } private: int result_; CompletionCallback callback_; ScopedVector<WebSocketFrame> frames_; WebSocketDeflatePredictorMock* predictor_; }; TEST_F(WebSocketDeflateStreamTest, ReadFailedImmediately) { ScopedVector<WebSocketFrame> frames; CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Return(ERR_FAILED)); } EXPECT_EQ(ERR_FAILED, deflate_stream_->ReadFrames(&frames, callback)); } TEST_F(WebSocketDeflateStreamTest, ReadUncompressedFrameImmediately) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, "hello"); ReadFramesStub stub(OK, &frames_to_output); ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } CompletionCallback callback; ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadUncompressedFrameAsync) { ReadFramesStub stub(ERR_IO_PENDING); ScopedVector<WebSocketFrame> frames; MockCallback mock_callback, checkpoint; CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(mock_callback, Call(OK)); } ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(0u, frames.size()); checkpoint.Call(0); AppendTo(stub.frames_passed(), WebSocketFrameHeader::kOpCodeText, kFinal, "hello"); stub.callback().Run(OK); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadFailedAsync) { ReadFramesStub stub(ERR_IO_PENDING); ScopedVector<WebSocketFrame> frames; MockCallback mock_callback, checkpoint; CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(mock_callback, Call(ERR_FAILED)); } ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(0u, frames.size()); checkpoint.Call(0); AppendTo(stub.frames_passed(), WebSocketFrameHeader::kOpCodeText, kFinal, "hello"); stub.callback().Run(ERR_FAILED); ASSERT_EQ(0u, frames.size()); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameImmediately) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7)); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameAsync) { ReadFramesStub stub(ERR_IO_PENDING); MockCallback mock_callback, checkpoint; CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(mock_callback, Call(OK)); } ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback)); checkpoint.Call(0); AppendTo(stub.frames_passed(), WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7)); stub.callback().Run(OK); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameFragmentImmediatelyButInflaterReturnsPending) { ScopedVector<WebSocketFrame> frames_to_output; const std::string data1("\xf2", 1); const std::string data2("\x48\xcd\xc9\xc9\x07\x00", 6); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, data1); ReadFramesStub stub1(OK, &frames_to_output), stub2(ERR_IO_PENDING); MockCallback mock_callback, checkpoint; CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub1, &ReadFramesStub::Call)) .WillOnce(Invoke(&stub2, &ReadFramesStub::Call)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(mock_callback, Call(OK)); } ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(0u, frames.size()); AppendTo(stub2.frames_passed(), WebSocketFrameHeader::kOpCodeText, kFinal, data2); checkpoint.Call(0); stub2.callback().Run(OK); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadInvalidCompressedPayload) { const std::string data("\xf2\x48\xcdINVALID", 10); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, data); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(ERR_WS_PROTOCOL_ERROR, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(0u, frames.size()); } TEST_F(WebSocketDeflateStreamTest, MergeMultipleFramesInReadFrames) { const std::string data1("\xf2\x48\xcd", 3); const std::string data2("\xc9\xc9\x07\x00", 4); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, data1); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal, data2); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadUncompressedEmptyFrames) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kNoFlag); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_FALSE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("", ToString(frames[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedEmptyFrames) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, std::string("\x02\x00", 1)); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedFrameFollowedByEmptyFrame) { const std::string data("\xf2\x48\xcd\xc9\xc9\x07\x00", 7); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, data); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(1u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[0])); } TEST_F(WebSocketDeflateStreamTest, ReadControlFrameBetweenDataFrames) { const std::string data1("\xf2\x48\xcd", 3); const std::string data2("\xc9\xc9\x07\x00", 4); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, data1); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodePing, kFinal); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, data2); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodePing, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("Hello", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, SplitToMultipleFramesInReadFrames) { WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT); deflater.Initialize(kWindowBits); const size_t kSize = kChunkSize * 3; const std::string original_data(kSize, 'a'); deflater.AddBytes(original_data.data(), original_data.size()); deflater.Finish(); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeBinary, kFinal | kReserved1, ToString(deflater.GetOutput(deflater.CurrentOutputSize()))); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(3u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeBinary, frames[0]->header.opcode); EXPECT_FALSE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[0]->header.payload_length)); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames[1]->header.opcode); EXPECT_FALSE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[1]->header.payload_length)); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames[2]->header.opcode); EXPECT_TRUE(frames[2]->header.final); EXPECT_FALSE(frames[2]->header.reserved1); EXPECT_EQ(kChunkSize, static_cast<size_t>(frames[2]->header.payload_length)); EXPECT_EQ(original_data, ToString(frames[0]) + ToString(frames[1]) + ToString(frames[2])); } TEST_F(WebSocketDeflateStreamTest, Reserved1TurnsOnDuringReadingCompressedContinuationFrame) { const std::string data1("\xf2\x48\xcd", 3); const std::string data2("\xc9\xc9\x07\x00", 4); ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kReserved1, data1); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal | kReserved1, data2); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(ERR_WS_PROTOCOL_ERROR, deflate_stream_->ReadFrames(&frames, callback)); } TEST_F(WebSocketDeflateStreamTest, Reserved1TurnsOnDuringReadingUncompressedContinuationFrame) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kNoFlag, "hello"); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeContinuation, kFinal | kReserved1, "world"); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(ERR_WS_PROTOCOL_ERROR, deflate_stream_->ReadFrames(&frames, callback)); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedMessages) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string( "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x31\x04\x00", 13)); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string("\x4a\x86\x33\x8d\x00\x00", 6)); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("compressed1", ToString(frames[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("compressed2", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, ReadUncompressedMessages) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, "uncompressed1"); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, "uncompressed2"); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("uncompressed1", ToString(frames[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("uncompressed2", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, ReadCompressedMessageThenUncompressedMessage) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string( "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x01\x00", 12)); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, "uncompressed"); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("compressed", ToString(frames[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("uncompressed", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, ReadUncompressedMessageThenCompressedMessage) { ScopedVector<WebSocketFrame> frames_to_output; AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal, "uncompressed"); AppendTo(&frames_to_output, WebSocketFrameHeader::kOpCodeText, kFinal | kReserved1, std::string( "\x4a\xce\xcf\x2d\x28\x4a\x2d\x2e\x4e\x4d\x01\x00", 12)); ReadFramesStub stub(OK, &frames_to_output); CompletionCallback callback; ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _)) .WillOnce(Invoke(&stub, &ReadFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->ReadFrames(&frames, callback)); ASSERT_EQ(2u, frames.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode); EXPECT_TRUE(frames[0]->header.final); EXPECT_FALSE(frames[0]->header.reserved1); EXPECT_EQ("uncompressed", ToString(frames[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[1]->header.opcode); EXPECT_TRUE(frames[1]->header.final); EXPECT_FALSE(frames[1]->header.reserved1); EXPECT_EQ("compressed", ToString(frames[1])); } TEST_F(WebSocketDeflateStreamTest, WriteEmpty) { ScopedVector<WebSocketFrame> frames; CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)).Times(0); } EXPECT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); } TEST_F(WebSocketDeflateStreamTest, WriteFailedImmediately) { ScopedVector<WebSocketFrame> frames; CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Return(ERR_FAILED)); } AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "hello"); predictor_->AddFramesToBeInput(frames); EXPECT_EQ(ERR_FAILED, deflate_stream_->WriteFrames(&frames, callback)); predictor_->Clear(); } TEST_F(WebSocketDeflateStreamTest, WriteFrameImmediately) { ScopedVector<WebSocketFrame> frames; CompletionCallback callback; WriteFramesStub stub(predictor_, OK); AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); predictor_->AddFramesToBeInput(frames); { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(_, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(1u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_TRUE(frames_passed[0]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[0])); } TEST_F(WebSocketDeflateStreamTest, WriteFrameAsync) { WriteFramesStub stub(predictor_, ERR_IO_PENDING); MockCallback mock_callback, checkpoint; CompletionCallback callback = base::Bind(&MockCallback::Call, base::Unretained(&mock_callback)); ScopedVector<WebSocketFrame> frames; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); EXPECT_CALL(checkpoint, Call(0)); EXPECT_CALL(mock_callback, Call(OK)); } AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); predictor_->AddFramesToBeInput(frames); ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->WriteFrames(&frames, callback)); checkpoint.Call(0); stub.callback().Run(OK); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(1u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_TRUE(frames_passed[0]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[0])); } TEST_F(WebSocketDeflateStreamTest, WriteControlFrameBetweenDataFrames) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "Hel"); AppendTo(&frames, WebSocketFrameHeader::kOpCodePing, kFinal); AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "lo"); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(2u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodePing, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_FALSE(frames_passed[0]->header.reserved1); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode); EXPECT_TRUE(frames_passed[1]->header.final); EXPECT_TRUE(frames_passed[1]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[1])); } TEST_F(WebSocketDeflateStreamTest, WriteEmptyMessage) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(1u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_TRUE(frames_passed[0]->header.reserved1); EXPECT_EQ(std::string("\x02\x00", 2), ToString(frames_passed[0])); } TEST_F(WebSocketDeflateStreamTest, WriteUncompressedMessage) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAA"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AAA"); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; predictor_->set_result(WebSocketDeflatePredictor::DO_NOT_DEFLATE); { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(2u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_FALSE(frames_passed[0]->header.final); EXPECT_FALSE(frames_passed[0]->header.reserved1); EXPECT_EQ("AAAA", ToString(frames_passed[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames_passed[1]->header.opcode); EXPECT_TRUE(frames_passed[1]->header.final); EXPECT_FALSE(frames_passed[1]->header.reserved1); EXPECT_EQ("AAA", ToString(frames_passed[1])); } TEST_F(WebSocketDeflateStreamTest, LargeDeflatedFramesShouldBeSplit) { WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT); LinearCongruentialGenerator lcg(133); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; const size_t size = 1024; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(_, _)) .WillRepeatedly(Invoke(&stub, &WriteFramesStub::Call)); } ScopedVector<WebSocketFrame> total_compressed_frames; deflater.Initialize(kWindowBits); while (true) { bool is_final = (total_compressed_frames.size() >= 2); ScopedVector<WebSocketFrame> frames; std::string data; for (size_t i = 0; i < size; ++i) data += static_cast<char>(lcg.Generate()); deflater.AddBytes(data.data(), data.size()); FrameFlag flag = is_final ? kFinal : kNoFlag; AppendTo(&frames, WebSocketFrameHeader::kOpCodeBinary, flag, data); predictor_->AddFramesToBeInput(frames); ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); total_compressed_frames.insert(total_compressed_frames.end(), stub.frames()->begin(), stub.frames()->end()); stub.frames()->weak_clear(); if (is_final) break; } deflater.Finish(); std::string total_deflated; for (size_t i = 0; i < total_compressed_frames.size(); ++i) { WebSocketFrame* frame = total_compressed_frames[i]; const WebSocketFrameHeader& header = frame->header; if (i > 0) { EXPECT_EQ(header.kOpCodeContinuation, header.opcode); EXPECT_FALSE(header.reserved1); } else { EXPECT_EQ(header.kOpCodeBinary, header.opcode); EXPECT_TRUE(header.reserved1); } const bool is_final_frame = (i + 1 == total_compressed_frames.size()); EXPECT_EQ(is_final_frame, header.final); if (!is_final_frame) EXPECT_GT(header.payload_length, 0ul); total_deflated += ToString(frame); } EXPECT_EQ(total_deflated, ToString(deflater.GetOutput(deflater.CurrentOutputSize()))); } TEST_F(WebSocketDeflateStreamTest, WriteMultipleMessages) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(2u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_TRUE(frames_passed[0]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode); EXPECT_TRUE(frames_passed[1]->header.final); EXPECT_TRUE(frames_passed[1]->header.reserved1); EXPECT_EQ(std::string("\xf2\x00\x11\x00\x00", 5), ToString(frames_passed[1])); } TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest, WriteMultipleMessages) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kFinal, "Hello"); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(2u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_TRUE(frames_passed[0]->header.final); EXPECT_TRUE(frames_passed[0]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[1]->header.opcode); EXPECT_TRUE(frames_passed[1]->header.final); EXPECT_TRUE(frames_passed[1]->header.reserved1); EXPECT_EQ(std::string("\xf2\x48\xcd\xc9\xc9\x07\x00", 7), ToString(frames_passed[1])); } // In order to check the stream works correctly for multiple // "PossiblyCompressedMessage"s, we test various messages at one test case. TEST_F(WebSocketDeflateStreamWithDoNotTakeOverContextTest, WritePossiblyCompressMessages) { ScopedVector<WebSocketFrame> frames; AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "He"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "llo"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "AAAAAAAAAA"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "AA"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeText, kNoFlag, "XX"); AppendTo(&frames, WebSocketFrameHeader::kOpCodeContinuation, kFinal, "YY"); predictor_->AddFramesToBeInput(frames); WriteFramesStub stub(predictor_, OK); CompletionCallback callback; predictor_->set_result(WebSocketDeflatePredictor::TRY_DEFLATE); { InSequence s; EXPECT_CALL(*mock_stream_, WriteFrames(&frames, _)) .WillOnce(Invoke(&stub, &WriteFramesStub::Call)); } ASSERT_EQ(OK, deflate_stream_->WriteFrames(&frames, callback)); const ScopedVector<WebSocketFrame>& frames_passed = *stub.frames(); ASSERT_EQ(5u, frames_passed.size()); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[0]->header.opcode); EXPECT_FALSE(frames_passed[0]->header.final); EXPECT_FALSE(frames_passed[0]->header.reserved1); EXPECT_EQ("He", ToString(frames_passed[0])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames_passed[1]->header.opcode); EXPECT_TRUE(frames_passed[1]->header.final); EXPECT_FALSE(frames_passed[1]->header.reserved1); EXPECT_EQ("llo", ToString(frames_passed[1])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[2]->header.opcode); EXPECT_TRUE(frames_passed[2]->header.final); EXPECT_TRUE(frames_passed[2]->header.reserved1); EXPECT_EQ(std::string("\x72\x74\x44\x00\x00\x00", 6), ToString(frames_passed[2])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_passed[3]->header.opcode); EXPECT_FALSE(frames_passed[3]->header.final); EXPECT_FALSE(frames_passed[3]->header.reserved1); EXPECT_EQ("XX", ToString(frames_passed[3])); EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, frames_passed[4]->header.opcode); EXPECT_TRUE(frames_passed[4]->header.final); EXPECT_FALSE(frames_passed[4]->header.reserved1); EXPECT_EQ("YY", ToString(frames_passed[4])); } } // namespace } // namespace net