Index: sdk/lib/io/secure_server_socket.dart |
diff --git a/sdk/lib/io/secure_server_socket.dart b/sdk/lib/io/secure_server_socket.dart |
index 7c8d78f98800e57bb0a6e1830dba43e345612311..7a9b34d7c203a0088b3923b29556bd8c9b8f847a 100644 |
--- a/sdk/lib/io/secure_server_socket.dart |
+++ b/sdk/lib/io/secure_server_socket.dart |
@@ -4,97 +4,232 @@ |
part of dart.io; |
-abstract class SecureServerSocket implements ServerSocket { |
+/** |
+ * The [SecureServerSocket] is a server socket, providing a stream of high-level |
+ * [Socket]s. |
+ * |
+ * See [SecureSocket] for more info. |
+ */ |
+class SecureServerSocket extends Stream<SecureSocket> implements ServerSocket { |
+ final RawSecureServerSocket _socket; |
+ |
+ SecureServerSocket._(RawSecureServerSocket this._socket); |
+ |
/** |
- * Constructs a new secure server socket, binds it to a given address |
- * and port, and listens on it. Incoming client connections are |
- * promoted to secure connections, using the server certificate given by |
- * certificate_name. The bindAddress must be given as a numeric address, |
- * not a host name. The certificate name is the distinguished name (DN) of |
- * the certificate, such as "CN=localhost" or "CN=myserver.mydomain.com". |
- * The certificate is looked up in the NSS certificate database set by |
- * SecureSocket.setCertificateDatabase. |
+ * Returns a future for a [SecureServerSocket]. When the future |
+ * completes the server socket is bound to the given [address] and |
+ * [port] and has started listening on it. |
+ * |
+ * If [port] has the value [:0:] (the default) an ephemeral port will |
+ * be chosen by the system. The actual port used can be retrieved |
+ * using the [port] getter. |
+ * |
+ * If [backlog] has the value of [:0:] a reasonable value will be |
+ * chosen by the system. |
+ * |
+ * Incoming client connections are promoted to secure connections, using |
+ * the server certificate given by [certificateName]. |
+ * |
+ * [address] must be given as a numeric address, not a host name. |
+ * |
+ * [certificateName] is the nickname or the distinguished name (DN) of |
+ * the certificate in the certificate database. It is looked up in the |
+ * NSS certificate database set by SecureSocket.setCertificateDatabase. |
+ * If [certificateName] contains "CN=", it is assumed to be a distinguished |
+ * name. Otherwise, it is looked up as a nickname. |
* |
* To request or require that clients authenticate by providing an SSL (TLS) |
- * client certificate, set the optional parameters requestClientCertificate or |
- * requireClientCertificate to true. Require implies request, so one doesn't |
- * need to specify both. To check whether a client certificate was received, |
- * check SecureSocket.peerCertificate after connecting. If no certificate |
+ * client certificate, set the optional parameter [requestClientCertificate] |
+ * or [requireClientCertificate] to true. Requiring a certificate implies |
+ * requesting a certificate, so one doesn't need to set both to true. |
+ * To check whether a client certificate was received, check |
+ * SecureSocket.peerCertificate after connecting. If no certificate |
* was received, the result will be null. |
*/ |
- factory SecureServerSocket(String bindAddress, |
- int port, |
- int backlog, |
- String certificate_name, |
- {bool requestClientCertificate: false, |
- bool requireClientCertificate: false}) { |
- return new _SecureServerSocket(bindAddress, |
- port, |
- backlog, |
- certificate_name, |
- requestClientCertificate, |
- requireClientCertificate); |
+ static Future<SecureServerSocket> bind( |
+ String address, |
+ int port, |
+ int backlog, |
+ String certificateName, |
+ {bool requestClientCertificate: false, |
+ bool requireClientCertificate: false}) { |
+ return RawSecureServerSocket.bind( |
+ address, |
+ port, |
+ backlog, |
+ certificateName, |
+ requestClientCertificate: requestClientCertificate, |
+ requireClientCertificate: requireClientCertificate).then( |
+ (serverSocket) => new SecureServerSocket._(serverSocket)); |
+ } |
+ |
+ StreamSubscription<SecureSocket> listen(void onData(SecureSocket socket), |
+ {void onError(AsyncError error), |
+ void onDone(), |
+ bool unsubscribeOnError}) { |
+ return _socket.map((rawSocket) => new SecureSocket._(rawSocket)) |
+ .listen(onData, |
+ onError: onError, |
+ onDone: onDone, |
+ unsubscribeOnError: unsubscribeOnError); |
} |
+ |
+ /** |
+ * Returns the port used by this socket. |
+ */ |
+ int get port => _socket.port; |
+ |
+ /** |
+ * Closes the socket. |
+ */ |
+ void close() => _socket.close(); |
} |
-class _SecureServerSocket implements SecureServerSocket { |
+/** |
+ * The RawSecureServerSocket is a server socket, providing a stream of low-level |
+ * [RawSecureSocket]s. |
+ * |
+ * See [RawSecureSocket] for more info. |
+ */ |
+class RawSecureServerSocket extends Stream<RawSecureSocket> { |
+ RawServerSocket _socket; |
+ StreamController<RawSecureSocket> _controller; |
+ StreamSubscription<RawSocket> _subscription; |
+ final String certificateName; |
+ final bool requestClientCertificate; |
+ final bool requireClientCertificate; |
+ bool _closed = false; |
- _SecureServerSocket(String bindAddress, |
- int port, |
- int backlog, |
- String this.certificate_name, |
- bool this.requestClientCertificate, |
- bool this.requireClientCertificate) { |
- socket = new ServerSocket(bindAddress, port, backlog); |
- socket.onConnection = this._onConnectionHandler; |
+ RawSecureServerSocket._(RawServerSocket serverSocket, |
+ String this.certificateName, |
+ bool this.requestClientCertificate, |
+ bool this.requireClientCertificate) { |
+ _socket = serverSocket; |
+ _controller = new StreamController<RawSecureSocket>( |
+ onPauseStateChange: _onPauseStateChange, |
+ onSubscriptionStateChange: _onSubscriptionStateChange); |
} |
- void set onConnection(void callback(Socket connection)) { |
- _onConnectionCallback = callback; |
+ /** |
+ * Returns a future for a [RawSecureServerSocket]. When the future |
+ * completes the server socket is bound to the given [address] and |
+ * [port] and has started listening on it. |
+ * |
+ * If [port] has the value [:0:] (the default) an ephemeral port will |
+ * be chosen by the system. The actual port used can be retrieved |
+ * using the [port] getter. |
+ * |
+ * If [backlog] has the value of [:0:] a reasonable value will be |
+ * chosen by the system. |
+ * |
+ * Incoming client connections are promoted to secure connections, |
+ * using the server certificate given by [certificateName]. |
+ * |
+ * [address] must be given as a numeric address, not a host name. |
+ * |
+ * [certificateName] is the nickname or the distinguished name (DN) of |
+ * the certificate in the certificate database. It is looked up in the |
+ * NSS certificate database set by SecureSocket.setCertificateDatabase. |
+ * If [certificateName] contains "CN=", it is assumed to be a distinguished |
+ * name. Otherwise, it is looked up as a nickname. |
+ * |
+ * To request or require that clients authenticate by providing an SSL (TLS) |
+ * client certificate, set the optional parameters requestClientCertificate or |
+ * requireClientCertificate to true. Require implies request, so one doesn't |
+ * need to specify both. To check whether a client certificate was received, |
+ * check SecureSocket.peerCertificate after connecting. If no certificate |
+ * was received, the result will be null. |
+ */ |
+ static Future<RawSecureServerSocket> bind( |
+ String address, |
+ int port, |
+ int backlog, |
+ String certificateName, |
+ {bool requestClientCertificate: false, |
+ bool requireClientCertificate: false}) { |
+ return RawServerSocket.bind(address, port, backlog) |
+ .then((serverSocket) => new RawSecureServerSocket._( |
+ serverSocket, |
+ certificateName, |
+ requestClientCertificate, |
+ requireClientCertificate)); |
} |
- void set onError(void callback(e)) { |
- socket.onError = callback; |
+ StreamSubscription<RawSecureSocket> listen(void onData(RawSecureSocket s), |
+ {void onError(AsyncError error), |
+ void onDone(), |
+ bool unsubscribeOnError}) { |
+ return _controller.stream.listen(onData, |
+ onError: onError, |
+ onDone: onDone, |
+ unsubscribeOnError: unsubscribeOnError); |
} |
/** |
* Returns the port used by this socket. |
*/ |
- int get port => socket.port; |
+ int get port => _socket.port; |
/** |
* Closes the socket. |
*/ |
void close() { |
- socket.close(); |
+ _closed = true; |
+ _socket.close(); |
} |
- void _onConnectionHandler(Socket connection) { |
- if (_onConnectionCallback == null) { |
- connection.close(); |
- throw new SocketIOException( |
- "SecureServerSocket with no onConnection callback connected to"); |
- } |
- if (certificate_name == null) { |
- connection.close(); |
- throw new SocketIOException( |
- "SecureServerSocket with server certificate not set connected to"); |
- } |
- var secure_connection = new _SecureSocket( |
+ void _onData(RawSocket connection) { |
+ _RawSecureSocket.connect( |
connection.remoteHost, |
connection.remotePort, |
- certificate_name, |
+ certificateName, |
is_server: true, |
socket: connection, |
requestClientCertificate: requestClientCertificate, |
- requireClientCertificate: requireClientCertificate); |
- _onConnectionCallback(secure_connection); |
+ requireClientCertificate: requireClientCertificate) |
+ .then((RawSecureSocket secureConnection) { |
+ if (_closed) { |
+ secureConnection.close(); |
+ } else { |
+ _controller.add(secureConnection); |
+ } |
+ }).catchError((e) { |
+ if (_closed) { |
+ throw e; |
+ } else { |
+ _controller.signalError(e); |
+ close(); |
+ } |
+ }); |
} |
- ServerSocket socket; |
- var _onConnectionCallback; |
- final String certificate_name; |
- final bool requestClientCertificate; |
- final bool requireClientCertificate; |
+ void _onError(e) { |
+ _controller.signalError(e); |
+ close(); |
+ } |
+ |
+ void _onDone() { |
+ _controller.close(); |
+ } |
+ |
+ void _onPauseStateChange() { |
+ if (_controller.isPaused) { |
+ _subscription.pause(); |
+ } else { |
+ _subscription.resume(); |
+ } |
+ } |
+ |
+ void _onSubscriptionStateChange() { |
+ if (_controller.hasSubscribers) { |
+ _subscription = _socket.listen(_onData, |
+ onDone: _onDone, |
+ onError: _onError); |
+ } else { |
+ close(); |
+ } |
+ } |
} |
+ |
+ |