diff options
Diffstat (limited to 'chromium/net/websockets/websocket_deflate_stream_test.cc')
-rw-r--r-- | chromium/net/websockets/websocket_deflate_stream_test.cc | 1206 |
1 files changed, 1206 insertions, 0 deletions
diff --git a/chromium/net/websockets/websocket_deflate_stream_test.cc b/chromium/net/websockets/websocket_deflate_stream_test.cc new file mode 100644 index 00000000000..1775962dce1 --- /dev/null +++ b/chromium/net/websockets/websocket_deflate_stream_test.cc @@ -0,0 +1,1206 @@ +// 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 |