Home | History | Annotate | Download | only in websockets
      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