Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(432)

Side by Side Diff: sdk/lib/io/http_headers.dart

Issue 72663005: Clean up HttpHeaders to always have them in a consistent state. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Cleanup. Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | sdk/lib/io/http_impl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/io/http_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698