1 /* 2 * Copyright (C) 2013 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 33 #include "modules/websockets/WebSocketPerMessageDeflate.h" 34 35 #include "core/platform/HistogramSupport.h" 36 #include "modules/websockets/WebSocketExtensionParser.h" 37 #include "wtf/HashMap.h" 38 #include "wtf/text/CString.h" 39 #include "wtf/text/StringHash.h" 40 #include "wtf/text/WTFString.h" 41 42 namespace WebCore { 43 44 class CompressionMessageExtensionProcessor : public WebSocketExtensionProcessor { 45 WTF_MAKE_FAST_ALLOCATED; 46 WTF_MAKE_NONCOPYABLE(CompressionMessageExtensionProcessor); 47 public: 48 static PassOwnPtr<CompressionMessageExtensionProcessor> create(WebSocketPerMessageDeflate& compress) 49 { 50 return adoptPtr(new CompressionMessageExtensionProcessor(compress)); 51 } 52 virtual ~CompressionMessageExtensionProcessor() { } 53 54 virtual String handshakeString() OVERRIDE; 55 virtual bool processResponse(const HashMap<String, String>&) OVERRIDE; 56 virtual String failureReason() OVERRIDE { return m_failureReason; } 57 58 private: 59 explicit CompressionMessageExtensionProcessor(WebSocketPerMessageDeflate&); 60 61 WebSocketPerMessageDeflate& m_compress; 62 bool m_responseProcessed; 63 String m_failureReason; 64 }; 65 66 CompressionMessageExtensionProcessor::CompressionMessageExtensionProcessor(WebSocketPerMessageDeflate& compress) 67 : WebSocketExtensionProcessor("permessage-deflate") 68 , m_compress(compress) 69 , m_responseProcessed(false) 70 { 71 } 72 73 String CompressionMessageExtensionProcessor::handshakeString() 74 { 75 return "permessage-deflate; c2s_max_window_bits"; 76 } 77 78 bool CompressionMessageExtensionProcessor::processResponse(const HashMap<String, String>& parameters) 79 { 80 int numProcessedParameters = 0; 81 if (m_responseProcessed) { 82 m_failureReason = "Received duplicate permessage-deflate response"; 83 return false; 84 } 85 m_responseProcessed = true; 86 WebSocketDeflater::ContextTakeOverMode mode = WebSocketDeflater::TakeOverContext; 87 int windowBits = 15; 88 89 HashMap<String, String>::const_iterator c2sNoContextTakeover = parameters.find("c2s_no_context_takeover"); 90 HashMap<String, String>::const_iterator c2sMaxWindowBits = parameters.find("c2s_max_window_bits"); 91 HashMap<String, String>::const_iterator s2cNoContextTakeover = parameters.find("s2c_no_context_takeover"); 92 HashMap<String, String>::const_iterator s2cMaxWindowBits = parameters.find("s2c_max_window_bits"); 93 94 if (c2sNoContextTakeover != parameters.end()) { 95 if (!c2sNoContextTakeover->value.isNull()) { 96 m_failureReason = "Received invalid c2s_no_context_takeover parameter"; 97 return false; 98 } 99 mode = WebSocketDeflater::DoNotTakeOverContext; 100 ++numProcessedParameters; 101 } 102 if (c2sMaxWindowBits != parameters.end()) { 103 if (!c2sMaxWindowBits->value.length()) { 104 m_failureReason = "c2s_max_window_bits parameter must have value"; 105 return false; 106 } 107 bool ok = false; 108 windowBits = c2sMaxWindowBits->value.toIntStrict(&ok); 109 if (!ok || windowBits < 8 || windowBits > 15 || c2sMaxWindowBits->value[0] == '+' || c2sMaxWindowBits->value[0] == '0') { 110 m_failureReason = "Received invalid c2s_max_window_bits parameter"; 111 return false; 112 } 113 ++numProcessedParameters; 114 } 115 if (s2cNoContextTakeover != parameters.end()) { 116 if (!s2cNoContextTakeover->value.isNull()) { 117 m_failureReason = "Received invalid s2c_no_context_takeover parameter"; 118 return false; 119 } 120 ++numProcessedParameters; 121 } 122 if (s2cMaxWindowBits != parameters.end()) { 123 if (!s2cMaxWindowBits->value.length()) { 124 m_failureReason = "s2c_max_window_bits parameter must have value"; 125 return false; 126 } 127 bool ok = false; 128 int bits = s2cMaxWindowBits->value.toIntStrict(&ok); 129 if (!ok || bits < 8 || bits > 15 || s2cMaxWindowBits->value[0] == '+' || s2cMaxWindowBits->value[0] == '0') { 130 m_failureReason = "Received invalid s2c_max_window_bits parameter"; 131 return false; 132 } 133 ++numProcessedParameters; 134 } 135 136 if (numProcessedParameters != parameters.size()) { 137 m_failureReason = "Received an unexpected permessage-deflate extension parameter"; 138 return false; 139 } 140 HistogramSupport::histogramEnumeration("WebCore.WebSocket.PerMessageDeflateContextTakeOverMode", mode, WebSocketDeflater::ContextTakeOverModeMax); 141 m_compress.enable(windowBits, mode); 142 // Since we don't request s2c_no_context_takeover and s2c_max_window_bits, they should be ignored. 143 return true; 144 } 145 146 WebSocketPerMessageDeflate::WebSocketPerMessageDeflate() 147 : m_enabled(false) 148 , m_deflateOngoing(false) 149 , m_inflateOngoing(false) 150 { 151 } 152 153 PassOwnPtr<WebSocketExtensionProcessor> WebSocketPerMessageDeflate::createExtensionProcessor() 154 { 155 return CompressionMessageExtensionProcessor::create(*this); 156 } 157 158 void WebSocketPerMessageDeflate::enable(int windowBits, WebSocketDeflater::ContextTakeOverMode mode) 159 { 160 m_deflater = WebSocketDeflater::create(windowBits, mode); 161 m_inflater = WebSocketInflater::create(); 162 if (!m_deflater->initialize() || !m_inflater->initialize()) { 163 m_deflater.clear(); 164 m_inflater.clear(); 165 return; 166 } 167 m_enabled = true; 168 m_deflateOngoing = false; 169 m_inflateOngoing = false; 170 } 171 172 bool WebSocketPerMessageDeflate::deflate(WebSocketFrame& frame) 173 { 174 if (!enabled()) 175 return true; 176 if (frame.compress) { 177 m_failureReason = "Some extension already uses the compress bit."; 178 return false; 179 } 180 if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) 181 return true; 182 183 if (frame.payloadLength > 0 && !m_deflater->addBytes(frame.payload, frame.payloadLength)) { 184 m_failureReason = "Failed to inflate a frame"; 185 return false; 186 } 187 if (frame.final && !m_deflater->finish()) { 188 m_failureReason = "Failed to finish compression"; 189 return false; 190 } 191 192 frame.compress = !m_deflateOngoing; 193 frame.payload = m_deflater->data(); 194 frame.payloadLength = m_deflater->size(); 195 m_deflateOngoing = !frame.final; 196 return true; 197 } 198 199 void WebSocketPerMessageDeflate::resetDeflateBuffer() 200 { 201 if (m_deflater) { 202 if (m_deflateOngoing) 203 m_deflater->softReset(); 204 else 205 m_deflater->reset(); 206 } 207 } 208 209 bool WebSocketPerMessageDeflate::inflate(WebSocketFrame& frame) 210 { 211 if (!enabled()) 212 return true; 213 if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) { 214 if (frame.compress) { 215 m_failureReason = "Received unexpected compressed frame"; 216 return false; 217 } 218 return true; 219 } 220 if (frame.compress) { 221 if (m_inflateOngoing) { 222 m_failureReason = "Received a frame that sets compressed bit while another decompression is ongoing"; 223 return false; 224 } 225 m_inflateOngoing = true; 226 } 227 228 if (!m_inflateOngoing) 229 return true; 230 231 if (frame.payloadLength > 0 && !m_inflater->addBytes(frame.payload, frame.payloadLength)) { 232 m_failureReason = "Failed to inflate a frame"; 233 return false; 234 } 235 if (frame.final && !m_inflater->finish()) { 236 m_failureReason = "Failed to finish decompression"; 237 return false; 238 } 239 frame.compress = false; 240 frame.payload = m_inflater->data(); 241 frame.payloadLength = m_inflater->size(); 242 m_inflateOngoing = !frame.final; 243 return true; 244 } 245 246 void WebSocketPerMessageDeflate::resetInflateBuffer() 247 { 248 if (m_inflater) 249 m_inflater->reset(); 250 } 251 252 void WebSocketPerMessageDeflate::didFail() 253 { 254 resetDeflateBuffer(); 255 resetInflateBuffer(); 256 } 257 258 } // namespace WebCore 259