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 48 PassOwnPtr<WebSocketDeflater> WebSocketDeflater::create(int windowBits, ContextTakeOverMode contextTakeOverMode) 49 { 50 return adoptPtr(new WebSocketDeflater(windowBits, contextTakeOverMode)); 51 } 52 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 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 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 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 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 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("\x02\x00", 2); 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, 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 229 void WebSocketInflater::reset() 230 { 231 m_buffer.clear(); 232 } 233 234 } // namespace WebCore 235 236