| 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; |
| 9 final String protocolVersion; |
| 10 |
| 11 bool _mutable = true; // Are the headers currently mutable? |
| 12 List<String> _noFoldingHeaders; |
| 13 |
| 14 int _contentLength = -1; |
| 15 bool _persistentConnection = true; |
| 16 bool _chunkedTransferEncoding = false; |
| 17 String _host; |
| 18 int _port; |
| 19 |
| 8 _HttpHeaders(String this.protocolVersion) | 20 _HttpHeaders(String this.protocolVersion) |
| 9 : _headers = new HashMap<String, List<String>>(); | 21 : _headers = new HashMap<String, List<String>>() { |
| 22 if (protocolVersion == "1.0") { |
| 23 _persistentConnection = false; |
| 24 } |
| 25 } |
| 10 | 26 |
| 11 List<String> operator[](String name) { | 27 List<String> operator[](String name) { |
| 12 name = name.toLowerCase(); | 28 name = name.toLowerCase(); |
| 13 return _headers[name]; | 29 return _headers[name]; |
| 14 } | 30 } |
| 15 | 31 |
| 16 String value(String name) { | 32 String value(String name) { |
| 17 name = name.toLowerCase(); | 33 name = name.toLowerCase(); |
| 18 List<String> values = _headers[name]; | 34 List<String> values = _headers[name]; |
| 19 if (values == null) return null; | 35 if (values == null) return null; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 63 | 79 |
| 64 void forEach(void f(String name, List<String> values)) { | 80 void forEach(void f(String name, List<String> values)) { |
| 65 _headers.forEach(f); | 81 _headers.forEach(f); |
| 66 } | 82 } |
| 67 | 83 |
| 68 void noFolding(String name) { | 84 void noFolding(String name) { |
| 69 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); | 85 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); |
| 70 _noFoldingHeaders.add(name); | 86 _noFoldingHeaders.add(name); |
| 71 } | 87 } |
| 72 | 88 |
| 73 bool get persistentConnection { | 89 bool get persistentConnection => _persistentConnection; |
| 74 List<String> connection = _headers[HttpHeaders.CONNECTION]; | |
| 75 if (protocolVersion == "1.1") { | |
| 76 if (connection == null) return true; | |
| 77 return !connection.any((value) => value.toLowerCase() == "close"); | |
| 78 } else { | |
| 79 if (connection == null) return false; | |
| 80 return connection.any((value) => value.toLowerCase() == "keep-alive"); | |
| 81 } | |
| 82 } | |
| 83 | 90 |
| 84 void set persistentConnection(bool persistentConnection) { | 91 void set persistentConnection(bool persistentConnection) { |
| 85 _checkMutable(); | 92 _checkMutable(); |
| 86 // Determine the value of the "Connection" header. | 93 if (persistentConnection == _persistentConnection) return; |
| 87 remove(HttpHeaders.CONNECTION, "close"); | 94 if (persistentConnection) { |
| 88 remove(HttpHeaders.CONNECTION, "keep-alive"); | 95 if (protocolVersion == "1.1") { |
| 89 if (protocolVersion == "1.1" && !persistentConnection) { | 96 remove(HttpHeaders.CONNECTION, "close"); |
| 90 add(HttpHeaders.CONNECTION, "close"); | 97 } else { |
| 91 } else if (protocolVersion == "1.0" && persistentConnection) { | 98 if (_contentLength == -1) { |
| 92 add(HttpHeaders.CONNECTION, "keep-alive"); | 99 throw new HttpException( |
| 100 "Trying to set 'Connection: Keep-Alive' on HTTP 1.0 headers with " |
| 101 "no ContentLength"); |
| 102 } |
| 103 add(HttpHeaders.CONNECTION, "keep-alive"); |
| 104 } |
| 105 } else { |
| 106 if (protocolVersion == "1.1") { |
| 107 add(HttpHeaders.CONNECTION, "close"); |
| 108 } else { |
| 109 remove(HttpHeaders.CONNECTION, "keep-alive"); |
| 110 } |
| 93 } | 111 } |
| 112 _persistentConnection = persistentConnection; |
| 94 } | 113 } |
| 95 | 114 |
| 96 int get contentLength => _contentLength; | 115 int get contentLength => _contentLength; |
| 97 | 116 |
| 98 void set contentLength(int contentLength) { | 117 void set contentLength(int contentLength) { |
| 99 _checkMutable(); | 118 _checkMutable(); |
| 119 if (protocolVersion == "1.0" && |
| 120 persistentConnection && |
| 121 contentLength == -1) { |
| 122 throw new HttpException( |
| 123 "Trying to clear ContentLength on HTTP 1.0 headers with " |
| 124 "'Connection: Keep-Alive' set"); |
| 125 } |
| 100 _contentLength = contentLength; | 126 _contentLength = contentLength; |
| 101 if (_contentLength >= 0) { | 127 if (_contentLength >= 0) { |
| 128 if (chunkedTransferEncoding) chunkedTransferEncoding = false; |
| 102 _set(HttpHeaders.CONTENT_LENGTH, contentLength.toString()); | 129 _set(HttpHeaders.CONTENT_LENGTH, contentLength.toString()); |
| 103 } else { | 130 } else { |
| 104 removeAll(HttpHeaders.CONTENT_LENGTH); | 131 removeAll(HttpHeaders.CONTENT_LENGTH); |
| 105 } | 132 } |
| 106 } | 133 } |
| 107 | 134 |
| 108 bool get chunkedTransferEncoding => _chunkedTransferEncoding; | 135 bool get chunkedTransferEncoding => _chunkedTransferEncoding; |
| 109 | 136 |
| 110 void set chunkedTransferEncoding(bool chunkedTransferEncoding) { | 137 void set chunkedTransferEncoding(bool chunkedTransferEncoding) { |
| 111 _checkMutable(); | 138 _checkMutable(); |
| 112 _chunkedTransferEncoding = chunkedTransferEncoding; | 139 if (chunkedTransferEncoding && protocolVersion == "1.0") { |
| 113 List<String> values = _headers[HttpHeaders.TRANSFER_ENCODING]; | 140 throw new HttpException( |
| 114 if ((values == null || values[values.length - 1] != "chunked") && | 141 "Trying to set 'Transfer-Encoding: Chunked' on HTTP 1.0 headers"); |
| 115 chunkedTransferEncoding) { | 142 } |
| 116 // Headers does not specify chunked encoding - add it if set. | 143 if (chunkedTransferEncoding == _chunkedTransferEncoding) return; |
| 144 if (chunkedTransferEncoding) { |
| 145 List<String> values = _headers[HttpHeaders.TRANSFER_ENCODING]; |
| 146 if ((values == null || values.last != "chunked")) { |
| 147 // Headers does not specify chunked encoding - add it if set. |
| 117 _addValue(HttpHeaders.TRANSFER_ENCODING, "chunked"); | 148 _addValue(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| 118 } else if (!chunkedTransferEncoding) { | 149 } |
| 150 contentLength = -1; |
| 151 } else { |
| 119 // Headers does specify chunked encoding - remove it if not set. | 152 // Headers does specify chunked encoding - remove it if not set. |
| 120 remove(HttpHeaders.TRANSFER_ENCODING, "chunked"); | 153 remove(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| 121 } | 154 } |
| 155 _chunkedTransferEncoding = chunkedTransferEncoding; |
| 122 } | 156 } |
| 123 | 157 |
| 124 String get host => _host; | 158 String get host => _host; |
| 125 | 159 |
| 126 void set host(String host) { | 160 void set host(String host) { |
| 127 _checkMutable(); | 161 _checkMutable(); |
| 128 _host = host; | 162 _host = host; |
| 129 _updateHostHeader(); | 163 _updateHostHeader(); |
| 130 } | 164 } |
| 131 | 165 |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 _port = int.parse(value.substring(pos + 1)); | 302 _port = int.parse(value.substring(pos + 1)); |
| 269 } on FormatException catch (e) { | 303 } on FormatException catch (e) { |
| 270 _port = null; | 304 _port = null; |
| 271 } | 305 } |
| 272 } | 306 } |
| 273 } | 307 } |
| 274 _set(HttpHeaders.HOST, value); | 308 _set(HttpHeaders.HOST, value); |
| 275 } else { | 309 } else { |
| 276 throw new HttpException("Unexpected type for header named $name"); | 310 throw new HttpException("Unexpected type for header named $name"); |
| 277 } | 311 } |
| 312 } else if (name == HttpHeaders.CONNECTION) { |
| 313 var lowerCaseValue = value.toLowerCase(); |
| 314 if (lowerCaseValue == 'close') { |
| 315 _persistentConnection = false; |
| 316 } else if (lowerCaseValue == 'keep-alive') { |
| 317 _persistentConnection = true; |
| 318 } |
| 319 _addValue(name, value); |
| 278 } else if (name == HttpHeaders.CONTENT_TYPE) { | 320 } else if (name == HttpHeaders.CONTENT_TYPE) { |
| 279 _set(HttpHeaders.CONTENT_TYPE, value); | 321 _set(HttpHeaders.CONTENT_TYPE, value); |
| 280 } else { | 322 } else { |
| 281 _addValue(name, value); | 323 _addValue(name, value); |
| 282 } | 324 } |
| 283 } | 325 } |
| 284 | 326 |
| 285 void _addValue(String name, Object value) { | 327 void _addValue(String name, Object value) { |
| 286 List<String> values = _headers[name]; | 328 List<String> values = _headers[name]; |
| 287 if (values == null) { | 329 if (values == null) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 314 | 356 |
| 315 _foldHeader(String name) { | 357 _foldHeader(String name) { |
| 316 if (name == HttpHeaders.SET_COOKIE || | 358 if (name == HttpHeaders.SET_COOKIE || |
| 317 (_noFoldingHeaders != null && | 359 (_noFoldingHeaders != null && |
| 318 _noFoldingHeaders.indexOf(name) != -1)) { | 360 _noFoldingHeaders.indexOf(name) != -1)) { |
| 319 return false; | 361 return false; |
| 320 } | 362 } |
| 321 return true; | 363 return true; |
| 322 } | 364 } |
| 323 | 365 |
| 324 void _synchronize() { | |
| 325 // If the content length is not known make sure chunked transfer | |
| 326 // encoding is used for HTTP 1.1. | |
| 327 if (contentLength < 0) { | |
| 328 if (protocolVersion == "1.0") { | |
| 329 persistentConnection = false; | |
| 330 } else { | |
| 331 chunkedTransferEncoding = true; | |
| 332 } | |
| 333 } | |
| 334 // If a Transfer-Encoding header field is present the | |
| 335 // Content-Length header MUST NOT be sent (RFC 2616 section 4.4). | |
| 336 if (chunkedTransferEncoding && | |
| 337 contentLength >= 0 && | |
| 338 protocolVersion == "1.1") { | |
| 339 contentLength = -1; | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 void _finalize() { | 366 void _finalize() { |
| 344 _synchronize(); | |
| 345 _mutable = false; | 367 _mutable = false; |
| 346 } | 368 } |
| 347 | 369 |
| 348 int _write(Uint8List buffer, int offset) { | 370 int _write(Uint8List buffer, int offset) { |
| 349 void write(List<int> bytes) { | 371 void write(List<int> bytes) { |
| 350 int len = bytes.length; | 372 int len = bytes.length; |
| 351 for (int i = 0; i < len; i++) { | 373 for (int i = 0; i < len; i++) { |
| 352 buffer[offset + i] = bytes[i]; | 374 buffer[offset + i] = bytes[i]; |
| 353 } | 375 } |
| 354 offset += len; | 376 offset += len; |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 456 if (done()) return; | 478 if (done()) return; |
| 457 expect(";"); | 479 expect(";"); |
| 458 } | 480 } |
| 459 } | 481 } |
| 460 List<String> values = _headers[HttpHeaders.COOKIE]; | 482 List<String> values = _headers[HttpHeaders.COOKIE]; |
| 461 if (values != null) { | 483 if (values != null) { |
| 462 values.forEach((headerValue) => parseCookieString(headerValue)); | 484 values.forEach((headerValue) => parseCookieString(headerValue)); |
| 463 } | 485 } |
| 464 return cookies; | 486 return cookies; |
| 465 } | 487 } |
| 466 | |
| 467 | |
| 468 bool _mutable = true; // Are the headers currently mutable? | |
| 469 Map<String, List<String>> _headers; | |
| 470 List<String> _noFoldingHeaders; | |
| 471 | |
| 472 int _contentLength = -1; | |
| 473 bool _chunkedTransferEncoding = false; | |
| 474 final String protocolVersion; | |
| 475 String _host; | |
| 476 int _port; | |
| 477 } | 488 } |
| 478 | 489 |
| 479 | 490 |
| 480 class _HeaderValue implements HeaderValue { | 491 class _HeaderValue implements HeaderValue { |
| 481 String _value; | 492 String _value; |
| 482 _UnmodifiableMap<String, String> _parameters; | 493 _UnmodifiableMap<String, String> _parameters; |
| 483 | 494 |
| 484 _HeaderValue([String this._value = "", Map<String, String> parameters]) { | 495 _HeaderValue([String this._value = "", Map<String, String> parameters]) { |
| 485 if (parameters != null) { | 496 if (parameters != null) { |
| 486 _parameters = | 497 _parameters = |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 828 void clear() { | 839 void clear() { |
| 829 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 840 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 830 } | 841 } |
| 831 void forEach(void f(K key, V value)) => _map.forEach(f); | 842 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 832 Iterable<K> get keys => _map.keys; | 843 Iterable<K> get keys => _map.keys; |
| 833 Iterable<V> get values => _map.values; | 844 Iterable<V> get values => _map.values; |
| 834 int get length => _map.length; | 845 int get length => _map.length; |
| 835 bool get isEmpty => _map.isEmpty; | 846 bool get isEmpty => _map.isEmpty; |
| 836 bool get isNotEmpty => _map.isNotEmpty; | 847 bool get isNotEmpty => _map.isNotEmpty; |
| 837 } | 848 } |
| OLD | NEW |