• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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