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
33 #include "modules/websockets/WebSocketDeflateFramer.h"
34
35 #include "wtf/HashMap.h"
36 #include "wtf/text/StringHash.h"
37 #include "wtf/text/WTFString.h"
38
39 namespace WebCore {
40
41 class WebSocketExtensionDeflateFrame : public WebSocketExtensionProcessor {
42 WTF_MAKE_FAST_ALLOCATED;
43 public:
create(WebSocketDeflateFramer * framer)44 static PassOwnPtr<WebSocketExtensionDeflateFrame> create(WebSocketDeflateFramer* framer)
45 {
46 return adoptPtr(new WebSocketExtensionDeflateFrame(framer));
47 }
~WebSocketExtensionDeflateFrame()48 virtual ~WebSocketExtensionDeflateFrame() { }
49
50 virtual String handshakeString() OVERRIDE;
51 virtual bool processResponse(const HashMap<String, String>&) OVERRIDE;
failureReason()52 virtual String failureReason() OVERRIDE { return m_failureReason; }
53
54 private:
55 WebSocketExtensionDeflateFrame(WebSocketDeflateFramer*);
56
57 WebSocketDeflateFramer* m_framer;
58 bool m_responseProcessed;
59 String m_failureReason;
60 };
61
62 // FXIME: Remove vendor prefix after the specification matured.
WebSocketExtensionDeflateFrame(WebSocketDeflateFramer * framer)63 WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame(WebSocketDeflateFramer* framer)
64 : WebSocketExtensionProcessor("x-webkit-deflate-frame")
65 , m_framer(framer)
66 , m_responseProcessed(false)
67 {
68 ASSERT(m_framer);
69 }
70
handshakeString()71 String WebSocketExtensionDeflateFrame::handshakeString()
72 {
73 return extensionToken(); // No parameter
74 }
75
processResponse(const HashMap<String,String> & serverParameters)76 bool WebSocketExtensionDeflateFrame::processResponse(const HashMap<String, String>& serverParameters)
77 {
78 if (m_responseProcessed) {
79 m_failureReason = "Received duplicate deflate-frame response";
80 return false;
81 }
82 m_responseProcessed = true;
83
84 unsigned expectedNumParameters = 0;
85 int windowBits = 15;
86 HashMap<String, String>::const_iterator parameter = serverParameters.find("max_window_bits");
87 if (parameter != serverParameters.end()) {
88 windowBits = parameter->value.toInt();
89 if (windowBits < 8 || windowBits > 15) {
90 m_failureReason = "Received invalid max_window_bits parameter";
91 return false;
92 }
93 expectedNumParameters++;
94 }
95
96 WebSocketDeflater::ContextTakeOverMode mode = WebSocketDeflater::TakeOverContext;
97 parameter = serverParameters.find("no_context_takeover");
98 if (parameter != serverParameters.end()) {
99 if (!parameter->value.isNull()) {
100 m_failureReason = "Received invalid no_context_takeover parameter";
101 return false;
102 }
103 mode = WebSocketDeflater::DoNotTakeOverContext;
104 expectedNumParameters++;
105 }
106
107 if (expectedNumParameters != serverParameters.size()) {
108 m_failureReason = "Received unexpected deflate-frame parameter";
109 return false;
110 }
111
112 m_framer->enableDeflate(windowBits, mode);
113 return true;
114 }
115
DeflateResultHolder(WebSocketDeflateFramer * framer)116 DeflateResultHolder::DeflateResultHolder(WebSocketDeflateFramer* framer)
117 : m_framer(framer)
118 , m_succeeded(true)
119 {
120 ASSERT(m_framer);
121 }
122
~DeflateResultHolder()123 DeflateResultHolder::~DeflateResultHolder()
124 {
125 m_framer->resetDeflateContext();
126 }
127
fail(const String & failureReason)128 void DeflateResultHolder::fail(const String& failureReason)
129 {
130 m_succeeded = false;
131 m_failureReason = failureReason;
132 }
133
InflateResultHolder(WebSocketDeflateFramer * framer)134 InflateResultHolder::InflateResultHolder(WebSocketDeflateFramer* framer)
135 : m_framer(framer)
136 , m_succeeded(true)
137 {
138 ASSERT(m_framer);
139 }
140
~InflateResultHolder()141 InflateResultHolder::~InflateResultHolder()
142 {
143 m_framer->resetInflateContext();
144 }
145
fail(const String & failureReason)146 void InflateResultHolder::fail(const String& failureReason)
147 {
148 m_succeeded = false;
149 m_failureReason = failureReason;
150 }
151
WebSocketDeflateFramer()152 WebSocketDeflateFramer::WebSocketDeflateFramer()
153 : m_enabled(false)
154 {
155 }
156
createExtensionProcessor()157 PassOwnPtr<WebSocketExtensionProcessor> WebSocketDeflateFramer::createExtensionProcessor()
158 {
159 return WebSocketExtensionDeflateFrame::create(this);
160 }
161
enableDeflate(int windowBits,WebSocketDeflater::ContextTakeOverMode mode)162 void WebSocketDeflateFramer::enableDeflate(int windowBits, WebSocketDeflater::ContextTakeOverMode mode)
163 {
164 m_deflater = WebSocketDeflater::create(windowBits, mode);
165 m_inflater = WebSocketInflater::create();
166 if (!m_deflater->initialize() || !m_inflater->initialize()) {
167 m_deflater.clear();
168 m_inflater.clear();
169 return;
170 }
171 m_enabled = true;
172 }
173
deflate(WebSocketFrame & frame)174 PassOwnPtr<DeflateResultHolder> WebSocketDeflateFramer::deflate(WebSocketFrame& frame)
175 {
176 OwnPtr<DeflateResultHolder> result = DeflateResultHolder::create(this);
177 if (!enabled() || !WebSocketFrame::isNonControlOpCode(frame.opCode) || !frame.payloadLength)
178 return result.release();
179 if (!m_deflater->addBytes(frame.payload, frame.payloadLength) || !m_deflater->finish()) {
180 result->fail("Failed to compress frame");
181 return result.release();
182 }
183 frame.compress = true;
184 frame.payload = m_deflater->data();
185 frame.payloadLength = m_deflater->size();
186 return result.release();
187 }
188
resetDeflateContext()189 void WebSocketDeflateFramer::resetDeflateContext()
190 {
191 if (m_deflater)
192 m_deflater->reset();
193 }
194
inflate(WebSocketFrame & frame)195 PassOwnPtr<InflateResultHolder> WebSocketDeflateFramer::inflate(WebSocketFrame& frame)
196 {
197 OwnPtr<InflateResultHolder> result = InflateResultHolder::create(this);
198 if (!enabled())
199 return result.release();
200 if (!frame.compress)
201 return result.release();
202 if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) {
203 result->fail("Received unexpected compressed frame");
204 return result.release();
205 }
206 if (!m_inflater->addBytes(frame.payload, frame.payloadLength) || !m_inflater->finish()) {
207 result->fail("Failed to decompress frame");
208 return result.release();
209 }
210 frame.compress = false;
211 frame.payload = m_inflater->data();
212 frame.payloadLength = m_inflater->size();
213 return result.release();
214 }
215
resetInflateContext()216 void WebSocketDeflateFramer::resetInflateContext()
217 {
218 if (m_inflater)
219 m_inflater->reset();
220 }
221
didFail()222 void WebSocketDeflateFramer::didFail()
223 {
224 resetDeflateContext();
225 resetInflateContext();
226 }
227
228 } // namespace WebCore
229