// 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