| Index: runtime/bin/http_impl.dart
|
| diff --git a/runtime/bin/http_impl.dart b/runtime/bin/http_impl.dart
|
| index 784ad824070f285038a447b988ce56804dd4596f..f8475ca2ab5a99c940052e0d2d8570afd56a8976 100644
|
| --- a/runtime/bin/http_impl.dart
|
| +++ b/runtime/bin/http_impl.dart
|
| @@ -2,17 +2,185 @@
|
| // 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) {
|
| + if (value is List) {
|
| + for (int i = 0; i < value.length; i++) {
|
| + _add(name, vlaie[i]);
|
| + }
|
| + } else {
|
| + _add(name, value);
|
| + }
|
| + }
|
| +
|
| + void set(String name, Object value) {
|
| + removeAll(name);
|
| + add(name, value);
|
| + }
|
| +
|
| + void remove(String name, Object value) {
|
| + 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) {
|
| + name = name.toLowerCase();
|
| + _headers.remove(name);
|
| + }
|
| +
|
| + String get host() => _host;
|
| + void set host(String host) {
|
| + _host = host;
|
| + _updateHostHeader();
|
| + }
|
| +
|
| + int get port() => _port;
|
| + void set port(int port) {
|
| + _port = port;
|
| + _updateHostHeader();
|
| + }
|
| +
|
| + Date get expires() {
|
| + if (_expires == null) {
|
| + List<String> values = _headers["expires"];
|
| + if (values != null) {
|
| + _expires = _HttpUtils.parseDate(values[0]);
|
| + }
|
| + }
|
| + return _expires;
|
| + }
|
| +
|
| + void set expires(Date expires) {
|
| + _expires = expires;
|
| + // Format "Expires" header with date in Greenwich Mean Time (GMT).
|
| + String formatted =
|
| + _HttpUtils.formatDate(_expires.changeTimeZone(new TimeZone.utc()));
|
| + _set("expires", formatted);
|
| + }
|
| +
|
| + void _add(String name, Object value) {
|
| + // TODO(sgjesse): Add immutable state throw HttpException is immutable.
|
| + if (name.toLowerCase() == "expires") {
|
| + if (value is Date) {
|
| + expires = value;
|
| + } else if (value is String) {
|
| + expires = _HttpUtils.parseDate(value);
|
| + } else {
|
| + throw new HttpException("Unexpected type for header named $name");
|
| + }
|
| + } else if (name.toLowerCase() == "host") {
|
| + int pos = value.indexOf(":");
|
| + if (pos == -1) {
|
| + _host = value;
|
| + _port = HttpClient.DEFAULT_HTTP_PORT;
|
| + } else {
|
| + _host = value.substring(0, pos);
|
| + if (pos + 1 == value.length) {
|
| + _port = HttpClient.DEFAULT_HTTP_PORT;
|
| + } else {
|
| + _port = Math.parseInt(value.substring(pos + 1));
|
| + }
|
| + }
|
| + _updateHostHeader();
|
| + } else {
|
| + name = name.toLowerCase();
|
| + List<String> values = _headers[name];
|
| + if (values == null) {
|
| + values = new List<String>();
|
| + _headers[name] = values;
|
| + }
|
| + 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);
|
| + }
|
| +
|
| + _updateHostHeader() {
|
| + String portPart = _port == HttpClient.DEFAULT_HTTP_PORT ? "" : ":$_port";
|
| + _set("host", "$host$portPart");
|
| + }
|
| +
|
| + _write(_HttpConnectionBase connection) {
|
| + final COLONSP = const [_CharCode.COLON, _CharCode.SP];
|
| + final COMMASP = const [_CharCode.COMMA, _CharCode.SP];
|
| + final CRLF = const [_CharCode.CR, _CharCode.LF];
|
| +
|
| + // Format headers.
|
| + _headers.forEach((String name, List<String> values) {
|
| + 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);
|
| + }
|
| + data = values[i].charCodes();
|
| + connection._write(data);
|
| + }
|
| + connection._write(CRLF);
|
| + });
|
| + }
|
| +
|
| + String toString() {
|
| + StringBuffer sb = new StringBuffer();
|
| + _headers.forEach((String name, List<String> values) {
|
| + sb.add(name);
|
| + sb.add(": ");
|
| + for (int i = 0; i < values.length; i++) {
|
| + if (i > 0) {
|
| + sb.add(": ");
|
| + }
|
| + sb.add(values[i]);
|
| + }
|
| + sb.add("\n");
|
| + });
|
| + return sb.toString();
|
| + }
|
| +
|
| + Map<String, List<String>> _headers;
|
| +
|
| + String _host;
|
| + int _port;
|
| + Date _expires;
|
| +}
|
| +
|
| +
|
| class _HttpRequestResponseBase {
|
| _HttpRequestResponseBase(_HttpConnectionBase this._httpConnection)
|
| : _contentLength = -1,
|
| - _headers = new Map();
|
| + _headers = new _HttpHeaders();
|
|
|
| int get contentLength() => _contentLength;
|
| - Map get headers() => _headers;
|
| -
|
| - void _setHeader(String name, String value) {
|
| - _headers[name.toLowerCase()] = value;
|
| - }
|
| + HttpHeaders get headers() => _headers;
|
|
|
| bool _write(List<int> data, bool copyBuffer) {
|
| bool allWritten = true;
|
| @@ -56,18 +224,7 @@ class _HttpRequestResponseBase {
|
| }
|
|
|
| bool _writeHeaders() {
|
| - List<int> data;
|
| -
|
| - // Format headers.
|
| - _headers.forEach((String name, String value) {
|
| - data = name.charCodes();
|
| - _httpConnection._write(data);
|
| - data = ": ".charCodes();
|
| - _httpConnection._write(data);
|
| - data = value.charCodes();
|
| - _httpConnection._write(data);
|
| - _writeCRLF();
|
| - });
|
| + _headers._write(_httpConnection);
|
| // Terminate header.
|
| return _writeCRLF();
|
| }
|
| @@ -97,7 +254,7 @@ class _HttpRequestResponseBase {
|
| }
|
|
|
| _HttpConnectionBase _httpConnection;
|
| - Map<String, String> _headers;
|
| + _HttpHeaders _headers;
|
|
|
| // Length of the content body. If this is set to -1 (default value)
|
| // when starting to send data chunked transfer encoding will be
|
| @@ -130,7 +287,7 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest {
|
| }
|
|
|
| void _onHeaderReceived(String name, String value) {
|
| - _setHeader(name, value);
|
| + _headers.add(name, value);
|
| }
|
|
|
| void _onHeadersComplete() {
|
| @@ -219,27 +376,6 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse {
|
| _reasonPhrase = reasonPhrase;
|
| }
|
|
|
| - Date get expires() => _expires;
|
| - void set expires(Date expires) {
|
| - if (_outputStream != null) throw new HttpException("Header already sent");
|
| - _expires = expires;
|
| - // Format "Expires" header with date in Greenwich Mean Time (GMT).
|
| - String formatted =
|
| - _HttpUtils.formatDate(_expires.changeTimeZone(new TimeZone.utc()));
|
| - _setHeader("Expires", formatted);
|
| - }
|
| -
|
| - // Set a header on the response. NOTE: If the same header is set
|
| - // more than once only the last one will be part of the response.
|
| - void setHeader(String name, String value) {
|
| - if (_outputStream != null) return new HttpException("Header already sent");
|
| - if (name.toLowerCase() == "expires") {
|
| - expires = _HttpUtils.parseDate(value);
|
| - } else {
|
| - _setHeader(name, value);
|
| - }
|
| - }
|
| -
|
| OutputStream get outputStream() {
|
| if (_state == DONE) throw new HttpException("Response closed");
|
| if (_outputStream == null) {
|
| @@ -363,16 +499,16 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse {
|
|
|
| // Determine the value of the "Connection" header.
|
| if (_protocolVersion == "1.1" && !_persistentConnection) {
|
| - setHeader("Connection", "close");
|
| + _headers.set("Connection", "close");
|
| } else if (_protocolVersion == "1.0" && _persistentConnection) {
|
| - setHeader("Connection", "keep-alive");
|
| + _headers.set("Connection", "keep-alive");
|
| }
|
| // Determine the value of the "Transfer-Encoding" header based on
|
| // whether the content length is known.
|
| if (_contentLength >= 0) {
|
| - setHeader("Content-Length", _contentLength.toString());
|
| + _headers.set("Content-Length", _contentLength.toString());
|
| } else {
|
| - setHeader("Transfer-Encoding", "chunked");
|
| + _headers.set("Transfer-Encoding", "chunked");
|
| }
|
|
|
| // Write headers.
|
| @@ -385,7 +521,6 @@ class _HttpResponse extends _HttpRequestResponseBase implements HttpResponse {
|
| int _statusCode;
|
| String _reasonPhrase;
|
| String _protocolVersion;
|
| - Date _expires;
|
| bool _persistentConnection;
|
| _HttpOutputStream _outputStream;
|
| int _state;
|
| @@ -733,39 +868,6 @@ class _HttpClientRequest
|
|
|
| void set contentLength(int contentLength) => _contentLength = contentLength;
|
|
|
| - String get host() => _host;
|
| - void set host(String host) {
|
| - _host = host;
|
| - _updateHostHeader();
|
| - }
|
| -
|
| - int get port() => _port;
|
| - void set port(int port) {
|
| - _port = port;
|
| - _updateHostHeader();
|
| - }
|
| -
|
| - void setHeader(String name, String value) {
|
| - if (_state != START) throw new HttpException("Header already sent");
|
| - if (name.toLowerCase() == "host") {
|
| - int pos = value.indexOf(":");
|
| - if (pos == -1) {
|
| - _host = value;
|
| - _port = HttpClient.DEFAULT_HTTP_PORT;
|
| - } else {
|
| - _host = value.substring(0, pos);
|
| - if (pos + 1 == value.length) {
|
| - _port = HttpClient.DEFAULT_HTTP_PORT;
|
| - } else {
|
| - _port = Math.parseInt(value.substring(pos + 1));
|
| - }
|
| - }
|
| - _updateHostHeader();
|
| - return;
|
| - }
|
| - _setHeader(name, value);
|
| - }
|
| -
|
| OutputStream get outputStream() {
|
| if (_state == DONE) throw new HttpException("Request closed");
|
| if (_outputStream == null) {
|
| @@ -778,11 +880,6 @@ class _HttpClientRequest
|
| return _outputStream;
|
| }
|
|
|
| - _updateHostHeader() {
|
| - String portPart = _port == HttpClient.DEFAULT_HTTP_PORT ? "" : ":$_port";
|
| - _setHeader("Host", "$host$portPart");
|
| - }
|
| -
|
| // Delegate functions for the HttpOutputStream implementation.
|
| bool _streamWrite(List<int> buffer, bool copyBuffer) {
|
| return _write(buffer, copyBuffer);
|
| @@ -830,9 +927,9 @@ class _HttpClientRequest
|
| // Determine the value of the "Transfer-Encoding" header based on
|
| // whether the content length is known.
|
| if (_contentLength >= 0) {
|
| - setHeader("Content-Length", _contentLength.toString());
|
| + _headers.set("Content-Length", _contentLength.toString());
|
| } else {
|
| - setHeader("Transfer-Encoding", "chunked");
|
| + _headers.set("Transfer-Encoding", "chunked");
|
| }
|
|
|
| // Write headers.
|
| @@ -842,8 +939,6 @@ class _HttpClientRequest
|
|
|
| String _method;
|
| String _uri;
|
| - String _host;
|
| - int _port;
|
| _HttpClientConnection _connection;
|
| _HttpOutputStream _outputStream;
|
| int _state;
|
| @@ -861,14 +956,6 @@ class _HttpClientResponse
|
| int get statusCode() => _statusCode;
|
| String get reasonPhrase() => _reasonPhrase;
|
|
|
| - Date get expires() {
|
| - String str = _headers["expires"];
|
| - if (str == null) return null;
|
| - return _HttpUtils.parseDate(str);
|
| - }
|
| -
|
| - Map get headers() => _headers;
|
| -
|
| InputStream get inputStream() {
|
| if (_inputStream == null) {
|
| _inputStream = new _HttpInputStream(this);
|
| @@ -886,7 +973,7 @@ class _HttpClientResponse
|
| }
|
|
|
| void _onHeaderReceived(String name, String value) {
|
| - _setHeader(name, value);
|
| + _headers.add(name, value);
|
| }
|
|
|
| void _onHeadersComplete() {
|
| @@ -1149,8 +1236,8 @@ class _HttpClient implements HttpClient {
|
| _HttpClientConnection connection) {
|
| connection._connectionEstablished(socketConn);
|
| HttpClientRequest request = connection.open(method, path);
|
| - request.host = host;
|
| - request.port = port;
|
| + request.headers.host = host;
|
| + request.headers.port = port;
|
| if (connection._onRequest != null) {
|
| connection._onRequest(request);
|
| } else {
|
|
|