OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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() : _headers = new Map<String, List<String>>(); | 8 _HttpHeaders(String this.protocolVersion) |
| 9 : _headers = new Map<String, List<String>>(); |
9 | 10 |
10 List<String> operator[](String name) { | 11 List<String> operator[](String name) { |
11 name = name.toLowerCase(); | 12 name = name.toLowerCase(); |
12 return _headers[name]; | 13 return _headers[name]; |
13 } | 14 } |
14 | 15 |
15 String value(String name) { | 16 String value(String name) { |
16 name = name.toLowerCase(); | 17 name = name.toLowerCase(); |
17 List<String> values = _headers[name]; | 18 List<String> values = _headers[name]; |
18 if (values == null) return null; | 19 if (values == null) return null; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 | 61 |
61 void forEach(void f(String name, List<String> values)) { | 62 void forEach(void f(String name, List<String> values)) { |
62 _headers.forEach(f); | 63 _headers.forEach(f); |
63 } | 64 } |
64 | 65 |
65 void noFolding(String name) { | 66 void noFolding(String name) { |
66 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); | 67 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); |
67 _noFoldingHeaders.add(name); | 68 _noFoldingHeaders.add(name); |
68 } | 69 } |
69 | 70 |
| 71 bool get persistentConnection { |
| 72 List<String> connection = this[HttpHeaders.CONNECTION]; |
| 73 if (protocolVersion == "1.1") { |
| 74 if (connection == null) return true; |
| 75 return !connection.any((value) => value.toLowerCase() == "close"); |
| 76 } else { |
| 77 if (connection == null) return false; |
| 78 return connection.any((value) => value.toLowerCase() == "keep-alive"); |
| 79 } |
| 80 } |
| 81 |
| 82 void set persistentConnection(bool persistentConnection) { |
| 83 _checkMutable(); |
| 84 // Determine the value of the "Connection" header. |
| 85 remove(HttpHeaders.CONNECTION, "close"); |
| 86 remove(HttpHeaders.CONNECTION, "keep-alive"); |
| 87 if (protocolVersion == "1.1" && !persistentConnection) { |
| 88 add(HttpHeaders.CONNECTION, "close"); |
| 89 } else if (protocolVersion == "1.0" && persistentConnection) { |
| 90 add(HttpHeaders.CONNECTION, "keep-alive"); |
| 91 } |
| 92 } |
| 93 |
70 int get contentLength => _contentLength; | 94 int get contentLength => _contentLength; |
71 | 95 |
72 void set contentLength(int contentLength) { | 96 void set contentLength(int contentLength) { |
73 _checkMutable(); | 97 _checkMutable(); |
74 _contentLength = contentLength; | 98 _contentLength = contentLength; |
75 if (_contentLength >= 0) { | 99 if (_contentLength >= 0) { |
76 _set("content-length", contentLength.toString()); | 100 _set("content-length", contentLength.toString()); |
77 } else { | 101 } else { |
78 removeAll("content-length"); | 102 removeAll("content-length"); |
79 } | 103 } |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 | 307 |
284 _foldHeader(String name) { | 308 _foldHeader(String name) { |
285 if (name == "set-cookie" || | 309 if (name == "set-cookie" || |
286 (_noFoldingHeaders != null && | 310 (_noFoldingHeaders != null && |
287 _noFoldingHeaders.indexOf(name) != -1)) { | 311 _noFoldingHeaders.indexOf(name) != -1)) { |
288 return false; | 312 return false; |
289 } | 313 } |
290 return true; | 314 return true; |
291 } | 315 } |
292 | 316 |
293 void _finalize(String protocolVersion) { | 317 void _finalize() { |
294 // If the content length is not known make sure chunked transfer | 318 // If the content length is not known make sure chunked transfer |
295 // encoding is used for HTTP 1.1. | 319 // encoding is used for HTTP 1.1. |
296 if (contentLength < 0 && protocolVersion == "1.1") { | 320 if (contentLength < 0 && protocolVersion == "1.1") { |
297 chunkedTransferEncoding = true; | 321 chunkedTransferEncoding = true; |
298 } | 322 } |
299 // If a Transfer-Encoding header field is present the | 323 // If a Transfer-Encoding header field is present the |
300 // Content-Length header MUST NOT be sent (RFC 2616 section 4.4). | 324 // Content-Length header MUST NOT be sent (RFC 2616 section 4.4). |
301 if (chunkedTransferEncoding && | 325 if (chunkedTransferEncoding && |
302 contentLength >= 0 && | 326 contentLength >= 0 && |
303 protocolVersion == "1.1") { | 327 protocolVersion == "1.1") { |
304 contentLength = -1; | 328 contentLength = -1; |
305 } | 329 } |
306 _mutable = false; | 330 _mutable = false; |
307 } | 331 } |
308 | 332 |
309 _write(_HttpConnectionBase connection) { | 333 _write(IOSink sink) { |
310 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; | 334 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; |
311 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; | 335 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; |
312 final CRLF = const [_CharCode.CR, _CharCode.LF]; | 336 final CRLF = const [_CharCode.CR, _CharCode.LF]; |
313 | 337 |
314 var bufferSize = 16 * 1024; | 338 var bufferSize = 16 * 1024; |
315 var buffer = new Uint8List(bufferSize); | 339 var buffer = new Uint8List(bufferSize); |
316 var bufferPos = 0; | 340 var bufferPos = 0; |
317 | 341 |
318 void writeBuffer() { | 342 void writeBuffer() { |
319 connection._writeFrom(buffer, 0, bufferPos); | 343 sink.add(buffer.getRange(0, bufferPos)); |
320 bufferPos = 0; | 344 bufferPos = 0; |
321 } | 345 } |
322 | 346 |
323 // Format headers. | 347 // Format headers. |
324 _headers.forEach((String name, List<String> values) { | 348 _headers.forEach((String name, List<String> values) { |
325 bool fold = _foldHeader(name); | 349 bool fold = _foldHeader(name); |
326 List<int> nameData; | 350 List<int> nameData; |
327 nameData = name.charCodes; | 351 nameData = name.charCodes; |
328 int nameDataLen = nameData.length; | 352 int nameDataLen = nameData.length; |
329 if (nameDataLen + 2 > bufferSize - bufferPos) writeBuffer(); | 353 if (nameDataLen + 2 > bufferSize - bufferPos) writeBuffer(); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 sb.add(": "); | 398 sb.add(": "); |
375 } | 399 } |
376 } | 400 } |
377 sb.add(values[i]); | 401 sb.add(values[i]); |
378 } | 402 } |
379 sb.add("\n"); | 403 sb.add("\n"); |
380 }); | 404 }); |
381 return sb.toString(); | 405 return sb.toString(); |
382 } | 406 } |
383 | 407 |
| 408 List<Cookie> _parseCookies() { |
| 409 // Parse a Cookie header value according to the rules in RFC 6265. |
| 410 var cookies = new List<Cookie>(); |
| 411 void parseCookieString(String s) { |
| 412 int index = 0; |
| 413 |
| 414 bool done() => index == s.length; |
| 415 |
| 416 void skipWS() { |
| 417 while (!done()) { |
| 418 if (s[index] != " " && s[index] != "\t") return; |
| 419 index++; |
| 420 } |
| 421 } |
| 422 |
| 423 String parseName() { |
| 424 int start = index; |
| 425 while (!done()) { |
| 426 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; |
| 427 index++; |
| 428 } |
| 429 return s.substring(start, index).toLowerCase(); |
| 430 } |
| 431 |
| 432 String parseValue() { |
| 433 int start = index; |
| 434 while (!done()) { |
| 435 if (s[index] == " " || s[index] == "\t" || s[index] == ";") break; |
| 436 index++; |
| 437 } |
| 438 return s.substring(start, index).toLowerCase(); |
| 439 } |
| 440 |
| 441 void expect(String expected) { |
| 442 if (done()) { |
| 443 throw new HttpException("Failed to parse header value [$s]"); |
| 444 } |
| 445 if (s[index] != expected) { |
| 446 throw new HttpException("Failed to parse header value [$s]"); |
| 447 } |
| 448 index++; |
| 449 } |
| 450 |
| 451 while (!done()) { |
| 452 skipWS(); |
| 453 if (done()) return; |
| 454 String name = parseName(); |
| 455 skipWS(); |
| 456 expect("="); |
| 457 skipWS(); |
| 458 String value = parseValue(); |
| 459 cookies.add(new _Cookie(name, value)); |
| 460 skipWS(); |
| 461 if (done()) return; |
| 462 expect(";"); |
| 463 } |
| 464 } |
| 465 List<String> values = this["cookie"]; |
| 466 if (values != null) { |
| 467 values.forEach((headerValue) => parseCookieString(headerValue)); |
| 468 } |
| 469 return cookies; |
| 470 } |
| 471 |
| 472 |
384 bool _mutable = true; // Are the headers currently mutable? | 473 bool _mutable = true; // Are the headers currently mutable? |
385 Map<String, List<String>> _headers; | 474 Map<String, List<String>> _headers; |
386 List<String> _noFoldingHeaders; | 475 List<String> _noFoldingHeaders; |
387 | 476 |
388 int _contentLength = -1; | 477 int _contentLength = -1; |
389 bool _chunkedTransferEncoding = false; | 478 bool _chunkedTransferEncoding = false; |
| 479 final String protocolVersion; |
390 String _host; | 480 String _host; |
391 int _port; | 481 int _port; |
392 } | 482 } |
393 | 483 |
394 | 484 |
395 class _HeaderValue implements HeaderValue { | 485 class _HeaderValue implements HeaderValue { |
396 _HeaderValue([String this.value = ""]); | 486 _HeaderValue([String this.value = ""]); |
397 | 487 |
398 _HeaderValue.fromString(String value, {this.parameterSeparator: ";"}) { | 488 _HeaderValue.fromString(String value, {this.parameterSeparator: ";"}) { |
399 // Parse the string. | 489 // Parse the string. |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 | 773 |
684 String name; | 774 String name; |
685 String value; | 775 String value; |
686 DateTime expires; | 776 DateTime expires; |
687 int maxAge; | 777 int maxAge; |
688 String domain; | 778 String domain; |
689 String path; | 779 String path; |
690 bool httpOnly = false; | 780 bool httpOnly = false; |
691 bool secure = false; | 781 bool secure = false; |
692 } | 782 } |
OLD | NEW |