Index: net/websockets/websocket_extension_parser.cc |
diff --git a/net/websockets/websocket_extension_parser.cc b/net/websockets/websocket_extension_parser.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ea9987b077e1055668bb4998488a167ebbcee433 |
--- /dev/null |
+++ b/net/websockets/websocket_extension_parser.cc |
@@ -0,0 +1,200 @@ |
+// 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 { |
+ |
+// Backup the head pointer if the parser has error. |
Adam Rice
2013/09/17 04:30:33
I think this grammar can be parsed with single-cha
yhirano
2013/09/17 06:03:31
I deleted some |has_error_ = false| statements.
Th
|
+class WebSocketExtensionParser::CurrentPointerBackup { |
+ public: |
+ explicit CurrentPointerBackup(WebSocketExtensionParser* parser); |
+ ~CurrentPointerBackup(); |
+ |
+ private: |
+ WebSocketExtensionParser* parser_; |
+ const char* original_head_; |
+}; |
+ |
+WebSocketExtensionParser::CurrentPointerBackup::CurrentPointerBackup( |
+ WebSocketExtensionParser* parser) |
+ : parser_(parser), original_head_(parser->current_) {} |
+ |
+WebSocketExtensionParser::CurrentPointerBackup::~CurrentPointerBackup() { |
+ if (parser_->has_error_) |
+ parser_->current_ = original_head_; |
+} |
+ |
+WebSocketExtensionParser::WebSocketExtensionParser() {} |
+ |
+WebSocketExtensionParser::~WebSocketExtensionParser() {} |
+ |
+void WebSocketExtensionParser::Parse(const char* data, size_t size) { |
+ current_ = data; |
+ end_ = &data[size]; |
+ extensions_.clear(); |
+ has_error_ = false; |
+ CurrentPointerBackup backup(this); |
+ |
+ ConsumeExtensionList(); |
+ has_error_ = has_error_ || (current_ != end_); |
+ if (has_error_) |
+ extensions_.clear(); |
+} |
+ |
+void WebSocketExtensionParser::Consume(const char* data, size_t size) { |
+ DCHECK(!has_error_); |
+ CurrentPointerBackup backup(this); |
+ ConsumeLWS(); |
+ DCHECK(!has_error_); |
+ if (UnconsumedBytes() < size) { |
+ has_error_ = true; |
+ return; |
+ } |
+ if (memcmp(data, current_, size)) { |
+ has_error_ = true; |
+ return; |
+ } |
+ current_ += size; |
+} |
+ |
+void WebSocketExtensionParser::ConsumeExtensionList() { |
+ DCHECK(!has_error_); |
+ WebSocketExtension extension(""); |
+ ConsumeExtension(&extension); |
+ if (has_error_) return; |
+ do { |
+ extensions_.push_back(extension); |
+ ConsumeExtension(&extension); |
+ } while (!has_error_); |
+ has_error_ = false; |
+} |
+ |
+void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) { |
+ DCHECK(!has_error_); |
+ CurrentPointerBackup backup(this); |
+ base::StringPiece name; |
+ ConsumeToken(&name); |
+ if (has_error_) return; |
+ *extension = WebSocketExtension(name.as_string()); |
+ |
+ |
+ while (true) { |
+ Consume(";", 1); |
+ if (has_error_) { |
+ has_error_ = false; |
+ return; |
+ } |
+ WebSocketExtension::Parameter parameter(""); |
+ ConsumeExtensionParameter(¶meter); |
+ if (has_error_) return; |
+ extension->Add(parameter); |
+ } |
+} |
+ |
+void WebSocketExtensionParser::ConsumeExtensionParameter( |
+ WebSocketExtension::Parameter* parameter) { |
+ DCHECK(!has_error_); |
+ CurrentPointerBackup backup(this); |
+ base::StringPiece name, value; |
+ std::string value_string; |
+ |
+ ConsumeToken(&name); |
+ if (has_error_) return; |
+ Consume("=", 1); |
+ if (has_error_) { |
+ has_error_ = false; |
+ *parameter = WebSocketExtension::Parameter(name.as_string()); |
+ return; |
+ } |
+ |
+ ConsumeToken(&value); |
+ if (has_error_) { |
+ has_error_ = false; |
+ ConsumeQuotedToken(&value_string); |
+ if (has_error_) return; |
+ } else { |
+ value_string = value.as_string(); |
+ } |
+ *parameter = WebSocketExtension::Parameter(name.as_string(), value_string); |
+} |
+ |
+void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) { |
+ DCHECK(!has_error_); |
+ CurrentPointerBackup backup(this); |
+ ConsumeLWS(); |
+ 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_); |
+ CurrentPointerBackup backup(this); |
+ Consume("\"", 1); |
+ 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_; |
+ } |
+ } |
+ Consume("\"", 1); |
+ has_error_ = has_error_ || token->empty(); |
+} |
+ |
+// Unlike the "implied *LWS" specification in RFC2616, this parser |
+// consumes *LWS, because there are no adjacent tokens in WebSocket |
+// extension grammer. |
+void WebSocketExtensionParser::ConsumeLWS() { |
+ DCHECK(!has_error_); |
+ size_t counter = 0; |
+ while (current_ < end_) { |
+ if (current_[0] == ' ' || current_[0] == '\t') { |
+ while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t')) |
+ ++current_; |
+ } else if (UnconsumedBytes() >= 3 && |
+ current_[0] == '\r' && current_[1] == '\n' && |
+ (current_[2] == ' ' || current_[2] == '\t')) { |
+ current_ += 2; |
+ while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t')) |
+ ++current_; |
+ break; |
+ } else { |
+ break; |
+ } |
+ ++counter; |
+ } |
+ return; |
+} |
+ |
+// 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); |
+} |
+ |
+} // namespace net |