OLD | NEW |
| (Empty) |
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 blink { | |
44 | |
45 static const int defaultMemLevel = 1; | |
46 static const size_t bufferIncrementUnit = 4096; | |
47 | |
48 PassOwnPtr<WebSocketDeflater> WebSocketDeflater::create(int windowBits, ContextT
akeOverMode contextTakeOverMode) | |
49 { | |
50 return adoptPtr(new WebSocketDeflater(windowBits, contextTakeOverMode)); | |
51 } | |
52 | |
53 WebSocketDeflater::WebSocketDeflater(int windowBits, ContextTakeOverMode context
TakeOverMode) | |
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 | |
64 bool WebSocketDeflater::initialize() | |
65 { | |
66 return deflateInit2(m_stream.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -m_wi
ndowBits, defaultMemLevel, Z_DEFAULT_STRATEGY) == Z_OK; | |
67 } | |
68 | |
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 | |
76 static void setStreamParameter(z_stream* stream, const char* inputData, size_t i
nputLength, 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 | |
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 remai
ning 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() + write
Position, 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 | |
105 bool WebSocketDeflater::finish() | |
106 { | |
107 if (!m_isBytesAdded) { | |
108 // Since consecutive calls of deflate with Z_SYNC_FLUSH and no input lea
d 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 | |
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 | |
142 void WebSocketDeflater::softReset() | |
143 { | |
144 m_buffer.clear(); | |
145 } | |
146 | |
147 PassOwnPtr<WebSocketInflater> WebSocketInflater::create(int windowBits) | |
148 { | |
149 return adoptPtr(new WebSocketInflater(windowBits)); | |
150 } | |
151 | |
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 | |
159 bool WebSocketInflater::initialize() | |
160 { | |
161 return inflateInit2(m_stream.get(), -m_windowBits) == Z_OK; | |
162 } | |
163 | |
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 | |
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 | |
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, remai
ningLength, 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 | |
229 void WebSocketInflater::reset() | |
230 { | |
231 m_buffer.clear(); | |
232 } | |
233 | |
234 } // namespace blink | |
235 | |
OLD | NEW |