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 "modules/websockets/WebSocketDeflater.h"
33
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"
41 #include <zlib.h>
42
43 namespace WebCore {
44
45 static const int defaultMemLevel = 1;
46 static const size_t bufferIncrementUnit = 4096;
47
create(int windowBits,ContextTakeOverMode contextTakeOverMode)48 PassOwnPtr<WebSocketDeflater> WebSocketDeflater::create(int windowBits, ContextTakeOverMode contextTakeOverMode)
49 {
50 return adoptPtr(new WebSocketDeflater(windowBits, contextTakeOverMode));
51 }
52
WebSocketDeflater(int windowBits,ContextTakeOverMode contextTakeOverMode)53 WebSocketDeflater::WebSocketDeflater(int windowBits, ContextTakeOverMode contextTakeOverMode)
54 : m_windowBits(windowBits)
55 , m_contextTakeOverMode(contextTakeOverMode)
56 , m_isBytesAdded(false)
57 {
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));
62 }
63
initialize()64 bool WebSocketDeflater::initialize()
65 {
66 return deflateInit2(m_stream.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -m_windowBits, defaultMemLevel, Z_DEFAULT_STRATEGY) == Z_OK;
67 }
68
~WebSocketDeflater()69 WebSocketDeflater::~WebSocketDeflater()
70 {
71 int result = deflateEnd(m_stream.get());
72 if (result != Z_OK)
73 WTF_LOG(Network, "WebSocketDeflater %p Destructor deflateEnd() failed: %d is returned", this, result);
74 }
75
setStreamParameter(z_stream * stream,const char * inputData,size_t inputLength,char * outputData,size_t outputLength)76 static void setStreamParameter(z_stream* stream, const char* inputData, size_t inputLength, char* outputData, size_t outputLength)
77 {
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;
82 }
83
addBytes(const char * data,size_t length)84 bool WebSocketDeflater::addBytes(const char* data, size_t length)
85 {
86 if (!length)
87 return false;
88
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);
91 do {
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);
96 if (result != Z_OK)
97 return false;
98 m_buffer.shrink(writePosition + maxLength - m_stream->avail_out);
99 maxLength *= 2;
100 } while (m_stream->avail_in > 0);
101 m_isBytesAdded = true;
102 return true;
103 }
104
finish()105 bool WebSocketDeflater::finish()
106 {
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);
112 return true;
113 }
114 while (true) {
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);
121 if (result == Z_OK)
122 break;
123 if (result != Z_BUF_ERROR)
124 return false;
125 }
126 // Remove 4 octets from the tail as the specification requires.
127 if (m_buffer.size() <= 4)
128 return false;
129 m_buffer.resize(m_buffer.size() - 4);
130 m_isBytesAdded = false;
131 return true;
132 }
133
reset()134 void WebSocketDeflater::reset()
135 {
136 m_buffer.clear();
137 m_isBytesAdded = false;
138 if (m_contextTakeOverMode == DoNotTakeOverContext)
139 deflateReset(m_stream.get());
140 }
141
softReset()142 void WebSocketDeflater::softReset()
143 {
144 m_buffer.clear();
145 }
146
create(int windowBits)147 PassOwnPtr<WebSocketInflater> WebSocketInflater::create(int windowBits)
148 {
149 return adoptPtr(new WebSocketInflater(windowBits));
150 }
151
WebSocketInflater(int windowBits)152 WebSocketInflater::WebSocketInflater(int windowBits)
153 : m_windowBits(windowBits)
154 {
155 m_stream = adoptPtr(new z_stream);
156 memset(m_stream.get(), 0, sizeof(z_stream));
157 }
158
initialize()159 bool WebSocketInflater::initialize()
160 {
161 return inflateInit2(m_stream.get(), -m_windowBits) == Z_OK;
162 }
163
~WebSocketInflater()164 WebSocketInflater::~WebSocketInflater()
165 {
166 int result = inflateEnd(m_stream.get());
167 if (result != Z_OK)
168 WTF_LOG(Network, "WebSocketInflater %p Destructor inflateEnd() failed: %d is returned", this, result);
169 }
170
addBytes(const char * data,size_t length)171 bool WebSocketInflater::addBytes(const char* data, size_t length)
172 {
173 if (!length)
174 return false;
175
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)
187 continue;
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)
191 return false;
192 continue;
193 }
194 if (result != Z_OK)
195 return false;
196 ASSERT(remainingLength > m_stream->avail_in);
197 }
198 ASSERT(consumedSoFar == length);
199 return true;
200 }
201
finish()202 bool WebSocketInflater::finish()
203 {
204 static const char strippedFields[] = "\0\0\xff\xff";
205 static const size_t strippedLength = 4;
206
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)
219 continue;
220 if (result != Z_OK && result != Z_STREAM_END)
221 return false;
222 ASSERT(remainingLength > m_stream->avail_in);
223 }
224 ASSERT(consumedSoFar == strippedLength);
225
226 return true;
227 }
228
reset()229 void WebSocketInflater::reset()
230 {
231 m_buffer.clear();
232 }
233
234 } // namespace WebCore
235
236