| // 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 |