Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / net / spdy / spdy_alt_svc_wire_format.cc
bloba418cd73e2df91c7bf7188ad529de882f2aebc1f
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.
5 #include "net/spdy/spdy_alt_svc_wire_format.h"
7 #include <limits>
8 #include <string>
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
13 namespace net {
15 namespace {
17 template <class T>
18 bool ParsePositiveIntegerImpl(StringPiece::const_iterator c,
19 StringPiece::const_iterator end,
20 T* value) {
21 *value = 0;
22 for (; c != end && isdigit(*c); ++c) {
23 if (*value > std::numeric_limits<T>::max() / 10) {
24 return false;
26 *value *= 10;
27 if (*value > std::numeric_limits<T>::max() - (*c - '0')) {
28 return false;
30 *value += *c - '0';
32 return (c == end && *value > 0);
35 } // namespace
37 // static
38 bool SpdyAltSvcWireFormat::ParseHeaderFieldValue(
39 StringPiece value,
40 AlternativeServiceVector* altsvc_vector) {
41 altsvc_vector->clear();
42 StringPiece::const_iterator c = value.begin();
43 while (c != value.end()) {
44 // Parse protocol-id.
45 StringPiece::const_iterator percent_encoded_protocol_id_end =
46 std::find(c, value.end(), '=');
47 std::string protocol_id;
48 if (percent_encoded_protocol_id_end == c ||
49 !PercentDecode(c, percent_encoded_protocol_id_end, &protocol_id)) {
50 return false;
52 c = percent_encoded_protocol_id_end;
53 if (c == value.end()) {
54 return false;
56 // Parse alt-authority.
57 DCHECK_EQ('=', *c);
58 ++c;
59 if (c == value.end() || *c != '"') {
60 return false;
62 ++c;
63 StringPiece::const_iterator alt_authority_begin = c;
64 for (; c != value.end() && *c != '"'; ++c) {
65 // Decode backslash encoding.
66 if (*c != '\\') {
67 continue;
69 ++c;
70 if (c == value.end()) {
71 return false;
74 if (c == alt_authority_begin || c == value.end()) {
75 return false;
77 DCHECK_EQ('"', *c);
78 std::string host;
79 uint16 port;
80 if (!ParseAltAuthority(alt_authority_begin, c, &host, &port)) {
81 return false;
83 ++c;
84 // Parse parameters.
85 uint32 max_age = 86400;
86 double p = 1.0;
87 StringPiece::const_iterator parameters_end = std::find(c, value.end(), ',');
88 while (c != parameters_end) {
89 SkipWhiteSpace(&c, parameters_end);
90 if (c == parameters_end) {
91 break;
93 if (*c != ';') {
94 return false;
96 ++c;
97 SkipWhiteSpace(&c, parameters_end);
98 if (c == parameters_end) {
99 break;
101 std::string parameter_name;
102 for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) {
103 parameter_name.push_back(tolower(*c));
105 SkipWhiteSpace(&c, parameters_end);
106 if (c == parameters_end || *c != '=') {
107 return false;
109 ++c;
110 SkipWhiteSpace(&c, parameters_end);
111 StringPiece::const_iterator parameter_value_begin = c;
112 for (; c != parameters_end && *c != ';' && *c != ' ' && *c != '\t'; ++c) {
114 if (c == parameter_value_begin) {
115 return false;
117 if (parameter_name.compare("ma") == 0) {
118 if (!ParsePositiveInteger32(parameter_value_begin, c, &max_age)) {
119 return false;
121 } else if (parameter_name.compare("p") == 0) {
122 if (!ParseProbability(parameter_value_begin, c, &p)) {
123 return false;
127 altsvc_vector->push_back(
128 AlternativeService(protocol_id, host, port, max_age, p));
129 for (; c != value.end() && (*c == ' ' || *c == '\t' || *c == ','); ++c) {
132 return true;
135 // static
136 std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
137 const AlternativeServiceVector& altsvc_vector) {
138 const char kNibbleToHex[] = "0123456789ABCDEF";
139 std::string value;
140 for (const AlternativeService& altsvc : altsvc_vector) {
141 if (!value.empty()) {
142 value.push_back(',');
144 // Percent escape protocol id according to
145 // http://tools.ietf.org/html/rfc7230#section-3.2.6.
146 for (char c : altsvc.protocol_id) {
147 if (isalnum(c)) {
148 value.push_back(c);
149 continue;
151 switch (c) {
152 case '!':
153 case '#':
154 case '$':
155 case '&':
156 case '\'':
157 case '*':
158 case '+':
159 case '-':
160 case '.':
161 case '^':
162 case '_':
163 case '`':
164 case '|':
165 case '~':
166 value.push_back(c);
167 break;
168 default:
169 value.push_back('%');
170 // Network byte order is big-endian.
171 value.push_back(kNibbleToHex[c >> 4]);
172 value.push_back(kNibbleToHex[c & 0x0f]);
173 break;
176 value.push_back('=');
177 value.push_back('"');
178 for (char c : altsvc.host) {
179 if (c == '"' || c == '\\') {
180 value.push_back('\\');
182 value.push_back(c);
184 base::StringAppendF(&value, ":%d\"", altsvc.port);
185 if (altsvc.max_age != 86400) {
186 base::StringAppendF(&value, "; ma=%d", altsvc.max_age);
188 if (altsvc.p != 1.0) {
189 base::StringAppendF(&value, "; p=%.2f", altsvc.p);
192 return value;
195 // static
196 void SpdyAltSvcWireFormat::SkipWhiteSpace(StringPiece::const_iterator* c,
197 StringPiece::const_iterator end) {
198 for (; *c != end && (**c == ' ' || **c == '\t'); ++*c) {
202 // static
203 bool SpdyAltSvcWireFormat::PercentDecode(StringPiece::const_iterator c,
204 StringPiece::const_iterator end,
205 std::string* output) {
206 output->clear();
207 for (; c != end; ++c) {
208 if (*c != '%') {
209 output->push_back(*c);
210 continue;
212 DCHECK_EQ('%', *c);
213 ++c;
214 if (c == end || !isxdigit(*c)) {
215 return false;
217 char decoded = tolower(*c);
218 // '0' is 0, 'a' is 10.
219 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
220 // Network byte order is big-endian.
221 decoded <<= 4;
222 ++c;
223 if (c == end || !isxdigit(*c)) {
224 return false;
226 decoded += tolower(*c);
227 // '0' is 0, 'a' is 10.
228 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
229 output->push_back(decoded);
231 return true;
234 // static
235 bool SpdyAltSvcWireFormat::ParseAltAuthority(StringPiece::const_iterator c,
236 StringPiece::const_iterator end,
237 std::string* host,
238 uint16* port) {
239 host->clear();
240 for (; c != end && *c != ':'; ++c) {
241 if (*c == '"') {
242 // Port is mandatory.
243 return false;
245 if (*c == '\\') {
246 ++c;
247 if (c == end) {
248 return false;
251 host->push_back(*c);
253 if (c == end) {
254 return false;
256 DCHECK_EQ(':', *c);
257 ++c;
258 return ParsePositiveInteger16(c, end, port);
261 // static
262 bool SpdyAltSvcWireFormat::ParsePositiveInteger16(
263 StringPiece::const_iterator c,
264 StringPiece::const_iterator end,
265 uint16* value) {
266 return ParsePositiveIntegerImpl<uint16>(c, end, value);
269 // static
270 bool SpdyAltSvcWireFormat::ParsePositiveInteger32(
271 StringPiece::const_iterator c,
272 StringPiece::const_iterator end,
273 uint32* value) {
274 return ParsePositiveIntegerImpl<uint32>(c, end, value);
277 // Probability is a decimal fraction between 0.0 and 1.0, inclusive, with
278 // optional leading zero, optional decimal point, and optional digits following
279 // the decimal point, with the restriction that there has to be at least one
280 // digit (that is, "" and "." are not valid).
281 // static
282 bool SpdyAltSvcWireFormat::ParseProbability(StringPiece::const_iterator c,
283 StringPiece::const_iterator end,
284 double* p) {
285 // "" is invalid.
286 if (c == end) {
287 return false;
289 // "." is invalid.
290 if (end - c == 1 && *c == '.') {
291 return false;
293 if (*c == '1') {
294 *p = 1.0;
295 ++c;
296 } else {
297 *p = 0.0;
298 if (*c == '0') {
299 ++c;
302 if (c == end) {
303 return true;
305 if (*c != '.') {
306 return false;
308 // So far we could have had ".", "0.", or "1.".
309 ++c;
310 double place_value = 0.1;
311 for (; c != end && isdigit(*c); ++c) {
312 *p += place_value * (*c - '0');
313 place_value *= 0.1;
315 return (c == end && *p <= 1.0);
318 } // namespace net