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
39namespace blink {
40
41class WebSocketExtensionDeflateFrame FINAL : public WebSocketExtensionProcessor {
42    WTF_MAKE_FAST_ALLOCATED;
43public:
44    static PassOwnPtr<WebSocketExtensionDeflateFrame> create(WebSocketDeflateFramer* framer)
45    {
46        return adoptPtr(new WebSocketExtensionDeflateFrame(framer));
47    }
48    virtual ~WebSocketExtensionDeflateFrame() { }
49
50    virtual String handshakeString() OVERRIDE;
51    virtual bool processResponse(const HashMap<String, String>&) OVERRIDE;
52    virtual String failureReason() OVERRIDE { return m_failureReason; }
53
54private:
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.
63WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame(WebSocketDeflateFramer* framer)
64    : WebSocketExtensionProcessor("x-webkit-deflate-frame")
65    , m_framer(framer)
66    , m_responseProcessed(false)
67{
68    ASSERT(m_framer);
69}
70
71String WebSocketExtensionDeflateFrame::handshakeString()
72{
73    return extensionToken(); // No parameter
74}
75
76bool 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
116DeflateResultHolder::DeflateResultHolder(WebSocketDeflateFramer* framer)
117    : m_framer(framer)
118    , m_succeeded(true)
119{
120    ASSERT(m_framer);
121}
122
123DeflateResultHolder::~DeflateResultHolder()
124{
125    m_framer->resetDeflateContext();
126}
127
128void DeflateResultHolder::fail(const String& failureReason)
129{
130    m_succeeded = false;
131    m_failureReason = failureReason;
132}
133
134InflateResultHolder::InflateResultHolder(WebSocketDeflateFramer* framer)
135    : m_framer(framer)
136    , m_succeeded(true)
137{
138    ASSERT(m_framer);
139}
140
141InflateResultHolder::~InflateResultHolder()
142{
143    m_framer->resetInflateContext();
144}
145
146void InflateResultHolder::fail(const String& failureReason)
147{
148    m_succeeded = false;
149    m_failureReason = failureReason;
150}
151
152WebSocketDeflateFramer::WebSocketDeflateFramer()
153    : m_enabled(false)
154{
155}
156
157PassOwnPtr<WebSocketExtensionProcessor> WebSocketDeflateFramer::createExtensionProcessor()
158{
159    return WebSocketExtensionDeflateFrame::create(this);
160}
161
162void 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
174PassOwnPtr<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
189void WebSocketDeflateFramer::resetDeflateContext()
190{
191    if (m_deflater)
192        m_deflater->reset();
193}
194
195PassOwnPtr<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
216void WebSocketDeflateFramer::resetInflateContext()
217{
218    if (m_inflater)
219        m_inflater->reset();
220}
221
222void WebSocketDeflateFramer::didFail()
223{
224    resetDeflateContext();
225    resetInflateContext();
226}
227
228} // namespace blink
229