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 |