Home | History | Annotate | Download | only in websockets
      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