// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/websockets/websocket_extension_parser.h" #include "base/logging.h" #include "net/http/http_util.h" namespace net { WebSocketExtensionParser::WebSocketExtensionParser() = default; WebSocketExtensionParser::~WebSocketExtensionParser() = default; bool WebSocketExtensionParser::Parse(const char* data, size_t size) { current_ = data; end_ = data + size; extensions_.clear(); bool failed = false; do { WebSocketExtension extension; if (!ConsumeExtension(&extension)) { failed = true; break; } extensions_.push_back(extension); ConsumeSpaces(); } while (ConsumeIfMatch(',')); if (!failed && current_ == end_) return true; extensions_.clear(); return false; } bool WebSocketExtensionParser::Consume(char c) { ConsumeSpaces(); if (current_ == end_ || c != *current_) return false; ++current_; return true; } bool WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) { base::StringPiece name; if (!ConsumeToken(&name)) return false; *extension = WebSocketExtension(name.as_string()); while (ConsumeIfMatch(';')) { WebSocketExtension::Parameter parameter((std::string())); if (!ConsumeExtensionParameter(¶meter)) return false; extension->Add(parameter); } return true; } bool WebSocketExtensionParser::ConsumeExtensionParameter( WebSocketExtension::Parameter* parameter) { base::StringPiece name, value; std::string value_string; if (!ConsumeToken(&name)) return false; if (!ConsumeIfMatch('=')) { *parameter = WebSocketExtension::Parameter(name.as_string()); return true; } if (Lookahead('\"')) { if (!ConsumeQuotedToken(&value_string)) return false; } else { if (!ConsumeToken(&value)) return false; value_string = value.as_string(); } *parameter = WebSocketExtension::Parameter(name.as_string(), value_string); return true; } bool WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) { ConsumeSpaces(); const char* head = current_; while (current_ < end_ && HttpUtil::IsTokenChar(*current_)) ++current_; if (current_ == head) return false; *token = base::StringPiece(head, current_ - head); return true; } bool WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) { if (!Consume('"')) return false; *token = ""; while (current_ < end_ && *current_ != '"') { if (*current_ == '\\') { ++current_; if (current_ == end_) return false; } if (!HttpUtil::IsTokenChar(*current_)) return false; *token += *current_; ++current_; } if (current_ == end_) return false; DCHECK_EQ(*current_, '"'); ++current_; return !token->empty(); } void WebSocketExtensionParser::ConsumeSpaces() { while (current_ < end_ && (*current_ == ' ' || *current_ == '\t')) ++current_; return; } bool WebSocketExtensionParser::Lookahead(char c) { const char* head = current_; bool result = Consume(c); current_ = head; return result; } bool WebSocketExtensionParser::ConsumeIfMatch(char c) { const char* head = current_; if (!Consume(c)) { current_ = head; return false; } return true; } } // namespace net