Home | History | Annotate | Download | only in websockets
      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/WebSocketExtensionParser.h"
     34 
     35 #include "wtf/ASCIICType.h"
     36 #include "wtf/text/CString.h"
     37 
     38 namespace WebCore {
     39 
     40 WebSocketExtensionParser::ParserStateBackup::~ParserStateBackup()
     41 {
     42     if (!m_isDisposed) {
     43         m_parser->m_current = m_current;
     44     }
     45 }
     46 
     47 bool WebSocketExtensionParser::finished()
     48 {
     49     return m_current >= m_end;
     50 }
     51 
     52 bool WebSocketExtensionParser::parsedSuccessfully()
     53 {
     54     return m_current == m_end;
     55 }
     56 
     57 static bool isSeparator(char character)
     58 {
     59     static const char* separatorCharacters = "()<>@,;:\\\"/[]?={} \t";
     60     const char* p = strchr(separatorCharacters, character);
     61     return p && *p;
     62 }
     63 
     64 void WebSocketExtensionParser::skipSpaces()
     65 {
     66     while (m_current < m_end && (*m_current == ' ' || *m_current == '\t'))
     67         ++m_current;
     68 }
     69 
     70 bool WebSocketExtensionParser::consumeToken()
     71 {
     72     ParserStateBackup backup(this);
     73     skipSpaces();
     74     const char* start = m_current;
     75     while (m_current < m_end && isASCIIPrintable(*m_current) && !isSeparator(*m_current))
     76         ++m_current;
     77     if (start < m_current) {
     78         m_currentToken = String(start, m_current - start);
     79         backup.dispose();
     80         return true;
     81     }
     82     return false;
     83 }
     84 
     85 bool WebSocketExtensionParser::consumeQuotedString()
     86 {
     87     ParserStateBackup backup(this);
     88     skipSpaces();
     89     if (m_current >= m_end || *m_current != '"')
     90         return false;
     91 
     92     Vector<char> buffer;
     93     ++m_current;
     94     while (m_current < m_end && *m_current != '"') {
     95         if (*m_current == '\\' && ++m_current >= m_end)
     96             return false;
     97         // RFC6455 requires that the value after quoted-string unescaping
     98         // MUST conform to the 'token' ABNF.
     99         if (!isASCIIPrintable(*m_current) || isSeparator(*m_current))
    100             return false;
    101         buffer.append(*m_current);
    102         ++m_current;
    103     }
    104     if (m_current >= m_end || *m_current != '"' || buffer.isEmpty())
    105         return false;
    106     m_currentToken = String::fromUTF8(buffer.data(), buffer.size());
    107     ++m_current;
    108     backup.dispose();
    109     return true;
    110 }
    111 
    112 bool WebSocketExtensionParser::consumeQuotedStringOrToken()
    113 {
    114     // This is ok because consumeQuotedString() restores m_current
    115     // on failure.
    116     return consumeQuotedString() || consumeToken();
    117 }
    118 
    119 bool WebSocketExtensionParser::consumeCharacter(char character)
    120 {
    121     ParserStateBackup backup(this);
    122     skipSpaces();
    123     if (m_current < m_end && *m_current == character) {
    124         ++m_current;
    125         backup.dispose();
    126         return true;
    127     }
    128     return false;
    129 }
    130 
    131 bool WebSocketExtensionParser::parseExtension(String& extensionToken, HashMap<String, String>& extensionParameters)
    132 {
    133     ParserStateBackup backup(this);
    134     // Parse extension-token.
    135     if (!consumeToken())
    136         return false;
    137 
    138     extensionToken = currentToken();
    139 
    140     // Parse extension-parameters if exists.
    141     while (consumeCharacter(';')) {
    142         if (!consumeToken())
    143             return false;
    144 
    145         String parameterToken = currentToken();
    146         if (consumeCharacter('=')) {
    147             if (consumeQuotedStringOrToken())
    148                 extensionParameters.add(parameterToken, currentToken());
    149             else
    150                 return false;
    151         } else {
    152             extensionParameters.add(parameterToken, String());
    153         }
    154     }
    155     skipSpaces();
    156     if (!finished() && !consumeCharacter(','))
    157         return false;
    158 
    159     backup.dispose();
    160     return true;
    161 }
    162 
    163 } // namespace WebCore
    164