1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_deflater.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <string.h>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include <algorithm>
10*6777b538SAndroid Build Coastguard Worker #include <vector>
11*6777b538SAndroid Build Coastguard Worker
12*6777b538SAndroid Build Coastguard Worker #include "base/check.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/containers/circular_deque.h"
15*6777b538SAndroid Build Coastguard Worker #include "net/base/io_buffer.h"
16*6777b538SAndroid Build Coastguard Worker #include "third_party/zlib/zlib.h"
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker namespace net {
19*6777b538SAndroid Build Coastguard Worker
WebSocketDeflater(ContextTakeOverMode mode)20*6777b538SAndroid Build Coastguard Worker WebSocketDeflater::WebSocketDeflater(ContextTakeOverMode mode) : mode_(mode) {}
21*6777b538SAndroid Build Coastguard Worker
~WebSocketDeflater()22*6777b538SAndroid Build Coastguard Worker WebSocketDeflater::~WebSocketDeflater() {
23*6777b538SAndroid Build Coastguard Worker if (stream_) {
24*6777b538SAndroid Build Coastguard Worker deflateEnd(stream_.get());
25*6777b538SAndroid Build Coastguard Worker stream_.reset(nullptr);
26*6777b538SAndroid Build Coastguard Worker }
27*6777b538SAndroid Build Coastguard Worker }
28*6777b538SAndroid Build Coastguard Worker
Initialize(int window_bits)29*6777b538SAndroid Build Coastguard Worker bool WebSocketDeflater::Initialize(int window_bits) {
30*6777b538SAndroid Build Coastguard Worker DCHECK(!stream_);
31*6777b538SAndroid Build Coastguard Worker stream_ = std::make_unique<z_stream>();
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker DCHECK_LE(8, window_bits);
34*6777b538SAndroid Build Coastguard Worker DCHECK_GE(15, window_bits);
35*6777b538SAndroid Build Coastguard Worker
36*6777b538SAndroid Build Coastguard Worker // Use a negative value to compress a raw deflate stream.
37*6777b538SAndroid Build Coastguard Worker //
38*6777b538SAndroid Build Coastguard Worker // Upgrade window_bits = 8 to 9 because zlib is unable to compress at
39*6777b538SAndroid Build Coastguard Worker // window_bits = 8. Historically, zlib has silently increased the window size
40*6777b538SAndroid Build Coastguard Worker // during compression in this case, although this is no longer done for raw
41*6777b538SAndroid Build Coastguard Worker // deflate streams since zlib 1.2.9.
42*6777b538SAndroid Build Coastguard Worker //
43*6777b538SAndroid Build Coastguard Worker // Because of a zlib deflate quirk, back-references will not use the entire
44*6777b538SAndroid Build Coastguard Worker // range of 1 << window_bits, but will instead use a restricted range of (1 <<
45*6777b538SAndroid Build Coastguard Worker // window_bits) - 262. With an increased window_bits = 9, back-references will
46*6777b538SAndroid Build Coastguard Worker // be within a range of 250. These can still be decompressed with window_bits
47*6777b538SAndroid Build Coastguard Worker // = 8 and the 256-byte window used there.
48*6777b538SAndroid Build Coastguard Worker //
49*6777b538SAndroid Build Coastguard Worker // Both the requirement to do this upgrade and the ability to compress with
50*6777b538SAndroid Build Coastguard Worker // window_bits = 9 while expecting a decompressor to function with window_bits
51*6777b538SAndroid Build Coastguard Worker // = 8 are quite specific to zlib's particular deflate implementation, but not
52*6777b538SAndroid Build Coastguard Worker // specific to any particular inflate implementation.
53*6777b538SAndroid Build Coastguard Worker //
54*6777b538SAndroid Build Coastguard Worker // See https://crbug.com/691074
55*6777b538SAndroid Build Coastguard Worker window_bits = -std::max(window_bits, 9);
56*6777b538SAndroid Build Coastguard Worker
57*6777b538SAndroid Build Coastguard Worker memset(stream_.get(), 0, sizeof(*stream_));
58*6777b538SAndroid Build Coastguard Worker int result = deflateInit2(stream_.get(),
59*6777b538SAndroid Build Coastguard Worker Z_DEFAULT_COMPRESSION,
60*6777b538SAndroid Build Coastguard Worker Z_DEFLATED,
61*6777b538SAndroid Build Coastguard Worker window_bits,
62*6777b538SAndroid Build Coastguard Worker 8, // default mem level
63*6777b538SAndroid Build Coastguard Worker Z_DEFAULT_STRATEGY);
64*6777b538SAndroid Build Coastguard Worker if (result != Z_OK) {
65*6777b538SAndroid Build Coastguard Worker deflateEnd(stream_.get());
66*6777b538SAndroid Build Coastguard Worker stream_.reset();
67*6777b538SAndroid Build Coastguard Worker return false;
68*6777b538SAndroid Build Coastguard Worker }
69*6777b538SAndroid Build Coastguard Worker constexpr size_t kFixedBufferSize = 4096;
70*6777b538SAndroid Build Coastguard Worker fixed_buffer_.resize(kFixedBufferSize);
71*6777b538SAndroid Build Coastguard Worker return true;
72*6777b538SAndroid Build Coastguard Worker }
73*6777b538SAndroid Build Coastguard Worker
AddBytes(const char * data,size_t size)74*6777b538SAndroid Build Coastguard Worker bool WebSocketDeflater::AddBytes(const char* data, size_t size) {
75*6777b538SAndroid Build Coastguard Worker if (!size)
76*6777b538SAndroid Build Coastguard Worker return true;
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker are_bytes_added_ = true;
79*6777b538SAndroid Build Coastguard Worker stream_->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));
80*6777b538SAndroid Build Coastguard Worker stream_->avail_in = size;
81*6777b538SAndroid Build Coastguard Worker
82*6777b538SAndroid Build Coastguard Worker int result = Deflate(Z_NO_FLUSH);
83*6777b538SAndroid Build Coastguard Worker DCHECK(result != Z_BUF_ERROR || !stream_->avail_in);
84*6777b538SAndroid Build Coastguard Worker return result == Z_BUF_ERROR;
85*6777b538SAndroid Build Coastguard Worker }
86*6777b538SAndroid Build Coastguard Worker
Finish()87*6777b538SAndroid Build Coastguard Worker bool WebSocketDeflater::Finish() {
88*6777b538SAndroid Build Coastguard Worker if (!are_bytes_added_) {
89*6777b538SAndroid Build Coastguard Worker // Since consecutive calls of deflate with Z_SYNC_FLUSH and no input
90*6777b538SAndroid Build Coastguard Worker // lead to an error, we create and return the output for the empty input
91*6777b538SAndroid Build Coastguard Worker // manually.
92*6777b538SAndroid Build Coastguard Worker buffer_.push_back('\x00');
93*6777b538SAndroid Build Coastguard Worker ResetContext();
94*6777b538SAndroid Build Coastguard Worker return true;
95*6777b538SAndroid Build Coastguard Worker }
96*6777b538SAndroid Build Coastguard Worker stream_->next_in = nullptr;
97*6777b538SAndroid Build Coastguard Worker stream_->avail_in = 0;
98*6777b538SAndroid Build Coastguard Worker
99*6777b538SAndroid Build Coastguard Worker int result = Deflate(Z_SYNC_FLUSH);
100*6777b538SAndroid Build Coastguard Worker // Deflate returning Z_BUF_ERROR means that it's successfully flushed and
101*6777b538SAndroid Build Coastguard Worker // blocked for input data.
102*6777b538SAndroid Build Coastguard Worker if (result != Z_BUF_ERROR) {
103*6777b538SAndroid Build Coastguard Worker ResetContext();
104*6777b538SAndroid Build Coastguard Worker return false;
105*6777b538SAndroid Build Coastguard Worker }
106*6777b538SAndroid Build Coastguard Worker // Remove 4 octets from the tail as the specification requires.
107*6777b538SAndroid Build Coastguard Worker if (CurrentOutputSize() < 4) {
108*6777b538SAndroid Build Coastguard Worker ResetContext();
109*6777b538SAndroid Build Coastguard Worker return false;
110*6777b538SAndroid Build Coastguard Worker }
111*6777b538SAndroid Build Coastguard Worker buffer_.resize(buffer_.size() - 4);
112*6777b538SAndroid Build Coastguard Worker ResetContext();
113*6777b538SAndroid Build Coastguard Worker return true;
114*6777b538SAndroid Build Coastguard Worker }
115*6777b538SAndroid Build Coastguard Worker
PushSyncMark()116*6777b538SAndroid Build Coastguard Worker void WebSocketDeflater::PushSyncMark() {
117*6777b538SAndroid Build Coastguard Worker DCHECK(!are_bytes_added_);
118*6777b538SAndroid Build Coastguard Worker const char data[] = {'\x00', '\x00', '\xff', '\xff'};
119*6777b538SAndroid Build Coastguard Worker buffer_.insert(buffer_.end(), &data[0], &data[sizeof(data)]);
120*6777b538SAndroid Build Coastguard Worker }
121*6777b538SAndroid Build Coastguard Worker
GetOutput(size_t size)122*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBufferWithSize> WebSocketDeflater::GetOutput(size_t size) {
123*6777b538SAndroid Build Coastguard Worker size_t length_to_copy = std::min(size, buffer_.size());
124*6777b538SAndroid Build Coastguard Worker base::circular_deque<char>::iterator begin = buffer_.begin();
125*6777b538SAndroid Build Coastguard Worker base::circular_deque<char>::iterator end = begin + length_to_copy;
126*6777b538SAndroid Build Coastguard Worker
127*6777b538SAndroid Build Coastguard Worker auto result = base::MakeRefCounted<IOBufferWithSize>(length_to_copy);
128*6777b538SAndroid Build Coastguard Worker std::copy(begin, end, result->data());
129*6777b538SAndroid Build Coastguard Worker buffer_.erase(begin, end);
130*6777b538SAndroid Build Coastguard Worker return result;
131*6777b538SAndroid Build Coastguard Worker }
132*6777b538SAndroid Build Coastguard Worker
ResetContext()133*6777b538SAndroid Build Coastguard Worker void WebSocketDeflater::ResetContext() {
134*6777b538SAndroid Build Coastguard Worker if (mode_ == DO_NOT_TAKE_OVER_CONTEXT)
135*6777b538SAndroid Build Coastguard Worker deflateReset(stream_.get());
136*6777b538SAndroid Build Coastguard Worker are_bytes_added_ = false;
137*6777b538SAndroid Build Coastguard Worker }
138*6777b538SAndroid Build Coastguard Worker
Deflate(int flush)139*6777b538SAndroid Build Coastguard Worker int WebSocketDeflater::Deflate(int flush) {
140*6777b538SAndroid Build Coastguard Worker int result = Z_OK;
141*6777b538SAndroid Build Coastguard Worker do {
142*6777b538SAndroid Build Coastguard Worker stream_->next_out = reinterpret_cast<Bytef*>(fixed_buffer_.data());
143*6777b538SAndroid Build Coastguard Worker stream_->avail_out = fixed_buffer_.size();
144*6777b538SAndroid Build Coastguard Worker result = deflate(stream_.get(), flush);
145*6777b538SAndroid Build Coastguard Worker size_t size = fixed_buffer_.size() - stream_->avail_out;
146*6777b538SAndroid Build Coastguard Worker buffer_.insert(buffer_.end(), fixed_buffer_.data(),
147*6777b538SAndroid Build Coastguard Worker fixed_buffer_.data() + size);
148*6777b538SAndroid Build Coastguard Worker } while (result == Z_OK);
149*6777b538SAndroid Build Coastguard Worker return result;
150*6777b538SAndroid Build Coastguard Worker }
151*6777b538SAndroid Build Coastguard Worker
152*6777b538SAndroid Build Coastguard Worker } // namespace net
153