Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 class _HttpHeaders implements HttpHeaders { | 5 class _HttpHeaders implements HttpHeaders { |
| 6 _HttpHeaders() : _headers = new Map<String, List<String>>(); | 6 _HttpHeaders() : _headers = new Map<String, List<String>>(); |
| 7 | 7 |
| 8 List<String> operator[](String name) { | 8 List<String> operator[](String name) { |
| 9 name = name.toLowerCase(); | 9 name = name.toLowerCase(); |
| 10 return _headers[name]; | 10 return _headers[name]; |
| 11 } | 11 } |
| 12 | 12 |
| 13 String value(String name) { | 13 String value(String name) { |
| 14 name = name.toLowerCase(); | 14 name = name.toLowerCase(); |
| 15 _syncHeaderValuesCache(name); | |
| 15 List<String> values = _headers[name]; | 16 List<String> values = _headers[name]; |
| 16 if (values == null) return null; | 17 if (values == null) return null; |
| 17 if (values.length > 1) { | 18 if (values.length > 1) { |
| 18 throw new HttpException("More than one value for header $name"); | 19 throw new HttpException("More than one value for header $name"); |
| 19 } | 20 } |
| 20 return values[0]; | 21 return values[0]; |
| 21 } | 22 } |
| 22 | 23 |
| 23 void add(String name, Object value) { | 24 void add(String name, Object value) { |
| 24 _checkMutable(); | 25 _checkMutable(); |
| 25 if (value is List) { | 26 if (value is List) { |
| 26 for (int i = 0; i < value.length; i++) { | 27 for (int i = 0; i < value.length; i++) { |
| 27 _add(name, value[i]); | 28 _add(name, value[i]); |
| 28 } | 29 } |
| 29 } else { | 30 } else { |
| 30 _add(name, value); | 31 _add(name, value); |
| 31 } | 32 } |
| 32 } | 33 } |
| 33 | 34 |
| 34 void set(String name, Object value) { | 35 void set(String name, Object value) { |
| 36 name = name.toLowerCase(); | |
| 35 _checkMutable(); | 37 _checkMutable(); |
| 36 removeAll(name); | 38 removeAll(name); |
| 37 add(name, value); | 39 add(name, value); |
| 38 } | 40 } |
| 39 | 41 |
| 40 void remove(String name, Object value) { | 42 void remove(String name, Object value) { |
| 41 _checkMutable(); | 43 _checkMutable(); |
| 42 name = name.toLowerCase(); | 44 name = name.toLowerCase(); |
| 43 List<String> values = _headers[name]; | 45 List<String> values = _headers[name]; |
| 44 if (values != null) { | 46 if (values != null) { |
| 45 int index = values.indexOf(value); | 47 int index = values.indexOf(value); |
| 46 if (index != -1) { | 48 if (index != -1) { |
| 47 values.removeRange(index, 1); | 49 values.removeRange(index, 1); |
| 48 } | 50 } |
| 49 } | 51 } |
| 52 _clearHeaderValueCache(name); | |
| 50 } | 53 } |
| 51 | 54 |
| 52 void removeAll(String name) { | 55 void removeAll(String name) { |
| 53 _checkMutable(); | 56 _checkMutable(); |
| 54 name = name.toLowerCase(); | 57 name = name.toLowerCase(); |
| 55 _headers.remove(name); | 58 _headers.remove(name); |
| 59 _clearHeaderValueCache(name); | |
| 56 } | 60 } |
| 57 | 61 |
| 58 void forEach(void f(String name, List<String> values)) { | 62 void forEach(void f(String name, List<String> values)) { |
| 63 _syncHeaderValuesCache(); | |
| 59 _headers.forEach(f); | 64 _headers.forEach(f); |
| 60 } | 65 } |
| 61 | 66 |
| 62 String get host() => _host; | 67 String get host() => _host; |
| 63 | 68 |
| 64 void set host(String host) { | 69 void set host(String host) { |
| 65 _checkMutable(); | 70 _checkMutable(); |
| 66 _host = host; | 71 _host = host; |
| 67 _updateHostHeader(); | 72 _updateHostHeader(); |
| 68 } | 73 } |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 108 } | 113 } |
| 109 | 114 |
| 110 void set expires(Date expires) { | 115 void set expires(Date expires) { |
| 111 _checkMutable(); | 116 _checkMutable(); |
| 112 // Format "Expires" header with date in Greenwich Mean Time (GMT). | 117 // Format "Expires" header with date in Greenwich Mean Time (GMT). |
| 113 String formatted = | 118 String formatted = |
| 114 _HttpUtils.formatDate(expires.changeTimeZone(new TimeZone.utc())); | 119 _HttpUtils.formatDate(expires.changeTimeZone(new TimeZone.utc())); |
| 115 _set("expires", formatted); | 120 _set("expires", formatted); |
| 116 } | 121 } |
| 117 | 122 |
| 123 void get contentType() { | |
| 124 if (_contentType == null) { | |
| 125 var values = _headers["content-type"]; | |
| 126 if (values != null) { | |
| 127 _contentType = new ContentType.fromString(values[0]); | |
| 128 } else { | |
| 129 _contentType = new ContentType(); | |
| 130 } | |
| 131 } | |
| 132 return _contentType; | |
| 133 } | |
| 134 | |
| 118 void _add(String name, Object value) { | 135 void _add(String name, Object value) { |
| 119 // TODO(sgjesse): Add immutable state throw HttpException is immutable. | 136 // TODO(sgjesse): Add immutable state throw HttpException is immutable. |
| 120 if (name.toLowerCase() == "date") { | 137 if (name.toLowerCase() == "date") { |
| 121 if (value is Date) { | 138 if (value is Date) { |
| 122 date = value; | 139 date = value; |
| 123 } else if (value is String) { | 140 } else if (value is String) { |
| 124 _set("date", value); | 141 _set("date", value); |
| 125 } else { | 142 } else { |
| 126 throw new HttpException("Unexpected type for header named $name"); | 143 throw new HttpException("Unexpected type for header named $name"); |
| 127 } | 144 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 148 _port = HttpClient.DEFAULT_HTTP_PORT; | 165 _port = HttpClient.DEFAULT_HTTP_PORT; |
| 149 } else { | 166 } else { |
| 150 try { | 167 try { |
| 151 _port = Math.parseInt(value.substring(pos + 1)); | 168 _port = Math.parseInt(value.substring(pos + 1)); |
| 152 } catch (BadNumberFormatException e) { | 169 } catch (BadNumberFormatException e) { |
| 153 _port = null; | 170 _port = null; |
| 154 } | 171 } |
| 155 } | 172 } |
| 156 _set("host", value); | 173 _set("host", value); |
| 157 } | 174 } |
| 175 } else if (name.toLowerCase() == "content-type") { | |
| 176 _set("content-type", value); | |
| 177 _contentType = null; | |
| 158 } else { | 178 } else { |
| 159 name = name.toLowerCase(); | 179 name = name.toLowerCase(); |
| 160 List<String> values = _headers[name]; | 180 List<String> values = _headers[name]; |
| 161 if (values == null) { | 181 if (values == null) { |
| 162 values = new List<String>(); | 182 values = new List<String>(); |
| 163 _headers[name] = values; | 183 _headers[name] = values; |
| 164 } | 184 } |
| 165 values.add(value.toString()); | 185 values.add(value.toString()); |
| 166 } | 186 } |
| 167 } | 187 } |
| 168 | 188 |
| 169 void _set(String name, String value) { | 189 void _set(String name, String value) { |
| 170 name = name.toLowerCase(); | 190 name = name.toLowerCase(); |
| 171 List<String> values = new List<String>(); | 191 List<String> values = new List<String>(); |
| 172 _headers[name] = values; | 192 _headers[name] = values; |
| 173 values.add(value); | 193 values.add(value); |
| 174 } | 194 } |
| 175 | 195 |
| 176 _checkMutable() { | 196 _checkMutable() { |
| 177 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); | 197 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); |
| 178 } | 198 } |
| 179 | 199 |
| 180 _updateHostHeader() { | 200 _updateHostHeader() { |
| 181 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; | 201 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; |
| 182 String portPart = defaultPort ? "" : ":$_port"; | 202 String portPart = defaultPort ? "" : ":$_port"; |
| 183 _set("host", "$host$portPart"); | 203 _set("host", "$host$portPart"); |
| 184 } | 204 } |
| 185 | 205 |
| 206 void _syncHeaderValuesCache([String name]) { | |
| 207 if (name == null) { | |
| 208 if (_contentType != null) { | |
| 209 _set("content-type", _contentType.toString()); | |
| 210 } | |
| 211 } else if (name == "content-type" && _contentType != null) { | |
| 212 _set("content-type", _contentType.toString()); | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 void _clearHeaderValueCache([String name]) { | |
| 217 if (name == null) { | |
| 218 _contentType = null; | |
| 219 } else if (name == "content-type") { | |
| 220 _contentType = null; | |
| 221 } | |
| 222 } | |
| 223 | |
| 186 _write(_HttpConnectionBase connection) { | 224 _write(_HttpConnectionBase connection) { |
| 187 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; | 225 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; |
| 188 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; | 226 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; |
| 189 final CRLF = const [_CharCode.CR, _CharCode.LF]; | 227 final CRLF = const [_CharCode.CR, _CharCode.LF]; |
| 190 | 228 |
| 229 _syncHeaderValuesCache(); | |
| 230 | |
| 191 // Format headers. | 231 // Format headers. |
| 192 _headers.forEach((String name, List<String> values) { | 232 _headers.forEach((String name, List<String> values) { |
| 193 List<int> data; | 233 List<int> data; |
| 194 data = name.charCodes(); | 234 data = name.charCodes(); |
| 195 connection._write(data); | 235 connection._write(data); |
| 196 connection._write(COLONSP); | 236 connection._write(COLONSP); |
| 197 for (int i = 0; i < values.length; i++) { | 237 for (int i = 0; i < values.length; i++) { |
| 198 if (i > 0) { | 238 if (i > 0) { |
| 199 connection._write(COMMASP); | 239 connection._write(COMMASP); |
| 200 } | 240 } |
| 201 data = values[i].charCodes(); | 241 data = values[i].charCodes(); |
| 202 connection._write(data); | 242 connection._write(data); |
| 203 } | 243 } |
| 204 connection._write(CRLF); | 244 connection._write(CRLF); |
| 205 }); | 245 }); |
| 206 } | 246 } |
| 207 | 247 |
| 208 String toString() { | 248 String toString() { |
| 249 _syncHeaderValuesCache(); | |
| 209 StringBuffer sb = new StringBuffer(); | 250 StringBuffer sb = new StringBuffer(); |
| 210 _headers.forEach((String name, List<String> values) { | 251 _headers.forEach((String name, List<String> values) { |
| 211 sb.add(name); | 252 sb.add(name); |
| 212 sb.add(": "); | 253 sb.add(": "); |
| 213 for (int i = 0; i < values.length; i++) { | 254 for (int i = 0; i < values.length; i++) { |
| 214 if (i > 0) { | 255 if (i > 0) { |
| 215 sb.add(", "); | 256 sb.add(", "); |
| 216 } | 257 } |
| 217 sb.add(values[i]); | 258 sb.add(values[i]); |
| 218 } | 259 } |
| 219 sb.add("\n"); | 260 sb.add("\n"); |
| 220 }); | 261 }); |
| 221 return sb.toString(); | 262 return sb.toString(); |
| 222 } | 263 } |
| 223 | 264 |
| 224 bool _mutable = true; // Are the headers currently mutable? | 265 bool _mutable = true; // Are the headers currently mutable? |
| 225 Map<String, List<String>> _headers; | 266 Map<String, List<String>> _headers; |
| 226 | 267 |
| 227 String _host; | 268 String _host; |
| 228 int _port; | 269 int _port; |
| 270 ContentType _contentType; | |
| 229 } | 271 } |
| 230 | 272 |
| 231 | 273 |
| 274 class _HeaderValue implements HeaderValue { | |
| 275 _HeaderValue([String this.value = ""]); | |
| 276 | |
| 277 _HeaderValue.fromString(String value) { | |
| 278 // Parse the string. | |
| 279 _parse(value); | |
| 280 } | |
| 281 | |
| 282 Map<String, String> get parameters() { | |
| 283 if (_parameters == null) _parameters = new Map<String, String>(); | |
| 284 return _parameters; | |
| 285 } | |
| 286 | |
| 287 String toString() { | |
| 288 StringBuffer sb = new StringBuffer(); | |
| 289 sb.add(value); | |
| 290 if (parameters != null && parameters.length > 0) { | |
| 291 _parameters.forEach((String name, String value) { | |
| 292 sb.add("; "); | |
| 293 sb.add(name); | |
| 294 sb.add("="); | |
| 295 sb.add(value); | |
| 296 }); | |
| 297 } | |
| 298 return sb.toString(); | |
| 299 } | |
| 300 | |
| 301 void _parse(String s) { | |
| 302 int index = 0; | |
| 303 | |
| 304 bool done() => index == s.length; | |
| 305 | |
| 306 void skipWS() { | |
| 307 while (!done()) { | |
| 308 if (s[index] != " " && s[index] != "\t") return; | |
| 309 index++; | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 String parseValue() { | |
| 314 int start = index; | |
| 315 while (!done()) { | |
| 316 if (s[index] == " " || s[index] == "\t" || s[index] == ";") break; | |
| 317 index++; | |
| 318 } | |
| 319 return s.substring(start, index).toLowerCase(); | |
| 320 } | |
| 321 | |
| 322 void expect(String expected) { | |
| 323 if (done()) throw new HttpException("Failed to parse header value [$s]"); | |
| 324 if (s[index] != expected) { | |
| 325 throw new HttpException("Failed to parse header value [$s]"); | |
| 326 } | |
| 327 index++; | |
| 328 } | |
| 329 | |
| 330 void parseParameters() { | |
| 331 _parameters = new Map<String, String>(); | |
| 332 | |
| 333 String parseParameterName() { | |
| 334 int start = index; | |
| 335 while (!done()) { | |
| 336 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; | |
| 337 index++; | |
| 338 } | |
| 339 return s.substring(start, index).toLowerCase(); | |
| 340 } | |
| 341 | |
| 342 String parseParameterValue() { | |
| 343 if (s[index] == "\"") { | |
| 344 // Parse quoted value. | |
| 345 StringBuffer sb = new StringBuffer(); | |
| 346 index++; | |
| 347 while (!done()) { | |
| 348 if (s[index] == "\\") { | |
| 349 if (index + 1 == s.length) { | |
| 350 throw new HttpException("Failed to parse header value [$s]"); | |
| 351 } | |
| 352 index++; | |
| 353 } else if (s[index] == "\"") { | |
| 354 index++; | |
| 355 break; | |
| 356 } | |
| 357 sb.add(s[index]); | |
| 358 index++; | |
| 359 } | |
| 360 return sb.toString(); | |
| 361 } else { | |
| 362 // Parse non-quoted value. | |
| 363 return parseValue(); | |
| 364 } | |
| 365 } | |
| 366 | |
| 367 while (!done()) { | |
| 368 skipWS(); | |
| 369 if (done()) return; | |
| 370 String name = parseParameterName(); | |
| 371 skipWS(); | |
| 372 expect("="); | |
| 373 skipWS(); | |
| 374 String value = parseParameterValue(); | |
| 375 _parameters[name] = value; | |
| 376 skipWS(); | |
| 377 if (done()) return; | |
| 378 expect(";"); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 skipWS(); | |
| 383 value = parseValue(); | |
| 384 skipWS(); | |
| 385 if (done()) return; | |
| 386 expect(";"); | |
| 387 parseParameters(); | |
| 388 } | |
| 389 | |
| 390 String value; | |
| 391 Map<String, String> _parameters; | |
| 392 } | |
| 393 | |
| 394 | |
| 395 class _ContentType extends _HeaderValue implements ContentType { | |
| 396 _ContentType([String this._primaryType = "", String this._subType = ""]); | |
| 397 | |
| 398 _ContentType.fromString(String value) : super.fromString(value); | |
| 399 | |
| 400 String get value() => "$_primaryType/$_subType"; | |
| 401 | |
| 402 void set value(String s) { | |
| 403 int index = s.indexOf("/"); | |
| 404 if (index == -1 || s.length == index - 1) { | |
|
Mads Ager (google)
2012/05/21 11:49:33
I agree with Anders about this being: index == (s.
Søren Gjesse
2012/05/22 12:47:16
Of cause you are both right.
| |
| 405 primaryType = s.trim().toLowerCase(); | |
| 406 } else { | |
| 407 primaryType = s.substring(0, index).trim().toLowerCase(); | |
| 408 subType = s.substring(index + 1).trim().toLowerCase(); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 String get primaryType() => _primaryType; | |
| 413 | |
| 414 void set primaryType(String s) { | |
| 415 _primaryType = s; | |
| 416 } | |
| 417 | |
| 418 String get subType() => _subType; | |
| 419 | |
| 420 void set subType(String s) { | |
| 421 _subType = s; | |
| 422 } | |
| 423 | |
| 424 String get charset() => parameters["charset"]; | |
| 425 | |
| 426 void set charset(String s) { | |
| 427 parameters["charset"] = s; | |
| 428 } | |
| 429 | |
| 430 String _primaryType = ""; | |
| 431 String _subType = ""; | |
| 432 } | |
| 433 | |
| 434 | |
| 232 class _HttpRequestResponseBase { | 435 class _HttpRequestResponseBase { |
| 233 final int START = 0; | 436 final int START = 0; |
| 234 final int HEADER_SENT = 1; | 437 final int HEADER_SENT = 1; |
| 235 final int DONE = 2; | 438 final int DONE = 2; |
| 236 final int UPGRADED = 3; | 439 final int UPGRADED = 3; |
| 237 | 440 |
| 238 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) | 441 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) |
| 239 : _headers = new _HttpHeaders() { | 442 : _headers = new _HttpHeaders() { |
| 240 _state = START; | 443 _state = START; |
| 241 } | 444 } |
| (...skipping 1405 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1647 | 1850 |
| 1648 | 1851 |
| 1649 class _RedirectInfo implements RedirectInfo { | 1852 class _RedirectInfo implements RedirectInfo { |
| 1650 const _RedirectInfo(int this.statusCode, | 1853 const _RedirectInfo(int this.statusCode, |
| 1651 String this.method, | 1854 String this.method, |
| 1652 Uri this.location); | 1855 Uri this.location); |
| 1653 final int statusCode; | 1856 final int statusCode; |
| 1654 final String method; | 1857 final String method; |
| 1655 final Uri location; | 1858 final Uri location; |
| 1656 } | 1859 } |
| OLD | NEW |