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