Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 class _HttpHeaders implements HttpHeaders { | 7 class _HttpHeaders implements HttpHeaders { |
| 8 final Map<String, List<String>> _headers; | 8 final Map<String, List<String>> _headers; |
| 9 final String protocolVersion; | 9 final String protocolVersion; |
| 10 | 10 |
| 11 bool _mutable = true; // Are the headers currently mutable? | 11 bool _mutable = true; // Are the headers currently mutable? |
| 12 List<String> _noFoldingHeaders; | 12 List<String> _noFoldingHeaders; |
| 13 | 13 |
| 14 int _contentLength = -1; | 14 int _contentLength = -1; |
| 15 bool _persistentConnection = true; | 15 bool _persistentConnection = true; |
| 16 bool _chunkedTransferEncoding = false; | 16 bool _chunkedTransferEncoding = false; |
| 17 String _host; | 17 String _host; |
| 18 int _port; | 18 int _port; |
| 19 | 19 |
| 20 _HttpHeaders(String this.protocolVersion) | 20 _HttpHeaders(this.protocolVersion) |
| 21 : _headers = new HashMap<String, List<String>>() { | 21 : _headers = new HashMap<String, List<String>>() { |
| 22 if (protocolVersion == "1.0") { | 22 if (protocolVersion == "1.0") { |
| 23 _persistentConnection = false; | 23 _persistentConnection = false; |
| 24 } | 24 } |
| 25 } | 25 } |
| 26 | 26 |
| 27 List<String> operator[](String name) { | 27 List<String> operator[](String name) => _headers[name.toLowerCase()]; |
| 28 name = name.toLowerCase(); | |
| 29 return _headers[name]; | |
| 30 } | |
| 31 | 28 |
| 32 String value(String name) { | 29 String value(String name) { |
| 33 name = name.toLowerCase(); | 30 name = name.toLowerCase(); |
| 34 List<String> values = _headers[name]; | 31 List<String> values = _headers[name]; |
| 35 if (values == null) return null; | 32 if (values == null) return null; |
| 36 if (values.length > 1) { | 33 if (values.length > 1) { |
| 37 throw new HttpException("More than one value for header $name"); | 34 throw new HttpException("More than one value for header $name"); |
| 38 } | 35 } |
| 39 return values[0]; | 36 return values[0]; |
| 40 } | 37 } |
| 41 | 38 |
| 42 void add(String name, value) { | 39 void add(String name, value) { |
| 43 _checkMutable(); | 40 _checkMutable(); |
| 44 _addAll(name.toLowerCase(), value); | 41 _addAll(name.toLowerCase(), value); |
| 45 } | 42 } |
| 46 | 43 |
| 47 void _addAll(String name, value) { | 44 void _addAll(String name, value) { |
| 48 if (value is List) { | 45 if (value is List) { |
| 49 for (int i = 0; i < value.length; i++) { | 46 value.forEach((v) => _add(name, v)); |
|
Lasse Reichstein Nielsen
2014/01/06 09:29:30
This may be a little slower than the original.
Pro
| |
| 50 _add(name, value[i]); | |
| 51 } | |
| 52 } else { | 47 } else { |
| 53 _add(name, value); | 48 _add(name, value); |
| 54 } | 49 } |
| 55 } | 50 } |
| 56 | 51 |
| 57 void set(String name, Object value) { | 52 void set(String name, Object value) { |
| 58 _checkMutable(); | 53 _checkMutable(); |
| 59 name = name.toLowerCase(); | 54 name = name.toLowerCase(); |
| 60 _headers.remove(name); | 55 _headers.remove(name); |
| 61 _addAll(name, value); | 56 _addAll(name, value); |
| (...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 245 } | 240 } |
| 246 | 241 |
| 247 void set contentType(ContentType contentType) { | 242 void set contentType(ContentType contentType) { |
| 248 _checkMutable(); | 243 _checkMutable(); |
| 249 _set(HttpHeaders.CONTENT_TYPE, contentType.toString()); | 244 _set(HttpHeaders.CONTENT_TYPE, contentType.toString()); |
| 250 } | 245 } |
| 251 | 246 |
| 252 // [name] must be a lower-case version of the name. | 247 // [name] must be a lower-case version of the name. |
| 253 void _add(String name, value) { | 248 void _add(String name, value) { |
| 254 // TODO(sgjesse): Add immutable state throw HttpException is immutable. | 249 // TODO(sgjesse): Add immutable state throw HttpException is immutable. |
| 255 if (name == HttpHeaders.CONTENT_LENGTH) { | 250 switch (name) { |
| 256 if (value is int) { | 251 case HttpHeaders.CONTENT_LENGTH: |
| 257 contentLength = value; | 252 if (value is int) { |
| 258 } else if (value is String) { | 253 contentLength = value; |
| 259 contentLength = int.parse(value); | 254 } else if (value is String) { |
| 260 } else { | 255 contentLength = int.parse(value); |
| 261 throw new HttpException("Unexpected type for header named $name"); | |
| 262 } | |
| 263 } else if (name == HttpHeaders.TRANSFER_ENCODING) { | |
| 264 if (value == "chunked") { | |
| 265 chunkedTransferEncoding = true; | |
| 266 } else { | |
| 267 _addValue(name, value); | |
| 268 } | |
| 269 } else if (name == HttpHeaders.DATE) { | |
| 270 if (value is DateTime) { | |
| 271 date = value; | |
| 272 } else if (value is String) { | |
| 273 _set(HttpHeaders.DATE, value); | |
| 274 } else { | |
| 275 throw new HttpException("Unexpected type for header named $name"); | |
| 276 } | |
| 277 } else if (name == HttpHeaders.EXPIRES) { | |
| 278 if (value is DateTime) { | |
| 279 expires = value; | |
| 280 } else if (value is String) { | |
| 281 _set(HttpHeaders.EXPIRES, value); | |
| 282 } else { | |
| 283 throw new HttpException("Unexpected type for header named $name"); | |
| 284 } | |
| 285 } else if (name == HttpHeaders.IF_MODIFIED_SINCE) { | |
| 286 if (value is DateTime) { | |
| 287 ifModifiedSince = value; | |
| 288 } else if (value is String) { | |
| 289 _set(HttpHeaders.IF_MODIFIED_SINCE, value); | |
| 290 } else { | |
| 291 throw new HttpException("Unexpected type for header named $name"); | |
| 292 } | |
| 293 } else if (name == HttpHeaders.HOST) { | |
| 294 if (value is String) { | |
| 295 int pos = value.indexOf(":"); | |
| 296 if (pos == -1) { | |
| 297 _host = value; | |
| 298 _port = HttpClient.DEFAULT_HTTP_PORT; | |
| 299 } else { | 256 } else { |
| 300 if (pos > 0) { | 257 throw new HttpException("Unexpected type for header named $name"); |
| 301 _host = value.substring(0, pos); | 258 } |
| 302 } else { | 259 break; |
| 303 _host = null; | 260 |
| 304 } | 261 case HttpHeaders.TRANSFER_ENCODING: |
| 305 if (pos + 1 == value.length) { | 262 if (value == "chunked") { |
| 263 chunkedTransferEncoding = true; | |
| 264 } else { | |
| 265 _addValue(name, value); | |
| 266 } | |
| 267 break; | |
| 268 | |
| 269 case HttpHeaders.DATE: | |
| 270 if (value is DateTime) { | |
| 271 date = value; | |
| 272 } else if (value is String) { | |
| 273 _set(HttpHeaders.DATE, value); | |
| 274 } else { | |
| 275 throw new HttpException("Unexpected type for header named $name"); | |
| 276 } | |
| 277 break; | |
| 278 | |
| 279 case HttpHeaders.EXPIRES: | |
| 280 if (value is DateTime) { | |
| 281 expires = value; | |
| 282 } else if (value is String) { | |
| 283 _set(HttpHeaders.EXPIRES, value); | |
| 284 } else { | |
| 285 throw new HttpException("Unexpected type for header named $name"); | |
| 286 } | |
| 287 break; | |
| 288 | |
| 289 case HttpHeaders.IF_MODIFIED_SINCE: | |
| 290 if (value is DateTime) { | |
| 291 ifModifiedSince = value; | |
| 292 } else if (value is String) { | |
| 293 _set(HttpHeaders.IF_MODIFIED_SINCE, value); | |
| 294 } else { | |
| 295 throw new HttpException("Unexpected type for header named $name"); | |
| 296 } | |
| 297 break; | |
| 298 | |
| 299 case HttpHeaders.HOST: | |
| 300 if (value is String) { | |
| 301 int pos = value.indexOf(":"); | |
| 302 if (pos == -1) { | |
| 303 _host = value; | |
| 306 _port = HttpClient.DEFAULT_HTTP_PORT; | 304 _port = HttpClient.DEFAULT_HTTP_PORT; |
| 307 } else { | 305 } else { |
| 308 try { | 306 if (pos > 0) { |
| 309 _port = int.parse(value.substring(pos + 1)); | 307 _host = value.substring(0, pos); |
| 310 } on FormatException catch (e) { | 308 } else { |
| 311 _port = null; | 309 _host = null; |
| 310 } | |
| 311 if (pos + 1 == value.length) { | |
| 312 _port = HttpClient.DEFAULT_HTTP_PORT; | |
| 313 } else { | |
| 314 try { | |
| 315 _port = int.parse(value.substring(pos + 1)); | |
| 316 } on FormatException catch (e) { | |
| 317 _port = null; | |
| 318 } | |
| 312 } | 319 } |
| 313 } | 320 } |
| 321 _set(HttpHeaders.HOST, value); | |
| 322 } else { | |
| 323 throw new HttpException("Unexpected type for header named $name"); | |
| 314 } | 324 } |
| 315 _set(HttpHeaders.HOST, value); | 325 break; |
| 316 } else { | 326 |
| 317 throw new HttpException("Unexpected type for header named $name"); | 327 case HttpHeaders.CONNECTION: |
| 318 } | 328 var lowerCaseValue = value.toLowerCase(); |
| 319 } else if (name == HttpHeaders.CONNECTION) { | 329 if (lowerCaseValue == 'close') { |
| 320 var lowerCaseValue = value.toLowerCase(); | 330 _persistentConnection = false; |
| 321 if (lowerCaseValue == 'close') { | 331 } else if (lowerCaseValue == 'keep-alive') { |
| 322 _persistentConnection = false; | 332 _persistentConnection = true; |
| 323 } else if (lowerCaseValue == 'keep-alive') { | 333 } |
| 324 _persistentConnection = true; | 334 _addValue(name, value); |
| 325 } | 335 break; |
| 326 _addValue(name, value); | 336 |
| 327 } else if (name == HttpHeaders.CONTENT_TYPE) { | 337 case HttpHeaders.CONTENT_TYPE: |
| 328 _set(HttpHeaders.CONTENT_TYPE, value); | 338 _set(HttpHeaders.CONTENT_TYPE, value); |
| 329 } else { | 339 break; |
| 330 _addValue(name, value); | 340 |
| 341 default: | |
| 342 _addValue(name, value); | |
| 331 } | 343 } |
| 332 } | 344 } |
| 333 | 345 |
| 334 void _addValue(String name, Object value) { | 346 void _addValue(String name, Object value) { |
| 335 List<String> values = _headers[name]; | 347 List<String> values = _headers[name]; |
| 336 if (values == null) { | 348 if (values == null) { |
| 337 values = new List<String>(); | 349 values = new List<String>(); |
| 338 _headers[name] = values; | 350 _headers[name] = values; |
| 339 } | 351 } |
| 340 if (value is DateTime) { | 352 if (value is DateTime) { |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 402 write(values[i].codeUnits); | 414 write(values[i].codeUnits); |
| 403 } | 415 } |
| 404 write(const [_CharCode.CR, _CharCode.LF]); | 416 write(const [_CharCode.CR, _CharCode.LF]); |
| 405 }); | 417 }); |
| 406 return offset; | 418 return offset; |
| 407 } | 419 } |
| 408 | 420 |
| 409 String toString() { | 421 String toString() { |
| 410 StringBuffer sb = new StringBuffer(); | 422 StringBuffer sb = new StringBuffer(); |
| 411 _headers.forEach((String name, List<String> values) { | 423 _headers.forEach((String name, List<String> values) { |
| 412 sb.write(name); | 424 sb..write(name) |
| 413 sb.write(": "); | 425 ..write(": "); |
|
Lasse Reichstein Nielsen
2014/01/06 09:29:30
Hmm, technically the second line should be indente
| |
| 414 bool fold = _foldHeader(name); | 426 bool fold = _foldHeader(name); |
| 415 for (int i = 0; i < values.length; i++) { | 427 for (int i = 0; i < values.length; i++) { |
| 416 if (i > 0) { | 428 if (i > 0) { |
| 417 if (fold) { | 429 if (fold) { |
| 418 sb.write(", "); | 430 sb.write(", "); |
| 419 } else { | 431 } else { |
| 420 sb.write("\n"); | 432 sb..write("\n") |
| 421 sb.write(name); | 433 ..write(name) |
| 422 sb.write(": "); | 434 ..write(": "); |
| 423 } | 435 } |
| 424 } | 436 } |
| 425 sb.write(values[i]); | 437 sb.write(values[i]); |
| 426 } | 438 } |
| 427 sb.write("\n"); | 439 sb.write("\n"); |
| 428 }); | 440 }); |
| 429 return sb.toString(); | 441 return sb.toString(); |
| 430 } | 442 } |
| 431 | 443 |
| 432 List<Cookie> _parseCookies() { | 444 List<Cookie> _parseCookies() { |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 526 Map<String, String> get parameters { | 538 Map<String, String> get parameters { |
| 527 _ensureParameters(); | 539 _ensureParameters(); |
| 528 return _parameters; | 540 return _parameters; |
| 529 } | 541 } |
| 530 | 542 |
| 531 String toString() { | 543 String toString() { |
| 532 StringBuffer sb = new StringBuffer(); | 544 StringBuffer sb = new StringBuffer(); |
| 533 sb.write(_value); | 545 sb.write(_value); |
| 534 if (parameters != null && parameters.length > 0) { | 546 if (parameters != null && parameters.length > 0) { |
| 535 _parameters.forEach((String name, String value) { | 547 _parameters.forEach((String name, String value) { |
| 536 sb.write("; "); | 548 sb..write("; ") |
| 537 sb.write(name); | 549 ..write(name) |
| 538 sb.write("="); | 550 ..write("=") |
| 539 sb.write(value); | 551 ..write(value); |
| 540 }); | 552 }); |
| 541 } | 553 } |
| 542 return sb.toString(); | 554 return sb.toString(); |
| 543 } | 555 } |
| 544 | 556 |
| 545 void _parse(String s, String parameterSeparator, bool preserveBackslash) { | 557 void _parse(String s, String parameterSeparator, bool preserveBackslash) { |
| 546 int index = 0; | 558 int index = 0; |
| 547 | 559 |
| 548 bool done() => index == s.length; | 560 bool done() => index == s.length; |
| 549 | 561 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 687 | 699 |
| 688 String get primaryType => _primaryType; | 700 String get primaryType => _primaryType; |
| 689 | 701 |
| 690 String get subType => _subType; | 702 String get subType => _subType; |
| 691 | 703 |
| 692 String get charset => parameters["charset"]; | 704 String get charset => parameters["charset"]; |
| 693 } | 705 } |
| 694 | 706 |
| 695 | 707 |
| 696 class _Cookie implements Cookie { | 708 class _Cookie implements Cookie { |
| 697 _Cookie([String this.name, String this.value]); | 709 String name; |
|
Lasse Reichstein Nielsen
2014/01/06 09:29:30
Are none of these really final?
| |
| 710 String value; | |
| 711 DateTime expires; | |
| 712 int maxAge; | |
| 713 String domain; | |
| 714 String path; | |
| 715 bool httpOnly = false; | |
| 716 bool secure = false; | |
| 717 | |
| 718 _Cookie([this.name, this.value]); | |
| 698 | 719 |
| 699 _Cookie.fromSetCookieValue(String value) { | 720 _Cookie.fromSetCookieValue(String value) { |
| 700 // Parse the 'set-cookie' header value. | 721 // Parse the 'set-cookie' header value. |
| 701 _parseSetCookieValue(value); | 722 _parseSetCookieValue(value); |
| 702 } | 723 } |
| 703 | 724 |
| 704 // Parse a 'set-cookie' header value according to the rules in RFC 6265. | 725 // Parse a 'set-cookie' header value according to the rules in RFC 6265. |
| 705 void _parseSetCookieValue(String s) { | 726 void _parseSetCookieValue(String s) { |
| 706 int index = 0; | 727 int index = 0; |
| 707 | 728 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 782 } | 803 } |
| 783 index++; // Skip the = character. | 804 index++; // Skip the = character. |
| 784 value = parseValue(); | 805 value = parseValue(); |
| 785 if (done()) return; | 806 if (done()) return; |
| 786 index++; // Skip the ; character. | 807 index++; // Skip the ; character. |
| 787 parseAttributes(); | 808 parseAttributes(); |
| 788 } | 809 } |
| 789 | 810 |
| 790 String toString() { | 811 String toString() { |
| 791 StringBuffer sb = new StringBuffer(); | 812 StringBuffer sb = new StringBuffer(); |
| 792 sb.write(name); | 813 sb..write(name) |
| 793 sb.write("="); | 814 ..write("=") |
| 794 sb.write(value); | 815 ..write(value); |
| 795 if (expires != null) { | 816 if (expires != null) { |
| 796 sb.write("; Expires="); | 817 sb..write("; Expires=") |
| 797 sb.write(HttpDate.format(expires)); | 818 ..write(HttpDate.format(expires)); |
| 798 } | 819 } |
| 799 if (maxAge != null) { | 820 if (maxAge != null) { |
| 800 sb.write("; Max-Age="); | 821 sb..write("; Max-Age=") |
| 801 sb.write(maxAge); | 822 ..write(maxAge); |
| 802 } | 823 } |
| 803 if (domain != null) { | 824 if (domain != null) { |
| 804 sb.write("; Domain="); | 825 sb..write("; Domain=") |
| 805 sb.write(domain); | 826 ..write(domain); |
| 806 } | 827 } |
| 807 if (path != null) { | 828 if (path != null) { |
| 808 sb.write("; Path="); | 829 sb..write("; Path=") |
| 809 sb.write(path); | 830 ..write(path); |
| 810 } | 831 } |
| 811 if (secure) sb.write("; Secure"); | 832 if (secure) sb.write("; Secure"); |
| 812 if (httpOnly) sb.write("; HttpOnly"); | 833 if (httpOnly) sb.write("; HttpOnly"); |
| 813 return sb.toString(); | 834 return sb.toString(); |
| 814 } | 835 } |
| 815 | |
| 816 String name; | |
| 817 String value; | |
| 818 DateTime expires; | |
| 819 int maxAge; | |
| 820 String domain; | |
| 821 String path; | |
| 822 bool httpOnly = false; | |
| 823 bool secure = false; | |
| 824 } | 836 } |
| 825 | 837 |
| 826 | 838 |
| 827 class _UnmodifiableMap<K, V> implements Map<K, V> { | 839 class _UnmodifiableMap<K, V> implements Map<K, V> { |
| 828 final Map _map; | 840 final Map _map; |
| 829 const _UnmodifiableMap(this._map); | 841 const _UnmodifiableMap(this._map); |
| 830 | 842 |
| 831 bool containsValue(Object value) => _map.containsValue(value); | 843 bool containsValue(Object value) => _map.containsValue(value); |
| 832 bool containsKey(Object key) => _map.containsKey(key); | 844 bool containsKey(Object key) => _map.containsKey(key); |
| 833 V operator [](Object key) => _map[key]; | 845 V operator [](Object key) => _map[key]; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 846 void clear() { | 858 void clear() { |
| 847 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 859 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 848 } | 860 } |
| 849 void forEach(void f(K key, V value)) => _map.forEach(f); | 861 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 850 Iterable<K> get keys => _map.keys; | 862 Iterable<K> get keys => _map.keys; |
| 851 Iterable<V> get values => _map.values; | 863 Iterable<V> get values => _map.values; |
| 852 int get length => _map.length; | 864 int get length => _map.length; |
| 853 bool get isEmpty => _map.isEmpty; | 865 bool get isEmpty => _map.isEmpty; |
| 854 bool get isNotEmpty => _map.isNotEmpty; | 866 bool get isNotEmpty => _map.isNotEmpty; |
| 855 } | 867 } |
| OLD | NEW |