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