/* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "modules/websockets/WebSocketDeflateFramer.h" #include "wtf/HashMap.h" #include "wtf/text/StringHash.h" #include "wtf/text/WTFString.h" namespace WebCore { class WebSocketExtensionDeflateFrame : public WebSocketExtensionProcessor { WTF_MAKE_FAST_ALLOCATED; public: static PassOwnPtr create(WebSocketDeflateFramer* framer) { return adoptPtr(new WebSocketExtensionDeflateFrame(framer)); } virtual ~WebSocketExtensionDeflateFrame() { } virtual String handshakeString() OVERRIDE; virtual bool processResponse(const HashMap&) OVERRIDE; virtual String failureReason() OVERRIDE { return m_failureReason; } private: WebSocketExtensionDeflateFrame(WebSocketDeflateFramer*); WebSocketDeflateFramer* m_framer; bool m_responseProcessed; String m_failureReason; }; // FXIME: Remove vendor prefix after the specification matured. WebSocketExtensionDeflateFrame::WebSocketExtensionDeflateFrame(WebSocketDeflateFramer* framer) : WebSocketExtensionProcessor("x-webkit-deflate-frame") , m_framer(framer) , m_responseProcessed(false) { ASSERT(m_framer); } String WebSocketExtensionDeflateFrame::handshakeString() { return extensionToken(); // No parameter } bool WebSocketExtensionDeflateFrame::processResponse(const HashMap& serverParameters) { if (m_responseProcessed) { m_failureReason = "Received duplicate deflate-frame response"; return false; } m_responseProcessed = true; unsigned expectedNumParameters = 0; int windowBits = 15; HashMap::const_iterator parameter = serverParameters.find("max_window_bits"); if (parameter != serverParameters.end()) { windowBits = parameter->value.toInt(); if (windowBits < 8 || windowBits > 15) { m_failureReason = "Received invalid max_window_bits parameter"; return false; } expectedNumParameters++; } WebSocketDeflater::ContextTakeOverMode mode = WebSocketDeflater::TakeOverContext; parameter = serverParameters.find("no_context_takeover"); if (parameter != serverParameters.end()) { if (!parameter->value.isNull()) { m_failureReason = "Received invalid no_context_takeover parameter"; return false; } mode = WebSocketDeflater::DoNotTakeOverContext; expectedNumParameters++; } if (expectedNumParameters != serverParameters.size()) { m_failureReason = "Received unexpected deflate-frame parameter"; return false; } m_framer->enableDeflate(windowBits, mode); return true; } DeflateResultHolder::DeflateResultHolder(WebSocketDeflateFramer* framer) : m_framer(framer) , m_succeeded(true) { ASSERT(m_framer); } DeflateResultHolder::~DeflateResultHolder() { m_framer->resetDeflateContext(); } void DeflateResultHolder::fail(const String& failureReason) { m_succeeded = false; m_failureReason = failureReason; } InflateResultHolder::InflateResultHolder(WebSocketDeflateFramer* framer) : m_framer(framer) , m_succeeded(true) { ASSERT(m_framer); } InflateResultHolder::~InflateResultHolder() { m_framer->resetInflateContext(); } void InflateResultHolder::fail(const String& failureReason) { m_succeeded = false; m_failureReason = failureReason; } WebSocketDeflateFramer::WebSocketDeflateFramer() : m_enabled(false) { } PassOwnPtr WebSocketDeflateFramer::createExtensionProcessor() { return WebSocketExtensionDeflateFrame::create(this); } void WebSocketDeflateFramer::enableDeflate(int windowBits, WebSocketDeflater::ContextTakeOverMode mode) { m_deflater = WebSocketDeflater::create(windowBits, mode); m_inflater = WebSocketInflater::create(); if (!m_deflater->initialize() || !m_inflater->initialize()) { m_deflater.clear(); m_inflater.clear(); return; } m_enabled = true; } PassOwnPtr WebSocketDeflateFramer::deflate(WebSocketFrame& frame) { OwnPtr result = DeflateResultHolder::create(this); if (!enabled() || !WebSocketFrame::isNonControlOpCode(frame.opCode) || !frame.payloadLength) return result.release(); if (!m_deflater->addBytes(frame.payload, frame.payloadLength) || !m_deflater->finish()) { result->fail("Failed to compress frame"); return result.release(); } frame.compress = true; frame.payload = m_deflater->data(); frame.payloadLength = m_deflater->size(); return result.release(); } void WebSocketDeflateFramer::resetDeflateContext() { if (m_deflater) m_deflater->reset(); } PassOwnPtr WebSocketDeflateFramer::inflate(WebSocketFrame& frame) { OwnPtr result = InflateResultHolder::create(this); if (!enabled()) return result.release(); if (!frame.compress) return result.release(); if (!WebSocketFrame::isNonControlOpCode(frame.opCode)) { result->fail("Received unexpected compressed frame"); return result.release(); } if (!m_inflater->addBytes(frame.payload, frame.payloadLength) || !m_inflater->finish()) { result->fail("Failed to decompress frame"); return result.release(); } frame.compress = false; frame.payload = m_inflater->data(); frame.payloadLength = m_inflater->size(); return result.release(); } void WebSocketDeflateFramer::resetInflateContext() { if (m_inflater) m_inflater->reset(); } void WebSocketDeflateFramer::didFail() { resetDeflateContext(); resetInflateContext(); } } // namespace WebCore