// 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() {} void WebSocketExtensionParser::Parse(const char* data, size_t size) { current_ = data; end_ = data + size; has_error_ = false; ConsumeExtension(&extension_); if (has_error_) return; ConsumeSpaces(); has_error_ = has_error_ || (current_ != end_); } void WebSocketExtensionParser::Consume(char c) { DCHECK(!has_error_); ConsumeSpaces(); DCHECK(!has_error_); if (current_ == end_ || c != current_[0]) { has_error_ = true; return; } ++current_; } void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) { DCHECK(!has_error_); base::StringPiece name; ConsumeToken(&name); if (has_error_) return; *extension = WebSocketExtension(name.as_string()); while (ConsumeIfMatch(';')) { WebSocketExtension::Parameter parameter((std::string())); ConsumeExtensionParameter(¶meter); if (has_error_) return; extension->Add(parameter); } } void WebSocketExtensionParser::ConsumeExtensionParameter( WebSocketExtension::Parameter* parameter) { DCHECK(!has_error_); base::StringPiece name, value; std::string value_string; ConsumeToken(&name); if (has_error_) return; if (!ConsumeIfMatch('=')) { *parameter = WebSocketExtension::Parameter(name.as_string()); return; } if (Lookahead('\"')) { ConsumeQuotedToken(&value_string); } else { ConsumeToken(&value); value_string = value.as_string(); } if (has_error_) return; *parameter = WebSocketExtension::Parameter(name.as_string(), value_string); } void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) { DCHECK(!has_error_); ConsumeSpaces(); DCHECK(!has_error_); const char* head = current_; while (current_ < end_ && !IsControl(current_[0]) && !IsSeparator(current_[0])) ++current_; if (current_ == head) { has_error_ = true; return; } *token = base::StringPiece(head, current_ - head); } void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) { DCHECK(!has_error_); Consume('"'); if (has_error_) return; *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] == '"') ++current_; else has_error_ = true; has_error_ = has_error_ || token->empty(); } void WebSocketExtensionParser::ConsumeSpaces() { DCHECK(!has_error_); while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t')) ++current_; return; } bool WebSocketExtensionParser::Lookahead(char c) { DCHECK(!has_error_); const char* head = current_; Consume(c); bool result = !has_error_; current_ = head; has_error_ = false; return result; } bool WebSocketExtensionParser::ConsumeIfMatch(char c) { DCHECK(!has_error_); const char* head = current_; Consume(c); if (has_error_) { current_ = head; has_error_ = false; 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