1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/websockets/websocket_deflater.h" 6 7 #include <string.h> 8 #include <algorithm> 9 #include <deque> 10 #include <vector> 11 12 #include "base/logging.h" 13 #include "net/base/io_buffer.h" 14 #include "third_party/zlib/zlib.h" 15 16 namespace net { 17 18 WebSocketDeflater::WebSocketDeflater(ContextTakeOverMode mode) 19 : mode_(mode), are_bytes_added_(false) {} 20 21 WebSocketDeflater::~WebSocketDeflater() { 22 if (stream_) { 23 deflateEnd(stream_.get()); 24 stream_.reset(NULL); 25 } 26 } 27 28 bool WebSocketDeflater::Initialize(int window_bits) { 29 DCHECK(!stream_); 30 stream_.reset(new z_stream); 31 32 DCHECK_LE(8, window_bits); 33 DCHECK_GE(15, window_bits); 34 memset(stream_.get(), 0, sizeof(*stream_)); 35 int result = deflateInit2(stream_.get(), 36 Z_DEFAULT_COMPRESSION, 37 Z_DEFLATED, 38 -window_bits, // Negative value for raw deflate 39 8, // default mem level 40 Z_DEFAULT_STRATEGY); 41 if (result != Z_OK) { 42 deflateEnd(stream_.get()); 43 stream_.reset(); 44 return false; 45 } 46 const size_t kFixedBufferSize = 4096; 47 fixed_buffer_.resize(kFixedBufferSize); 48 return true; 49 } 50 51 bool WebSocketDeflater::AddBytes(const char* data, size_t size) { 52 if (!size) 53 return true; 54 55 are_bytes_added_ = true; 56 stream_->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data)); 57 stream_->avail_in = size; 58 59 int result = Deflate(Z_NO_FLUSH); 60 DCHECK(result != Z_BUF_ERROR || !stream_->avail_in); 61 return result == Z_BUF_ERROR; 62 } 63 64 bool WebSocketDeflater::Finish() { 65 if (!are_bytes_added_) { 66 // Since consecutive calls of deflate with Z_SYNC_FLUSH and no input 67 // lead to an error, we create and return the output for the empty input 68 // manually. 69 buffer_.push_back('\x00'); 70 ResetContext(); 71 return true; 72 } 73 stream_->next_in = NULL; 74 stream_->avail_in = 0; 75 76 int result = Deflate(Z_SYNC_FLUSH); 77 // Deflate returning Z_BUF_ERROR means that it's successfully flushed and 78 // blocked for input data. 79 if (result != Z_BUF_ERROR) { 80 ResetContext(); 81 return false; 82 } 83 // Remove 4 octets from the tail as the specification requires. 84 if (CurrentOutputSize() < 4) { 85 ResetContext(); 86 return false; 87 } 88 buffer_.resize(buffer_.size() - 4); 89 ResetContext(); 90 return true; 91 } 92 93 void WebSocketDeflater::PushSyncMark() { 94 DCHECK(!are_bytes_added_); 95 const char data[] = {'\x00', '\x00', '\xff', '\xff'}; 96 buffer_.insert(buffer_.end(), &data[0], &data[sizeof(data)]); 97 } 98 99 scoped_refptr<IOBufferWithSize> WebSocketDeflater::GetOutput(size_t size) { 100 std::deque<char>::iterator begin = buffer_.begin(); 101 std::deque<char>::iterator end = begin + std::min(size, buffer_.size()); 102 103 scoped_refptr<IOBufferWithSize> result = new IOBufferWithSize(end - begin); 104 std::copy(begin, end, result->data()); 105 buffer_.erase(begin, end); 106 return result; 107 } 108 109 void WebSocketDeflater::ResetContext() { 110 if (mode_ == DO_NOT_TAKE_OVER_CONTEXT) 111 deflateReset(stream_.get()); 112 are_bytes_added_ = false; 113 } 114 115 int WebSocketDeflater::Deflate(int flush) { 116 int result = Z_OK; 117 do { 118 stream_->next_out = reinterpret_cast<Bytef*>(&fixed_buffer_[0]); 119 stream_->avail_out = fixed_buffer_.size(); 120 result = deflate(stream_.get(), flush); 121 size_t size = fixed_buffer_.size() - stream_->avail_out; 122 buffer_.insert(buffer_.end(), &fixed_buffer_[0], &fixed_buffer_[0] + size); 123 } while (result == Z_OK); 124 return result; 125 } 126 127 } // namespace net 128