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 blink { 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 blink 164