• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "modules/websockets/WebSocketExtensionParser.h"
36 #include "public/platform/Platform.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 FINAL : public WebSocketExtensionProcessor {
45     WTF_MAKE_FAST_ALLOCATED;
46     WTF_MAKE_NONCOPYABLE(CompressionMessageExtensionProcessor);
47 public:
create(WebSocketPerMessageDeflate & compress)48     static PassOwnPtr<CompressionMessageExtensionProcessor> create(WebSocketPerMessageDeflate& compress)
49     {
50         return adoptPtr(new CompressionMessageExtensionProcessor(compress));
51     }
~CompressionMessageExtensionProcessor()52     virtual ~CompressionMessageExtensionProcessor() { }
53 
54     virtual String handshakeString() OVERRIDE;
55     virtual bool processResponse(const HashMap<String, String>&) OVERRIDE;
failureReason()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 
CompressionMessageExtensionProcessor(WebSocketPerMessageDeflate & compress)66 CompressionMessageExtensionProcessor::CompressionMessageExtensionProcessor(WebSocketPerMessageDeflate& compress)
67     : WebSocketExtensionProcessor("permessage-deflate")
68     , m_compress(compress)
69     , m_responseProcessed(false)
70 {
71 }
72 
handshakeString()73 String CompressionMessageExtensionProcessor::handshakeString()
74 {
75     return "permessage-deflate; client_max_window_bits";
76 }
77 
processResponse(const HashMap<String,String> & parameters)78 bool CompressionMessageExtensionProcessor::processResponse(const HashMap<String, String>& parameters)
79 {
80     unsigned 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 clientNoContextTakeover = parameters.find("client_no_context_takeover");
90     HashMap<String, String>::const_iterator clientMaxWindowBits = parameters.find("client_max_window_bits");
91     HashMap<String, String>::const_iterator serverNoContextTakeover = parameters.find("server_no_context_takeover");
92     HashMap<String, String>::const_iterator serverMaxWindowBits = parameters.find("server_max_window_bits");
93 
94     if (clientNoContextTakeover != parameters.end()) {
95         if (!clientNoContextTakeover->value.isNull()) {
96             m_failureReason = "Received invalid client_no_context_takeover parameter";
97             return false;
98         }
99         mode = WebSocketDeflater::DoNotTakeOverContext;
100         ++numProcessedParameters;
101     }
102     if (clientMaxWindowBits != parameters.end()) {
103         if (!clientMaxWindowBits->value.length()) {
104             m_failureReason = "client_max_window_bits parameter must have value";
105             return false;
106         }
107         bool ok = false;
108         windowBits = clientMaxWindowBits->value.toIntStrict(&ok);
109         if (!ok || windowBits < 8 || windowBits > 15 || clientMaxWindowBits->value[0] == '+' || clientMaxWindowBits->value[0] == '0') {
110             m_failureReason = "Received invalid client_max_window_bits parameter";
111             return false;
112         }
113         ++numProcessedParameters;
114     }
115     if (serverNoContextTakeover != parameters.end()) {
116         if (!serverNoContextTakeover->value.isNull()) {
117             m_failureReason = "Received invalid server_no_context_takeover parameter";
118             return false;
119         }
120         ++numProcessedParameters;
121     }
122     if (serverMaxWindowBits != parameters.end()) {
123         if (!serverMaxWindowBits->value.length()) {
124             m_failureReason = "server_max_window_bits parameter must have value";
125             return false;
126         }
127         bool ok = false;
128         int bits = serverMaxWindowBits->value.toIntStrict(&ok);
129         if (!ok || bits < 8 || bits > 15 || serverMaxWindowBits->value[0] == '+' || serverMaxWindowBits->value[0] == '0') {
130             m_failureReason = "Received invalid server_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     blink::Platform::current()->histogramEnumeration("WebCore.WebSocket.PerMessageDeflateContextTakeOverMode", mode, WebSocketDeflater::ContextTakeOverModeMax);
141     m_compress.enable(windowBits, mode);
142     // Since we don't request server_no_context_takeover and server_max_window_bits, they should be ignored.
143     return true;
144 }
145 
WebSocketPerMessageDeflate()146 WebSocketPerMessageDeflate::WebSocketPerMessageDeflate()
147     : m_enabled(false)
148     , m_deflateOngoing(false)
149     , m_inflateOngoing(false)
150 {
151 }
152 
createExtensionProcessor()153 PassOwnPtr<WebSocketExtensionProcessor> WebSocketPerMessageDeflate::createExtensionProcessor()
154 {
155     return CompressionMessageExtensionProcessor::create(*this);
156 }
157 
enable(int windowBits,WebSocketDeflater::ContextTakeOverMode mode)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 
deflate(WebSocketFrame & frame)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.opCode == WebSocketFrame::OpCodeText || frame.opCode == WebSocketFrame::OpCodeBinary)
184         && frame.final
185         && frame.payloadLength <= 2) {
186         // A trivial optimization: If a message consists of one frame and its
187         // payload length is very short, we don't compress it.
188         return true;
189     }
190 
191     if (frame.payloadLength > 0 && !m_deflater->addBytes(frame.payload, frame.payloadLength)) {
192         m_failureReason = "Failed to deflate a frame";
193         return false;
194     }
195     if (frame.final && !m_deflater->finish()) {
196         m_failureReason = "Failed to finish compression";
197         return false;
198     }
199 
200     frame.compress = !m_deflateOngoing;
201     frame.payload = m_deflater->data();
202     frame.payloadLength = m_deflater->size();
203     m_deflateOngoing = !frame.final;
204     return true;
205 }
206 
resetDeflateBuffer()207 void WebSocketPerMessageDeflate::resetDeflateBuffer()
208 {
209     if (m_deflater) {
210         if (m_deflateOngoing)
211             m_deflater->softReset();
212         else
213             m_deflater->reset();
214     }
215 }
216 
inflate(WebSocketFrame & frame)217 bool WebSocketPerMessageDeflate::inflate(WebSocketFrame& frame)
218 {
219     if (!enabled())
220         return true;
221     if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) {
222         if (frame.compress) {
223             m_failureReason = "Received unexpected compressed frame";
224             return false;
225         }
226         return true;
227     }
228     if (frame.compress) {
229         if (m_inflateOngoing) {
230             m_failureReason = "Received a frame that sets compressed bit while another decompression is ongoing";
231             return false;
232         }
233         m_inflateOngoing = true;
234     }
235 
236     if (!m_inflateOngoing)
237         return true;
238 
239     if (frame.payloadLength > 0 && !m_inflater->addBytes(frame.payload, frame.payloadLength)) {
240         m_failureReason = "Failed to inflate a frame";
241         return false;
242     }
243     if (frame.final && !m_inflater->finish()) {
244         m_failureReason = "Failed to finish decompression";
245         return false;
246     }
247     frame.compress = false;
248     frame.payload = m_inflater->data();
249     frame.payloadLength = m_inflater->size();
250     m_inflateOngoing = !frame.final;
251     return true;
252 }
253 
resetInflateBuffer()254 void WebSocketPerMessageDeflate::resetInflateBuffer()
255 {
256     if (m_inflater)
257         m_inflater->reset();
258 }
259 
didFail()260 void WebSocketPerMessageDeflate::didFail()
261 {
262     resetDeflateBuffer();
263     resetInflateBuffer();
264 }
265 
266 } // namespace WebCore
267