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

Side by Side Diff: runtime/bin/http_impl.dart

Issue 10414076: Support for cookies in the HTTP library (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed second round of comments Created 8 years, 7 months 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 | « runtime/bin/http.dart ('k') | runtime/bin/http_utils.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) 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 class _HttpHeaders implements HttpHeaders { 5 class _HttpHeaders implements HttpHeaders {
6 _HttpHeaders() : _headers = new Map<String, List<String>>(); 6 _HttpHeaders() : _headers = new Map<String, List<String>>();
7 7
8 List<String> operator[](String name) { 8 List<String> operator[](String name) {
9 name = name.toLowerCase(); 9 name = name.toLowerCase();
10 return _headers[name]; 10 return _headers[name];
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 void removeAll(String name) { 53 void removeAll(String name) {
54 _checkMutable(); 54 _checkMutable();
55 name = name.toLowerCase(); 55 name = name.toLowerCase();
56 _headers.remove(name); 56 _headers.remove(name);
57 } 57 }
58 58
59 void forEach(void f(String name, List<String> values)) { 59 void forEach(void f(String name, List<String> values)) {
60 _headers.forEach(f); 60 _headers.forEach(f);
61 } 61 }
62 62
63 void noFolding(String name) {
64 if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>();
65 _noFoldingHeaders.add(name);
66 }
67
63 String get host() => _host; 68 String get host() => _host;
64 69
65 void set host(String host) { 70 void set host(String host) {
66 _checkMutable(); 71 _checkMutable();
67 _host = host; 72 _host = host;
68 _updateHostHeader(); 73 _updateHostHeader();
69 } 74 }
70 75
71 int get port() => _port; 76 int get port() => _port;
72 77
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 _checkMutable() { 198 _checkMutable() {
194 if (!_mutable) throw new HttpException("HTTP headers are not mutable"); 199 if (!_mutable) throw new HttpException("HTTP headers are not mutable");
195 } 200 }
196 201
197 _updateHostHeader() { 202 _updateHostHeader() {
198 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; 203 bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT;
199 String portPart = defaultPort ? "" : ":$_port"; 204 String portPart = defaultPort ? "" : ":$_port";
200 _set("host", "$host$portPart"); 205 _set("host", "$host$portPart");
201 } 206 }
202 207
208 _foldHeader(String name) {
209 if (name == "set-cookie" ||
210 (_noFoldingHeaders != null &&
211 _noFoldingHeaders.indexOf(name) != -1)) {
212 return false;
213 }
214 return true;
215 }
216
203 _write(_HttpConnectionBase connection) { 217 _write(_HttpConnectionBase connection) {
204 final COLONSP = const [_CharCode.COLON, _CharCode.SP]; 218 final COLONSP = const [_CharCode.COLON, _CharCode.SP];
205 final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; 219 final COMMASP = const [_CharCode.COMMA, _CharCode.SP];
206 final CRLF = const [_CharCode.CR, _CharCode.LF]; 220 final CRLF = const [_CharCode.CR, _CharCode.LF];
207 221
208 // Format headers. 222 // Format headers.
209 _headers.forEach((String name, List<String> values) { 223 _headers.forEach((String name, List<String> values) {
224 bool fold = _foldHeader(name);
210 List<int> data; 225 List<int> data;
211 data = name.charCodes(); 226 data = name.charCodes();
212 connection._write(data); 227 connection._write(data);
213 connection._write(COLONSP); 228 connection._write(COLONSP);
214 for (int i = 0; i < values.length; i++) { 229 for (int i = 0; i < values.length; i++) {
215 if (i > 0) { 230 if (i > 0) {
216 connection._write(COMMASP); 231 if (fold) {
232 connection._write(COMMASP);
233 } else {
234 connection._write(CRLF);
235 data = name.charCodes();
236 connection._write(data);
237 connection._write(COLONSP);
238 }
217 } 239 }
218 data = values[i].charCodes(); 240 data = values[i].charCodes();
219 connection._write(data); 241 connection._write(data);
220 } 242 }
221 connection._write(CRLF); 243 connection._write(CRLF);
222 }); 244 });
223 } 245 }
224 246
225 String toString() { 247 String toString() {
226 StringBuffer sb = new StringBuffer(); 248 StringBuffer sb = new StringBuffer();
227 _headers.forEach((String name, List<String> values) { 249 _headers.forEach((String name, List<String> values) {
228 sb.add(name); 250 sb.add(name);
229 sb.add(": "); 251 sb.add(": ");
252 bool fold = _foldHeader(name);
230 for (int i = 0; i < values.length; i++) { 253 for (int i = 0; i < values.length; i++) {
231 if (i > 0) { 254 if (i > 0) {
232 sb.add(", "); 255 if (fold) {
256 sb.add(", ");
257 } else {
258 sb.add("\n");
259 sb.add(name);
260 sb.add(": ");
261 }
233 } 262 }
234 sb.add(values[i]); 263 sb.add(values[i]);
235 } 264 }
236 sb.add("\n"); 265 sb.add("\n");
237 }); 266 });
238 return sb.toString(); 267 return sb.toString();
239 } 268 }
240 269
241 bool _mutable = true; // Are the headers currently mutable? 270 bool _mutable = true; // Are the headers currently mutable?
242 Map<String, List<String>> _headers; 271 Map<String, List<String>> _headers;
272 List<String> _noFoldingHeaders;
243 273
244 String _host; 274 String _host;
245 int _port; 275 int _port;
246 } 276 }
247 277
248 278
249 class _HeaderValue implements HeaderValue { 279 class _HeaderValue implements HeaderValue {
250 _HeaderValue([String this.value = ""]); 280 _HeaderValue([String this.value = ""]);
251 281
252 _HeaderValue.fromString(String value) { 282 _HeaderValue.fromString(String value) {
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 431
402 void set charset(String s) { 432 void set charset(String s) {
403 parameters["charset"] = s; 433 parameters["charset"] = s;
404 } 434 }
405 435
406 String _primaryType = ""; 436 String _primaryType = "";
407 String _subType = ""; 437 String _subType = "";
408 } 438 }
409 439
410 440
441 class _Cookie implements Cookie {
442 _Cookie([String this.name, String this.value]);
443
444 _Cookie.fromSetCookieValue(String value) {
445 // Parse the Set-Cookie header value.
446 _parseSetCookieValue(value);
447 }
448
449 // Parse a Set-Cookie header value according to the rules in RFC 6265.
450 void _parseSetCookieValue(String s) {
451 int index = 0;
452
453 bool done() => index == s.length;
454
455 String parseName() {
456 int start = index;
457 while (!done()) {
458 if (s[index] == "=") break;
459 index++;
460 }
461 return s.substring(start, index).trim().toLowerCase();
462 }
463
464 String parseValue() {
465 int start = index;
466 while (!done()) {
467 if (s[index] == ";") break;
468 index++;
469 }
470 return s.substring(start, index).trim().toLowerCase();
471 }
472
473 void expect(String expected) {
474 if (done()) throw new HttpException("Failed to parse header value [$s]");
475 if (s[index] != expected) {
476 throw new HttpException("Failed to parse header value [$s]");
477 }
478 index++;
479 }
480
481 void parseAttributes() {
482 String parseAttributeName() {
483 int start = index;
484 while (!done()) {
485 if (s[index] == "=" || s[index] == ";") break;
486 index++;
487 }
488 return s.substring(start, index).trim().toLowerCase();
489 }
490
491 String parseAttributeValue() {
492 int start = index;
493 while (!done()) {
494 if (s[index] == ";") break;
495 index++;
496 }
497 return s.substring(start, index).trim().toLowerCase();
498 }
499
500 while (!done()) {
501 String name = parseAttributeName();
502 String value = "";
503 if (!done() && s[index] == "=") {
504 index++; // Skip the = character.
505 value = parseAttributeValue();
506 }
507 if (name == "expires") {
508 expires = _HttpUtils.parseDate(value, strict: false);
509 } else if (name == "max-age") {
510 maxAge = Math.parseInt(value);
511 } else if (name == "domain") {
512 domain = value;
513 } else if (name == "path") {
514 path = value;
515 } else if (name == "httponly") {
516 httpOnly = true;
517 } else if (name == "secure") {
518 secure = true;
519 }
520 if (!done()) index++; // Skip the ; character
521 }
522 }
523
524 name = parseName();
525 if (done() || name.length == 0) {
526 throw new HttpException("Failed to parse header value [$s]");
527 }
528 index++; // Skip the = character.
529 value = parseValue();
530 if (done()) return;
531 index++; // Skip the ; character.
532 parseAttributes();
533 }
534
535 String toString() {
536 StringBuffer sb = new StringBuffer();
537 sb.add(name);
538 sb.add("=");
539 sb.add(value);
540 if (expires != null) {
541 sb.add("; Expires=");
542 sb.add(_HttpUtils.formatDate(expires));
543 }
544 if (maxAge != null) {
545 sb.add("; Max-Age=");
546 sb.add(maxAge);
547 }
548 if (domain != null) {
549 sb.add("; Domain=");
550 sb.add(domain);
551 }
552 if (path != null) {
553 sb.add("; Path=");
554 sb.add(path);
555 }
556 if (secure) sb.add("; Secure");
557 if (httpOnly) sb.add("; HttpOnly");
558 return sb.toString();
559 }
560
561 String name;
562 String value;
563 Date expires;
564 int maxAge;
565 String domain;
566 String path;
567 bool httpOnly = false;
568 bool secure = false;
569 }
570
571
411 class _HttpRequestResponseBase { 572 class _HttpRequestResponseBase {
412 final int START = 0; 573 final int START = 0;
413 final int HEADER_SENT = 1; 574 final int HEADER_SENT = 1;
414 final int DONE = 2; 575 final int DONE = 2;
415 final int UPGRADED = 3; 576 final int UPGRADED = 3;
416 577
417 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) 578 _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection)
418 : _headers = new _HttpHeaders() { 579 : _headers = new _HttpHeaders() {
419 _state = START; 580 _state = START;
420 } 581 }
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 if (_bodyBytesWritten + bytes > _contentLength) { 703 if (_bodyBytesWritten + bytes > _contentLength) {
543 throw new HttpException("Writing more than specified content length"); 704 throw new HttpException("Writing more than specified content length");
544 } 705 }
545 _bodyBytesWritten += bytes; 706 _bodyBytesWritten += bytes;
546 } 707 }
547 708
548 int _state; 709 int _state;
549 710
550 _HttpConnectionBase _httpConnection; 711 _HttpConnectionBase _httpConnection;
551 _HttpHeaders _headers; 712 _HttpHeaders _headers;
713 List<Cookie> _cookies;
552 String _protocolVersion = "1.1"; 714 String _protocolVersion = "1.1";
553 715
554 // Length of the content body. If this is set to -1 (default value) 716 // Length of the content body. If this is set to -1 (default value)
555 // when starting to send data chunked transfer encoding will be 717 // when starting to send data chunked transfer encoding will be
556 // used. 718 // used.
557 int _contentLength = -1; 719 int _contentLength = -1;
558 // Number of body bytes written. This is only actual body data not 720 // Number of body bytes written. This is only actual body data not
559 // including headers or chunk information of using chinked transfer 721 // including headers or chunk information of using chinked transfer
560 // encoding. 722 // encoding.
561 int _bodyBytesWritten = 0; 723 int _bodyBytesWritten = 0;
562 } 724 }
563 725
564 726
565 // Parsed HTTP request providing information on the HTTP headers. 727 // Parsed HTTP request providing information on the HTTP headers.
566 class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { 728 class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest {
567 _HttpRequest(_HttpConnection connection) : super(connection); 729 _HttpRequest(_HttpConnection connection) : super(connection);
568 730
569 String get method() => _method; 731 String get method() => _method;
570 String get uri() => _uri; 732 String get uri() => _uri;
571 String get path() => _path; 733 String get path() => _path;
572 String get queryString() => _queryString; 734 String get queryString() => _queryString;
573 Map get queryParameters() => _queryParameters; 735 Map get queryParameters() => _queryParameters;
574 736
737 List<Cookie> get cookies() {
738 if (_cookies != null) return _cookies;
739
740 // Parse a Cookie header value according to the rules in RFC 6265.
741 void _parseCookieString(String s) {
742 int index = 0;
743
744 bool done() => index == s.length;
745
746 void skipWS() {
747 while (!done()) {
748 if (s[index] != " " && s[index] != "\t") return;
749 index++;
750 }
751 }
752
753 String parseName() {
754 int start = index;
755 while (!done()) {
756 if (s[index] == " " || s[index] == "\t" || s[index] == "=") break;
757 index++;
758 }
759 return s.substring(start, index).toLowerCase();
760 }
761
762 String parseValue() {
763 int start = index;
764 while (!done()) {
765 if (s[index] == " " || s[index] == "\t" || s[index] == ";") break;
766 index++;
767 }
768 return s.substring(start, index).toLowerCase();
769 }
770
771 void expect(String expected) {
772 if (done()) {
773 throw new HttpException("Failed to parse header value [$s]");
774 }
775 if (s[index] != expected) {
776 throw new HttpException("Failed to parse header value [$s]");
777 }
778 index++;
779 }
780
781 while (!done()) {
782 skipWS();
783 if (done()) return;
784 String name = parseName();
785 skipWS();
786 expect("=");
787 skipWS();
788 String value = parseValue();
789 _cookies.add(new _Cookie(name, value));
790 skipWS();
791 if (done()) return;
792 expect(";");
793 }
794 }
795
796 _cookies = new List<Cookie>();
797 List<String> headerValues = headers["cookie"];
798 if (headerValues != null) {
799 headerValues.forEach((headerValue) => _parseCookieString(headerValue));
800 }
801 return _cookies;
802 }
803
575 InputStream get inputStream() { 804 InputStream get inputStream() {
576 if (_inputStream == null) { 805 if (_inputStream == null) {
577 _inputStream = new _HttpInputStream(this); 806 _inputStream = new _HttpInputStream(this);
578 } 807 }
579 return _inputStream; 808 return _inputStream;
580 } 809 }
581 810
582 String get protocolVersion() => _protocolVersion; 811 String get protocolVersion() => _protocolVersion;
583 812
584 void _onRequestStart(String method, String uri, String version) { 813 void _onRequestStart(String method, String uri, String version) {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
666 if (_outputStream != null) throw new HttpException("Header already sent"); 895 if (_outputStream != null) throw new HttpException("Header already sent");
667 _statusCode = statusCode; 896 _statusCode = statusCode;
668 } 897 }
669 898
670 String get reasonPhrase() => _findReasonPhrase(_statusCode); 899 String get reasonPhrase() => _findReasonPhrase(_statusCode);
671 void set reasonPhrase(String reasonPhrase) { 900 void set reasonPhrase(String reasonPhrase) {
672 if (_outputStream != null) throw new HttpException("Header already sent"); 901 if (_outputStream != null) throw new HttpException("Header already sent");
673 _reasonPhrase = reasonPhrase; 902 _reasonPhrase = reasonPhrase;
674 } 903 }
675 904
905 List<Cookie> get cookies() {
906 if (_cookies == null) _cookies = new List<Cookie>();
907 return _cookies;
908 }
909
676 OutputStream get outputStream() { 910 OutputStream get outputStream() {
677 if (_state >= DONE) throw new HttpException("Response closed"); 911 if (_state >= DONE) throw new HttpException("Response closed");
678 if (_outputStream == null) { 912 if (_outputStream == null) {
679 _outputStream = new _HttpOutputStream(this); 913 _outputStream = new _HttpOutputStream(this);
680 } 914 }
681 return _outputStream; 915 return _outputStream;
682 } 916 }
683 917
684 DetachedSocket detachSocket() { 918 DetachedSocket detachSocket() {
685 if (_state >= DONE) throw new HttpException("Response closed"); 919 if (_state >= DONE) throw new HttpException("Response closed");
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
812 _writeCRLF(); 1046 _writeCRLF();
813 1047
814 // Determine the value of the "Transfer-Encoding" header based on 1048 // Determine the value of the "Transfer-Encoding" header based on
815 // whether the content length is known. 1049 // whether the content length is known.
816 if (_contentLength > 0) { 1050 if (_contentLength > 0) {
817 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString()); 1051 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString());
818 } else if (_contentLength < 0) { 1052 } else if (_contentLength < 0) {
819 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); 1053 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
820 } 1054 }
821 1055
1056 // Add all the cookies set to the headers.
1057 if (_cookies != null) {
1058 _cookies.forEach((cookie) {
1059 _headers.add("set-cookie", cookie);
1060 });
1061 }
1062
822 // Write headers. 1063 // Write headers.
823 bool allWritten = _writeHeaders(); 1064 bool allWritten = _writeHeaders();
824 _state = HEADER_SENT; 1065 _state = HEADER_SENT;
825 return allWritten; 1066 return allWritten;
826 } 1067 }
827 1068
828 // Response status code. 1069 // Response status code.
829 int _statusCode; 1070 int _statusCode;
830 String _reasonPhrase; 1071 String _reasonPhrase;
831 _HttpOutputStream _outputStream; 1072 _HttpOutputStream _outputStream;
(...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after
1248 if (_method == "GET") { 1489 if (_method == "GET") {
1249 _contentLength = 0; 1490 _contentLength = 0;
1250 } 1491 }
1251 } 1492 }
1252 1493
1253 void set contentLength(int contentLength) { 1494 void set contentLength(int contentLength) {
1254 if (_state >= HEADER_SENT) throw new HttpException("Header already sent"); 1495 if (_state >= HEADER_SENT) throw new HttpException("Header already sent");
1255 _contentLength = contentLength; 1496 _contentLength = contentLength;
1256 } 1497 }
1257 1498
1499 List<Cookie> get cookies() {
1500 if (_cookies == null) _cookies = new List<Cookie>();
1501 return _cookies;
1502 }
1503
1258 OutputStream get outputStream() { 1504 OutputStream get outputStream() {
1259 if (_state == DONE) throw new HttpException("Request closed"); 1505 if (_state == DONE) throw new HttpException("Request closed");
1260 if (_outputStream == null) { 1506 if (_outputStream == null) {
1261 _outputStream = new _HttpOutputStream(this); 1507 _outputStream = new _HttpOutputStream(this);
1262 } 1508 }
1263 return _outputStream; 1509 return _outputStream;
1264 } 1510 }
1265 1511
1266 // Delegate functions for the HttpOutputStream implementation. 1512 // Delegate functions for the HttpOutputStream implementation.
1267 bool _streamWrite(List<int> buffer, bool copyBuffer) { 1513 bool _streamWrite(List<int> buffer, bool copyBuffer) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1312 1558
1313 // Determine the value of the "Transfer-Encoding" header based on 1559 // Determine the value of the "Transfer-Encoding" header based on
1314 // whether the content length is known. If there is no content 1560 // whether the content length is known. If there is no content
1315 // neither "Content-Length" nor "Transfer-Encoding" is set 1561 // neither "Content-Length" nor "Transfer-Encoding" is set
1316 if (_contentLength > 0) { 1562 if (_contentLength > 0) {
1317 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString()); 1563 _headers.set(HttpHeaders.CONTENT_LENGTH, _contentLength.toString());
1318 } else if (_contentLength < 0) { 1564 } else if (_contentLength < 0) {
1319 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); 1565 _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
1320 } 1566 }
1321 1567
1568 // Add the cookies to the headers.
1569 if (_cookies != null) {
1570 StringBuffer sb = new StringBuffer();
1571 for (int i = 0; i < _cookies.length; i++) {
1572 if (i > 0) sb.add("; ");
1573 sb.add(_cookies[i].name);
1574 sb.add("=");
1575 sb.add(_cookies[i].value);
1576 }
1577 _headers.add("cookie", sb.toString());
1578 }
1579
1322 // Write headers. 1580 // Write headers.
1323 _writeHeaders(); 1581 _writeHeaders();
1324 _state = HEADER_SENT; 1582 _state = HEADER_SENT;
1325 } 1583 }
1326 1584
1327 String _method; 1585 String _method;
1328 String _uri; 1586 String _uri;
1329 _HttpClientConnection _connection; 1587 _HttpClientConnection _connection;
1330 _HttpOutputStream _outputStream; 1588 _HttpOutputStream _outputStream;
1331 Function _streamErrorHandler; 1589 Function _streamErrorHandler;
(...skipping 10 matching lines...) Expand all
1342 int get statusCode() => _statusCode; 1600 int get statusCode() => _statusCode;
1343 String get reasonPhrase() => _reasonPhrase; 1601 String get reasonPhrase() => _reasonPhrase;
1344 1602
1345 bool get isRedirect() { 1603 bool get isRedirect() {
1346 return statusCode == HttpStatus.MOVED_PERMANENTLY || 1604 return statusCode == HttpStatus.MOVED_PERMANENTLY ||
1347 statusCode == HttpStatus.FOUND || 1605 statusCode == HttpStatus.FOUND ||
1348 statusCode == HttpStatus.SEE_OTHER || 1606 statusCode == HttpStatus.SEE_OTHER ||
1349 statusCode == HttpStatus.TEMPORARY_REDIRECT; 1607 statusCode == HttpStatus.TEMPORARY_REDIRECT;
1350 } 1608 }
1351 1609
1610 List<Cookie> get cookies() {
1611 if (_cookies != null) return _cookies;
1612 _cookies = new List<Cookie>();
1613 List<String> values = _headers["set-cookie"];
1614 if (values != null) {
1615 values.forEach((value) {
1616 _cookies.add(new Cookie.fromSetCookieValue(value));
1617 });
1618 }
1619 return _cookies;
1620 }
1621
1352 InputStream get inputStream() { 1622 InputStream get inputStream() {
1353 if (_inputStream == null) { 1623 if (_inputStream == null) {
1354 _inputStream = new _HttpInputStream(this); 1624 _inputStream = new _HttpInputStream(this);
1355 } 1625 }
1356 return _inputStream; 1626 return _inputStream;
1357 } 1627 }
1358 1628
1359 void _onRequestStart(String method, String uri, String version) { 1629 void _onRequestStart(String method, String uri, String version) {
1360 // TODO(sgjesse): Error handling 1630 // TODO(sgjesse): Error handling
1361 } 1631 }
(...skipping 464 matching lines...) Expand 10 before | Expand all | Expand 10 after
1826 2096
1827 2097
1828 class _RedirectInfo implements RedirectInfo { 2098 class _RedirectInfo implements RedirectInfo {
1829 const _RedirectInfo(int this.statusCode, 2099 const _RedirectInfo(int this.statusCode,
1830 String this.method, 2100 String this.method,
1831 Uri this.location); 2101 Uri this.location);
1832 final int statusCode; 2102 final int statusCode;
1833 final String method; 2103 final String method;
1834 final Uri location; 2104 final Uri location;
1835 } 2105 }
OLDNEW
« no previous file with comments | « runtime/bin/http.dart ('k') | runtime/bin/http_utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698