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 | |
44 | namespace WebCore { |
45 | |
46 | static const int defaultMemLevel = 1; |
47 | static const size_t bufferIncrementUnit = 4096; |
48 | |
49 | WebSocketDeflater::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 | |
59 | bool WebSocketDeflater::initialize() |
60 | { |
61 | return deflateInit2(m_stream.get(), Z_DEFAULT_COMPRESSION, Z_DEFLATED, -m_windowBits, defaultMemLevel, Z_DEFAULT_STRATEGY) == Z_OK; |
62 | } |
63 | |
64 | WebSocketDeflater::~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 | |
71 | static 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 | |
79 | bool 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 | |
96 | bool 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 | |
117 | void WebSocketDeflater::reset() |
118 | { |
119 | m_buffer.clear(); |
120 | if (m_contextTakeOverMode == DoNotTakeOverContext) |
121 | deflateReset(m_stream.get()); |
122 | } |
123 | |
124 | WebSocketInflater::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 | |
131 | bool WebSocketInflater::initialize() |
132 | { |
133 | return inflateInit2(m_stream.get(), -m_windowBits) == Z_OK; |
134 | } |
135 | |
136 | WebSocketInflater::~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 | |
143 | bool 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 | |
174 | bool 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 | |
201 | void WebSocketInflater::reset() |
202 | { |
203 | m_buffer.clear(); |
204 | } |
205 | |
206 | } // namespace WebCore |
207 | |
208 | #endif // USE(ZLIB) |
209 | |