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 |