OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015 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/spdy/spdy_alt_svc_wire_format.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cctype> | |
9 #include <limits> | |
10 | |
11 #include "base/logging.h" | |
12 #include "net/spdy/platform/api/spdy_string_utils.h" | |
13 | |
14 namespace net { | |
15 | |
16 namespace { | |
17 | |
18 template <class T> | |
19 bool ParsePositiveIntegerImpl(SpdyStringPiece::const_iterator c, | |
20 SpdyStringPiece::const_iterator end, | |
21 T* value) { | |
22 *value = 0; | |
23 for (; c != end && std::isdigit(*c); ++c) { | |
24 if (*value > std::numeric_limits<T>::max() / 10) { | |
25 return false; | |
26 } | |
27 *value *= 10; | |
28 if (*value > std::numeric_limits<T>::max() - (*c - '0')) { | |
29 return false; | |
30 } | |
31 *value += *c - '0'; | |
32 } | |
33 return (c == end && *value > 0); | |
34 } | |
35 | |
36 } // namespace | |
37 | |
38 SpdyAltSvcWireFormat::AlternativeService::AlternativeService() {} | |
39 | |
40 SpdyAltSvcWireFormat::AlternativeService::AlternativeService( | |
41 const SpdyString& protocol_id, | |
42 const SpdyString& host, | |
43 uint16_t port, | |
44 uint32_t max_age, | |
45 VersionVector version) | |
46 : protocol_id(protocol_id), | |
47 host(host), | |
48 port(port), | |
49 max_age(max_age), | |
50 version(version) {} | |
51 | |
52 SpdyAltSvcWireFormat::AlternativeService::~AlternativeService() {} | |
53 | |
54 SpdyAltSvcWireFormat::AlternativeService::AlternativeService( | |
55 const AlternativeService& other) = default; | |
56 | |
57 // static | |
58 bool SpdyAltSvcWireFormat::ParseHeaderFieldValue( | |
59 SpdyStringPiece value, | |
60 AlternativeServiceVector* altsvc_vector) { | |
61 // Empty value is invalid according to the specification. | |
62 if (value.empty()) { | |
63 return false; | |
64 } | |
65 altsvc_vector->clear(); | |
66 if (value == SpdyStringPiece("clear")) { | |
67 return true; | |
68 } | |
69 SpdyStringPiece::const_iterator c = value.begin(); | |
70 while (c != value.end()) { | |
71 // Parse protocol-id. | |
72 SpdyStringPiece::const_iterator percent_encoded_protocol_id_end = | |
73 std::find(c, value.end(), '='); | |
74 SpdyString protocol_id; | |
75 if (percent_encoded_protocol_id_end == c || | |
76 !PercentDecode(c, percent_encoded_protocol_id_end, &protocol_id)) { | |
77 return false; | |
78 } | |
79 c = percent_encoded_protocol_id_end; | |
80 if (c == value.end()) { | |
81 return false; | |
82 } | |
83 // Parse alt-authority. | |
84 DCHECK_EQ('=', *c); | |
85 ++c; | |
86 if (c == value.end() || *c != '"') { | |
87 return false; | |
88 } | |
89 ++c; | |
90 SpdyStringPiece::const_iterator alt_authority_begin = c; | |
91 for (; c != value.end() && *c != '"'; ++c) { | |
92 // Decode backslash encoding. | |
93 if (*c != '\\') { | |
94 continue; | |
95 } | |
96 ++c; | |
97 if (c == value.end()) { | |
98 return false; | |
99 } | |
100 } | |
101 if (c == alt_authority_begin || c == value.end()) { | |
102 return false; | |
103 } | |
104 DCHECK_EQ('"', *c); | |
105 SpdyString host; | |
106 uint16_t port; | |
107 if (!ParseAltAuthority(alt_authority_begin, c, &host, &port)) { | |
108 return false; | |
109 } | |
110 ++c; | |
111 // Parse parameters. | |
112 uint32_t max_age = 86400; | |
113 VersionVector version; | |
114 SpdyStringPiece::const_iterator parameters_end = | |
115 std::find(c, value.end(), ','); | |
116 while (c != parameters_end) { | |
117 SkipWhiteSpace(&c, parameters_end); | |
118 if (c == parameters_end) { | |
119 break; | |
120 } | |
121 if (*c != ';') { | |
122 return false; | |
123 } | |
124 ++c; | |
125 SkipWhiteSpace(&c, parameters_end); | |
126 if (c == parameters_end) { | |
127 break; | |
128 } | |
129 SpdyString parameter_name; | |
130 for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) { | |
131 parameter_name.push_back(tolower(*c)); | |
132 } | |
133 SkipWhiteSpace(&c, parameters_end); | |
134 if (c == parameters_end || *c != '=') { | |
135 return false; | |
136 } | |
137 ++c; | |
138 SkipWhiteSpace(&c, parameters_end); | |
139 SpdyStringPiece::const_iterator parameter_value_begin = c; | |
140 for (; c != parameters_end && *c != ';' && *c != ' ' && *c != '\t'; ++c) { | |
141 } | |
142 if (c == parameter_value_begin) { | |
143 return false; | |
144 } | |
145 if (parameter_name.compare("ma") == 0) { | |
146 if (!ParsePositiveInteger32(parameter_value_begin, c, &max_age)) { | |
147 return false; | |
148 } | |
149 } else if (parameter_name.compare("v") == 0) { | |
150 // Version is a comma separated list of positive integers enclosed in | |
151 // quotation marks. Since it can contain commas, which are not | |
152 // delineating alternative service entries, |parameters_end| and |c| can | |
153 // be invalid. | |
154 if (*parameter_value_begin != '"') { | |
155 return false; | |
156 } | |
157 c = std::find(parameter_value_begin + 1, value.end(), '"'); | |
158 if (c == value.end()) { | |
159 return false; | |
160 } | |
161 ++c; | |
162 parameters_end = std::find(c, value.end(), ','); | |
163 SpdyStringPiece::const_iterator v_begin = parameter_value_begin + 1; | |
164 while (v_begin < c) { | |
165 SpdyStringPiece::const_iterator v_end = v_begin; | |
166 while (v_end < c - 1 && *v_end != ',') { | |
167 ++v_end; | |
168 } | |
169 uint16_t v; | |
170 if (!ParsePositiveInteger16(v_begin, v_end, &v)) { | |
171 return false; | |
172 } | |
173 version.push_back(v); | |
174 v_begin = v_end + 1; | |
175 if (v_begin == c - 1) { | |
176 // List ends in comma. | |
177 return false; | |
178 } | |
179 } | |
180 } | |
181 } | |
182 altsvc_vector->emplace_back(protocol_id, host, port, max_age, version); | |
183 for (; c != value.end() && (*c == ' ' || *c == '\t' || *c == ','); ++c) { | |
184 } | |
185 } | |
186 return true; | |
187 } | |
188 | |
189 // static | |
190 SpdyString SpdyAltSvcWireFormat::SerializeHeaderFieldValue( | |
191 const AlternativeServiceVector& altsvc_vector) { | |
192 if (altsvc_vector.empty()) { | |
193 return SpdyString("clear"); | |
194 } | |
195 const char kNibbleToHex[] = "0123456789ABCDEF"; | |
196 SpdyString value; | |
197 for (const AlternativeService& altsvc : altsvc_vector) { | |
198 if (!value.empty()) { | |
199 value.push_back(','); | |
200 } | |
201 // Percent escape protocol id according to | |
202 // http://tools.ietf.org/html/rfc7230#section-3.2.6. | |
203 for (char c : altsvc.protocol_id) { | |
204 if (isalnum(c)) { | |
205 value.push_back(c); | |
206 continue; | |
207 } | |
208 switch (c) { | |
209 case '!': | |
210 case '#': | |
211 case '$': | |
212 case '&': | |
213 case '\'': | |
214 case '*': | |
215 case '+': | |
216 case '-': | |
217 case '.': | |
218 case '^': | |
219 case '_': | |
220 case '`': | |
221 case '|': | |
222 case '~': | |
223 value.push_back(c); | |
224 break; | |
225 default: | |
226 value.push_back('%'); | |
227 // Network byte order is big-endian. | |
228 value.push_back(kNibbleToHex[c >> 4]); | |
229 value.push_back(kNibbleToHex[c & 0x0f]); | |
230 break; | |
231 } | |
232 } | |
233 value.push_back('='); | |
234 value.push_back('"'); | |
235 for (char c : altsvc.host) { | |
236 if (c == '"' || c == '\\') { | |
237 value.push_back('\\'); | |
238 } | |
239 value.push_back(c); | |
240 } | |
241 SpdyStringAppendF(&value, ":%d\"", altsvc.port); | |
242 if (altsvc.max_age != 86400) { | |
243 SpdyStringAppendF(&value, "; ma=%d", altsvc.max_age); | |
244 } | |
245 if (!altsvc.version.empty()) { | |
246 value.append("; v=\""); | |
247 for (VersionVector::const_iterator it = altsvc.version.begin(); | |
248 it != altsvc.version.end(); ++it) { | |
249 if (it != altsvc.version.begin()) { | |
250 value.append(","); | |
251 } | |
252 SpdyStringAppendF(&value, "%d", *it); | |
253 } | |
254 value.append("\""); | |
255 } | |
256 } | |
257 return value; | |
258 } | |
259 | |
260 // static | |
261 void SpdyAltSvcWireFormat::SkipWhiteSpace(SpdyStringPiece::const_iterator* c, | |
262 SpdyStringPiece::const_iterator end) { | |
263 for (; *c != end && (**c == ' ' || **c == '\t'); ++*c) { | |
264 } | |
265 } | |
266 | |
267 // static | |
268 bool SpdyAltSvcWireFormat::PercentDecode(SpdyStringPiece::const_iterator c, | |
269 SpdyStringPiece::const_iterator end, | |
270 SpdyString* output) { | |
271 output->clear(); | |
272 for (; c != end; ++c) { | |
273 if (*c != '%') { | |
274 output->push_back(*c); | |
275 continue; | |
276 } | |
277 DCHECK_EQ('%', *c); | |
278 ++c; | |
279 if (c == end || !std::isxdigit(*c)) { | |
280 return false; | |
281 } | |
282 // Network byte order is big-endian. | |
283 char decoded = SpdyHexDigitToInt(*c) << 4; | |
284 ++c; | |
285 if (c == end || !std::isxdigit(*c)) { | |
286 return false; | |
287 } | |
288 decoded += SpdyHexDigitToInt(*c); | |
289 output->push_back(decoded); | |
290 } | |
291 return true; | |
292 } | |
293 | |
294 // static | |
295 bool SpdyAltSvcWireFormat::ParseAltAuthority( | |
296 SpdyStringPiece::const_iterator c, | |
297 SpdyStringPiece::const_iterator end, | |
298 SpdyString* host, | |
299 uint16_t* port) { | |
300 host->clear(); | |
301 if (c == end) { | |
302 return false; | |
303 } | |
304 if (*c == '[') { | |
305 for (; c != end && *c != ']'; ++c) { | |
306 if (*c == '"') { | |
307 // Port is mandatory. | |
308 return false; | |
309 } | |
310 host->push_back(*c); | |
311 } | |
312 if (c == end) { | |
313 return false; | |
314 } | |
315 DCHECK_EQ(']', *c); | |
316 host->push_back(*c); | |
317 ++c; | |
318 } else { | |
319 for (; c != end && *c != ':'; ++c) { | |
320 if (*c == '"') { | |
321 // Port is mandatory. | |
322 return false; | |
323 } | |
324 if (*c == '\\') { | |
325 ++c; | |
326 if (c == end) { | |
327 return false; | |
328 } | |
329 } | |
330 host->push_back(*c); | |
331 } | |
332 } | |
333 if (c == end || *c != ':') { | |
334 return false; | |
335 } | |
336 DCHECK_EQ(':', *c); | |
337 ++c; | |
338 return ParsePositiveInteger16(c, end, port); | |
339 } | |
340 | |
341 // static | |
342 bool SpdyAltSvcWireFormat::ParsePositiveInteger16( | |
343 SpdyStringPiece::const_iterator c, | |
344 SpdyStringPiece::const_iterator end, | |
345 uint16_t* value) { | |
346 return ParsePositiveIntegerImpl<uint16_t>(c, end, value); | |
347 } | |
348 | |
349 // static | |
350 bool SpdyAltSvcWireFormat::ParsePositiveInteger32( | |
351 SpdyStringPiece::const_iterator c, | |
352 SpdyStringPiece::const_iterator end, | |
353 uint32_t* value) { | |
354 return ParsePositiveIntegerImpl<uint32_t>(c, end, value); | |
355 } | |
356 | |
357 } // namespace net | |
OLD | NEW |