| 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 _HttpHeaders(String this.protocolVersion) | 8 _HttpHeaders(String this.protocolVersion) |
| 9 : _headers = new Map<String, List<String>>(); | 9 : _headers = new Map<String, List<String>>(); |
| 10 | 10 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 62 void forEach(void f(String name, List<String> values)) { | 62 void forEach(void f(String name, List<String> values)) { |
| 63 _headers.forEach(f); | 63 _headers.forEach(f); |
| 64 } | 64 } |
| 65 | 65 |
| 66 void noFolding(String name) { | 66 void noFolding(String name) { |
| 67 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); | 67 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); |
| 68 _noFoldingHeaders.add(name); | 68 _noFoldingHeaders.add(name); |
| 69 } | 69 } |
| 70 | 70 |
| 71 bool get persistentConnection { | 71 bool get persistentConnection { |
| 72 List<String> connection = this[HttpHeaders.CONNECTION]; | 72 List<String> connection = _headers[HttpHeaders.CONNECTION]; |
| 73 if (protocolVersion == "1.1") { | 73 if (protocolVersion == "1.1") { |
| 74 if (connection == null) return true; | 74 if (connection == null) return true; |
| 75 return !connection.any((value) => value.toLowerCase() == "close"); | 75 return !connection.any((value) => value.toLowerCase() == "close"); |
| 76 } else { | 76 } else { |
| 77 if (connection == null) return false; | 77 if (connection == null) return false; |
| 78 return connection.any((value) => value.toLowerCase() == "keep-alive"); | 78 return connection.any((value) => value.toLowerCase() == "keep-alive"); |
| 79 } | 79 } |
| 80 } | 80 } |
| 81 | 81 |
| 82 void set persistentConnection(bool persistentConnection) { | 82 void set persistentConnection(bool persistentConnection) { |
| 83 _checkMutable(); | 83 _checkMutable(); |
| 84 // Determine the value of the "Connection" header. | 84 // Determine the value of the "Connection" header. |
| 85 remove(HttpHeaders.CONNECTION, "close"); | 85 remove(HttpHeaders.CONNECTION, "close"); |
| 86 remove(HttpHeaders.CONNECTION, "keep-alive"); | 86 remove(HttpHeaders.CONNECTION, "keep-alive"); |
| 87 if (protocolVersion == "1.1" && !persistentConnection) { | 87 if (protocolVersion == "1.1" && !persistentConnection) { |
| 88 add(HttpHeaders.CONNECTION, "close"); | 88 add(HttpHeaders.CONNECTION, "close"); |
| 89 } else if (protocolVersion == "1.0" && persistentConnection) { | 89 } else if (protocolVersion == "1.0" && persistentConnection) { |
| 90 add(HttpHeaders.CONNECTION, "keep-alive"); | 90 add(HttpHeaders.CONNECTION, "keep-alive"); |
| 91 } | 91 } |
| 92 } | 92 } |
| 93 | 93 |
| 94 int get contentLength => _contentLength; | 94 int get contentLength => _contentLength; |
| 95 | 95 |
| 96 void set contentLength(int contentLength) { | 96 void set contentLength(int contentLength) { |
| 97 _checkMutable(); | 97 _checkMutable(); |
| 98 _contentLength = contentLength; | 98 _contentLength = contentLength; |
| 99 if (_contentLength >= 0) { | 99 if (_contentLength >= 0) { |
| 100 _set("content-length", contentLength.toString()); | 100 _set(HttpHeaders.CONTENT_LENGTH, contentLength.toString()); |
| 101 } else { | 101 } else { |
| 102 removeAll("content-length"); | 102 removeAll(HttpHeaders.CONTENT_LENGTH); |
| 103 } | 103 } |
| 104 } | 104 } |
| 105 | 105 |
| 106 bool get chunkedTransferEncoding => _chunkedTransferEncoding; | 106 bool get chunkedTransferEncoding => _chunkedTransferEncoding; |
| 107 | 107 |
| 108 void set chunkedTransferEncoding(bool chunkedTransferEncoding) { | 108 void set chunkedTransferEncoding(bool chunkedTransferEncoding) { |
| 109 _checkMutable(); | 109 _checkMutable(); |
| 110 _chunkedTransferEncoding = chunkedTransferEncoding; | 110 _chunkedTransferEncoding = chunkedTransferEncoding; |
| 111 List<String> values = _headers["transfer-encoding"]; | 111 List<String> values = _headers[HttpHeaders.TRANSFER_ENCODING]; |
| 112 if (values == null || values[values.length - 1] != "chunked") { | 112 if ((values == null || values[values.length - 1] != "chunked") && |
| 113 chunkedTransferEncoding) { |
| 113 // Headers does not specify chunked encoding - add it if set. | 114 // Headers does not specify chunked encoding - add it if set. |
| 114 if (chunkedTransferEncoding) _addValue("transfer-encoding", "chunked"); | 115 _addValue(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| 115 } else { | 116 } else if (!chunkedTransferEncoding) { |
| 116 // Headers does specify chunked encoding - remove it if not set. | 117 // Headers does specify chunked encoding - remove it if not set. |
| 117 if (!chunkedTransferEncoding) remove("transfer-encoding", "chunked"); | 118 remove(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| 118 } | 119 } |
| 119 } | 120 } |
| 120 | 121 |
| 121 String get host => _host; | 122 String get host => _host; |
| 122 | 123 |
| 123 void set host(String host) { | 124 void set host(String host) { |
| 124 _checkMutable(); | 125 _checkMutable(); |
| 125 _host = host; | 126 _host = host; |
| 126 _updateHostHeader(); | 127 _updateHostHeader(); |
| 127 } | 128 } |
| 128 | 129 |
| 129 int get port => _port; | 130 int get port => _port; |
| 130 | 131 |
| 131 void set port(int port) { | 132 void set port(int port) { |
| 132 _checkMutable(); | 133 _checkMutable(); |
| 133 _port = port; | 134 _port = port; |
| 134 _updateHostHeader(); | 135 _updateHostHeader(); |
| 135 } | 136 } |
| 136 | 137 |
| 137 DateTime get ifModifiedSince { | 138 DateTime get ifModifiedSince { |
| 138 List<String> values = _headers["if-modified-since"]; | 139 List<String> values = _headers[HttpHeaders.IF_MODIFIED_SINCE]; |
| 139 if (values != null) { | 140 if (values != null) { |
| 140 try { | 141 try { |
| 141 return _HttpUtils.parseDate(values[0]); | 142 return _HttpUtils.parseDate(values[0]); |
| 142 } on Exception catch (e) { | 143 } on Exception catch (e) { |
| 143 return null; | 144 return null; |
| 144 } | 145 } |
| 145 } | 146 } |
| 146 return null; | 147 return null; |
| 147 } | 148 } |
| 148 | 149 |
| 149 void set ifModifiedSince(DateTime ifModifiedSince) { | 150 void set ifModifiedSince(DateTime ifModifiedSince) { |
| 150 _checkMutable(); | 151 _checkMutable(); |
| 151 // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). | 152 // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). |
| 152 String formatted = _HttpUtils.formatDate(ifModifiedSince.toUtc()); | 153 String formatted = _HttpUtils.formatDate(ifModifiedSince.toUtc()); |
| 153 _set("if-modified-since", formatted); | 154 _set(HttpHeaders.IF_MODIFIED_SINCE, formatted); |
| 154 } | 155 } |
| 155 | 156 |
| 156 DateTime get date { | 157 DateTime get date { |
| 157 List<String> values = _headers["date"]; | 158 List<String> values = _headers[HttpHeaders.DATE]; |
| 158 if (values != null) { | 159 if (values != null) { |
| 159 try { | 160 try { |
| 160 return _HttpUtils.parseDate(values[0]); | 161 return _HttpUtils.parseDate(values[0]); |
| 161 } on Exception catch (e) { | 162 } on Exception catch (e) { |
| 162 return null; | 163 return null; |
| 163 } | 164 } |
| 164 } | 165 } |
| 165 return null; | 166 return null; |
| 166 } | 167 } |
| 167 | 168 |
| 168 void set date(DateTime date) { | 169 void set date(DateTime date) { |
| 169 _checkMutable(); | 170 _checkMutable(); |
| 170 // Format "DateTime" header with date in Greenwich Mean Time (GMT). | 171 // Format "DateTime" header with date in Greenwich Mean Time (GMT). |
| 171 String formatted = _HttpUtils.formatDate(date.toUtc()); | 172 String formatted = _HttpUtils.formatDate(date.toUtc()); |
| 172 _set("date", formatted); | 173 _set("date", formatted); |
| 173 } | 174 } |
| 174 | 175 |
| 175 DateTime get expires { | 176 DateTime get expires { |
| 176 List<String> values = _headers["expires"]; | 177 List<String> values = _headers[HttpHeaders.EXPIRES]; |
| 177 if (values != null) { | 178 if (values != null) { |
| 178 try { | 179 try { |
| 179 return _HttpUtils.parseDate(values[0]); | 180 return _HttpUtils.parseDate(values[0]); |
| 180 } on Exception catch (e) { | 181 } on Exception catch (e) { |
| 181 return null; | 182 return null; |
| 182 } | 183 } |
| 183 } | 184 } |
| 184 return null; | 185 return null; |
| 185 } | 186 } |
| 186 | 187 |
| 187 void set expires(DateTime expires) { | 188 void set expires(DateTime expires) { |
| 188 _checkMutable(); | 189 _checkMutable(); |
| 189 // Format "Expires" header with date in Greenwich Mean Time (GMT). | 190 // Format "Expires" header with date in Greenwich Mean Time (GMT). |
| 190 String formatted = _HttpUtils.formatDate(expires.toUtc()); | 191 String formatted = _HttpUtils.formatDate(expires.toUtc()); |
| 191 _set("expires", formatted); | 192 _set(HttpHeaders.EXPIRES, formatted); |
| 192 } | 193 } |
| 193 | 194 |
| 194 ContentType get contentType { | 195 ContentType get contentType { |
| 195 var values = _headers["content-type"]; | 196 var values = _headers["content-type"]; |
| 196 if (values != null) { | 197 if (values != null) { |
| 197 return new ContentType.fromString(values[0]); | 198 return new ContentType.fromString(values[0]); |
| 198 } else { | 199 } else { |
| 199 return new ContentType(); | 200 return new ContentType(); |
| 200 } | 201 } |
| 201 } | 202 } |
| 202 | 203 |
| 203 void set contentType(ContentType contentType) { | 204 void set contentType(ContentType contentType) { |
| 204 _checkMutable(); | 205 _checkMutable(); |
| 205 _set("content-type", contentType.toString()); | 206 _set(HttpHeaders.CONTENT_TYPE, contentType.toString()); |
| 206 } | 207 } |
| 207 | 208 |
| 208 void _add(String name, Object value) { | 209 void _add(String name, Object value) { |
| 209 var lowerCaseName = name.toLowerCase(); | 210 var lowerCaseName = name.toLowerCase(); |
| 210 // TODO(sgjesse): Add immutable state throw HttpException is immutable. | 211 // TODO(sgjesse): Add immutable state throw HttpException is immutable. |
| 211 if (lowerCaseName == "content-length") { | 212 if (lowerCaseName == HttpHeaders.CONTENT_LENGTH) { |
| 212 if (value is int) { | 213 if (value is int) { |
| 213 contentLength = value; | 214 contentLength = value; |
| 214 } else if (value is String) { | 215 } else if (value is String) { |
| 215 contentLength = int.parse(value); | 216 contentLength = int.parse(value); |
| 216 } else { | 217 } else { |
| 217 throw new HttpException("Unexpected type for header named $name"); | 218 throw new HttpException("Unexpected type for header named $name"); |
| 218 } | 219 } |
| 219 } else if (lowerCaseName == "transfer-encoding") { | 220 } else if (lowerCaseName == HttpHeaders.TRANSFER_ENCODING) { |
| 220 if (value == "chunked") { | 221 if (value == "chunked") { |
| 221 chunkedTransferEncoding = true; | 222 chunkedTransferEncoding = true; |
| 222 } else { | 223 } else { |
| 223 _addValue(lowerCaseName, value); | 224 _addValue(lowerCaseName, value); |
| 224 } | 225 } |
| 225 } else if (lowerCaseName == "date") { | 226 } else if (lowerCaseName == HttpHeaders.DATE) { |
| 226 if (value is DateTime) { | 227 if (value is DateTime) { |
| 227 date = value; | 228 date = value; |
| 228 } else if (value is String) { | 229 } else if (value is String) { |
| 229 _set("date", value); | 230 _set(HttpHeaders.DATE, value); |
| 230 } else { | 231 } else { |
| 231 throw new HttpException("Unexpected type for header named $name"); | 232 throw new HttpException("Unexpected type for header named $name"); |
| 232 } | 233 } |
| 233 } else if (lowerCaseName == "expires") { | 234 } else if (lowerCaseName == HttpHeaders.EXPIRES) { |
| 234 if (value is DateTime) { | 235 if (value is DateTime) { |
| 235 expires = value; | 236 expires = value; |
| 236 } else if (value is String) { | 237 } else if (value is String) { |
| 237 _set("expires", value); | 238 _set(HttpHeaders.EXPIRES, value); |
| 238 } else { | 239 } else { |
| 239 throw new HttpException("Unexpected type for header named $name"); | 240 throw new HttpException("Unexpected type for header named $name"); |
| 240 } | 241 } |
| 241 } else if (lowerCaseName == "if-modified-since") { | 242 } else if (lowerCaseName == HttpHeaders.IF_MODIFIED_SINCE) { |
| 242 if (value is DateTime) { | 243 if (value is DateTime) { |
| 243 ifModifiedSince = value; | 244 ifModifiedSince = value; |
| 244 } else if (value is String) { | 245 } else if (value is String) { |
| 245 _set("if-modified-since", value); | 246 _set(HttpHeaders.IF_MODIFIED_SINCE, value); |
| 246 } else { | 247 } else { |
| 247 throw new HttpException("Unexpected type for header named $name"); | 248 throw new HttpException("Unexpected type for header named $name"); |
| 248 } | 249 } |
| 249 } else if (lowerCaseName == "host") { | 250 } else if (lowerCaseName == HttpHeaders.HOST) { |
| 250 if (value is String) { | 251 if (value is String) { |
| 251 int pos = (value as String).indexOf(":"); | 252 int pos = (value as String).indexOf(":"); |
| 252 if (pos == -1) { | 253 if (pos == -1) { |
| 253 _host = value; | 254 _host = value; |
| 254 _port = HttpClient.DEFAULT_HTTP_PORT; | 255 _port = HttpClient.DEFAULT_HTTP_PORT; |
| 255 } else { | 256 } else { |
| 256 if (pos > 0) { | 257 if (pos > 0) { |
| 257 _host = (value as String).substring(0, pos); | 258 _host = (value as String).substring(0, pos); |
| 258 } else { | 259 } else { |
| 259 _host = null; | 260 _host = null; |
| 260 } | 261 } |
| 261 if (pos + 1 == value.length) { | 262 if (pos + 1 == value.length) { |
| 262 _port = HttpClient.DEFAULT_HTTP_PORT; | 263 _port = HttpClient.DEFAULT_HTTP_PORT; |
| 263 } else { | 264 } else { |
| 264 try { | 265 try { |
| 265 _port = int.parse(value.substring(pos + 1)); | 266 _port = int.parse(value.substring(pos + 1)); |
| 266 } on FormatException catch (e) { | 267 } on FormatException catch (e) { |
| 267 _port = null; | 268 _port = null; |
| 268 } | 269 } |
| 269 } | 270 } |
| 270 } | 271 } |
| 271 _set("host", value); | 272 _set(HttpHeaders.HOST, value); |
| 272 } else { | 273 } else { |
| 273 throw new HttpException("Unexpected type for header named $name"); | 274 throw new HttpException("Unexpected type for header named $name"); |
| 274 } | 275 } |
| 275 } else if (lowerCaseName == "content-type") { | 276 } else if (lowerCaseName == HttpHeaders.CONTENT_TYPE) { |
| 276 _set("content-type", value); | 277 _set(HttpHeaders.CONTENT_TYPE, value); |
| 277 } else { | 278 } else { |
| 278 _addValue(lowerCaseName, value); | 279 _addValue(lowerCaseName, value); |
| 279 } | 280 } |
| 280 } | 281 } |
| 281 | 282 |
| 282 void _addValue(String name, Object value) { | 283 void _addValue(String name, Object value) { |
| 283 List<String> values = _headers[name]; | 284 List<String> values = _headers[name]; |
| 284 if (values == null) { | 285 if (values == null) { |
| 285 values = new List<String>(); | 286 values = new List<String>(); |
| 286 _headers[name] = values; | 287 _headers[name] = values; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 303 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); | 304 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); |
| 304 } | 305 } |
| 305 | 306 |
| 306 _updateHostHeader() { | 307 _updateHostHeader() { |
| 307 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; | 308 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; |
| 308 String portPart = defaultPort ? "" : ":$_port"; | 309 String portPart = defaultPort ? "" : ":$_port"; |
| 309 _set("host", "$host$portPart"); | 310 _set("host", "$host$portPart"); |
| 310 } | 311 } |
| 311 | 312 |
| 312 _foldHeader(String name) { | 313 _foldHeader(String name) { |
| 313 if (name == "set-cookie" || | 314 if (name == HttpHeaders.SET_COOKIE || |
| 314 (_noFoldingHeaders != null && | 315 (_noFoldingHeaders != null && |
| 315 _noFoldingHeaders.indexOf(name) != -1)) { | 316 _noFoldingHeaders.indexOf(name) != -1)) { |
| 316 return false; | 317 return false; |
| 317 } | 318 } |
| 318 return true; | 319 return true; |
| 319 } | 320 } |
| 320 | 321 |
| 321 void _finalize() { | 322 void _finalize() { |
| 322 // If the content length is not known make sure chunked transfer | 323 // If the content length is not known make sure chunked transfer |
| 323 // encoding is used for HTTP 1.1. | 324 // encoding is used for HTTP 1.1. |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 459 skipWS(); | 460 skipWS(); |
| 460 expect("="); | 461 expect("="); |
| 461 skipWS(); | 462 skipWS(); |
| 462 String value = parseValue(); | 463 String value = parseValue(); |
| 463 cookies.add(new _Cookie(name, value)); | 464 cookies.add(new _Cookie(name, value)); |
| 464 skipWS(); | 465 skipWS(); |
| 465 if (done()) return; | 466 if (done()) return; |
| 466 expect(";"); | 467 expect(";"); |
| 467 } | 468 } |
| 468 } | 469 } |
| 469 List<String> values = this["cookie"]; | 470 List<String> values = _headers[HttpHeaders.COOKIE]; |
| 470 if (values != null) { | 471 if (values != null) { |
| 471 values.forEach((headerValue) => parseCookieString(headerValue)); | 472 values.forEach((headerValue) => parseCookieString(headerValue)); |
| 472 } | 473 } |
| 473 return cookies; | 474 return cookies; |
| 474 } | 475 } |
| 475 | 476 |
| 476 | 477 |
| 477 bool _mutable = true; // Are the headers currently mutable? | 478 bool _mutable = true; // Are the headers currently mutable? |
| 478 Map<String, List<String>> _headers; | 479 Map<String, List<String>> _headers; |
| 479 List<String> _noFoldingHeaders; | 480 List<String> _noFoldingHeaders; |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 652 | 653 |
| 653 String _primaryType = ""; | 654 String _primaryType = ""; |
| 654 String _subType = ""; | 655 String _subType = ""; |
| 655 } | 656 } |
| 656 | 657 |
| 657 | 658 |
| 658 class _Cookie implements Cookie { | 659 class _Cookie implements Cookie { |
| 659 _Cookie([String this.name, String this.value]); | 660 _Cookie([String this.name, String this.value]); |
| 660 | 661 |
| 661 _Cookie.fromSetCookieValue(String value) { | 662 _Cookie.fromSetCookieValue(String value) { |
| 662 // Parse the Set-Cookie header value. | 663 // Parse the 'set-cookie' header value. |
| 663 _parseSetCookieValue(value); | 664 _parseSetCookieValue(value); |
| 664 } | 665 } |
| 665 | 666 |
| 666 // Parse a Set-Cookie header value according to the rules in RFC 6265. | 667 // Parse a 'set-cookie' header value according to the rules in RFC 6265. |
| 667 void _parseSetCookieValue(String s) { | 668 void _parseSetCookieValue(String s) { |
| 668 int index = 0; | 669 int index = 0; |
| 669 | 670 |
| 670 bool done() => index == s.length; | 671 bool done() => index == s.length; |
| 671 | 672 |
| 672 String parseName() { | 673 String parseName() { |
| 673 int start = index; | 674 int start = index; |
| 674 while (!done()) { | 675 while (!done()) { |
| 675 if (s[index] == "=") break; | 676 if (s[index] == "=") break; |
| 676 index++; | 677 index++; |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 777 | 778 |
| 778 String name; | 779 String name; |
| 779 String value; | 780 String value; |
| 780 DateTime expires; | 781 DateTime expires; |
| 781 int maxAge; | 782 int maxAge; |
| 782 String domain; | 783 String domain; |
| 783 String path; | 784 String path; |
| 784 bool httpOnly = false; | 785 bool httpOnly = false; |
| 785 bool secure = false; | 786 bool secure = false; |
| 786 } | 787 } |
| OLD | NEW |