1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/websockets/websocket_extension_parser.h"
6
7#include "base/strings/string_util.h"
8
9namespace net {
10
11WebSocketExtensionParser::WebSocketExtensionParser() {}
12
13WebSocketExtensionParser::~WebSocketExtensionParser() {}
14
15void WebSocketExtensionParser::Parse(const char* data, size_t size) {
16  current_ = data;
17  end_ = data + size;
18  has_error_ = false;
19
20  ConsumeExtension(&extension_);
21  if (has_error_) return;
22  ConsumeSpaces();
23  has_error_ = has_error_ || (current_ != end_);
24}
25
26void WebSocketExtensionParser::Consume(char c) {
27  DCHECK(!has_error_);
28  ConsumeSpaces();
29  DCHECK(!has_error_);
30  if (current_ == end_ || c != current_[0]) {
31    has_error_ = true;
32    return;
33  }
34  ++current_;
35}
36
37void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) {
38  DCHECK(!has_error_);
39  base::StringPiece name;
40  ConsumeToken(&name);
41  if (has_error_) return;
42  *extension = WebSocketExtension(name.as_string());
43
44  while (ConsumeIfMatch(';')) {
45    WebSocketExtension::Parameter parameter((std::string()));
46    ConsumeExtensionParameter(&parameter);
47    if (has_error_) return;
48    extension->Add(parameter);
49  }
50}
51
52void WebSocketExtensionParser::ConsumeExtensionParameter(
53    WebSocketExtension::Parameter* parameter) {
54  DCHECK(!has_error_);
55  base::StringPiece name, value;
56  std::string value_string;
57
58  ConsumeToken(&name);
59  if (has_error_) return;
60  if (!ConsumeIfMatch('=')) {
61    *parameter = WebSocketExtension::Parameter(name.as_string());
62    return;
63  }
64
65  if (Lookahead('\"')) {
66    ConsumeQuotedToken(&value_string);
67  } else {
68    ConsumeToken(&value);
69    value_string = value.as_string();
70  }
71  if (has_error_) return;
72  *parameter = WebSocketExtension::Parameter(name.as_string(), value_string);
73}
74
75void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) {
76  DCHECK(!has_error_);
77  ConsumeSpaces();
78  DCHECK(!has_error_);
79  const char* head = current_;
80  while (current_ < end_ &&
81         !IsControl(current_[0]) && !IsSeparator(current_[0]))
82    ++current_;
83  if (current_ == head) {
84    has_error_ = true;
85    return;
86  }
87  *token = base::StringPiece(head, current_ - head);
88}
89
90void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) {
91  DCHECK(!has_error_);
92  Consume('"');
93  if (has_error_) return;
94  *token = "";
95  while (current_ < end_ && !IsControl(current_[0])) {
96    if (UnconsumedBytes() >= 2 && current_[0] == '\\') {
97      char next = current_[1];
98      if (IsControl(next) || IsSeparator(next)) break;
99      *token += next;
100      current_ += 2;
101    } else if (IsSeparator(current_[0])) {
102      break;
103    } else {
104      *token += current_[0];
105      ++current_;
106    }
107  }
108  // We can't use Consume here because we don't want to consume spaces.
109  if (current_ < end_ && current_[0] == '"')
110    ++current_;
111  else
112    has_error_ = true;
113  has_error_ = has_error_ || token->empty();
114}
115
116void WebSocketExtensionParser::ConsumeSpaces() {
117  DCHECK(!has_error_);
118  while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t'))
119    ++current_;
120  return;
121}
122
123bool WebSocketExtensionParser::Lookahead(char c) {
124  DCHECK(!has_error_);
125  const char* head = current_;
126
127  Consume(c);
128  bool result = !has_error_;
129  current_ = head;
130  has_error_ = false;
131  return result;
132}
133
134bool WebSocketExtensionParser::ConsumeIfMatch(char c) {
135  DCHECK(!has_error_);
136  const char* head = current_;
137
138  Consume(c);
139  if (has_error_) {
140    current_ = head;
141    has_error_ = false;
142    return false;
143  }
144  return true;
145}
146
147// static
148bool WebSocketExtensionParser::IsControl(char c) {
149  return (0 <= c && c <= 31) || c == 127;
150}
151
152// static
153bool WebSocketExtensionParser::IsSeparator(char c) {
154  const char separators[] = "()<>@,;:\\\"/[]?={} \t";
155  return strchr(separators, c) != NULL;
156}
157
158}  // namespace net
159