| Index: sdk/lib/io/secure_socket.dart | 
| diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart | 
| index a8b0eaafacf7ff0ba81e3d648d944444af1adcc9..8733f32dd3e58c896d87065f67f0f658c65a442e 100644 | 
| --- a/sdk/lib/io/secure_socket.dart | 
| +++ b/sdk/lib/io/secure_socket.dart | 
| @@ -150,16 +150,18 @@ abstract class SecureSocket implements Socket { | 
| X509Certificate get peerCertificate; | 
|  | 
| /** | 
| -   * Initializes the NSS library.  If [initialize] is not called, the library | 
| +   * Initializes the NSS library. If [initialize] is not called, the library | 
| * is automatically initialized as if [initialize] were called with no | 
| -   * arguments. | 
| +   * arguments. If [initialize] is called more than once, or called after | 
| +   * automatic initialization has happened (when a secure connection is made), | 
| +   * then a TlsException is thrown. | 
| * | 
| * The optional argument [database] is the path to a certificate database | 
| * directory containing root certificates for verifying certificate paths on | 
| * client connections, and server certificates to provide on server | 
| -   * connections.  The argument [password] should be used when creating | 
| +   * connections. The argument [password] should be used when creating | 
| * secure server sockets, to allow the private key of the server | 
| -   * certificate to be fetched.  If [useBuiltinRoots] is true (the default), | 
| +   * certificate to be fetched. If [useBuiltinRoots] is true (the default), | 
| * then a built-in set of root certificates for trusted certificate | 
| * authorities is merged with the certificates in the database. | 
| * The list of built-in root certificates, and documentation about this | 
| @@ -167,7 +169,7 @@ abstract class SecureSocket implements Socket { | 
| * http://www.mozilla.org/projects/security/certs/included/ . | 
| * | 
| * If the [database] argument is omitted, then only the | 
| -   * builtin root certificates are used.  If [useBuiltinRoots] is also false, | 
| +   * builtin root certificates are used. If [useBuiltinRoots] is also false, | 
| * then no certificates are available. | 
| * | 
| * Examples: | 
| @@ -437,6 +439,9 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| bool sendClientCertificate: false, | 
| bool onBadCertificate(X509Certificate certificate)}) { | 
| var future; | 
| +    _verifyFields(host, requestedPort, certificateName, is_server, | 
| +                 requestClientCertificate, requireClientCertificate, | 
| +                 sendClientCertificate, onBadCertificate); | 
| if (host is String) { | 
| if (socket != null) { | 
| future = new Future.value( | 
| @@ -484,7 +489,6 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| _stream = _controller.stream; | 
| // Throw an ArgumentError if any field is invalid.  After this, all | 
| // errors will be reported through the future or the stream. | 
| -    _verifyFields(); | 
| _secureFilter.init(); | 
| _filterPointer = _secureFilter._pointer(); | 
| _secureFilter.registerHandshakeCompleteCallback( | 
| @@ -499,6 +503,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| futureSocket = new Future.value(socket); | 
| } | 
| futureSocket.then((rawSocket) { | 
| +      _connectPending = true; | 
| _socket = rawSocket; | 
| _socket.readEventsEnabled = true; | 
| _socket.writeEventsEnabled = false; | 
| @@ -506,14 +511,13 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| // If a current subscription is provided use this otherwise | 
| // create a new one. | 
| _socketSubscription = _socket.listen(_eventDispatcher, | 
| -                                             onError: _errorHandler, | 
| +                                             onError: _reportError, | 
| onDone: _doneHandler); | 
| } else { | 
| _socketSubscription.onData(_eventDispatcher); | 
| -        _socketSubscription.onError(_errorHandler); | 
| +        _socketSubscription.onError(_reportError); | 
| _socketSubscription.onDone(_doneHandler); | 
| } | 
| -      _connectPending = true; | 
| _secureFilter.connect(address.host, | 
| (address as dynamic)._sockaddr_storage, | 
| port, | 
| @@ -525,10 +529,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| sendClientCertificate); | 
| _secureHandshake(); | 
| }) | 
| -    .catchError((error) { | 
| -      _handshakeComplete.completeError(error); | 
| -      _close(); | 
| -    }); | 
| +    .catchError(_reportError); | 
| } | 
|  | 
| StreamSubscription listen(void onData(RawSocketEvent data), | 
| @@ -542,12 +543,22 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| cancelOnError: cancelOnError); | 
| } | 
|  | 
| -  void _verifyFields() { | 
| -    assert(is_server is bool); | 
| -    assert(_socket == null || _socket is RawSocket); | 
| -    if (address is! InternetAddress) { | 
| -      throw new ArgumentError( | 
| -          "RawSecureSocket constructor: host is not an InternetAddress"); | 
| +  static void _verifyFields(host, | 
| +                            int requestedPort, | 
| +                            String certificateName, | 
| +                            bool is_server, | 
| +                            bool requestClientCertificate, | 
| +                            bool requireClientCertificate, | 
| +                            bool sendClientCertificate, | 
| +                            Function onBadCertificate) { | 
| +    if (host is! String && host is! InternetAddress) { | 
| +      throw new ArgumentError("host is not a String or an InternetAddress"); | 
| +    } | 
| +    if (requestedPort is! int) { | 
| +      throw new ArgumentError("requestedPort is not an int"); | 
| +    } | 
| +    if (requestedPort < 0 || requestedPort > 65535) { | 
| +      throw new ArgumentError("requestedPort is not in the range 0..65535"); | 
| } | 
| if (certificateName != null && certificateName is! String) { | 
| throw new ArgumentError("certificateName is not null or a String"); | 
| @@ -693,12 +704,16 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| } | 
|  | 
| void _eventDispatcher(RawSocketEvent event) { | 
| -    if (event == RawSocketEvent.READ) { | 
| -      _readHandler(); | 
| -    } else if (event == RawSocketEvent.WRITE) { | 
| -      _writeHandler(); | 
| -    } else if (event == RawSocketEvent.READ_CLOSED) { | 
| -      _closeHandler(); | 
| +    try { | 
| +      if (event == RawSocketEvent.READ) { | 
| +        _readHandler(); | 
| +      } else if (event == RawSocketEvent.WRITE) { | 
| +        _writeHandler(); | 
| +      } else if (event == RawSocketEvent.READ_CLOSED) { | 
| +        _closeHandler(); | 
| +      } | 
| +    } catch (e) { | 
| +      _reportError(e); | 
| } | 
| } | 
|  | 
| @@ -718,20 +733,13 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| } | 
| } | 
|  | 
| -  void _errorHandler(e) { | 
| -    _reportError(e, 'Error on underlying RawSocket'); | 
| -  } | 
| - | 
| -  void _reportError(e, String message) { | 
| -    // TODO(whesse): Call _reportError from all internal functions that throw. | 
| -    if (e is SocketException) { | 
| -      e = new SocketException('$message (${e.message})', e.osError); | 
| -    } else if (e is OSError) { | 
| -      e = new SocketException(message, e); | 
| -    } else { | 
| -      e = new SocketException('$message (${e.toString()})', null); | 
| -    } | 
| +  void _reportError(e) { | 
| if (_connectPending) { | 
| +      // _connectPending is true after the underlying connection has been | 
| +      // made, but before the handshake has completed. | 
| +      if (e is! TlsException) { | 
| +        e = new HandshakeException("$e", null); | 
| +      } | 
| _handshakeComplete.completeError(e); | 
| } else { | 
| _controller.addError(e); | 
| @@ -756,8 +764,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| _socketClosedRead = true; | 
| if (_filterStatus.readEmpty) { | 
| _reportError( | 
| -          new SocketException('Connection terminated during handshake'), | 
| -          'RawSecureSocket error'); | 
| +          new HandshakeException('Connection terminated during handshake')); | 
| } else { | 
| _secureHandshake(); | 
| } | 
| @@ -772,7 +779,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| _writeSocket(); | 
| _scheduleFilter(); | 
| } catch (e) { | 
| -      _reportError(e, "RawSecureSocket error"); | 
| +      _reportError(e); | 
| } | 
| } | 
|  | 
| @@ -838,9 +845,8 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| if (_status == HANDSHAKE) { | 
| _secureFilter.handshake(); | 
| if (_status == HANDSHAKE) { | 
| -              _reportError( | 
| -                  new SocketException('Connection terminated during handshake'), | 
| -                  'RawSecureSocket error'); | 
| +              throw new HandshakeException( | 
| +                  'Connection terminated during handshake'); | 
| } | 
| } | 
| _closeHandler(); | 
| @@ -855,7 +861,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| if (_status == HANDSHAKE) _secureHandshake(); | 
| } | 
| _tryFilter(); | 
| -      }); | 
| +      }).catchError(_reportError); | 
| } | 
| } | 
|  | 
| @@ -872,12 +878,7 @@ class _RawSecureSocket extends Stream<RawSocketEvent> | 
| } | 
| return result; | 
| } else if (!_socketClosedRead) { | 
| -      try { | 
| -        return _socket.read(bytes); | 
| -      } catch (e) { | 
| -        _reportError(e, "RawSecureSocket error reading encrypted socket"); | 
| -        return null; | 
| -      } | 
| +      return _socket.read(bytes); | 
| } else { | 
| return null; | 
| } | 
| @@ -1161,3 +1162,57 @@ abstract class _SecureFilter { | 
|  | 
| List<_ExternalBuffer> get buffers; | 
| } | 
| + | 
| +/** A secure networking exception caused by a failure in the | 
| + *  TLS/SSL protocol. | 
| + */ | 
| +class TlsException implements IOException { | 
| +  final String type; | 
| +  final String message; | 
| +  final OSError osError; | 
| + | 
| +  const TlsException([String message = "", | 
| +                      OSError osError = null]) | 
| +     : this._("TlsException", message, osError); | 
| + | 
| +  const TlsException._(String this.type, | 
| +                       String this.message, | 
| +                       OSError this.osError); | 
| + | 
| +  String toString() { | 
| +    StringBuffer sb = new StringBuffer(); | 
| +    sb.write(type); | 
| +    if (!message.isEmpty) { | 
| +      sb.write(": $message"); | 
| +      if (osError != null) { | 
| +        sb.write(" ($osError)"); | 
| +      } | 
| +    } else if (osError != null) { | 
| +      sb.write(": $osError"); | 
| +    } | 
| +    return sb.toString(); | 
| +  } | 
| +} | 
| + | 
| + | 
| +/** | 
| + * An exception that happens in the handshake phase of establishing | 
| + * a secure network connection. | 
| + */ | 
| +class HandshakeException extends TlsException { | 
| +  const HandshakeException([String message = "", | 
| +                            OSError osError = null]) | 
| +     : super._("HandshakeException", message, osError); | 
| +} | 
| + | 
| + | 
| +/** | 
| + * An exception that happens in the handshake phase of establishing | 
| + * a secure network connection, when looking up or verifying a | 
| + * certificate. | 
| + */ | 
| +class CertificateException extends TlsException { | 
| +  const CertificateException([String message = "", | 
| +                            OSError osError = null]) | 
| +     : super._("CertificateException", message, osError); | 
| +} | 
|  |