Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Unified Diff: sdk/lib/io/http_impl.dart

Issue 12328114: Rewrite the Future-pipeline from the Socket to the HttpClientRequest/HttpResponse. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add missing test file. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/bin/socket_patch.dart ('k') | sdk/lib/io/io_stream_consumer.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/http_impl.dart
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index a4bfe397d463791166dcdeb7e4700f5d7d1af1c7..3c81acbf092923b9ef375fbb3bf0b2d8ed8c7582 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -738,17 +738,12 @@ class _DoneTransformer implements StreamTransformer<List<int>, List<int>> {
// Transformer that validates the data written.
class _DataValidatorTransformer
implements StreamTransformer<List<int>, List<int>> {
- final StreamController<List<int>> _controller
- = new StreamController<List<int>>();
+ final StreamController<List<int>> _controller =
+ new StreamController<List<int>>();
int _bytesWritten = 0;
- Completer _completer = new Completer();
int expectedTransferLength;
- _DataValidatorTransformer();
-
- Future get validatorFuture => _completer.future;
-
Stream<List<int>> bind(Stream<List<int>> stream) {
var subscription;
subscription = stream.listen(
@@ -756,45 +751,31 @@ class _DataValidatorTransformer
if (expectedTransferLength != null) {
_bytesWritten += data.length;
if (_bytesWritten > expectedTransferLength) {
- _controller.close();
subscription.cancel();
- if (_completer != null) {
- _completer.completeError(new HttpException(
- "Content size exceeds specified contentLength. "
- "$_bytesWritten bytes written while expected "
- "$expectedTransferLength."));
- _completer = null;
- }
+ _controller.signalError(new HttpException(
+ "Content size exceeds specified contentLength. "
+ "$_bytesWritten bytes written while expected "
+ "$expectedTransferLength."));
+ _controller.close();
return;
}
}
_controller.add(data);
},
onError: (error) {
+ _controller.signalError(error);
_controller.close();
- if (_completer != null) {
- _completer.completeError(error);
- _completer = null;
- }
},
onDone: () {
- _controller.close();
if (expectedTransferLength != null) {
if (_bytesWritten < expectedTransferLength) {
- if (_completer != null) {
- _completer.completeError(new HttpException(
- "Content size below specified contentLength. "
- " $_bytesWritten bytes written while expected "
- "$expectedTransferLength."));
- _completer = null;
- return;
- }
+ _controller.signalError(new HttpException(
+ "Content size below specified contentLength. "
+ " $_bytesWritten bytes written while expected "
+ "$expectedTransferLength."));
}
}
- if (_completer != null) {
- _completer.complete(this);
- _completer = null;
- }
+ _controller.close();
},
unsubscribeOnError: true);
return _controller.stream;
@@ -803,31 +784,25 @@ class _DataValidatorTransformer
// Extends StreamConsumer as this is an internal type, only used to pipe to.
class _HttpOutgoing implements StreamConsumer<List<int>, dynamic> {
- final Completer _dataCompleter = new Completer();
- final Completer _streamCompleter = new Completer();
final _DataValidatorTransformer _validator = new _DataValidatorTransformer();
+ Function _onStream;
+ final Completer _consumeCompleter = new Completer();
- // Future that completes when all data is written.
- Future get dataDone => _dataCompleter.future;
-
- // Future that completes with the Stream, once the _HttpClientConnection is
- // bound to one.
- Future<Stream<List<int>>> get stream => _streamCompleter.future;
+ Future onStream(Future callback(Stream<List<int>> stream)) {
+ _onStream = callback;
+ return _consumeCompleter.future;
+ }
void setTransferLength(int transferLength) {
_validator.expectedTransferLength = transferLength;
}
Future consume(Stream<List<int>> stream) {
- stream = stream.transform(_validator);
- _streamCompleter.complete(stream);
- _validator.validatorFuture.catchError((e) {
- _dataCompleter.completeError(e);
- });
- return _validator.validatorFuture.then((v) {
- _dataCompleter.complete();
- return v;
- });
+ _onStream(stream.transform(_validator))
+ .then((_) => _consumeCompleter.complete(),
+ onError: _consumeCompleter.completeError);
+ // Use .then to ensure a Future branch.
+ return _consumeCompleter.future.then((_) => this);
}
}
@@ -840,7 +815,7 @@ class _HttpClientConnection {
final _HttpClient _httpClient;
Completer<_HttpIncoming> _nextResponseCompleter;
- Future _writeDoneFuture;
+ Future _streamFuture;
_HttpClientConnection(String this.key,
Socket this._socket,
@@ -870,37 +845,53 @@ class _HttpClientConnection {
});
}
- Future<_HttpIncoming> sendRequest(_HttpOutgoing outgoing) {
- return outgoing.stream
- .then((stream) {
- // Close socket if output data is invalid.
- outgoing.dataDone.catchError((e) {
- close();
- });
+ _HttpClientRequest send(Uri uri, int port, String method, bool isDirect) {
+ var outgoing = new _HttpOutgoing();
+ // Create new request object, wrapping the outgoing connection.
+ var request = new _HttpClientRequest(outgoing,
+ uri,
+ method,
+ !isDirect,
+ _httpClient,
+ this);
+ request.headers.host = uri.domain;
+ request.headers.port = port;
+ if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+ // If the URL contains user information use that for basic
+ // authorization
+ String auth =
+ CryptoUtils.bytesToBase64(_encodeString(uri.userInfo));
+ request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
+ } else {
+ // Look for credentials.
+ _Credentials cr = _httpClient._findCredentials(uri);
+ if (cr != null) {
+ cr.authorize(request);
+ }
+ }
+ // Start sending the request (lazy, delayed until the user provides
+ // data).
+ _httpParser.responseToMethod = method;
+ _streamFuture = outgoing.onStream((stream) {
// Sending request, set up response completer.
_nextResponseCompleter = new Completer();
- _writeDoneFuture = _socket.addStream(stream);
// Listen for response.
- return _nextResponseCompleter.future
+ _nextResponseCompleter.future
.whenComplete(() {
_nextResponseCompleter = null;
})
.then((incoming) {
incoming.dataDone.then((_) {
- if (!incoming.headers.persistentConnection) {
- close();
+ if (incoming.headers.persistentConnection &&
+ request.persistentConnection) {
+ _subscription.resume();
+ // Return connection, now we are done.
+ _httpClient._returnConnection(this);
} else {
- // Wait for the socket to be done with writing, before we
- // continue.
- _writeDoneFuture.then((_) {
- _subscription.resume();
- // Return connection, now we are done.
- _httpClient._returnConnection(this);
- });
+ destroy();
}
});
- // TODO(ajohnsen): Can there be an error on dataDone?
- return incoming;
+ request._onIncoming(incoming);
})
// If we see a state error, we failed to get the 'first' element.
// Transform the error to a HttpParserException, for consistency.
@@ -911,32 +902,36 @@ class _HttpClientConnection {
.catchError((error) {
// We are done with the socket.
destroy();
- throw error;
+ request._onError(error);
});
- });
+
+ return _socket.addStream(stream)
+ .catchError((e) {
+ destroy();
+ if (e.error is HttpException) throw e;
+ // TODO(ajohnsen): Where to send Socket errors?
+ });
+ });
+ return request;
}
Future<Socket> detachSocket() {
- return _writeDoneFuture.then((_) =>
- new _DetachedSocket(_socket, _httpParser.detachIncoming()));
+ return _streamFuture
+ .then((_) => new _DetachedSocket(_socket, _httpParser.detachIncoming()),
+ onError: (_) {});
}
void destroy() {
- _socket.destroy();
_httpClient._connectionClosed(this);
+ _socket.destroy();
}
void close() {
- var future = _writeDoneFuture;
- if (future == null) future = new Future.immediate(null);
_httpClient._connectionClosed(this);
- future.then((_) {
- _socket.close();
- // TODO(ajohnsen): Add timeout.
- // Delay destroy until socket is actually done writing.
- _socket.done.then((_) => _socket.destroy(),
- onError: (_) => _socket.destroy());
- });
+ _streamFuture
+ // TODO(ajohnsen): Add timeout.
+ .then((_) => _socket.destroy(),
+ onError: (_) {});
}
HttpConnectionInfo get connectionInfo => _HttpConnectionInfo.create(_socket);
@@ -1055,48 +1050,12 @@ class _HttpClient implements HttpClient {
return new Future.immediateError(error, stackTrace);
}
}
- return _getConnection(uri.domain, port, proxyConf, isSecure).then((info) {
- // Create new internal outgoing connection.
- var outgoing = new _HttpOutgoing();
- // Create new request object, wrapping the outgoing connection.
- var request = new _HttpClientRequest(outgoing,
- uri,
- method.toUpperCase(),
- !info.proxy.isDirect,
- this,
- info.connection);
- request.headers.host = uri.domain;
- request.headers.port = port;
- if (uri.userInfo != null && !uri.userInfo.isEmpty) {
- // If the URL contains user information use that for basic
- // authorization
- String auth =
- CryptoUtils.bytesToBase64(_encodeString(uri.userInfo));
- request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
- } else {
- // Look for credentials.
- _Credentials cr = _findCredentials(uri);
- if (cr != null) {
- cr.authorize(request);
- }
- }
- // Start sending the request (lazy, delayed until the user provides
- // data).
- info.connection._httpParser.responseToMethod = method;
- info.connection.sendRequest(outgoing)
- .then((incoming) {
- // The full request have been sent and a response is received
- // containing status-code, headers and etc.
- request._onIncoming(incoming);
- })
- .catchError((error) {
- // An error occoured before the http-header was parsed. This
- // could be either a socket-error or parser-error.
- request._onError(error);
- });
- // Return the request to the user. Immediate socket errors are not
- // handled, thus forwarded to the user.
- return request;
+ return _getConnection(uri.domain, port, proxyConf, isSecure)
+ .then((info) {
+ return info.connection.send(uri,
+ port,
+ method.toUpperCase(),
+ info.proxy.isDirect);
});
}
@@ -1220,7 +1179,7 @@ class _HttpConnection {
final _HttpParser _httpParser;
StreamSubscription _subscription;
- Future _writeDoneFuture;
+ Future _streamFuture;
_HttpConnection(Socket this._socket, _HttpServer this._httpServer)
: _httpParser = new _HttpParser.requestParser() {
@@ -1233,37 +1192,38 @@ class _HttpConnection {
_subscription.pause();
_state = _ACTIVE;
var outgoing = new _HttpOutgoing();
- _writeDoneFuture = outgoing.stream.then(_socket.addStream);
- var response = new _HttpResponse(
- incoming.headers.protocolVersion,
- outgoing);
+ var response = new _HttpResponse(incoming.headers.protocolVersion,
+ outgoing);
var request = new _HttpRequest(response, incoming, _httpServer, this);
+ outgoing.onStream((stream) {
+ return _streamFuture = _socket.addStream(stream)
+ .then((_) {
+ if (_state == _DETACHED) return;
+ if (response.persistentConnection &&
+ request.persistentConnection &&
+ incoming.fullBodyRead) {
+ _state = _IDLE;
+ // Resume the subscription for incoming requests as the
+ // request is now processed.
+ _subscription.resume();
+ } else {
+ // Close socket, keep-alive not used or body sent before
+ // received data was handled.
+ destroy();
+ }
+ })
+ .catchError((e) {
+ destroy();
+ if (e.error is HttpException) throw e;
+ // TODO(ajohnsen): Where to send Socket errors?
+ });
+ });
response._ignoreBody = request.method == "HEAD";
response._httpRequest = request;
- outgoing.dataDone.then((_) {
- if (_state == _DETACHED) return;
- if (response.headers.persistentConnection &&
- incoming.fullBodyRead) {
- // Wait for the socket to be done with writing, before we
- // continue.
- _writeDoneFuture.then((_) {
- _state = _IDLE;
- // Resume the subscription for incoming requests as the
- // request is now processed.
- _subscription.resume();
- });
- } else {
- // Close socket, keep-alive not used or body sent before received
- // data was handled.
- close();
- }
- }).catchError((e) {
- close();
- });
_httpServer._handleRequest(request);
},
onDone: () {
- close();
+ destroy();
},
onError: (error) {
_httpServer._handleError(error);
@@ -1278,21 +1238,6 @@ class _HttpConnection {
_httpServer._connectionClosed(this);
}
- void close() {
- if (_state == _CLOSING || _state == _DETACHED) return;
- _state = _CLOSING;
- var future = _writeDoneFuture;
- if (future == null) future = new Future.immediate(null);
- _httpServer._connectionClosed(this);
- future.then((_) {
- _socket.close();
- // TODO(ajohnsen): Add timeout.
- // Delay destroy until socket is actually done writing.
- _socket.done.then((_) => _socket.destroy(),
- onError: (_) => _socket.destroy());
- });
- }
-
Future<Socket> detachSocket() {
_state = _DETACHED;
// Remove connection from server.
@@ -1300,7 +1245,7 @@ class _HttpConnection {
_HttpDetachedIncoming detachedIncoming = _httpParser.detachIncoming();
- return _writeDoneFuture.then((_) {
+ return _streamFuture.then((_) {
return new _DetachedSocket(_socket, detachedIncoming);
});
}
« no previous file with comments | « runtime/bin/socket_patch.dart ('k') | sdk/lib/io/io_stream_consumer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698