| 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 |