Move StartsWith[ASCII] to base namespace.
[chromium-blink-merge.git] / net / spdy / spdy_alt_svc_wire_format.cc
blob95f08c44d4aea9dac1db54f17b3cd7ca20002e12
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(StringPiece value,
39 AlternativeService* altsvc) {
40 altsvc->max_age = 86400;
41 altsvc->p = 1.0;
43 StringPiece::const_iterator c = value.begin();
44 StringPiece::const_iterator percent_encoded_protocol_id_end =
45 std::find(c, value.end(), '=');
46 if (percent_encoded_protocol_id_end == c ||
47 !PercentDecode(c, percent_encoded_protocol_id_end,
48 &(altsvc->protocol_id))) {
49 return false;
51 c = percent_encoded_protocol_id_end;
52 if (c == value.end()) {
53 return false;
55 DCHECK_EQ('=', *c);
56 ++c;
57 if (c == value.end() || *c != '"') {
58 return false;
60 ++c;
61 StringPiece::const_iterator alt_authority_begin = c;
62 for (; c != value.end() && *c != '"'; ++c) {
63 // Decode backslash encoding.
64 if (*c != '\\') {
65 continue;
67 ++c;
68 if (c == value.end()) {
69 return false;
72 if (c == alt_authority_begin || c == value.end()) {
73 return false;
75 DCHECK_EQ('"', *c);
76 if (!ParseAltAuthority(alt_authority_begin, c, &(altsvc->host),
77 &(altsvc->port))) {
78 return false;
80 ++c;
81 StringPiece::const_iterator parameters_end = std::find(c, value.end(), ',');
82 while (c != parameters_end) {
83 SkipWhiteSpace(&c, parameters_end);
84 if (c == parameters_end) {
85 return true;
87 if (*c != ';') {
88 return false;
90 ++c;
91 SkipWhiteSpace(&c, parameters_end);
92 if (c == parameters_end) {
93 return true;
95 std::string parameter_name;
96 for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) {
97 parameter_name.push_back(tolower(*c));
99 SkipWhiteSpace(&c, parameters_end);
100 if (c == parameters_end || *c != '=') {
101 return false;
103 ++c;
104 SkipWhiteSpace(&c, parameters_end);
105 StringPiece::const_iterator parameter_value_begin = c;
106 for (; c != parameters_end && *c != ';' && *c != ' ' && *c != '\t'; ++c) {
108 if (c == parameter_value_begin) {
109 return false;
111 if (parameter_name.compare("ma") == 0) {
112 if (!ParsePositiveInteger32(parameter_value_begin, c,
113 &(altsvc->max_age))) {
114 return false;
116 } else if (parameter_name.compare("p") == 0) {
117 if (!ParseProbability(parameter_value_begin, c, &(altsvc->p))) {
118 return false;
121 SkipWhiteSpace(&c, parameters_end);
123 // TODO(bnc): Parse additional alternative services delimited by ','.
124 return true;
127 // static
128 std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue(
129 const AlternativeService& altsvc) {
130 const char kNibbleToHex[] = "0123456789ABCDEF";
131 std::string value;
132 // Percent escape protocol id according to
133 // http://tools.ietf.org/html/rfc7230#section-3.2.6.
134 for (char c : altsvc.protocol_id) {
135 if (isalnum(c)) {
136 value.push_back(c);
137 continue;
139 switch (c) {
140 case '!':
141 case '#':
142 case '$':
143 case '&':
144 case '\'':
145 case '*':
146 case '+':
147 case '-':
148 case '.':
149 case '^':
150 case '_':
151 case '`':
152 case '|':
153 case '~':
154 value.push_back(c);
155 break;
156 default:
157 value.push_back('%');
158 // Network byte order is big-endian.
159 value.push_back(kNibbleToHex[c >> 4]);
160 value.push_back(kNibbleToHex[c & 0x0f]);
161 break;
164 value.push_back('=');
165 value.push_back('"');
166 for (char c : altsvc.host) {
167 if (c == '"' || c == '\\') {
168 value.push_back('\\');
170 value.push_back(c);
172 base::StringAppendF(&value, ":%d\"", altsvc.port);
173 if (altsvc.max_age != 86400) {
174 base::StringAppendF(&value, "; ma=%d", altsvc.max_age);
176 if (altsvc.p != 1.0) {
177 base::StringAppendF(&value, "; p=%.2f", altsvc.p);
179 return value;
182 // static
183 void SpdyAltSvcWireFormat::SkipWhiteSpace(StringPiece::const_iterator* c,
184 StringPiece::const_iterator end) {
185 for (; *c != end && (**c == ' ' || **c == '\t'); ++*c) {
189 // static
190 bool SpdyAltSvcWireFormat::PercentDecode(StringPiece::const_iterator c,
191 StringPiece::const_iterator end,
192 std::string* output) {
193 output->clear();
194 for (; c != end; ++c) {
195 if (*c != '%') {
196 output->push_back(*c);
197 continue;
199 DCHECK_EQ('%', *c);
200 ++c;
201 if (c == end || !isxdigit(*c)) {
202 return false;
204 char decoded = tolower(*c);
205 // '0' is 0, 'a' is 10.
206 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
207 // Network byte order is big-endian.
208 decoded <<= 4;
209 ++c;
210 if (c == end || !isxdigit(*c)) {
211 return false;
213 decoded += tolower(*c);
214 // '0' is 0, 'a' is 10.
215 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a');
216 output->push_back(decoded);
218 return true;
221 // static
222 bool SpdyAltSvcWireFormat::ParseAltAuthority(StringPiece::const_iterator c,
223 StringPiece::const_iterator end,
224 std::string* host,
225 uint16* port) {
226 host->clear();
227 for (; c != end && *c != ':'; ++c) {
228 if (*c == '"') {
229 // Port is mandatory.
230 return false;
232 if (*c == '\\') {
233 ++c;
234 if (c == end) {
235 return false;
238 host->push_back(*c);
240 if (c == end) {
241 return false;
243 DCHECK_EQ(':', *c);
244 ++c;
245 return ParsePositiveInteger16(c, end, port);
248 // static
249 bool SpdyAltSvcWireFormat::ParsePositiveInteger16(
250 StringPiece::const_iterator c,
251 StringPiece::const_iterator end,
252 uint16* value) {
253 return ParsePositiveIntegerImpl<uint16>(c, end, value);
256 // static
257 bool SpdyAltSvcWireFormat::ParsePositiveInteger32(
258 StringPiece::const_iterator c,
259 StringPiece::const_iterator end,
260 uint32* value) {
261 return ParsePositiveIntegerImpl<uint32>(c, end, value);
264 // Probability is a decimal fraction between 0.0 and 1.0, inclusive, with
265 // optional leading zero, optional decimal point, and optional digits following
266 // the decimal point, with the restriction that there has to be at least one
267 // digit (that is, "" and "." are not valid).
268 // static
269 bool SpdyAltSvcWireFormat::ParseProbability(StringPiece::const_iterator c,
270 StringPiece::const_iterator end,
271 double* p) {
272 // "" is invalid.
273 if (c == end) {
274 return false;
276 // "." is invalid.
277 if (end - c == 1 && *c == '.') {
278 return false;
280 if (*c == '1') {
281 *p = 1.0;
282 ++c;
283 } else {
284 *p = 0.0;
285 if (*c == '0') {
286 ++c;
289 if (c == end) {
290 return true;
292 if (*c != '.') {
293 return false;
295 // So far we could have had ".", "0.", or "1.".
296 ++c;
297 double place_value = 0.1;
298 for (; c != end && isdigit(*c); ++c) {
299 *p += place_value * (*c - '0');
300 place_value *= 0.1;
302 return (c == end && *p <= 1.0);
305 } // namespace net