1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "WebSocketDeflater.h"
33
34#if USE(ZLIB)
35
36#include "Logging.h"
37#include <wtf/FastMalloc.h>
38#include <wtf/HashMap.h>
39#include <wtf/StdLibExtras.h>
40#include <wtf/text/StringHash.h>
41#include <wtf/text/WTFString.h>
42#include <zlib.h>
43
44namespace WebCore {
45
46static const int defaultMemLevel = 1;
47static const size_t bufferIncrementUnit = 4096;
48
49WebSocketDeflater::WebSocketDeflater(int windowBits, ContextTakeOverMode contextTakeOverMode)
50 : m_windowBits(windowBits)
51 , m_contextTakeOverMode(contextTakeOverMode)
52{
53 ASSERT(m_windowBits >= 8);
54 ASSERT(m_windowBits <= 15);
55 m_stream = std::make_unique<z_stream>();
56 memset(m_stream.get(), 0, sizeof(z_stream));
57}
58
59bool WebSocketDeflater::initialize()
60{
61 return deflateInit2(m_stream.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -m_windowBits, defaultMemLevel, Z_DEFAULT_STRATEGY) == Z_OK;
62}
63
64WebSocketDeflater::~WebSocketDeflater()
65{
66 int result = deflateEnd(m_stream.get());
67 if (result != Z_OK)
68 LOG(Network, "WebSocketDeflater %p Destructor deflateEnd() failed: %d is returned", this, result);
69}
70
71static void setStreamParameter(z_stream* stream, const char* inputData, size_t inputLength, char* outputData, size_t outputLength)
72{
73 stream->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(inputData));
74 stream->avail_in = inputLength;
75 stream->next_out = reinterpret_cast<Bytef*>(outputData);
76 stream->avail_out = outputLength;
77}
78
79bool WebSocketDeflater::addBytes(const char* data, size_t length)
80{
81 if (!length)
82 return false;
83
84 size_t maxLength = deflateBound(m_stream.get(), length);
85 size_t writePosition = m_buffer.size();
86 m_buffer.grow(writePosition + maxLength);
87 setStreamParameter(m_stream.get(), data, length, m_buffer.data() + writePosition, maxLength);
88 int result = deflate(m_stream.get(), Z_NO_FLUSH);
89 if (result != Z_OK || m_stream->avail_in > 0)
90 return false;
91
92 m_buffer.shrink(writePosition + maxLength - m_stream->avail_out);
93 return true;
94}
95
96bool WebSocketDeflater::finish()
97{
98 while (true) {
99 size_t writePosition = m_buffer.size();
100 m_buffer.grow(writePosition + bufferIncrementUnit);
101 size_t availableCapacity = m_buffer.size() - writePosition;
102 setStreamParameter(m_stream.get(), 0, 0, m_buffer.data() + writePosition, availableCapacity);
103 int result = deflate(m_stream.get(), Z_SYNC_FLUSH);
104 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
105 if (result == Z_OK)
106 break;
107 if (result != Z_BUF_ERROR)
108 return false;
109 }
110 // Remove 4 octets from the tail as the specification requires.
111 if (m_buffer.size() <= 4)
112 return false;
113 m_buffer.shrink(m_buffer.size() - 4);
114 return true;
115}
116
117void WebSocketDeflater::reset()
118{
119 m_buffer.clear();
120 if (m_contextTakeOverMode == DoNotTakeOverContext)
121 deflateReset(m_stream.get());
122}
123
124WebSocketInflater::WebSocketInflater(int windowBits)
125 : m_windowBits(windowBits)
126{
127 m_stream = std::make_unique<z_stream>();
128 memset(m_stream.get(), 0, sizeof(z_stream));
129}
130
131bool WebSocketInflater::initialize()
132{
133 return inflateInit2(m_stream.get(), -m_windowBits) == Z_OK;
134}
135
136WebSocketInflater::~WebSocketInflater()
137{
138 int result = inflateEnd(m_stream.get());
139 if (result != Z_OK)
140 LOG(Network, "WebSocketInflater %p Destructor inflateEnd() failed: %d is returned", this, result);
141}
142
143bool WebSocketInflater::addBytes(const char* data, size_t length)
144{
145 if (!length)
146 return false;
147
148 size_t consumedSoFar = 0;
149 while (consumedSoFar < length) {
150 size_t writePosition = m_buffer.size();
151 m_buffer.grow(writePosition + bufferIncrementUnit);
152 size_t availableCapacity = m_buffer.size() - writePosition;
153 size_t remainingLength = length - consumedSoFar;
154 setStreamParameter(m_stream.get(), data + consumedSoFar, remainingLength, m_buffer.data() + writePosition, availableCapacity);
155 int result = inflate(m_stream.get(), Z_NO_FLUSH);
156 consumedSoFar += remainingLength - m_stream->avail_in;
157 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
158 if (result == Z_BUF_ERROR)
159 continue;
160 if (result == Z_STREAM_END) {
161 // Received a block with BFINAL set to 1. Reset decompression state.
162 if (inflateReset(m_stream.get()) != Z_OK)
163 return false;
164 continue;
165 }
166 if (result != Z_OK)
167 return false;
168 ASSERT(remainingLength > m_stream->avail_in);
169 }
170 ASSERT(consumedSoFar == length);
171 return true;
172}
173
174bool WebSocketInflater::finish()
175{
176 static const char* strippedFields = "\0\0\xff\xff";
177 static const size_t strippedLength = 4;
178
179 // Appends 4 octests of 0x00 0x00 0xff 0xff
180 size_t consumedSoFar = 0;
181 while (consumedSoFar < strippedLength) {
182 size_t writePosition = m_buffer.size();
183 m_buffer.grow(writePosition + bufferIncrementUnit);
184 size_t availableCapacity = m_buffer.size() - writePosition;
185 size_t remainingLength = strippedLength - consumedSoFar;
186 setStreamParameter(m_stream.get(), strippedFields + consumedSoFar, remainingLength, m_buffer.data() + writePosition, availableCapacity);
187 int result = inflate(m_stream.get(), Z_FINISH);
188 consumedSoFar += remainingLength - m_stream->avail_in;
189 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
190 if (result == Z_BUF_ERROR)
191 continue;
192 if (result != Z_OK && result != Z_STREAM_END)
193 return false;
194 ASSERT(remainingLength > m_stream->avail_in);
195 }
196 ASSERT(consumedSoFar == strippedLength);
197
198 return true;
199}
200
201void WebSocketInflater::reset()
202{
203 m_buffer.clear();
204}
205
206} // namespace WebCore
207
208#endif // USE(ZLIB)
209