Index: sdk/lib/io/http_impl.dart |
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart |
index e61fcc28626da13167c9a1648d6335d9ebaf68d9..fc69108579c0a95e86f0ba3ef153916f4f40b47f 100644 |
--- a/sdk/lib/io/http_impl.dart |
+++ b/sdk/lib/io/http_impl.dart |
@@ -2,632 +2,6 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-class _HttpHeaders implements HttpHeaders { |
- _HttpHeaders() : _headers = new Map<String, List<String>>(); |
- |
- List<String> operator[](String name) { |
- name = name.toLowerCase(); |
- return _headers[name]; |
- } |
- |
- String value(String name) { |
- name = name.toLowerCase(); |
- List<String> values = _headers[name]; |
- if (values == null) return null; |
- if (values.length > 1) { |
- throw new HttpException("More than one value for header $name"); |
- } |
- return values[0]; |
- } |
- |
- void add(String name, Object value) { |
- _checkMutable(); |
- if (value is List) { |
- for (int i = 0; i < value.length; i++) { |
- _add(name, value[i]); |
- } |
- } else { |
- _add(name, value); |
- } |
- } |
- |
- void set(String name, Object value) { |
- name = name.toLowerCase(); |
- _checkMutable(); |
- removeAll(name); |
- add(name, value); |
- } |
- |
- void remove(String name, Object value) { |
- _checkMutable(); |
- name = name.toLowerCase(); |
- List<String> values = _headers[name]; |
- if (values != null) { |
- int index = values.indexOf(value); |
- if (index != -1) { |
- values.removeRange(index, 1); |
- } |
- } |
- } |
- |
- void removeAll(String name) { |
- _checkMutable(); |
- name = name.toLowerCase(); |
- _headers.remove(name); |
- } |
- |
- void forEach(void f(String name, List<String> values)) { |
- _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) { |
- _checkMutable(); |
- _host = host; |
- _updateHostHeader(); |
- } |
- |
- int get port => _port; |
- |
- void set port(int port) { |
- _checkMutable(); |
- _port = port; |
- _updateHostHeader(); |
- } |
- |
- Date get ifModifiedSince { |
- List<String> values = _headers["if-modified-since"]; |
- if (values != null) { |
- try { |
- return _HttpUtils.parseDate(values[0]); |
- } on Exception catch (e) { |
- return null; |
- } |
- } |
- return null; |
- } |
- |
- void set ifModifiedSince(Date ifModifiedSince) { |
- _checkMutable(); |
- // Format "ifModifiedSince" header with date in Greenwich Mean Time (GMT). |
- String formatted = _HttpUtils.formatDate(ifModifiedSince.toUtc()); |
- _set("if-modified-since", formatted); |
- } |
- |
- Date get date { |
- List<String> values = _headers["date"]; |
- if (values != null) { |
- try { |
- return _HttpUtils.parseDate(values[0]); |
- } on Exception catch (e) { |
- return null; |
- } |
- } |
- return null; |
- } |
- |
- void set date(Date date) { |
- _checkMutable(); |
- // Format "Date" header with date in Greenwich Mean Time (GMT). |
- String formatted = _HttpUtils.formatDate(date.toUtc()); |
- _set("date", formatted); |
- } |
- |
- Date get expires { |
- List<String> values = _headers["expires"]; |
- if (values != null) { |
- try { |
- return _HttpUtils.parseDate(values[0]); |
- } on Exception catch (e) { |
- return null; |
- } |
- } |
- return null; |
- } |
- |
- void set expires(Date expires) { |
- _checkMutable(); |
- // Format "Expires" header with date in Greenwich Mean Time (GMT). |
- String formatted = _HttpUtils.formatDate(expires.toUtc()); |
- _set("expires", formatted); |
- } |
- |
- ContentType get contentType { |
- var values = _headers["content-type"]; |
- if (values != null) { |
- return new ContentType.fromString(values[0]); |
- } else { |
- return new ContentType(); |
- } |
- } |
- |
- void set contentType(ContentType contentType) { |
- _checkMutable(); |
- _set("content-type", contentType.toString()); |
- } |
- |
- void _add(String name, Object value) { |
- var lowerCaseName = name.toLowerCase(); |
- // TODO(sgjesse): Add immutable state throw HttpException is immutable. |
- if (lowerCaseName == "date") { |
- if (value is Date) { |
- date = value; |
- } else if (value is String) { |
- _set("date", value); |
- } else { |
- throw new HttpException("Unexpected type for header named $name"); |
- } |
- } else if (lowerCaseName == "expires") { |
- if (value is Date) { |
- expires = value; |
- } else if (value is String) { |
- _set("expires", value); |
- } else { |
- throw new HttpException("Unexpected type for header named $name"); |
- } |
- } else if (lowerCaseName == "if-modified-since") { |
- if (value is Date) { |
- ifModifiedSince = value; |
- } else if (value is String) { |
- _set("if-modified-since", value); |
- } else { |
- throw new HttpException("Unexpected type for header named $name"); |
- } |
- } else if (lowerCaseName == "host") { |
- int pos = value.indexOf(":"); |
- if (pos == -1) { |
- _host = value; |
- _port = HttpClient.DEFAULT_HTTP_PORT; |
- } else { |
- if (pos > 0) { |
- _host = value.substring(0, pos); |
- } else { |
- _host = null; |
- } |
- if (pos + 1 == value.length) { |
- _port = HttpClient.DEFAULT_HTTP_PORT; |
- } else { |
- try { |
- _port = parseInt(value.substring(pos + 1)); |
- } on FormatException catch (e) { |
- _port = null; |
- } |
- } |
- _set("host", value); |
- } |
- } else if (lowerCaseName == "content-type") { |
- _set("content-type", value); |
- } else { |
- name = lowerCaseName; |
- List<String> values = _headers[name]; |
- if (values == null) { |
- values = new List<String>(); |
- _headers[name] = values; |
- } |
- if (value is Date) { |
- values.add(_HttpUtils.formatDate(value)); |
- } else { |
- values.add(value.toString()); |
- } |
- } |
- } |
- |
- void _set(String name, String value) { |
- name = name.toLowerCase(); |
- List<String> values = new List<String>(); |
- _headers[name] = values; |
- values.add(value); |
- } |
- |
- _checkMutable() { |
- if (!_mutable) throw new HttpException("HTTP headers are not mutable"); |
- } |
- |
- _updateHostHeader() { |
- bool defaultPort = _port == null || _port == HttpClient.DEFAULT_HTTP_PORT; |
- String portPart = defaultPort ? "" : ":$_port"; |
- _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]; |
- final CRLF = const [_CharCode.CR, _CharCode.LF]; |
- |
- var bufferSize = 16 * 1024; |
- var buffer = new Uint8List(bufferSize); |
- var bufferPos = 0; |
- |
- void writeBuffer() { |
- connection._writeFrom(buffer, 0, bufferPos); |
- bufferPos = 0; |
- } |
- |
- // Format headers. |
- _headers.forEach((String name, List<String> values) { |
- bool fold = _foldHeader(name); |
- List<int> nameData; |
- nameData = name.charCodes; |
- int nameDataLen = nameData.length; |
- if (nameDataLen + 2 > bufferSize - bufferPos) writeBuffer(); |
- buffer.setRange(bufferPos, nameDataLen, nameData); |
- bufferPos += nameDataLen; |
- buffer[bufferPos++] = _CharCode.COLON; |
- buffer[bufferPos++] = _CharCode.SP; |
- for (int i = 0; i < values.length; i++) { |
- List<int> data = values[i].charCodes; |
- int dataLen = data.length; |
- // Worst case here is writing the name, value and 6 additional bytes. |
- if (nameDataLen + dataLen + 6 > bufferSize - bufferPos) writeBuffer(); |
- if (i > 0) { |
- if (fold) { |
- buffer[bufferPos++] = _CharCode.COMMA; |
- buffer[bufferPos++] = _CharCode.SP; |
- } else { |
- buffer[bufferPos++] = _CharCode.CR; |
- buffer[bufferPos++] = _CharCode.LF; |
- buffer.setRange(bufferPos, nameDataLen, nameData); |
- bufferPos += nameDataLen; |
- buffer[bufferPos++] = _CharCode.COLON; |
- buffer[bufferPos++] = _CharCode.SP; |
- } |
- } |
- buffer.setRange(bufferPos, dataLen, data); |
- bufferPos += dataLen; |
- } |
- buffer[bufferPos++] = _CharCode.CR; |
- buffer[bufferPos++] = _CharCode.LF; |
- }); |
- writeBuffer(); |
- } |
- |
- String toString() { |
- StringBuffer sb = new StringBuffer(); |
- _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) { |
- if (fold) { |
- sb.add(", "); |
- } else { |
- sb.add("\n"); |
- sb.add(name); |
- sb.add(": "); |
- } |
- } |
- sb.add(values[i]); |
- } |
- sb.add("\n"); |
- }); |
- return sb.toString(); |
- } |
- |
- bool _mutable = true; // Are the headers currently mutable? |
- Map<String, List<String>> _headers; |
- List<String> _noFoldingHeaders; |
- |
- String _host; |
- int _port; |
-} |
- |
- |
-class _HeaderValue implements HeaderValue { |
- _HeaderValue([String this.value = ""]); |
- |
- _HeaderValue.fromString(String value, {this.parameterSeparator: ";"}) { |
- // Parse the string. |
- _parse(value); |
- } |
- |
- Map<String, String> get parameters { |
- if (_parameters == null) _parameters = new Map<String, String>(); |
- return _parameters; |
- } |
- |
- String toString() { |
- StringBuffer sb = new StringBuffer(); |
- sb.add(value); |
- if (parameters != null && parameters.length > 0) { |
- _parameters.forEach((String name, String value) { |
- sb.add("; "); |
- sb.add(name); |
- sb.add("="); |
- sb.add(value); |
- }); |
- } |
- return sb.toString(); |
- } |
- |
- void _parse(String s) { |
- int index = 0; |
- |
- bool done() => index == s.length; |
- |
- void skipWS() { |
- while (!done()) { |
- if (s[index] != " " && s[index] != "\t") return; |
- index++; |
- } |
- } |
- |
- String parseValue() { |
- int start = index; |
- while (!done()) { |
- if (s[index] == " " || |
- s[index] == "\t" || |
- s[index] == parameterSeparator) break; |
- index++; |
- } |
- return s.substring(start, index).toLowerCase(); |
- } |
- |
- void expect(String expected) { |
- if (done() || s[index] != expected) { |
- throw new HttpException("Failed to parse header value"); |
- } |
- index++; |
- } |
- |
- void maybeExpect(String expected) { |
- if (s[index] == expected) index++; |
- } |
- |
- void parseParameters() { |
- _parameters = new Map<String, String>(); |
- |
- String parseParameterName() { |
- int start = index; |
- while (!done()) { |
- if (s[index] == " " || s[index] == "\t" || s[index] == "=") break; |
- index++; |
- } |
- return s.substring(start, index).toLowerCase(); |
- } |
- |
- String parseParameterValue() { |
- if (s[index] == "\"") { |
- // Parse quoted value. |
- StringBuffer sb = new StringBuffer(); |
- index++; |
- while (!done()) { |
- if (s[index] == "\\") { |
- if (index + 1 == s.length) { |
- throw new HttpException("Failed to parse header value"); |
- } |
- index++; |
- } else if (s[index] == "\"") { |
- index++; |
- break; |
- } |
- sb.add(s[index]); |
- index++; |
- } |
- return sb.toString(); |
- } else { |
- // Parse non-quoted value. |
- return parseValue(); |
- } |
- } |
- |
- while (!done()) { |
- skipWS(); |
- if (done()) return; |
- String name = parseParameterName(); |
- skipWS(); |
- expect("="); |
- skipWS(); |
- String value = parseParameterValue(); |
- _parameters[name] = value; |
- skipWS(); |
- if (done()) return; |
- expect(parameterSeparator); |
- } |
- } |
- |
- skipWS(); |
- value = parseValue(); |
- skipWS(); |
- if (done()) return; |
- maybeExpect(parameterSeparator); |
- parseParameters(); |
- } |
- |
- String value; |
- String parameterSeparator; |
- Map<String, String> _parameters; |
-} |
- |
- |
-class _ContentType extends _HeaderValue implements ContentType { |
- _ContentType(String primaryType, String subType) |
- : _primaryType = primaryType, _subType = subType, super(""); |
- |
- _ContentType.fromString(String value) : super.fromString(value); |
- |
- String get value => "$_primaryType/$_subType"; |
- |
- void set value(String s) { |
- int index = s.indexOf("/"); |
- if (index == -1 || index == (s.length - 1)) { |
- primaryType = s.trim().toLowerCase(); |
- subType = ""; |
- } else { |
- primaryType = s.substring(0, index).trim().toLowerCase(); |
- subType = s.substring(index + 1).trim().toLowerCase(); |
- } |
- } |
- |
- String get primaryType => _primaryType; |
- |
- void set primaryType(String s) { |
- _primaryType = s; |
- } |
- |
- String get subType => _subType; |
- |
- void set subType(String s) { |
- _subType = s; |
- } |
- |
- String get charset => parameters["charset"]; |
- |
- void set charset(String s) { |
- parameters["charset"] = s; |
- } |
- |
- String _primaryType = ""; |
- String _subType = ""; |
-} |
- |
- |
-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.parseCookieDate(value); |
- } else if (name == "max-age") { |
- maxAge = 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) { |
- throw new HttpException("Failed to parse header value [$s]"); |
- } |
- 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; |
-} |
- |
- |
// The close queue handles graceful closing of HTTP connections. When |
// a connection is added to the queue it will enter a wait state |
// waiting for all data written and possibly socket shutdown from |
@@ -701,16 +75,13 @@ class _CloseQueue { |
class _HttpRequestResponseBase { |
- final int START = 0; |
- final int HEADER_SENT = 1; |
- final int DONE = 2; |
- final int UPGRADED = 3; |
+ static const int START = 0; |
+ static const int HEADER_SENT = 1; |
+ static const int DONE = 2; |
+ static const int UPGRADED = 3; |
_HttpRequestResponseBase(_HttpConnectionBase this._httpConnection) |
- : _headers = new _HttpHeaders() { |
- _state = START; |
- _headResponse = false; |
- } |
+ : _state = START, _headResponse = false; |
int get contentLength => _contentLength; |
HttpHeaders get headers => _headers; |
@@ -958,17 +329,14 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
return _session = sessionManager.createSession(init); |
} |
- void _onRequestStart(String method, String uri, String version) { |
+ void _onRequestReceived(String method, |
+ String uri, |
+ String version, |
+ _HttpHeaders headers) { |
_method = method; |
_uri = uri; |
_parseRequestUri(uri); |
- } |
- |
- void _onHeaderReceived(String name, String value) { |
- _headers.add(name, value); |
- } |
- |
- void _onHeadersComplete() { |
+ _headers = headers; |
if (_httpConnection._server._sessionManagerInstance != null) { |
// Map to session if exists. |
var sessionId = cookies.reduce(null, (last, cookie) { |
@@ -1055,10 +423,14 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
_HttpResponse(_HttpConnection httpConnection) |
: super(httpConnection), |
- _statusCode = HttpStatus.OK; |
+ _statusCode = HttpStatus.OK { |
+ _headers = new _HttpHeaders(); |
+ } |
void set contentLength(int contentLength) { |
- if (_state >= HEADER_SENT) throw new HttpException("Header already sent"); |
+ if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
+ throw new HttpException("Header already sent"); |
+ } |
_contentLength = contentLength; |
} |
@@ -1080,7 +452,9 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
} |
OutputStream get outputStream { |
- if (_state >= DONE) throw new HttpException("Response closed"); |
+ if (_state >= _HttpRequestResponseBase.DONE) { |
+ throw new HttpException("Response closed"); |
+ } |
if (_outputStream == null) { |
_outputStream = new _HttpOutputStream(this); |
} |
@@ -1088,12 +462,14 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
} |
DetachedSocket detachSocket() { |
- if (_state >= DONE) throw new HttpException("Response closed"); |
+ if (_state >= _HttpRequestResponseBase.DONE) { |
+ throw new HttpException("Response closed"); |
+ } |
// Ensure that headers are written. |
- if (_state == START) { |
+ if (_state == _HttpRequestResponseBase.START) { |
_writeHeader(); |
} |
- _state = UPGRADED; |
+ _state = _HttpRequestResponseBase.UPGRADED; |
// Ensure that any trailing data is written. |
_writeDone(); |
// Indicate to the connection that the response handling is done. |
@@ -1117,7 +493,7 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
void _streamClose() { |
_ensureHeadersSent(); |
- _state = DONE; |
+ _state = _HttpRequestResponseBase.DONE; |
// Stop tracking no pending write events. |
_httpConnection._onNoPendingWrites = null; |
// Ensure that any trailing data is written. |
@@ -1127,7 +503,7 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
} |
void _streamSetNoPendingWriteHandler(callback()) { |
- if (_state != DONE) { |
+ if (_state != _HttpRequestResponseBase.DONE) { |
_httpConnection._onNoPendingWrites = callback; |
} |
} |
@@ -1246,7 +622,7 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse { |
// Write headers. |
bool allWritten = _writeHeaders(); |
- _state = HEADER_SENT; |
+ _state = _HttpRequestResponseBase.HEADER_SENT; |
return allWritten; |
} |
@@ -1444,9 +820,7 @@ class _HttpConnection extends _HttpConnectionBase { |
_HttpConnection(HttpServer this._server) { |
_httpParser = new _HttpParser.requestParser(); |
// Register HTTP parser callbacks. |
- _httpParser.requestStart = _onRequestStart; |
- _httpParser.headerReceived = _onHeaderReceived; |
- _httpParser.headersComplete = _onHeadersComplete; |
+ _httpParser.requestStart = _onRequestReceived; |
_httpParser.dataReceived = _onDataReceived; |
_httpParser.dataEnd = _onDataEnd; |
_httpParser.error = _onError; |
@@ -1472,23 +846,18 @@ class _HttpConnection extends _HttpConnectionBase { |
if (_socket != null) _socket.close(); |
} |
- void _onRequestStart(String method, String uri, String version) { |
+ void _onRequestReceived(String method, |
+ String uri, |
+ String version, |
+ _HttpHeaders headers) { |
_state = _HttpConnectionBase.ACTIVE; |
// Create new request and response objects for this request. |
_request = new _HttpRequest(this); |
_response = new _HttpResponse(this); |
- _request._onRequestStart(method, uri, version); |
+ _request._onRequestReceived(method, uri, version, headers); |
_request._protocolVersion = version; |
_response._protocolVersion = version; |
_response._headResponse = method == "HEAD"; |
- } |
- |
- void _onHeaderReceived(String name, String value) { |
- _request._onHeaderReceived(name, value); |
- } |
- |
- void _onHeadersComplete() { |
- _request._onHeadersComplete(); |
_response.persistentConnection = _httpParser.persistentConnection; |
if (onRequestReceived != null) { |
onRequestReceived(_request, _response); |
@@ -1673,6 +1042,7 @@ class _HttpClientRequest |
Uri this._uri, |
_HttpClientConnection connection) |
: super(connection) { |
+ _headers = new _HttpHeaders(); |
_connection = connection; |
// Default GET and HEAD requests to have no content. |
if (_method == "GET" || _method == "HEAD") { |
@@ -1681,7 +1051,9 @@ class _HttpClientRequest |
} |
void set contentLength(int contentLength) { |
- if (_state >= HEADER_SENT) throw new HttpException("Header already sent"); |
+ if (_state >= _HttpRequestResponseBase.HEADER_SENT) { |
+ throw new HttpException("Header already sent"); |
+ } |
_contentLength = contentLength; |
} |
@@ -1715,7 +1087,7 @@ class _HttpClientRequest |
void _streamClose() { |
_ensureHeadersSent(); |
- _state = DONE; |
+ _state = _HttpRequestResponseBase.DONE; |
// Stop tracking no pending write events. |
_httpConnection._onNoPendingWrites = null; |
// Ensure that any trailing data is written. |
@@ -1724,7 +1096,7 @@ class _HttpClientRequest |
} |
void _streamSetNoPendingWriteHandler(callback()) { |
- if (_state != DONE) { |
+ if (_state != _HttpRequestResponseBase.DONE) { |
_httpConnection._onNoPendingWrites = callback; |
} |
} |
@@ -1788,7 +1160,7 @@ class _HttpClientRequest |
// Write headers. |
_writeHeaders(); |
- _state = HEADER_SENT; |
+ _state = _HttpRequestResponseBase.HEADER_SENT; |
} |
String _method; |
@@ -1835,13 +1207,50 @@ class _HttpClientResponse |
return _inputStream; |
} |
- void _onResponseStart(int statusCode, String reasonPhrase, String version) { |
+ void _onResponseReceived(int statusCode, |
+ String reasonPhrase, |
+ String version, |
+ _HttpHeaders headers) { |
_statusCode = statusCode; |
_reasonPhrase = reasonPhrase; |
- } |
+ _headers = headers; |
+ // Get parsed content length. |
+ _contentLength = _httpConnection._httpParser.contentLength; |
- void _onHeaderReceived(String name, String value) { |
- _headers.add(name, value); |
+ // Prepare for receiving data. |
+ _headers._mutable = false; |
+ _buffer = new _BufferList(); |
+ |
+ if (isRedirect && _connection.followRedirects) { |
+ if (_connection._redirects == null || |
+ _connection._redirects.length < _connection.maxRedirects) { |
+ // Check the location header. |
+ List<String> location = headers[HttpHeaders.LOCATION]; |
+ if (location == null || location.length > 1) { |
+ throw new RedirectException("Invalid redirect", |
+ _connection._redirects); |
+ } |
+ // Check for redirect loop |
+ if (_connection._redirects != null) { |
+ Uri redirectUrl = new Uri.fromString(location[0]); |
+ for (int i = 0; i < _connection._redirects.length; i++) { |
+ if (_connection._redirects[i].location.toString() == |
+ redirectUrl.toString()) { |
+ throw new RedirectLoopException(_connection._redirects); |
+ } |
+ } |
+ } |
+ // Drain body and redirect. |
+ inputStream.onData = inputStream.read; |
+ _connection.redirect(); |
+ } else { |
+ throw new RedirectLimitExceededException(_connection._redirects); |
+ } |
+ } else if (statusCode == HttpStatus.UNAUTHORIZED) { |
+ _handleUnauthorized(); |
+ } else if (_connection._onResponse != null) { |
+ _connection._onResponse(this); |
+ } |
} |
void _handleUnauthorized() { |
@@ -1917,46 +1326,6 @@ class _HttpClientResponse |
} |
} |
- void _onHeadersComplete() { |
- // Get parsed content length. |
- _contentLength = _httpConnection._httpParser.contentLength; |
- |
- // Prepare for receiving data. |
- _headers._mutable = false; |
- _buffer = new _BufferList(); |
- |
- if (isRedirect && _connection.followRedirects) { |
- if (_connection._redirects == null || |
- _connection._redirects.length < _connection.maxRedirects) { |
- // Check the location header. |
- List<String> location = headers[HttpHeaders.LOCATION]; |
- if (location == null || location.length > 1) { |
- throw new RedirectException("Invalid redirect", |
- _connection._redirects); |
- } |
- // Check for redirect loop |
- if (_connection._redirects != null) { |
- Uri redirectUrl = new Uri.fromString(location[0]); |
- for (int i = 0; i < _connection._redirects.length; i++) { |
- if (_connection._redirects[i].location.toString() == |
- redirectUrl.toString()) { |
- throw new RedirectLoopException(_connection._redirects); |
- } |
- } |
- } |
- // Drain body and redirect. |
- inputStream.onData = inputStream.read; |
- _connection.redirect(); |
- } else { |
- throw new RedirectLimitExceededException(_connection._redirects); |
- } |
- } else if (statusCode == HttpStatus.UNAUTHORIZED) { |
- _handleUnauthorized(); |
- } else if (_connection._onResponse != null) { |
- _connection._onResponse(this); |
- } |
- } |
- |
void _onDataReceived(List<int> data) { |
_buffer.add(data); |
if (_inputStream != null) _inputStream._dataReceived(); |
@@ -2011,9 +1380,7 @@ class _HttpClientConnection |
super._connectionEstablished(socketConn._socket); |
_socketConn = socketConn; |
// Register HTTP parser callbacks. |
- _httpParser.responseStart = _onResponseStart; |
- _httpParser.headerReceived = _onHeaderReceived; |
- _httpParser.headersComplete = _onHeadersComplete; |
+ _httpParser.responseStart = _onResponseReceived; |
_httpParser.dataReceived = _onDataReceived; |
_httpParser.dataEnd = _onDataEnd; |
_httpParser.error = _onError; |
@@ -2086,16 +1453,11 @@ class _HttpClientConnection |
} |
} |
- void _onResponseStart(int statusCode, String reasonPhrase, String version) { |
- _response._onResponseStart(statusCode, reasonPhrase, version); |
- } |
- |
- void _onHeaderReceived(String name, String value) { |
- _response._onHeaderReceived(name, value); |
- } |
- |
- void _onHeadersComplete() { |
- _response._onHeadersComplete(); |
+ void _onResponseReceived(int statusCode, |
+ String reasonPhrase, |
+ String version, |
+ _HttpHeaders headers) { |
+ _response._onResponseReceived(statusCode, reasonPhrase, version, headers); |
} |
void _onDataReceived(List<int> data) { |