summaryrefslogtreecommitdiffstats
path: root/net/websockets
diff options
context:
space:
mode:
Diffstat (limited to 'net/websockets')
-rw-r--r--net/websockets/README3
-rw-r--r--net/websockets/websocket_inflater.cc280
-rw-r--r--net/websockets/websocket_inflater.h130
-rw-r--r--net/websockets/websocket_inflater_test.cc245
4 files changed, 658 insertions, 0 deletions
diff --git a/net/websockets/README b/net/websockets/README
index 1d1e1c3..e9fd214f 100644
--- a/net/websockets/README
+++ b/net/websockets/README
@@ -53,6 +53,9 @@ websocket_frame_parser.cc
websocket_frame_parser.h
websocket_frame_parser_test.cc
websocket_frame_test.cc
+websocket_inflater.cc
+websocket_inflater.h
+websocket_inflater_test.cc
websocket_mux.h
websocket_stream_base.h
websocket_stream.cc
diff --git a/net/websockets/websocket_inflater.cc b/net/websockets/websocket_inflater.cc
new file mode 100644
index 0000000..fbfb079
--- /dev/null
+++ b/net/websockets/websocket_inflater.cc
@@ -0,0 +1,280 @@
+// 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_inflater.h"
+
+#include <algorithm>
+#include <deque>
+#include <vector>
+
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "third_party/zlib/zlib.h"
+
+namespace net {
+
+namespace {
+
+class ShrinkableIOBufferWithSize : public IOBufferWithSize {
+ public:
+ explicit ShrinkableIOBufferWithSize(int size)
+ : IOBufferWithSize(size) {}
+
+ void Shrink(int new_size) {
+ DCHECK_LE(new_size, size_);
+ size_ = new_size;
+ }
+
+ private:
+ virtual ~ShrinkableIOBufferWithSize() {}
+};
+
+} // namespace
+
+WebSocketInflater::WebSocketInflater()
+ : input_queue_(kDefaultInputIOBufferCapacity),
+ output_buffer_(kDefaultBufferCapacity) {}
+
+WebSocketInflater::WebSocketInflater(size_t input_queue_capacity,
+ size_t output_buffer_capacity)
+ : input_queue_(input_queue_capacity),
+ output_buffer_(output_buffer_capacity) {
+ DCHECK_GT(input_queue_capacity, 0u);
+ DCHECK_GT(output_buffer_capacity, 0u);
+}
+
+bool WebSocketInflater::Initialize(int window_bits) {
+ DCHECK_LE(8, window_bits);
+ DCHECK_GE(15, window_bits);
+ stream_.reset(new z_stream);
+ memset(stream_.get(), 0, sizeof(*stream_));
+ int result = inflateInit2(stream_.get(), -window_bits);
+ if (result != Z_OK) {
+ inflateEnd(stream_.get());
+ stream_.reset();
+ return false;
+ }
+ return true;
+}
+
+WebSocketInflater::~WebSocketInflater() {
+ if (stream_) {
+ inflateEnd(stream_.get());
+ stream_.reset();
+ }
+}
+
+bool WebSocketInflater::AddBytes(const char* data, size_t size) {
+ if (!size)
+ return true;
+
+ if (!input_queue_.IsEmpty()) {
+ // choked
+ input_queue_.Push(data, size);
+ return true;
+ }
+
+ int result = InflateWithFlush(data, size);
+ if (stream_->avail_in > 0)
+ input_queue_.Push(&data[size - stream_->avail_in], stream_->avail_in);
+
+ return result == Z_OK || result == Z_BUF_ERROR;
+}
+
+bool WebSocketInflater::Finish() {
+ return AddBytes("\x00\x00\xff\xff", 4);
+}
+
+scoped_refptr<IOBufferWithSize> WebSocketInflater::GetOutput(size_t size) {
+ scoped_refptr<ShrinkableIOBufferWithSize> buffer =
+ new ShrinkableIOBufferWithSize(size);
+ size_t num_bytes_copied = 0;
+
+ while (num_bytes_copied < size && output_buffer_.Size() > 0) {
+ size_t num_bytes_to_copy =
+ std::min(output_buffer_.Size(), size - num_bytes_copied);
+ output_buffer_.Read(&buffer->data()[num_bytes_copied], num_bytes_to_copy);
+ num_bytes_copied += num_bytes_to_copy;
+ int result = InflateChokedInput();
+ if (result != Z_OK && result != Z_BUF_ERROR)
+ return NULL;
+ }
+ buffer->Shrink(num_bytes_copied);
+ return buffer;
+}
+
+int WebSocketInflater::InflateWithFlush(const char* next_in, size_t avail_in) {
+ int result = Inflate(next_in, avail_in, Z_NO_FLUSH);
+ if (result != Z_OK && result != Z_BUF_ERROR)
+ return result;
+
+ if (CurrentOutputSize() > 0)
+ return result;
+ // CurrentOutputSize() == 0 means there is no data to be output,
+ // so we should make sure it by using Z_SYNC_FLUSH.
+ return Inflate(reinterpret_cast<const char*>(stream_->next_in),
+ stream_->avail_in,
+ Z_SYNC_FLUSH);
+}
+
+int WebSocketInflater::Inflate(const char* next_in,
+ size_t avail_in,
+ int flush) {
+ stream_->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(next_in));
+ stream_->avail_in = avail_in;
+
+ int result = Z_BUF_ERROR;
+ do {
+ std::pair<char*, size_t> tail = output_buffer_.GetTail();
+ if (!tail.second)
+ break;
+
+ stream_->next_out = reinterpret_cast<Bytef*>(tail.first);
+ stream_->avail_out = tail.second;
+ result = inflate(stream_.get(), flush);
+ output_buffer_.AdvanceTail(tail.second - stream_->avail_out);
+ if (result == Z_STREAM_END) {
+ // Received a block with BFINAL set to 1. Reset the decompression state.
+ result = inflateReset(stream_.get());
+ } else if (tail.second == stream_->avail_out) {
+ break;
+ }
+ } while (result == Z_OK || result == Z_BUF_ERROR);
+ return result;
+}
+
+int WebSocketInflater::InflateChokedInput() {
+ if (input_queue_.IsEmpty())
+ return InflateWithFlush(NULL, 0);
+
+ int result = Z_BUF_ERROR;
+ while (!input_queue_.IsEmpty()) {
+ std::pair<char*, size_t> top = input_queue_.Top();
+
+ result = InflateWithFlush(top.first, top.second);
+ input_queue_.Consume(top.second - stream_->avail_in);
+
+ if (result != Z_OK && result != Z_BUF_ERROR)
+ return result;
+
+ if (stream_->avail_in > 0) {
+ // There are some data which are not consumed.
+ break;
+ }
+ }
+ return result;
+}
+
+WebSocketInflater::OutputBuffer::OutputBuffer(size_t capacity)
+ : capacity_(capacity),
+ buffer_(capacity_ + 1), // 1 for sentinel
+ head_(0),
+ tail_(0) {}
+
+WebSocketInflater::OutputBuffer::~OutputBuffer() {}
+
+size_t WebSocketInflater::OutputBuffer::Size() const {
+ return (tail_ + buffer_.size() - head_) % buffer_.size();
+}
+
+std::pair<char*, size_t> WebSocketInflater::OutputBuffer::GetTail() {
+ return std::make_pair(&buffer_[tail_],
+ std::min(capacity_ - Size(), buffer_.size() - tail_));
+}
+
+void WebSocketInflater::OutputBuffer::Read(char* dest, size_t size) {
+ DCHECK_LE(size, Size());
+
+ size_t num_bytes_copied = 0;
+ if (tail_ < head_) {
+ size_t num_bytes_to_copy = std::min(size, buffer_.size() - head_);
+ memcpy(&dest[num_bytes_copied], &buffer_[head_], num_bytes_to_copy);
+ AdvanceHead(num_bytes_to_copy);
+ num_bytes_copied += num_bytes_to_copy;
+ }
+
+ if (num_bytes_copied == size)
+ return;
+ DCHECK_LE(head_, tail_);
+ size_t num_bytes_to_copy = size - num_bytes_copied;
+ DCHECK_LE(num_bytes_to_copy, tail_ - head_);
+ memcpy(&dest[num_bytes_copied], &buffer_[head_], num_bytes_to_copy);
+ AdvanceHead(num_bytes_to_copy);
+ num_bytes_copied += num_bytes_to_copy;
+ DCHECK_EQ(size, num_bytes_copied);
+ return;
+}
+
+void WebSocketInflater::OutputBuffer::AdvanceHead(size_t advance) {
+ DCHECK_LE(advance, Size());
+ head_ = (head_ + advance) % buffer_.size();
+}
+
+void WebSocketInflater::OutputBuffer::AdvanceTail(size_t advance) {
+ DCHECK_LE(advance + Size(), capacity_);
+ tail_ = (tail_ + advance) % buffer_.size();
+}
+
+WebSocketInflater::InputQueue::InputQueue(size_t capacity)
+ : capacity_(capacity), head_of_first_buffer_(0), tail_of_last_buffer_(0) {}
+
+WebSocketInflater::InputQueue::~InputQueue() {}
+
+std::pair<char*, size_t> WebSocketInflater::InputQueue::Top() {
+ DCHECK(!IsEmpty());
+ if (buffers_.size() == 1) {
+ return std::make_pair(&buffers_.front()->data()[head_of_first_buffer_],
+ tail_of_last_buffer_ - head_of_first_buffer_);
+ }
+ return std::make_pair(&buffers_.front()->data()[head_of_first_buffer_],
+ capacity_ - head_of_first_buffer_);
+}
+
+void WebSocketInflater::InputQueue::Push(const char* data, size_t size) {
+ if (!size)
+ return;
+
+ size_t num_copied_bytes = 0;
+ if (!IsEmpty())
+ num_copied_bytes += PushToLastBuffer(data, size);
+
+ while (num_copied_bytes < size) {
+ DCHECK(IsEmpty() || tail_of_last_buffer_ == capacity_);
+
+ buffers_.push_back(new IOBufferWithSize(capacity_));
+ tail_of_last_buffer_ = 0;
+ num_copied_bytes +=
+ PushToLastBuffer(&data[num_copied_bytes], size - num_copied_bytes);
+ }
+}
+
+void WebSocketInflater::InputQueue::Consume(size_t size) {
+ DCHECK(!IsEmpty());
+ DCHECK_LE(size + head_of_first_buffer_, capacity_);
+
+ head_of_first_buffer_ += size;
+ if (head_of_first_buffer_ == capacity_) {
+ buffers_.pop_front();
+ head_of_first_buffer_ = 0;
+ }
+ if (buffers_.size() == 1 && head_of_first_buffer_ == tail_of_last_buffer_) {
+ buffers_.pop_front();
+ head_of_first_buffer_ = 0;
+ tail_of_last_buffer_ = 0;
+ }
+}
+
+size_t WebSocketInflater::InputQueue::PushToLastBuffer(const char* data,
+ size_t size) {
+ DCHECK(!IsEmpty());
+ size_t num_bytes_to_copy = std::min(size, capacity_ - tail_of_last_buffer_);
+ if (!num_bytes_to_copy)
+ return 0;
+ IOBufferWithSize* buffer = buffers_.back().get();
+ memcpy(&buffer->data()[tail_of_last_buffer_], data, num_bytes_to_copy);
+ tail_of_last_buffer_ += num_bytes_to_copy;
+ return num_bytes_to_copy;
+}
+
+} // namespace net
diff --git a/net/websockets/websocket_inflater.h b/net/websockets/websocket_inflater.h
new file mode 100644
index 0000000..e57317f
--- /dev/null
+++ b/net/websockets/websocket_inflater.h
@@ -0,0 +1,130 @@
+// 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.
+
+#ifndef NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_
+#define NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_
+
+#include <deque>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+
+extern "C" struct z_stream_s;
+
+namespace net {
+
+class IOBufferWithSize;
+
+// WebSocketInflater uncompresses data compressed by DEFLATE algorithm.
+class NET_EXPORT_PRIVATE WebSocketInflater {
+ public:
+ WebSocketInflater();
+ // |input_queue_capacity| is a capacity for each contiguous block in the
+ // input queue. The input queue can grow without limit.
+ WebSocketInflater(size_t input_queue_capacity, size_t output_buffer_capacity);
+ ~WebSocketInflater();
+
+ // Returns true if there is no error.
+ // |window_bits| must be between 8 and 15 (both inclusive).
+ // This function must be called exactly once before calling any of the
+ // following functions.
+ bool Initialize(int window_bits);
+
+ // Adds bytes to |stream_|.
+ // Returns true if there is no error.
+ // If the size of the output data reaches the capacity of the output buffer,
+ // the following input data will be "choked", i.e. stored in the input queue,
+ // staying compressed.
+ bool AddBytes(const char* data, size_t size);
+
+ // Flushes the input.
+ // Returns true if there is no error.
+ bool Finish();
+
+ // Returns up to |size| bytes of the decompressed output.
+ // Returns null if there is an inflation error.
+ // The returned bytes will be dropped from the current output and never be
+ // returned again.
+ // If some input data is choked, calling this function may restart the
+ // inflation process.
+ // This means that even if you call |Finish()| and call |GetOutput()| with
+ // size = |CurrentOutputSize()|, the inflater may have some remaining data.
+ // To confirm the inflater emptiness, you should check whether
+ // |CurrentOutputSize()| is zero.
+ scoped_refptr<IOBufferWithSize> GetOutput(size_t size);
+
+ // Returns the size of the current inflated output.
+ size_t CurrentOutputSize() const { return output_buffer_.Size(); }
+
+ static const size_t kDefaultBufferCapacity = 512;
+ static const size_t kDefaultInputIOBufferCapacity = 512;
+
+ private:
+ // Ring buffer with fixed capacity.
+ class OutputBuffer {
+ public:
+ explicit OutputBuffer(size_t capacity);
+ ~OutputBuffer();
+
+ size_t Size() const;
+ // Returns (tail pointer, availabe size).
+ // A user can push data to the queue by writing the data to
+ // the area returned by this function and calling AdvanceTail.
+ std::pair<char*, size_t> GetTail();
+ void Read(char* dest, size_t size);
+ void AdvanceTail(size_t advance);
+
+ private:
+ void AdvanceHead(size_t advance);
+
+ const size_t capacity_;
+ std::vector<char> buffer_;
+ size_t head_;
+ size_t tail_;
+ };
+
+ class InputQueue {
+ public:
+ // |capacity| is used for the capacity of each IOBuffer in this queue.
+ // this queue itself can grow without limit.
+ explicit InputQueue(size_t capacity);
+ ~InputQueue();
+
+ // Returns (data pointer, size), the first component of unconsumed data.
+ // The type of data pointer is non-const because |inflate| function
+ // requires so.
+ std::pair<char*, size_t> Top();
+ bool IsEmpty() const { return buffers_.empty(); }
+ void Push(const char* data, size_t size);
+ // Consumes the topmost |size| bytes.
+ // |size| must be less than or equal to the first buffer size.
+ void Consume(size_t size);
+
+ private:
+ size_t PushToLastBuffer(const char* data, size_t size);
+
+ const size_t capacity_;
+ size_t head_of_first_buffer_;
+ size_t tail_of_last_buffer_;
+ std::deque<scoped_refptr<IOBufferWithSize> > buffers_;
+ };
+
+ int InflateWithFlush(const char* next_in, size_t avail_in);
+ int Inflate(const char* next_in, size_t avail_in, int flush);
+ int InflateChokedInput();
+
+ scoped_ptr<z_stream_s> stream_;
+ InputQueue input_queue_;
+ OutputBuffer output_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketInflater);
+};
+
+} // namespace net
+
+#endif // NET_WEBSOCKETS_WEBSOCKET_INFLATER_H_
diff --git a/net/websockets/websocket_inflater_test.cc b/net/websockets/websocket_inflater_test.cc
new file mode 100644
index 0000000..722e29e8
--- /dev/null
+++ b/net/websockets/websocket_inflater_test.cc
@@ -0,0 +1,245 @@
+// 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_inflater.h"
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/base/io_buffer.h"
+#include "net/websockets/websocket_deflater.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+std::string ToString(IOBufferWithSize* buffer) {
+ return std::string(buffer->data(), buffer->size());
+}
+
+class LinearCongruentialGenerator {
+ public:
+ explicit LinearCongruentialGenerator(uint32_t seed);
+ uint32_t Generate();
+
+ private:
+ uint32_t current_;
+
+ static const uint32_t a_ = 1103515245;
+ static const uint32_t c_ = 12345;
+ static const uint32_t m_ = 1 << 31;
+};
+
+LinearCongruentialGenerator::LinearCongruentialGenerator(uint32_t seed)
+ : current_(seed) {}
+
+uint32_t LinearCongruentialGenerator::Generate() {
+ uint32_t result = current_;
+ current_ = (current_ * a_ + c_) % m_;
+ return result;
+}
+
+TEST(WebSocketInflaterTest, Construct) {
+ WebSocketInflater inflater;
+ ASSERT_TRUE(inflater.Initialize(15));
+
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+}
+
+TEST(WebSocketInflaterTest, InflateHelloTakeOverContext) {
+ WebSocketInflater inflater;
+ ASSERT_TRUE(inflater.Initialize(15));
+ scoped_refptr<IOBufferWithSize> actual1, actual2;
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ actual1 = inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(actual1);
+ EXPECT_EQ("Hello", ToString(actual1.get()));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x00\x11\x00\x00", 5));
+ ASSERT_TRUE(inflater.Finish());
+ actual2 = inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(actual2);
+ EXPECT_EQ("Hello", ToString(actual2.get()));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+}
+
+TEST(WebSocketInflaterTest, InflateHelloSmallCapacity) {
+ WebSocketInflater inflater(1, 1);
+ ASSERT_TRUE(inflater.Initialize(15));
+ std::string actual;
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(1u, inflater.CurrentOutputSize());
+ scoped_refptr<IOBufferWithSize> buffer = inflater.GetOutput(1);
+ ASSERT_TRUE(buffer);
+ ASSERT_EQ(1, buffer->size());
+ actual += ToString(buffer.get());
+ }
+ EXPECT_EQ("Hello", actual);
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+}
+
+TEST(WebSocketInflaterTest, InflateHelloSmallCapacityGetTotalOutput) {
+ WebSocketInflater inflater(1, 1);
+ ASSERT_TRUE(inflater.Initialize(15));
+ scoped_refptr<IOBufferWithSize> actual;
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ ASSERT_EQ(1u, inflater.CurrentOutputSize());
+ actual = inflater.GetOutput(1024);
+ EXPECT_EQ("Hello", ToString(actual));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+}
+
+TEST(WebSocketInflaterTest, InflateInvalidData) {
+ WebSocketInflater inflater;
+ ASSERT_TRUE(inflater.Initialize(15));
+ EXPECT_FALSE(inflater.AddBytes("\xf2\x48\xcd\xc9INVALID DATA", 16));
+}
+
+TEST(WebSocketInflaterTest, ChokedInvalidData) {
+ WebSocketInflater inflater(1, 1);
+ ASSERT_TRUE(inflater.Initialize(15));
+
+ EXPECT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9INVALID DATA", 16));
+ EXPECT_TRUE(inflater.Finish());
+ EXPECT_EQ(1u, inflater.CurrentOutputSize());
+ EXPECT_FALSE(inflater.GetOutput(1024));
+}
+
+TEST(WebSocketInflaterTest, MultipleAddBytesCalls) {
+ WebSocketInflater inflater;
+ ASSERT_TRUE(inflater.Initialize(15));
+ std::string input("\xf2\x48\xcd\xc9\xc9\x07\x00", 7);
+ scoped_refptr<IOBufferWithSize> actual;
+
+ for (size_t i = 0; i < input.size(); ++i) {
+ ASSERT_TRUE(inflater.AddBytes(&input[i], 1));
+ }
+ ASSERT_TRUE(inflater.Finish());
+ actual = inflater.GetOutput(5);
+ ASSERT_TRUE(actual);
+ EXPECT_EQ("Hello", ToString(actual.get()));
+}
+
+TEST(WebSocketInflaterTest, Reset) {
+ WebSocketInflater inflater;
+ ASSERT_TRUE(inflater.Initialize(15));
+ scoped_refptr<IOBufferWithSize> actual1, actual2;
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ actual1 = inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(actual1);
+ EXPECT_EQ("Hello", ToString(actual1.get()));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+
+ // Reset the stream with a block [BFINAL = 1, BTYPE = 00, LEN = 0]
+ ASSERT_TRUE(inflater.AddBytes("\x01", 1));
+ ASSERT_TRUE(inflater.Finish());
+ ASSERT_EQ(0u, inflater.CurrentOutputSize());
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ actual2 = inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(actual2);
+ EXPECT_EQ("Hello", ToString(actual2.get()));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+}
+
+TEST(WebSocketInflaterTest, ResetAndLostContext) {
+ WebSocketInflater inflater;
+ scoped_refptr<IOBufferWithSize> actual1, actual2;
+ ASSERT_TRUE(inflater.Initialize(15));
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ actual1 = inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(actual1);
+ EXPECT_EQ("Hello", ToString(actual1.get()));
+ EXPECT_EQ(0u, inflater.CurrentOutputSize());
+
+ // Reset the stream with a block [BFINAL = 1, BTYPE = 00, LEN = 0]
+ ASSERT_TRUE(inflater.AddBytes("\x01", 1));
+ ASSERT_TRUE(inflater.Finish());
+ ASSERT_EQ(0u, inflater.CurrentOutputSize());
+
+ // The context is already reset.
+ ASSERT_FALSE(inflater.AddBytes("\xf2\x00\x11\x00\x00", 5));
+}
+
+TEST(WebSocketInflaterTest, CallAddBytesAndFinishWithoutGetOutput) {
+ WebSocketInflater inflater;
+ scoped_refptr<IOBufferWithSize> actual1, actual2;
+ ASSERT_TRUE(inflater.Initialize(15));
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ EXPECT_EQ(5u, inflater.CurrentOutputSize());
+
+ // This is a test for detecting memory leaks with valgrind.
+}
+
+TEST(WebSocketInflaterTest, CallAddBytesAndFinishWithoutGetOutputChoked) {
+ WebSocketInflater inflater(1, 1);
+ scoped_refptr<IOBufferWithSize> actual1, actual2;
+ ASSERT_TRUE(inflater.Initialize(15));
+
+ ASSERT_TRUE(inflater.AddBytes("\xf2\x48\xcd\xc9\xc9\x07\x00", 7));
+ ASSERT_TRUE(inflater.Finish());
+ EXPECT_EQ(1u, inflater.CurrentOutputSize());
+
+ // This is a test for detecting memory leaks with valgrind.
+}
+
+TEST(WebSocketInflaterTest, LargeRandomDeflateInflate) {
+ const size_t size = 64 * 1024;
+ LinearCongruentialGenerator generator(133);
+ std::vector<char> input;
+ std::vector<char> output;
+ scoped_refptr<IOBufferWithSize> compressed;
+
+ WebSocketDeflater deflater(WebSocketDeflater::TAKE_OVER_CONTEXT);
+ ASSERT_TRUE(deflater.Initialize(8));
+ WebSocketInflater inflater(256, 256);
+ ASSERT_TRUE(inflater.Initialize(8));
+
+ for (size_t i = 0; i < size; ++i)
+ input.push_back(static_cast<char>(generator.Generate()));
+
+ ASSERT_TRUE(deflater.AddBytes(&input[0], input.size()));
+ ASSERT_TRUE(deflater.Finish());
+
+ compressed = deflater.GetOutput(deflater.CurrentOutputSize());
+
+ ASSERT_TRUE(compressed);
+ ASSERT_EQ(0u, deflater.CurrentOutputSize());
+
+ ASSERT_TRUE(inflater.AddBytes(compressed->data(), compressed->size()));
+ ASSERT_TRUE(inflater.Finish());
+
+ while (inflater.CurrentOutputSize() > 0) {
+ scoped_refptr<IOBufferWithSize> uncompressed =
+ inflater.GetOutput(inflater.CurrentOutputSize());
+ ASSERT_TRUE(uncompressed);
+ output.insert(output.end(),
+ uncompressed->data(),
+ uncompressed->data() + uncompressed->size());
+ }
+
+ EXPECT_EQ(output, input);
+}
+
+} // unnamed namespace
+
+} // namespace net