// 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),
predictor_(NULL) {}
virtual ~WebSocketDeflateStreamTest() {}
virtual void SetUp() {
Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, kWindowBits);
}
protected:
// Initialize deflate_stream_ with the given parameters.
void Initialize(WebSocketDeflater::ContextTakeOverMode mode,
int window_bits) {
mock_stream_ = new testing::StrictMock<MockWebSocketStream>;
predictor_ = new WebSocketDeflatePredictorMock;
deflate_stream_.reset(new WebSocketDeflateStream(
scoped_ptr<WebSocketStream>(mock_stream_),
mode,
window_bits,
scoped_ptr<WebSocketDeflatePredictor>(predictor_)));
}
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 WebSocketDeflateStreamTest {
public:
WebSocketDeflateStreamWithDoNotTakeOverContextTest() {}
virtual ~WebSocketDeflateStreamWithDoNotTakeOverContextTest() {}
virtual void SetUp() {
Initialize(WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT, kWindowBits);
}
};
class WebSocketDeflateStreamWithClientWindowBitsTest
: public WebSocketDeflateStreamTest {
public:
WebSocketDeflateStreamWithClientWindowBitsTest() {}
virtual ~WebSocketDeflateStreamWithClientWindowBitsTest() {}
// Overridden to postpone the call to Initialize().
virtual void SetUp() {}
// This needs to be called explicitly from the tests.
void SetUpWithWindowBits(int window_bits) {
Initialize(WebSocketDeflater::TAKE_OVER_CONTEXT, window_bits);
}
// Add a frame which will be compressed to a smaller size if the window
// size is large enough.
void AddCompressibleFrameString() {
const std::string word = "Chromium";
const std::string payload = word + std::string(256, 'a') + word;
AppendTo(&frames_, WebSocketFrameHeader::kOpCodeText, kFinal, payload);
predictor_->AddFramesToBeInput(frames_);
}
protected:
ScopedVector<WebSocketFrame> frames_;
};
// 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, InflaterInternalDataCanBeEmpty) {
WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
deflater.Initialize(kWindowBits);
const std::string original_data(kChunkSize, 'a');
deflater.AddBytes(original_data.data(), original_data.size());
deflater.Finish();
ScopedVector<WebSocketFrame> frames_to_output;
AppendTo(&frames_to_output,
WebSocketFrameHeader::kOpCodeBinary,
kReserved1,
ToString(deflater.GetOutput(deflater.CurrentOutputSize())));
AppendTo(&frames_to_output,
WebSocketFrameHeader::kOpCodeBinary,
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::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_TRUE(frames[1]->header.final);
EXPECT_FALSE(frames[1]->header.reserved1);
EXPECT_EQ(0u, static_cast<size_t>(frames[1]->header.payload_length));
EXPECT_EQ(original_data, ToString(frames[0]) + ToString(frames[1]));
}
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]));
}
// This is a regression test for crbug.com/343506.
TEST_F(WebSocketDeflateStreamTest, ReadEmptyAsyncFrame) {
ScopedVector<ReadFramesStub> stub_vector;
stub_vector.push_back(new ReadFramesStub(ERR_IO_PENDING));
stub_vector.push_back(new ReadFramesStub(ERR_IO_PENDING));
MockCallback mock_callback;
CompletionCallback callback =
base::Bind(&MockCallback::Call, base::Unretained(&mock_callback));
ScopedVector<WebSocketFrame> frames;
{
InSequence s;
EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
.WillOnce(Invoke(stub_vector[0], &ReadFramesStub::Call));
EXPECT_CALL(*mock_stream_, ReadFrames(&frames, _))
.WillOnce(Invoke(stub_vector[1], &ReadFramesStub::Call));
EXPECT_CALL(mock_callback, Call(OK));
}
ASSERT_EQ(ERR_IO_PENDING, deflate_stream_->ReadFrames(&frames, callback));
AppendTo(stub_vector[0]->frames_passed(),
WebSocketFrameHeader::kOpCodeText,
kReserved1,
std::string());
stub_vector[0]->callback().Run(OK);
AppendTo(stub_vector[1]->frames_passed(),
WebSocketFrameHeader::kOpCodeContinuation,
kFinal,
std::string("\x02\x00"));
stub_vector[1]->callback().Run(OK);
ASSERT_EQ(1u, frames.size());
EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames[0]->header.opcode);
EXPECT_EQ("", ToString(frames[0]));
}
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("\x00", 1), 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]));
}
// This is based on the similar test from websocket_deflater_test.cc
TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits8) {
SetUpWithWindowBits(8);
CompletionCallback callback;
AddCompressibleFrameString();
WriteFramesStub stub(predictor_, OK);
{
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(std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x39\xa3"
"(?7\xb3\x34\x17\x00", 21),
ToString(frames_passed[0]));
}
// The same input with window_bits=10 returns smaller output.
TEST_F(WebSocketDeflateStreamWithClientWindowBitsTest, WindowBits10) {
SetUpWithWindowBits(10);
CompletionCallback callback;
AddCompressibleFrameString();
WriteFramesStub stub(predictor_, OK);
{
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(
std::string("r\xce(\xca\xcf\xcd,\xcdM\x1c\xe1\xc0\x19\x1a\x0e\0\0", 17),
ToString(frames_passed[0]));
}
} // namespace
} // namespace net