summaryrefslogtreecommitdiff
path: root/chromium/net/websockets/websocket_deflate_stream_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/websockets/websocket_deflate_stream_test.cc')
-rw-r--r--chromium/net/websockets/websocket_deflate_stream_test.cc1206
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