| // Copyright 2013 The Chromium Authors |
| // 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/check_op.h" |
| #include "base/strings/string_piece.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(std::string(name)); |
| |
| 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(std::string(name)); |
| return true; |
| } |
| |
| if (Lookahead('\"')) { |
| if (!ConsumeQuotedToken(&value_string)) |
| return false; |
| } else { |
| if (!ConsumeToken(&value)) |
| return false; |
| value_string = std::string(value); |
| } |
| *parameter = WebSocketExtension::Parameter(std::string(name), 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 |