OLD | NEW |
---|---|
(Empty) | |
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 | |
9 namespace net { | |
10 | |
11 // Restore the current pointer if the parser has an error. | |
12 class WebSocketExtensionParser::CurrentPointerBackup { | |
Adam Rice
2013/09/17 07:45:58
Sorry my comment was not clear enough. The parser
yhirano
2013/09/17 09:17:39
Done.
| |
13 public: | |
14 explicit CurrentPointerBackup(WebSocketExtensionParser* parser); | |
15 ~CurrentPointerBackup(); | |
16 | |
17 private: | |
18 WebSocketExtensionParser* parser_; | |
19 const char* original_head_; | |
20 }; | |
21 | |
22 WebSocketExtensionParser::CurrentPointerBackup::CurrentPointerBackup( | |
23 WebSocketExtensionParser* parser) | |
24 : parser_(parser), original_head_(parser->current_) {} | |
25 | |
26 WebSocketExtensionParser::CurrentPointerBackup::~CurrentPointerBackup() { | |
27 if (parser_->has_error_) | |
28 parser_->current_ = original_head_; | |
29 } | |
30 | |
31 WebSocketExtensionParser::WebSocketExtensionParser() {} | |
32 | |
33 WebSocketExtensionParser::~WebSocketExtensionParser() {} | |
34 | |
35 void WebSocketExtensionParser::Parse(const char* data, size_t size) { | |
36 current_ = data; | |
37 end_ = &data[size]; | |
38 extensions_.clear(); | |
39 has_error_ = false; | |
40 CurrentPointerBackup backup(this); | |
41 | |
42 ConsumeExtensionList(); | |
43 has_error_ = has_error_ || (current_ != end_); | |
44 if (has_error_) | |
45 extensions_.clear(); | |
46 } | |
47 | |
48 void WebSocketExtensionParser::Consume(const char* data, size_t size) { | |
49 DCHECK(!has_error_); | |
50 CurrentPointerBackup backup(this); | |
51 ConsumeLWS(); | |
52 DCHECK(!has_error_); | |
53 if (UnconsumedBytes() < size) { | |
54 has_error_ = true; | |
55 return; | |
56 } | |
57 if (memcmp(data, current_, size)) { | |
58 has_error_ = true; | |
59 return; | |
60 } | |
61 current_ += size; | |
62 } | |
63 | |
64 void WebSocketExtensionParser::ConsumeExtensionList() { | |
Adam Rice
2013/09/17 07:45:58
We only need to parse one extension at a time, so
yhirano
2013/09/17 09:17:39
Done.
| |
65 DCHECK(!has_error_); | |
66 WebSocketExtension extension(""); | |
67 ConsumeExtension(&extension); | |
68 if (has_error_) return; | |
69 do { | |
70 extensions_.push_back(extension); | |
71 ConsumeExtension(&extension); | |
72 } while (!has_error_); | |
73 has_error_ = false; | |
74 } | |
75 | |
76 void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) { | |
77 DCHECK(!has_error_); | |
78 CurrentPointerBackup backup(this); | |
79 base::StringPiece name; | |
80 ConsumeToken(&name); | |
81 if (has_error_) return; | |
82 *extension = WebSocketExtension(name.as_string()); | |
83 | |
84 while (ConsumeIfMatch(";", 1)) { | |
85 WebSocketExtension::Parameter parameter(""); | |
86 ConsumeExtensionParameter(¶meter); | |
87 if (has_error_) return; | |
88 extension->Add(parameter); | |
89 } | |
90 } | |
91 | |
92 void WebSocketExtensionParser::ConsumeExtensionParameter( | |
93 WebSocketExtension::Parameter* parameter) { | |
94 DCHECK(!has_error_); | |
95 CurrentPointerBackup backup(this); | |
96 base::StringPiece name, value; | |
97 std::string value_string; | |
98 | |
99 ConsumeToken(&name); | |
100 if (has_error_) return; | |
101 if (!ConsumeIfMatch("=", 1)) { | |
102 *parameter = WebSocketExtension::Parameter(name.as_string()); | |
103 return; | |
104 } | |
105 | |
106 if (Lookahead("\"", 1)) { | |
107 ConsumeQuotedToken(&value_string); | |
108 } else { | |
109 ConsumeToken(&value); | |
110 value_string = value.as_string(); | |
111 } | |
112 if (has_error_) return; | |
113 *parameter = WebSocketExtension::Parameter(name.as_string(), value_string); | |
114 } | |
115 | |
116 void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) { | |
117 DCHECK(!has_error_); | |
118 CurrentPointerBackup backup(this); | |
119 ConsumeLWS(); | |
120 DCHECK(!has_error_); | |
121 const char* head = current_; | |
122 while (current_ < end_ && | |
123 !IsControl(current_[0]) && !IsSeparator(current_[0])) | |
124 ++current_; | |
125 if (current_ == head) { | |
126 has_error_ = true; | |
127 return; | |
128 } | |
129 *token = base::StringPiece(head, current_ - head); | |
130 } | |
131 | |
132 void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) { | |
133 DCHECK(!has_error_); | |
134 CurrentPointerBackup backup(this); | |
135 Consume("\"", 1); | |
136 if (has_error_) return; | |
137 *token = ""; | |
138 while (current_ < end_ && !IsControl(current_[0])) { | |
139 if (UnconsumedBytes() >= 2 && current_[0] == '\\') { | |
140 char next = current_[1]; | |
141 if (IsControl(next) || IsSeparator(next)) break; | |
142 *token += next; | |
143 current_ += 2; | |
144 } else if (IsSeparator(current_[0])) { | |
145 break; | |
146 } else { | |
147 *token += current_[0]; | |
148 ++current_; | |
149 } | |
150 } | |
151 Consume("\"", 1); | |
152 has_error_ = has_error_ || token->empty(); | |
153 } | |
154 | |
155 // Unlike the "implied *LWS" specification in RFC2616, this parser | |
156 // consumes *LWS, because there are no adjacent tokens in WebSocket | |
157 // extension grammer. | |
158 void WebSocketExtensionParser::ConsumeLWS() { | |
159 DCHECK(!has_error_); | |
160 size_t counter = 0; | |
161 while (current_ < end_) { | |
162 if (current_[0] == ' ' || current_[0] == '\t') { | |
163 while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t')) | |
164 ++current_; | |
165 } else if (UnconsumedBytes() >= 3 && | |
166 current_[0] == '\r' && current_[1] == '\n' && | |
Adam Rice
2013/09/17 07:45:58
Line coalescing is implemented by HttpResponseHead
yhirano
2013/09/17 09:17:39
Done.
| |
167 (current_[2] == ' ' || current_[2] == '\t')) { | |
168 current_ += 2; | |
169 while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t')) | |
170 ++current_; | |
171 break; | |
172 } else { | |
173 break; | |
174 } | |
175 ++counter; | |
176 } | |
177 return; | |
178 } | |
179 | |
180 bool WebSocketExtensionParser::Lookahead(const char* data, size_t size) { | |
Adam Rice
2013/09/17 07:45:58
Having "size" makes this method much more complica
yhirano
2013/09/17 09:17:39
I like this prototype because we can check an erro
| |
181 DCHECK(!has_error_); | |
182 const char* head = current_; | |
183 | |
184 ConsumeLWS(); | |
185 if (!has_error_) | |
186 Consume(data, size); | |
187 | |
188 bool result = !has_error_; | |
189 current_ = head; | |
190 has_error_ = false; | |
191 return result; | |
192 } | |
193 | |
194 bool WebSocketExtensionParser::ConsumeIfMatch(const char* data, size_t size) { | |
195 DCHECK(!has_error_); | |
196 const char* head = current_; | |
197 | |
198 ConsumeLWS(); | |
199 if (!has_error_) | |
200 Consume(data, size); | |
201 | |
202 if (has_error_) { | |
203 current_ = head; | |
204 has_error_ = false; | |
205 return false; | |
206 } | |
207 return true; | |
208 } | |
209 | |
210 // static | |
211 bool WebSocketExtensionParser::IsControl(char c) { | |
212 return (0 <= c && c <= 31) || c == 127; | |
213 } | |
214 | |
215 // static | |
216 bool WebSocketExtensionParser::IsSeparator(char c) { | |
217 const char separators[] = "()<>@,;:\\\"/[]?={} \t"; | |
218 return strchr(separators, c); | |
219 } | |
220 | |
221 } // namespace net | |
OLD | NEW |