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 <limits> |
| 8 #include <string> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "base/strings/stringprintf.h" |
| 12 |
| 13 namespace net { |
| 14 |
| 15 namespace { |
| 16 |
| 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; |
| 25 } |
| 26 *value *= 10; |
| 27 if (*value > std::numeric_limits<T>::max() - (*c - '0')) { |
| 28 return false; |
| 29 } |
| 30 *value += *c - '0'; |
| 31 } |
| 32 return (c == end && *value > 0); |
| 33 } |
| 34 |
| 35 } // namespace |
| 36 |
| 37 // static |
| 38 bool SpdyAltSvcWireFormat::ParseHeaderFieldValue(StringPiece value, |
| 39 std::string* protocol_id, |
| 40 std::string* host, |
| 41 uint16* port, |
| 42 uint32* max_age, |
| 43 double* p) { |
| 44 *max_age = 86400; |
| 45 *p = 1.0; |
| 46 |
| 47 StringPiece::const_iterator c = value.begin(); |
| 48 StringPiece::const_iterator percent_encoded_protocol_id_end = |
| 49 std::find(c, value.end(), '='); |
| 50 if (percent_encoded_protocol_id_end == c || |
| 51 !PercentDecode(c, percent_encoded_protocol_id_end, protocol_id)) { |
| 52 return false; |
| 53 } |
| 54 c = percent_encoded_protocol_id_end; |
| 55 if (c == value.end()) { |
| 56 return false; |
| 57 } |
| 58 DCHECK_EQ('=', *c); |
| 59 ++c; |
| 60 if (c == value.end() || *c != '"') { |
| 61 return false; |
| 62 } |
| 63 ++c; |
| 64 StringPiece::const_iterator alt_authority_begin = c; |
| 65 for (; c != value.end() && *c != '"'; ++c) { |
| 66 // Decode backslash encoding. |
| 67 if (*c != '\\') { |
| 68 continue; |
| 69 } |
| 70 ++c; |
| 71 if (c == value.end()) { |
| 72 return false; |
| 73 } |
| 74 } |
| 75 if (c == alt_authority_begin || c == value.end()) { |
| 76 return false; |
| 77 } |
| 78 DCHECK_EQ('"', *c); |
| 79 if (!ParseAltAuthority(alt_authority_begin, c, host, port)) { |
| 80 return false; |
| 81 } |
| 82 ++c; |
| 83 StringPiece::const_iterator parameters_end = std::find(c, value.end(), ','); |
| 84 while (c != parameters_end) { |
| 85 SkipWhiteSpace(&c, parameters_end); |
| 86 if (c == parameters_end) { |
| 87 return true; |
| 88 } |
| 89 if (*c != ';') { |
| 90 return false; |
| 91 } |
| 92 ++c; |
| 93 SkipWhiteSpace(&c, parameters_end); |
| 94 if (c == parameters_end) { |
| 95 return true; |
| 96 } |
| 97 std::string parameter_name; |
| 98 for (; c != parameters_end && *c != '=' && *c != ' ' && *c != '\t'; ++c) { |
| 99 parameter_name.push_back(tolower(*c)); |
| 100 } |
| 101 SkipWhiteSpace(&c, parameters_end); |
| 102 if (c == parameters_end || *c != '=') { |
| 103 return false; |
| 104 } |
| 105 ++c; |
| 106 SkipWhiteSpace(&c, parameters_end); |
| 107 StringPiece::const_iterator parameter_value_begin = c; |
| 108 for (; c != parameters_end && *c != ';' && *c != ' ' && *c != '\t'; ++c) { |
| 109 } |
| 110 if (c == parameter_value_begin) { |
| 111 return false; |
| 112 } |
| 113 if (parameter_name.compare("ma") == 0) { |
| 114 if (!ParsePositiveInteger32(parameter_value_begin, c, max_age)) { |
| 115 return false; |
| 116 } |
| 117 } else if (parameter_name.compare("p") == 0) { |
| 118 if (!ParseProbability(parameter_value_begin, c, p)) { |
| 119 return false; |
| 120 } |
| 121 } |
| 122 SkipWhiteSpace(&c, parameters_end); |
| 123 } |
| 124 // TODO(bnc): Parse additional alternative services delimited by ','. |
| 125 return true; |
| 126 } |
| 127 |
| 128 // static |
| 129 std::string SpdyAltSvcWireFormat::SerializeHeaderFieldValue( |
| 130 const std::string& protocol_id, |
| 131 const std::string& host, |
| 132 uint16 port, |
| 133 uint32 max_age, |
| 134 double p) { |
| 135 const char kNibbleToHex[] = "0123456789ABCDEF"; |
| 136 std::string value; |
| 137 // Percent escape protocol id according to |
| 138 // http://tools.ietf.org/html/rfc7230#section-3.2.6. |
| 139 for (char c : protocol_id) { |
| 140 if (isalnum(c)) { |
| 141 value.push_back(c); |
| 142 continue; |
| 143 } |
| 144 switch (c) { |
| 145 case '!': |
| 146 case '#': |
| 147 case '$': |
| 148 case '&': |
| 149 case '\'': |
| 150 case '*': |
| 151 case '+': |
| 152 case '-': |
| 153 case '.': |
| 154 case '^': |
| 155 case '_': |
| 156 case '`': |
| 157 case '|': |
| 158 case '~': |
| 159 value.push_back(c); |
| 160 break; |
| 161 default: |
| 162 value.push_back('%'); |
| 163 // Network byte order is big-endian. |
| 164 value.push_back(kNibbleToHex[c >> 4]); |
| 165 value.push_back(kNibbleToHex[c & 0x0f]); |
| 166 break; |
| 167 } |
| 168 } |
| 169 value.push_back('='); |
| 170 value.push_back('"'); |
| 171 for (char c : host) { |
| 172 if (c == '"' || c == '\\') { |
| 173 value.push_back('\\'); |
| 174 } |
| 175 value.push_back(c); |
| 176 } |
| 177 base::StringAppendF(&value, ":%d\"", port); |
| 178 if (max_age != 86400) { |
| 179 base::StringAppendF(&value, "; ma=%d", max_age); |
| 180 } |
| 181 if (p != 1.0) { |
| 182 base::StringAppendF(&value, "; p=%.2f", p); |
| 183 } |
| 184 return value; |
| 185 } |
| 186 |
| 187 // static |
| 188 void SpdyAltSvcWireFormat::SkipWhiteSpace(StringPiece::const_iterator* c, |
| 189 StringPiece::const_iterator end) { |
| 190 for (; *c != end && (**c == ' ' || **c == '\t'); ++*c) { |
| 191 } |
| 192 } |
| 193 |
| 194 // static |
| 195 bool SpdyAltSvcWireFormat::PercentDecode(StringPiece::const_iterator c, |
| 196 StringPiece::const_iterator end, |
| 197 std::string* output) { |
| 198 output->clear(); |
| 199 for (; c != end; ++c) { |
| 200 if (*c != '%') { |
| 201 output->push_back(*c); |
| 202 continue; |
| 203 } |
| 204 DCHECK_EQ('%', *c); |
| 205 ++c; |
| 206 if (c == end || !isxdigit(*c)) { |
| 207 return false; |
| 208 } |
| 209 char decoded = tolower(*c); |
| 210 // '0' is 0, 'a' is 10. |
| 211 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a'); |
| 212 // Network byte order is big-endian. |
| 213 decoded <<= 4; |
| 214 ++c; |
| 215 if (c == end || !isxdigit(*c)) { |
| 216 return false; |
| 217 } |
| 218 decoded += tolower(*c); |
| 219 // '0' is 0, 'a' is 10. |
| 220 decoded += isdigit(*c) ? (0 - '0') : (10 - 'a'); |
| 221 output->push_back(decoded); |
| 222 } |
| 223 return true; |
| 224 } |
| 225 |
| 226 // static |
| 227 bool SpdyAltSvcWireFormat::ParseAltAuthority(StringPiece::const_iterator c, |
| 228 StringPiece::const_iterator end, |
| 229 std::string* host, |
| 230 uint16* port) { |
| 231 host->clear(); |
| 232 for (; c != end && *c != ':'; ++c) { |
| 233 if (*c == '"') { |
| 234 // Port is mandatory. |
| 235 return false; |
| 236 } |
| 237 if (*c == '\\') { |
| 238 ++c; |
| 239 if (c == end) { |
| 240 return false; |
| 241 } |
| 242 } |
| 243 host->push_back(*c); |
| 244 } |
| 245 if (c == end) { |
| 246 return false; |
| 247 } |
| 248 DCHECK_EQ(':', *c); |
| 249 ++c; |
| 250 return ParsePositiveInteger16(c, end, port); |
| 251 } |
| 252 |
| 253 // static |
| 254 bool SpdyAltSvcWireFormat::ParsePositiveInteger16( |
| 255 StringPiece::const_iterator c, |
| 256 StringPiece::const_iterator end, |
| 257 uint16* value) { |
| 258 return ParsePositiveIntegerImpl<uint16>(c, end, value); |
| 259 } |
| 260 |
| 261 // static |
| 262 bool SpdyAltSvcWireFormat::ParsePositiveInteger32( |
| 263 StringPiece::const_iterator c, |
| 264 StringPiece::const_iterator end, |
| 265 uint32* value) { |
| 266 return ParsePositiveIntegerImpl<uint32>(c, end, value); |
| 267 } |
| 268 |
| 269 // Probability is a decimal fraction between 0.0 and 1.0, inclusive, with |
| 270 // optional leading zero, optional decimal point, and optional digits following |
| 271 // the decimal point, with the restriction that there has to be at least one |
| 272 // digit (that is, "" and "." are not valid). |
| 273 // static |
| 274 bool SpdyAltSvcWireFormat::ParseProbability(StringPiece::const_iterator c, |
| 275 StringPiece::const_iterator end, |
| 276 double* p) { |
| 277 // "" is invalid. |
| 278 if (c == end) { |
| 279 return false; |
| 280 } |
| 281 // "." is invalid. |
| 282 if (end - c == 1 && *c == '.') { |
| 283 return false; |
| 284 } |
| 285 if (*c == '1') { |
| 286 *p = 1.0; |
| 287 ++c; |
| 288 } else { |
| 289 *p = 0.0; |
| 290 if (*c == '0') { |
| 291 ++c; |
| 292 } |
| 293 } |
| 294 if (c == end) { |
| 295 return true; |
| 296 } |
| 297 if (*c != '.') { |
| 298 return false; |
| 299 } |
| 300 // So far we could have had ".", "0.", or "1.". |
| 301 ++c; |
| 302 double place_value = 0.1; |
| 303 for (; c != end && isdigit(*c); ++c) { |
| 304 *p += place_value * (*c - '0'); |
| 305 place_value *= 0.1; |
| 306 } |
| 307 return (c == end && *p <= 1.0); |
| 308 } |
| 309 |
| 310 } // namespace net |
OLD | NEW |