2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "modules/websockets/WebSocketDeflater.h"
34 #include "platform/Logging.h"
35 #include "wtf/FastMalloc.h"
36 #include "wtf/HashMap.h"
37 #include "wtf/StdLibExtras.h"
38 #include "wtf/StringExtras.h"
39 #include "wtf/text/StringHash.h"
40 #include "wtf/text/WTFString.h"
45 static const int defaultMemLevel = 1;
46 static const size_t bufferIncrementUnit = 4096;
48 PassOwnPtr<WebSocketDeflater> WebSocketDeflater::create(int windowBits, ContextTakeOverMode contextTakeOverMode)
50 return adoptPtr(new WebSocketDeflater(windowBits, contextTakeOverMode));
53 WebSocketDeflater::WebSocketDeflater(int windowBits, ContextTakeOverMode contextTakeOverMode)
54 : m_windowBits(windowBits)
55 , m_contextTakeOverMode(contextTakeOverMode)
56 , m_isBytesAdded(false)
58 ASSERT(m_windowBits >= 8);
59 ASSERT(m_windowBits <= 15);
60 m_stream = adoptPtr(new z_stream);
61 memset(m_stream.get(), 0, sizeof(z_stream));
64 bool WebSocketDeflater::initialize()
66 return deflateInit2(m_stream.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -m_windowBits, defaultMemLevel, Z_DEFAULT_STRATEGY) == Z_OK;
69 WebSocketDeflater::~WebSocketDeflater()
71 int result = deflateEnd(m_stream.get());
73 WTF_LOG(Network, "WebSocketDeflater %p Destructor deflateEnd() failed: %d is returned", this, result);
76 static void setStreamParameter(z_stream* stream, const char* inputData, size_t inputLength, char* outputData, size_t outputLength)
78 stream->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(inputData));
79 stream->avail_in = inputLength;
80 stream->next_out = reinterpret_cast<Bytef*>(outputData);
81 stream->avail_out = outputLength;
84 bool WebSocketDeflater::addBytes(const char* data, size_t length)
89 // The estimation by deflateBound is not accurate if the zlib has some remaining input of the last compression.
90 size_t maxLength = deflateBound(m_stream.get(), length);
92 size_t writePosition = m_buffer.size();
93 m_buffer.grow(writePosition + maxLength);
94 setStreamParameter(m_stream.get(), data, length, m_buffer.data() + writePosition, maxLength);
95 int result = deflate(m_stream.get(), Z_NO_FLUSH);
98 m_buffer.shrink(writePosition + maxLength - m_stream->avail_out);
100 } while (m_stream->avail_in > 0);
101 m_isBytesAdded = true;
105 bool WebSocketDeflater::finish()
107 if (!m_isBytesAdded) {
108 // Since consecutive calls of deflate with Z_SYNC_FLUSH and no input lead to an error,
109 // we create and return the output for the empty input manually.
110 ASSERT(!m_buffer.size());
111 m_buffer.append("\x00", 1);
115 size_t writePosition = m_buffer.size();
116 m_buffer.grow(writePosition + bufferIncrementUnit);
117 size_t availableCapacity = m_buffer.size() - writePosition;
118 setStreamParameter(m_stream.get(), 0, 0, m_buffer.data() + writePosition, availableCapacity);
119 int result = deflate(m_stream.get(), Z_SYNC_FLUSH);
120 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
123 if (result != Z_BUF_ERROR)
126 // Remove 4 octets from the tail as the specification requires.
127 if (m_buffer.size() <= 4)
129 m_buffer.resize(m_buffer.size() - 4);
130 m_isBytesAdded = false;
134 void WebSocketDeflater::reset()
137 m_isBytesAdded = false;
138 if (m_contextTakeOverMode == DoNotTakeOverContext)
139 deflateReset(m_stream.get());
142 void WebSocketDeflater::softReset()
147 PassOwnPtr<WebSocketInflater> WebSocketInflater::create(int windowBits)
149 return adoptPtr(new WebSocketInflater(windowBits));
152 WebSocketInflater::WebSocketInflater(int windowBits)
153 : m_windowBits(windowBits)
155 m_stream = adoptPtr(new z_stream);
156 memset(m_stream.get(), 0, sizeof(z_stream));
159 bool WebSocketInflater::initialize()
161 return inflateInit2(m_stream.get(), -m_windowBits) == Z_OK;
164 WebSocketInflater::~WebSocketInflater()
166 int result = inflateEnd(m_stream.get());
168 WTF_LOG(Network, "WebSocketInflater %p Destructor inflateEnd() failed: %d is returned", this, result);
171 bool WebSocketInflater::addBytes(const char* data, size_t length)
176 size_t consumedSoFar = 0;
177 while (consumedSoFar < length) {
178 size_t writePosition = m_buffer.size();
179 m_buffer.grow(writePosition + bufferIncrementUnit);
180 size_t availableCapacity = m_buffer.size() - writePosition;
181 size_t remainingLength = length - consumedSoFar;
182 setStreamParameter(m_stream.get(), data + consumedSoFar, remainingLength, m_buffer.data() + writePosition, availableCapacity);
183 int result = inflate(m_stream.get(), Z_NO_FLUSH);
184 consumedSoFar += remainingLength - m_stream->avail_in;
185 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
186 if (result == Z_BUF_ERROR)
188 if (result == Z_STREAM_END) {
189 // Received a block with BFINAL set to 1. Reset decompression state.
190 if (inflateReset(m_stream.get()) != Z_OK)
196 ASSERT(remainingLength > m_stream->avail_in);
198 ASSERT(consumedSoFar == length);
202 bool WebSocketInflater::finish()
204 static const char strippedFields[] = "\0\0\xff\xff";
205 static const size_t strippedLength = 4;
207 // Appends 4 octests of 0x00 0x00 0xff 0xff
208 size_t consumedSoFar = 0;
209 while (consumedSoFar < strippedLength) {
210 size_t writePosition = m_buffer.size();
211 m_buffer.grow(writePosition + bufferIncrementUnit);
212 size_t availableCapacity = m_buffer.size() - writePosition;
213 size_t remainingLength = strippedLength - consumedSoFar;
214 setStreamParameter(m_stream.get(), strippedFields + consumedSoFar, remainingLength, m_buffer.data() + writePosition, availableCapacity);
215 int result = inflate(m_stream.get(), Z_FINISH);
216 consumedSoFar += remainingLength - m_stream->avail_in;
217 m_buffer.shrink(writePosition + availableCapacity - m_stream->avail_out);
218 if (result == Z_BUF_ERROR)
220 if (result != Z_OK && result != Z_STREAM_END)
222 ASSERT(remainingLength > m_stream->avail_in);
224 ASSERT(consumedSoFar == strippedLength);
229 void WebSocketInflater::reset()