| 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)); |
| 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)..write(": "); |
| 413 sb.write(": "); | |
| 414 bool fold = _foldHeader(name); | 425 bool fold = _foldHeader(name); |
| 415 for (int i = 0; i < values.length; i++) { | 426 for (int i = 0; i < values.length; i++) { |
| 416 if (i > 0) { | 427 if (i > 0) { |
| 417 if (fold) { | 428 if (fold) { |
| 418 sb.write(", "); | 429 sb.write(", "); |
| 419 } else { | 430 } else { |
| 420 sb.write("\n"); | 431 sb..write("\n")..write(name)..write(": "); |
| 421 sb.write(name); | |
| 422 sb.write(": "); | |
| 423 } | 432 } |
| 424 } | 433 } |
| 425 sb.write(values[i]); | 434 sb.write(values[i]); |
| 426 } | 435 } |
| 427 sb.write("\n"); | 436 sb.write("\n"); |
| 428 }); | 437 }); |
| 429 return sb.toString(); | 438 return sb.toString(); |
| 430 } | 439 } |
| 431 | 440 |
| 432 List<Cookie> _parseCookies() { | 441 List<Cookie> _parseCookies() { |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 Map<String, String> get parameters { | 535 Map<String, String> get parameters { |
| 527 _ensureParameters(); | 536 _ensureParameters(); |
| 528 return _parameters; | 537 return _parameters; |
| 529 } | 538 } |
| 530 | 539 |
| 531 String toString() { | 540 String toString() { |
| 532 StringBuffer sb = new StringBuffer(); | 541 StringBuffer sb = new StringBuffer(); |
| 533 sb.write(_value); | 542 sb.write(_value); |
| 534 if (parameters != null && parameters.length > 0) { | 543 if (parameters != null && parameters.length > 0) { |
| 535 _parameters.forEach((String name, String value) { | 544 _parameters.forEach((String name, String value) { |
| 536 sb.write("; "); | 545 sb..write("; ")..write(name)..write("=")..write(value); |
| 537 sb.write(name); | |
| 538 sb.write("="); | |
| 539 sb.write(value); | |
| 540 }); | 546 }); |
| 541 } | 547 } |
| 542 return sb.toString(); | 548 return sb.toString(); |
| 543 } | 549 } |
| 544 | 550 |
| 545 void _parse(String s, String parameterSeparator, bool preserveBackslash) { | 551 void _parse(String s, String parameterSeparator, bool preserveBackslash) { |
| 546 int index = 0; | 552 int index = 0; |
| 547 | 553 |
| 548 bool done() => index == s.length; | 554 bool done() => index == s.length; |
| 549 | 555 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 687 | 693 |
| 688 String get primaryType => _primaryType; | 694 String get primaryType => _primaryType; |
| 689 | 695 |
| 690 String get subType => _subType; | 696 String get subType => _subType; |
| 691 | 697 |
| 692 String get charset => parameters["charset"]; | 698 String get charset => parameters["charset"]; |
| 693 } | 699 } |
| 694 | 700 |
| 695 | 701 |
| 696 class _Cookie implements Cookie { | 702 class _Cookie implements Cookie { |
| 697 _Cookie([String this.name, String this.value]); | 703 String name; |
| 704 String value; |
| 705 DateTime expires; |
| 706 int maxAge; |
| 707 String domain; |
| 708 String path; |
| 709 bool httpOnly = false; |
| 710 bool secure = false; |
| 711 |
| 712 _Cookie([this.name, this.value]); |
| 698 | 713 |
| 699 _Cookie.fromSetCookieValue(String value) { | 714 _Cookie.fromSetCookieValue(String value) { |
| 700 // Parse the 'set-cookie' header value. | 715 // Parse the 'set-cookie' header value. |
| 701 _parseSetCookieValue(value); | 716 _parseSetCookieValue(value); |
| 702 } | 717 } |
| 703 | 718 |
| 704 // Parse a 'set-cookie' header value according to the rules in RFC 6265. | 719 // Parse a 'set-cookie' header value according to the rules in RFC 6265. |
| 705 void _parseSetCookieValue(String s) { | 720 void _parseSetCookieValue(String s) { |
| 706 int index = 0; | 721 int index = 0; |
| 707 | 722 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 782 } | 797 } |
| 783 index++; // Skip the = character. | 798 index++; // Skip the = character. |
| 784 value = parseValue(); | 799 value = parseValue(); |
| 785 if (done()) return; | 800 if (done()) return; |
| 786 index++; // Skip the ; character. | 801 index++; // Skip the ; character. |
| 787 parseAttributes(); | 802 parseAttributes(); |
| 788 } | 803 } |
| 789 | 804 |
| 790 String toString() { | 805 String toString() { |
| 791 StringBuffer sb = new StringBuffer(); | 806 StringBuffer sb = new StringBuffer(); |
| 792 sb.write(name); | 807 sb..write(name)..write("=")..write(value); |
| 793 sb.write("="); | |
| 794 sb.write(value); | |
| 795 if (expires != null) { | 808 if (expires != null) { |
| 796 sb.write("; Expires="); | 809 sb..write("; Expires=")..write(HttpDate.format(expires)); |
| 797 sb.write(HttpDate.format(expires)); | |
| 798 } | 810 } |
| 799 if (maxAge != null) { | 811 if (maxAge != null) { |
| 800 sb.write("; Max-Age="); | 812 sb..write("; Max-Age=")..write(maxAge); |
| 801 sb.write(maxAge); | |
| 802 } | 813 } |
| 803 if (domain != null) { | 814 if (domain != null) { |
| 804 sb.write("; Domain="); | 815 sb..write("; Domain=")..write(domain); |
| 805 sb.write(domain); | |
| 806 } | 816 } |
| 807 if (path != null) { | 817 if (path != null) { |
| 808 sb.write("; Path="); | 818 sb..write("; Path=")..write(path); |
| 809 sb.write(path); | |
| 810 } | 819 } |
| 811 if (secure) sb.write("; Secure"); | 820 if (secure) sb.write("; Secure"); |
| 812 if (httpOnly) sb.write("; HttpOnly"); | 821 if (httpOnly) sb.write("; HttpOnly"); |
| 813 return sb.toString(); | 822 return sb.toString(); |
| 814 } | 823 } |
| 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 } | 824 } |
| 825 | 825 |
| 826 | 826 |
| 827 class _UnmodifiableMap<K, V> implements Map<K, V> { | 827 class _UnmodifiableMap<K, V> implements Map<K, V> { |
| 828 final Map _map; | 828 final Map _map; |
| 829 const _UnmodifiableMap(this._map); | 829 const _UnmodifiableMap(this._map); |
| 830 | 830 |
| 831 bool containsValue(Object value) => _map.containsValue(value); | 831 bool containsValue(Object value) => _map.containsValue(value); |
| 832 bool containsKey(Object key) => _map.containsKey(key); | 832 bool containsKey(Object key) => _map.containsKey(key); |
| 833 V operator [](Object key) => _map[key]; | 833 V operator [](Object key) => _map[key]; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 846 void clear() { | 846 void clear() { |
| 847 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 847 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 848 } | 848 } |
| 849 void forEach(void f(K key, V value)) => _map.forEach(f); | 849 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 850 Iterable<K> get keys => _map.keys; | 850 Iterable<K> get keys => _map.keys; |
| 851 Iterable<V> get values => _map.values; | 851 Iterable<V> get values => _map.values; |
| 852 int get length => _map.length; | 852 int get length => _map.length; |
| 853 bool get isEmpty => _map.isEmpty; | 853 bool get isEmpty => _map.isEmpty; |
| 854 bool get isNotEmpty => _map.isNotEmpty; | 854 bool get isNotEmpty => _map.isNotEmpty; |
| 855 } | 855 } |
| OLD | NEW |