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 |