| 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) {
|
|
|