Chromium Code Reviews| Index: runtime/bin/http_impl.dart |
| diff --git a/runtime/bin/http_impl.dart b/runtime/bin/http_impl.dart |
| index eec6b398b243fb45040d388eb4e94c55fd99a4aa..935781e1f784bfbe611a16bd3b3f7796486527af 100644 |
| --- a/runtime/bin/http_impl.dart |
| +++ b/runtime/bin/http_impl.dart |
| @@ -846,6 +846,7 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
| InputStream get inputStream { |
| if (_inputStream == null) { |
| _inputStream = new _HttpInputStream(this); |
| + _inputStream._streamMarkedClosed = _dataEndCalled; |
| } |
| return _inputStream; |
| } |
| @@ -875,6 +876,7 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
| void _onDataEnd() { |
| if (_inputStream != null) _inputStream._closeReceived(); |
| + _dataEndCalled = true; |
| } |
| // Escaped characters in uri are expected to have been parsed. |
| @@ -917,6 +919,7 @@ class _HttpRequest extends _HttpRequestResponseBase implements HttpRequest { |
| Map<String, String> _queryParameters; |
| _HttpInputStream _inputStream; |
| _BufferList _buffer; |
| + bool _dataEndCalled = false; |
| Function _streamErrorHandler; |
| } |
| @@ -1544,7 +1547,7 @@ class _HttpServer implements HttpServer { |
| class _HttpClientRequest |
| extends _HttpRequestResponseBase implements HttpClientRequest { |
| _HttpClientRequest(String this._method, |
| - String this._uri, |
| + Uri this._uri, |
| _HttpClientConnection connection) |
| : super(connection) { |
| _connection = connection; |
| @@ -1613,7 +1616,23 @@ class _HttpClientRequest |
| data = _method.toString().charCodes(); |
| _httpConnection._write(data); |
| _writeSP(); |
| - data = _uri.toString().charCodes(); |
| + // Send the path for direct connections and the whole URL for |
| + // proxy connections. |
| + if (!_connection._usingProxy) { |
| + String path; |
| + if (_uri.query != "") { |
| + if (_uri.fragment != "") { |
| + path = "${_uri.path}?${_uri.query}#${_uri.fragment}"; |
| + } else { |
| + path = "${_uri.path}?${_uri.query}"; |
| + } |
| + } else { |
| + path = _uri.path; |
| + } |
| + data = path.charCodes(); |
| + } else { |
| + data = _uri.toString().charCodes(); |
| + } |
| _httpConnection._write(data); |
| _writeSP(); |
| _httpConnection._write(_Const.HTTP11); |
| @@ -1646,7 +1665,7 @@ class _HttpClientRequest |
| } |
| String _method; |
| - String _uri; |
| + Uri _uri; |
| _HttpClientConnection _connection; |
| _HttpOutputStream _outputStream; |
| Function _streamErrorHandler; |
| @@ -1685,6 +1704,7 @@ class _HttpClientResponse |
| InputStream get inputStream { |
| if (_inputStream == null) { |
| _inputStream = new _HttpInputStream(this); |
| + _inputStream._streamMarkedClosed = _dataEndCalled; |
| } |
| return _inputStream; |
| } |
| @@ -1746,6 +1766,7 @@ class _HttpClientResponse |
| void _onDataEnd() { |
| _connection._responseDone(); |
| if (_inputStream != null) _inputStream._closeReceived(); |
| + _dataEndCalled = true; |
| } |
| // Delegate functions for the HttpInputStream implementation. |
| @@ -1773,6 +1794,8 @@ class _HttpClientResponse |
| _HttpClientConnection _connection; |
| _HttpInputStream _inputStream; |
| _BufferList _buffer; |
| + bool _dataEndCalled = false; |
| + |
| Function _streamErrorHandler; |
| } |
| @@ -1810,7 +1833,7 @@ class _HttpClientConnection |
| _socketConn = null; |
| } |
| - HttpClientRequest open(String method, String uri) { |
| + HttpClientRequest open(String method, Uri uri) { |
| _method = method; |
| // Tell the HTTP parser the method it is expecting a response to. |
| _httpParser.responseToMethod = method; |
| @@ -1913,6 +1936,7 @@ class _HttpClientConnection |
| HttpClientRequest _request; |
| HttpClientResponse _response; |
| String _method; |
| + bool _usingProxy; |
| // Redirect handling |
| bool followRedirects = true; |
| @@ -1948,6 +1972,58 @@ class _SocketConnection { |
| Date _returnTime; |
| } |
| +class _ProxyConfiguration { |
| + static const String PROXY_PREFIX = "PROXY "; |
| + static const String DIRECT_PREFIX = "DIRECT"; |
| + |
| + _ProxyConfiguration(String configuration) : proxies = new List<_Proxy>() { |
|
Mads Ager (google)
2012/10/08 16:59:39
Funky indentation.
Søren Gjesse
2012/10/09 14:05:22
Done.
|
| + if (configuration == null) { |
| + throw new HttpException("Invalid proxy configuration $configuration"); |
| + } |
| + List<String> list = configuration.split(";"); |
| + list.forEach((String proxy) { |
| + proxy = proxy.trim(); |
| + if (!proxy.isEmpty()) { |
| + if (proxy.startsWith(PROXY_PREFIX)) { |
| + int colon = proxy.indexOf(":"); |
| + if (colon == -1 || colon == 0 || colon == proxy.length - 1) { |
| + throw new HttpException( |
| + "Invalid proxy configuration $configuration"); |
| + } |
| + // Skip the "PROXY " prefix. |
| + String host = proxy.substring(PROXY_PREFIX.length, colon).trim(); |
| + String portString = proxy.substring(colon + 1).trim(); |
| + int port; |
| + try { |
| + port = int.parse(portString); |
| + } on FormatException catch (e) { |
| + throw new HttpException( |
| + "Invalid proxy configuration $configuration, " |
| + "invalid port '$portString'"); |
| + } |
| + proxies.add(new _Proxy(host, port)); |
| + } else if (proxy.trim() == DIRECT_PREFIX) { |
| + proxies.add(new _Proxy.direct()); |
| + } else { |
| + throw new HttpException("Invalid proxy configuration $configuration"); |
| + } |
| + } |
| + }); |
| + } |
| + const _ProxyConfiguration.direct() |
|
Mads Ager (google)
2012/10/08 16:59:39
Maybe add a space between the constructors.
Søren Gjesse
2012/10/09 14:05:22
Done.
|
| + : proxies = [const _Proxy.direct()]; |
| + |
| + final List<_Proxy> proxies; |
| +} |
| + |
| +class _Proxy { |
| + const _Proxy(this.host, this.port) : direct = false; |
| + const _Proxy.direct() : host = null, port = null, direct = true; |
| + |
| + final String host; |
| + final int port; |
| + final bool direct; |
| +} |
| class _HttpClient implements HttpClient { |
| static const int DEFAULT_EVICTION_TIMEOUT = 60000; |
| @@ -1975,7 +2051,7 @@ class _HttpClient implements HttpClient { |
| } |
| HttpClientConnection openUrl(String method, Uri url) { |
| - _openUrl(method, url); |
| + return _openUrl(method, url); |
| } |
| HttpClientConnection _openUrl(String method, |
| @@ -2002,6 +2078,8 @@ class _HttpClient implements HttpClient { |
| HttpClientConnection postUrl(Uri url) => _openUrl("POST", url); |
| + set findProxy(String f(Uri uri)) => _findProxy = f; |
| + |
| void shutdown() { |
| _openSockets.forEach((String key, Queue<_SocketConnection> connections) { |
| while (!connections.isEmpty()) { |
| @@ -2034,19 +2112,11 @@ class _HttpClient implements HttpClient { |
| int port = url.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : url.port; |
| void _connectionOpened(_SocketConnection socketConn, |
| - _HttpClientConnection connection) { |
| + _HttpClientConnection connection, |
| + bool usingProxy) { |
| + connection._usingProxy = usingProxy; |
| connection._connectionEstablished(socketConn); |
| - String path; |
| - if (url.query != "") { |
| - if (url.fragment != "") { |
| - path = "${url.path}?${url.query}#${url.fragment}"; |
| - } else { |
| - path = "${url.path}?${url.query}"; |
| - } |
| - } else { |
| - path = url.path; |
| - } |
| - HttpClientRequest request = connection.open(method, path); |
| + HttpClientRequest request = connection.open(method, url); |
| request.headers.host = host; |
| request.headers.port = port; |
| if (connection._onRequest != null) { |
| @@ -2062,12 +2132,32 @@ class _HttpClient implements HttpClient { |
| } |
| connection.onDetach = () => _activeSockets.remove(connection._socketConn); |
| + // Check to see if a proxy server should be used for this connection. |
| + _ProxyConfiguration proxyConfiguration = const _ProxyConfiguration.direct(); |
| + if (_findProxy != null) { |
| + // TODO(sgjesse): Keep a map of these as normally only a few |
| + // configuration strings will be used. |
| + proxyConfiguration = new _ProxyConfiguration(_findProxy(url)); |
| + } |
| + |
| + // Determine the actual host to connect to. |
| + String connectHost; |
| + int connectPort; |
| + _Proxy proxy = proxyConfiguration.proxies[0]; |
| + if (proxy.direct) { |
| + connectHost = host; |
| + connectPort = port; |
| + } else { |
| + connectHost = proxy.host; |
| + connectPort = proxy.port; |
| + } |
| + |
| // If there are active connections for this key get the first one |
| // otherwise create a new one. |
| - String key = _connectionKey(host, port); |
| + String key = _connectionKey(connectHost, connectPort); |
| Queue socketConnections = _openSockets[key]; |
| if (socketConnections == null || socketConnections.isEmpty()) { |
| - Socket socket = new Socket(host, port); |
| + Socket socket = new Socket(connectHost, connectPort); |
| // Until the connection is established handle connection errors |
| // here as the HttpClientConnection object is not yet associated |
| // with the socket. |
| @@ -2083,14 +2173,19 @@ class _HttpClient implements HttpClient { |
| // the connected socket. |
| socket.onError = null; |
| _SocketConnection socketConn = |
| - new _SocketConnection(host, port, socket); |
| + new _SocketConnection(connectHost, connectPort, socket); |
| _activeSockets.add(socketConn); |
| - _connectionOpened(socketConn, connection); |
| + _connectionOpened(socketConn, |
| + connection, |
| + !proxyConfiguration.proxies[0].direct); |
|
Mads Ager (google)
2012/10/08 16:59:39
Just !proxy.direct?
Søren Gjesse
2012/10/09 14:05:22
Already fixed.
|
| }; |
| } else { |
| _SocketConnection socketConn = socketConnections.removeFirst(); |
| _activeSockets.add(socketConn); |
| - new Timer(0, (ignored) => _connectionOpened(socketConn, connection)); |
| + new Timer(0, (ignored) => |
| + _connectionOpened(socketConn, |
| + connection, |
| + !proxyConfiguration.proxies[0].direct)); |
|
Mads Ager (google)
2012/10/08 16:59:39
!proxy.direct.
Søren Gjesse
2012/10/09 14:05:22
Already fixed.
|
| // Get rid of eviction timer if there are no more active connections. |
| if (socketConnections.isEmpty()) _openSockets.remove(key); |