// 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/strings/string_util.h" namespace net { WebSocketExtensionParser::WebSocketExtensionParser() {} WebSocketExtensionParser::~WebSocketExtensionParser() {} bool WebSocketExtensionParser::Parse(const char* data, size_t size) { current_ = data; end_ = data + size; extensions_.clear(); bool failed = false; while (true) { WebSocketExtension extension; if (!ConsumeExtension(&extension)) { failed = true; break; } extensions_.push_back(extension); ConsumeSpaces(); if (!ConsumeIfMatch(',')) { break; } } if (!failed && current_ == end_) return true; extensions_.clear(); return false; } bool WebSocketExtensionParser::Consume(char c) { ConsumeSpaces(); if (current_ == end_ || c != current_[0]) { 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_ && !IsControl(current_[0]) && !IsSeparator(current_[0])) ++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_ && !IsControl(current_[0])) { if (UnconsumedBytes() >= 2 && current_[0] == '\\') { char next = current_[1]; if (IsControl(next) || IsSeparator(next)) break; *token += next; current_ += 2; } else if (IsSeparator(current_[0])) { break; } else { *token += current_[0]; ++current_; } } // We can't use Consume here because we don't want to consume spaces. if (current_ >= end_ || current_[0] != '"') return false; ++current_; return !token->empty(); } void WebSocketExtensionParser::ConsumeSpaces() { while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\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; } // static bool WebSocketExtensionParser::IsControl(char c) { return (0 <= c && c <= 31) || c == 127; } // static bool WebSocketExtensionParser::IsSeparator(char c) { const char separators[] = "()<>@,;:\\\"/[]?={} \t"; return strchr(separators, c) != NULL; } } // namespace net