Chromium Code Reviews| Index: runtime/bin/http_impl.dart |
| diff --git a/runtime/bin/http_impl.dart b/runtime/bin/http_impl.dart |
| index 6c4db412700a266f8c7009da6a0a09628da3ede1..3498777bd7566348cbe4e67d74b75673dfa84332 100644 |
| --- a/runtime/bin/http_impl.dart |
| +++ b/runtime/bin/http_impl.dart |
| @@ -60,6 +60,11 @@ class _HttpHeaders implements HttpHeaders { |
| _headers.forEach(f); |
| } |
| + void noFolding(String name) { |
| + if (_noFoldingHeaders == null) _noFoldingHeaders = new List<String>(); |
| + _noFoldingHeaders.add(name); |
| + } |
| + |
| String get host() => _host; |
| void set host(String host) { |
| @@ -200,6 +205,15 @@ class _HttpHeaders implements HttpHeaders { |
| _set("host", "$host$portPart"); |
| } |
| + _foldHeader(String name) { |
| + if (name == "set-cookie" || |
| + (_noFoldingHeaders != null && |
| + _noFoldingHeaders.indexOf(name) != -1)) { |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| _write(_HttpConnectionBase connection) { |
| final COLONSP = const [_CharCode.COLON, _CharCode.SP]; |
| final COMMASP = const [_CharCode.COMMA, _CharCode.SP]; |
| @@ -207,13 +221,21 @@ class _HttpHeaders implements HttpHeaders { |
| // Format headers. |
| _headers.forEach((String name, List<String> values) { |
| + bool fold = _foldHeader(name); |
| List<int> data; |
| data = name.charCodes(); |
| connection._write(data); |
| connection._write(COLONSP); |
| for (int i = 0; i < values.length; i++) { |
| if (i > 0) { |
| - connection._write(COMMASP); |
| + if (fold) { |
| + connection._write(COMMASP); |
| + } else { |
| + connection._write(CRLF); |
| + data = name.charCodes(); |
| + connection._write(data); |
| + connection._write(COLONSP); |
| + } |
| } |
| data = values[i].charCodes(); |
| connection._write(data); |
| @@ -227,9 +249,16 @@ class _HttpHeaders implements HttpHeaders { |
| _headers.forEach((String name, List<String> values) { |
| sb.add(name); |
| sb.add(": "); |
| + bool fold = _foldHeader(name); |
| for (int i = 0; i < values.length; i++) { |
| if (i > 0) { |
| - sb.add(", "); |
| + if (fold) { |
| + sb.add(", "); |
| + } else { |
| + sb.add("\n"); |
| + sb.add(name); |
| + sb.add(": "); |
| + } |
| } |
| sb.add(values[i]); |
| } |
| @@ -240,6 +269,7 @@ class _HttpHeaders implements HttpHeaders { |
| bool _mutable = true; // Are the headers currently mutable? |
| Map<String, List<String>> _headers; |
| + List<String> _noFoldingHeaders; |
| String _host; |
| int _port; |
| @@ -408,6 +438,138 @@ class _ContentType extends _HeaderValue implements ContentType { |
| } |
| +class _Cookie implements Cookie { |
| + _Cookie([String this.name, String this.value]); |
| + |
| + _Cookie.fromSetCookieValue(String value) { |
| + // Parse the Set-Cookie header value. |
| + _parseSetCookieValue(value); |
| + } |
| + |
| + // Parse a Set-Cookie header value according to the rules in RFC 6265. |
| + void _parseSetCookieValue(String s) { |
| + int index = 0; |
| + |
| + bool done() => index == s.length; |
| + |
| + String parseName() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == "=") break; |
| + index++; |
| + } |
| + return s.substring(start, index).trim().toLowerCase(); |
| + } |
| + |
| + String parseValue() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == ";") break; |
| + index++; |
| + } |
| + return s.substring(start, index).trim().toLowerCase(); |
| + } |
| + |
| + void expect(String expected) { |
| + if (done()) throw new HttpException("Failed to parse header value [$s]"); |
| + if (s[index] != expected) { |
| + throw new HttpException("Failed to parse header value [$s]"); |
| + } |
| + index++; |
| + } |
| + |
| + void parseAttributes() { |
| + String parseAttributeName() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == "=" || s[index] == ";") break; |
| + index++; |
| + } |
| + return s.substring(start, index).trim().toLowerCase(); |
| + } |
| + |
| + String parseAttributeValue() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == ";") break; |
| + index++; |
| + } |
| + return s.substring(start, index).trim().toLowerCase(); |
| + } |
| + |
| + while (!done()) { |
| + String name = parseAttributeName(); |
| + String value = ""; |
| + if (!done() && s[index] == "=") { |
| + index++; // Skip the = character. |
| + value = parseAttributeValue(); |
| + } |
| + if (name == "expires") { |
| + expires = _HttpUtils.parseDate(value, strict: false); |
| + } else if (name == "max-age") { |
| + maxAge = Math.parseInt(value); |
| + } else if (name == "domain") { |
| + domain = value; |
| + } else if (name == "path") { |
| + path = value; |
| + } else if (name == "httponly") { |
| + httpOnly = true; |
| + } else if (name == "secure") { |
| + secure = true; |
| + } |
| + if (!done()) index++; // Skip the ; character |
| + } |
| + } |
| + |
| + name = parseName(); |
| + if (done() || name.length == 0) { |
| + _valid = false; |
|
Mads Ager (google)
2012/05/24 11:42:35
_valid no longer exists - throw exception and add
Søren Gjesse
2012/05/24 12:54:26
Done.
|
| + return; |
| + } |
| + index++; // Skip the = character. |
| + value = parseValue(); |
| + if (done()) return; |
| + index++; // Skip the ; character. |
| + parseAttributes(); |
| + } |
| + |
| + String toString() { |
| + StringBuffer sb = new StringBuffer(); |
| + sb.add(name); |
| + sb.add("="); |
| + sb.add(value); |
| + if (expires != null) { |
| + sb.add("; Expires="); |
| + sb.add(_HttpUtils.formatDate(expires)); |
| + } |
| + if (maxAge != null) { |
| + sb.add("; Max-Age="); |
| + sb.add(maxAge); |
| + } |
| + if (domain != null) { |
| + sb.add("; Domain="); |
| + sb.add(domain); |
| + } |
| + if (path != null) { |
| + sb.add("; Path="); |
| + sb.add(path); |
| + } |
| + if (secure) sb.add("; Secure"); |
| + if (httpOnly) sb.add("; HttpOnly"); |
| + return sb.toString(); |
| + } |
| + |
| + String name; |
| + String value; |
| + Date expires; |
| + int maxAge; |
| + String domain; |
| + String path; |
| + bool httpOnly = false; |
| + bool secure = false; |
| +} |
| + |
| + |
| class _HttpRequestResponseBase { |
| final int START = 0; |
| final int HEADER_SENT = 1; |
| @@ -549,6 +711,7 @@ class _HttpRequestResponseBase { |
| _HttpConnectionBase _httpConnection; |
| _HttpHeaders _headers; |
| + List<Cookie> _cookies; |
| String _protocolVersion = "1.1"; |
| // Length of the content body. If this is set to -1 (default value) |
| @@ -572,6 +735,73 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
| String get queryString() => _queryString; |
| Map get queryParameters() => _queryParameters; |
| + List<Cookie> get cookies() { |
| + if (_cookies != null) return _cookies; |
| + |
| + // Parse a Cookie header value according to the rules in RFC 6265. |
| + void _parseCookieString(String s) { |
| + int index = 0; |
| + |
| + bool done() => index == s.length; |
| + |
| + void skipWS() { |
| + while (!done()) { |
| + if (s[index] != " " && s[index] != "\t") return; |
| + index++; |
| + } |
| + } |
| + |
| + String parseName() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; |
| + index++; |
| + } |
| + return s.substring(start, index).toLowerCase(); |
| + } |
| + |
| + String parseValue() { |
| + int start = index; |
| + while (!done()) { |
| + if (s[index] == " " || s[index] == "\t" || s[index] == ";") break; |
| + index++; |
| + } |
| + return s.substring(start, index).toLowerCase(); |
| + } |
| + |
| + void expect(String expected) { |
| + if (done()) { |
| + throw new HttpException("Failed to parse header value [$s]"); |
| + } |
| + if (s[index] != expected) { |
| + throw new HttpException("Failed to parse header value [$s]"); |
| + } |
| + index++; |
| + } |
| + |
| + while (!done()) { |
| + skipWS(); |
| + if (done()) return; |
| + String name = parseName(); |
| + skipWS(); |
| + expect("="); |
| + skipWS(); |
| + String value = parseValue(); |
| + _cookies.add(new _Cookie(name, value)); |
| + skipWS(); |
| + if (done()) return; |
| + expect(";"); |
| + } |
| + } |
| + |
| + _cookies = new List<Cookie>(); |
| + List<String> headerValues = headers["cookie"]; |
| + if (headerValues != null) { |
| + headerValues.forEach((headerValue) => _parseCookieString(headerValue)); |
| + } |
| + return _cookies; |
| + } |
| + |
| InputStream get inputStream() { |
| if (_inputStream == null) { |
| _inputStream = new _HttpInputStream(this); |
| @@ -673,6 +903,11 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
| _reasonPhrase = reasonPhrase; |
| } |
| + List<Cookie> get cookies() { |
| + if (_cookies == null) _cookies = new List<Cookie>(); |
| + return _cookies; |
| + } |
| + |
| OutputStream get outputStream() { |
| if (_state >= DONE) throw new HttpException("Response closed"); |
| if (_outputStream == null) { |
| @@ -819,6 +1054,13 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
| _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| } |
| + // Add all the cookies set to the headers. |
| + if (_cookies != null) { |
| + _cookies.forEach((cookie) { |
| + _headers.add("set-cookie", cookie); |
| + }); |
| + } |
| + |
| // Write headers. |
| bool allWritten = _writeHeaders(); |
| _state = HEADER_SENT; |
| @@ -1255,6 +1497,11 @@ class _HttpClientRequest |
| _contentLength = contentLength; |
| } |
| + List<Cookie> get cookies() { |
| + if (_cookies == null) _cookies = new List<Cookie>(); |
| + return _cookies; |
| + } |
| + |
| OutputStream get outputStream() { |
| if (_state == DONE) throw new HttpException("Request closed"); |
| if (_outputStream == null) { |
| @@ -1319,6 +1566,18 @@ class _HttpClientRequest |
| _headers.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| } |
| + // Add the cookies to the headers. |
| + if (_cookies != null) { |
| + StringBuffer sb = new StringBuffer(); |
| + for (int i = 0; i < _cookies.length; i++) { |
| + if (i > 0) sb.add("; "); |
| + sb.add(_cookies[i].name); |
| + sb.add("="); |
| + sb.add(_cookies[i].value); |
| + } |
| + _headers.add("cookie", sb.toString()); |
| + } |
| + |
| // Write headers. |
| _writeHeaders(); |
| _state = HEADER_SENT; |
| @@ -1349,6 +1608,18 @@ class _HttpClientResponse |
| statusCode == HttpStatus.TEMPORARY_REDIRECT; |
| } |
| + List<Cookie> get cookies() { |
| + if (_cookies != null) return _cookies; |
| + _cookies = new List<Cookie>(); |
| + List<String> values = _headers["set-cookie"]; |
| + if (values != null) { |
| + values.forEach((value) { |
| + _cookies.add(new Cookie.fromSetCookieValue(value)); |
| + }); |
| + } |
| + return _cookies; |
| + } |
| + |
| InputStream get inputStream() { |
| if (_inputStream == null) { |
| _inputStream = new _HttpInputStream(this); |