Example #1
0
    def __init__(self, url=None, **options):
        """
    Creates a connection. A newly created connection must be opened
    with the Connection.open() method before it can be used.

    @type url: str
    @param url: [ <username> [ / <password> ] @ ] <host> [ : <port> ]
    @type host: str
    @param host: the name or ip address of the remote host (overriden by url)
    @type port: int
    @param port: the port number of the remote host (overriden by url)
    @type transport: str
    @param transport: one of tcp, tcp+tls, or ssl (alias for tcp+tls)
    @type heartbeat: int
    @param heartbeat: heartbeat interval in seconds

    @type username: str
    @param username: the username for authentication (overriden by url)
    @type password: str
    @param password: the password for authentication (overriden by url)
    @type sasl_mechanisms: str
    @param sasl_mechanisms: space separated list of permitted sasl mechanisms
    @type sasl_service: str
    @param sasl_service: the service name if needed by the SASL mechanism in use
    @type sasl_min_ssf: int
    @param sasl_min_ssf: the minimum acceptable security strength factor
    @type sasl_max_ssf: int
    @param sasl_max_ssf: the maximum acceptable security strength factor

    @type reconnect: bool
    @param reconnect: enable/disable automatic reconnect
    @type reconnect_timeout: float
    @param reconnect_timeout: total time to attempt reconnect
    @type reconnect_interval_min: float
    @param reconnect_interval_min: minimum interval between reconnect attempts
    @type reconnect_interval_max: float
    @param reconnect_interval_max: maximum interval between reconnect attempts
    @type reconnect_interval: float
    @param reconnect_interval: set both min and max reconnect intervals
    @type reconnect_limit: int
    @param reconnect_limit: limit the total number of reconnect attempts
    @type reconnect_urls: list[str]
    @param reconnect_urls: list of backup hosts specified as urls

    @type address_ttl: float
    @param address_ttl: time until cached address resolution expires

    @type ssl_keyfile: str
    @param ssl_keyfile: file with client's private key (PEM format)
    @type ssl_certfile: str
    @param ssl_certfile: file with client's public (eventually priv+pub) key (PEM format)
    @type ssl_trustfile: str
    @param ssl_trustfile: file trusted certificates to validate the server
    @type ssl_skip_hostname_check: bool
    @param ssl_skip_hostname_check: disable verification of hostname in
    certificate. Use with caution - disabling hostname checking leaves you
    vulnerable to Man-in-the-Middle attacks.

    @rtype: Connection
    @return: a disconnected Connection
    """
        if url is None:
            url = options.get("host")
        if isinstance(url, basestring):
            url = URL(url)
        self.host = url.host
        if options.has_key("transport"):
            self.transport = options.get("transport")
        elif url.scheme == url.AMQP:
            self.transport = "tcp"
        elif url.scheme == url.AMQPS:
            self.transport = "ssl"
        else:
            self.transport = "tcp"
        if self.transport in ("ssl", "tcp+tls"):
            self.port = default(url.port, options.get("port", AMQPS_PORT))
        else:
            self.port = default(url.port, options.get("port", AMQP_PORT))
        self.heartbeat = options.get("heartbeat")
        self.username = default(url.user, options.get("username", None))
        self.password = default(url.password, options.get("password", None))
        self.auth_username = None

        self.sasl_mechanisms = options.get("sasl_mechanisms")
        self.sasl_service = options.get("sasl_service", "qpidd")
        self.sasl_min_ssf = options.get("sasl_min_ssf")
        self.sasl_max_ssf = options.get("sasl_max_ssf")

        self.reconnect = options.get("reconnect", False)
        self.reconnect_timeout = options.get("reconnect_timeout")
        reconnect_interval = options.get("reconnect_interval")
        self.reconnect_interval_min = options.get("reconnect_interval_min", default(reconnect_interval, 1))
        self.reconnect_interval_max = options.get("reconnect_interval_max", default(reconnect_interval, 2 * 60))
        self.reconnect_limit = options.get("reconnect_limit")
        self.reconnect_urls = options.get("reconnect_urls", [])
        self.reconnect_log = options.get("reconnect_log", True)

        self.address_ttl = options.get("address_ttl", 60)
        self.tcp_nodelay = options.get("tcp_nodelay", False)

        self.ssl_keyfile = options.get("ssl_keyfile", None)
        self.ssl_certfile = options.get("ssl_certfile", None)
        self.ssl_trustfile = options.get("ssl_trustfile", None)
        self.ssl_skip_hostname_check = options.get("ssl_skip_hostname_check", False)
        self.client_properties = options.get("client_properties", {})

        self.options = options

        self.id = str(uuid4())
        self.session_counter = 0
        self.sessions = {}
        self._open = False
        self._connected = False
        self._transport_connected = False
        self._lock = RLock()
        self._condition = Condition(self._lock)
        self._waiter = Waiter(self._condition)
        self._modcount = Serial(0)
        self.error = None
        from driver import Driver

        self._driver = Driver(self)
Example #2
0
  def __init__(self, url=None, **options):
    """
    Creates a connection. A newly created connection must be connected
    with the Connection.connect() method before it can be used.

    @type url: str
    @param url: [ <username> [ / <password> ] @ ] <host> [ : <port> ]
    @type host: str
    @param host: the name or ip address of the remote host (overriden by url)
    @type port: int
    @param port: the port number of the remote host (overriden by url)
    @type transport: str
    @param transport: one of tcp, tcp+tls, or ssl (alias for tcp+tls)
    @type heartbeat: int
    @param heartbeat: heartbeat interval in seconds

    @type username: str
    @param username: the username for authentication (overriden by url)
    @type password: str
    @param password: the password for authentication (overriden by url)

    @type sasl_mechanisms: str
    @param sasl_mechanisms: space separated list of permitted sasl mechanisms
    @type sasl_service: str
    @param sasl_service: ???
    @type sasl_min_ssf: ???
    @param sasl_min_ssf: ???
    @type sasl_max_ssf: ???
    @param sasl_max_ssf: ???

    @type reconnect: bool
    @param reconnect: enable/disable automatic reconnect
    @type reconnect_timeout: float
    @param reconnect_timeout: total time to attempt reconnect
    @type reconnect_internal_min: float
    @param reconnect_internal_min: minimum interval between reconnect attempts
    @type reconnect_internal_max: float
    @param reconnect_internal_max: maximum interval between reconnect attempts
    @type reconnect_internal: float
    @param reconnect_interval: set both min and max reconnect intervals
    @type reconnect_limit: int
    @param reconnect_limit: limit the total number of reconnect attempts
    @type reconnect_urls: list[str]
    @param reconnect_urls: list of backup hosts specified as urls

    @type address_ttl: float
    @param address_ttl: time until cached address resolution expires

    @type ssl_keyfile: str
    @param ssl_keyfile: file with client's private key (PEM format)
    @type ssl_certfile: str
    @param ssl_certfile: file with client's public (eventually priv+pub) key (PEM format)
    @type ssl_trustfile: str
    @param ssl_trustfile: file trusted certificates to validate the server

    @rtype: Connection
    @return: a disconnected Connection
    """
    if url is None:
      url = options.get("host")
    if isinstance(url, basestring):
      url = URL(url)
    self.host = url.host
    if options.has_key("transport"):
      self.transport = options.get("transport")
    elif url.scheme == url.AMQP:
      self.transport = "tcp"
    elif url.scheme == url.AMQPS:
      self.transport = "ssl"
    else:
      self.transport = "tcp"
    if self.transport in ("ssl", "tcp+tls"):
      self.port = default(url.port, options.get("port", AMQPS_PORT))
    else:
      self.port = default(url.port, options.get("port", AMQP_PORT))
    self.heartbeat = options.get("heartbeat")
    self.username = default(url.user, options.get("username", None))
    self.password = default(url.password, options.get("password", None))
    self.auth_username = None

    self.sasl_mechanisms = options.get("sasl_mechanisms")
    self.sasl_service = options.get("sasl_service", "qpidd")
    self.sasl_min_ssf = options.get("sasl_min_ssf")
    self.sasl_max_ssf = options.get("sasl_max_ssf")

    self.reconnect = options.get("reconnect", False)
    self.reconnect_timeout = options.get("reconnect_timeout")
    reconnect_interval = options.get("reconnect_interval")
    self.reconnect_interval_min = options.get("reconnect_interval_min",
                                              default(reconnect_interval, 1))
    self.reconnect_interval_max = options.get("reconnect_interval_max",
                                              default(reconnect_interval, 2*60))
    self.reconnect_limit = options.get("reconnect_limit")
    self.reconnect_urls = options.get("reconnect_urls", [])
    self.reconnect_log = options.get("reconnect_log", True)

    self.address_ttl = options.get("address_ttl", 60)
    self.tcp_nodelay = options.get("tcp_nodelay", False)

    self.ssl_keyfile = options.get("ssl_keyfile", None)
    self.ssl_certfile = options.get("ssl_certfile", None)
    self.ssl_trustfile = options.get("ssl_trustfile", None)
    self.client_properties = options.get("client_properties", {})

    self.options = options


    self.id = str(uuid4())
    self.session_counter = 0
    self.sessions = {}
    self._open = False
    self._connected = False
    self._transport_connected = False
    self._lock = RLock()
    self._condition = Condition(self._lock)
    self._waiter = Waiter(self._condition)
    self._modcount = Serial(0)
    self.error = None
    from driver import Driver
    self._driver = Driver(self)
Example #3
0
class Connection(Endpoint):

    """
  A Connection manages a group of L{Sessions<Session>} and connects
  them with a remote endpoint.
  """

    @static
    def establish(url=None, timeout=None, **options):
        """
    Constructs a L{Connection} with the supplied parameters and opens
    it.
    """
        conn = Connection(url, **options)
        conn.open(timeout=timeout)
        return conn

    def __init__(self, url=None, **options):
        """
    Creates a connection. A newly created connection must be opened
    with the Connection.open() method before it can be used.

    @type url: str
    @param url: [ <username> [ / <password> ] @ ] <host> [ : <port> ]
    @type host: str
    @param host: the name or ip address of the remote host (overriden by url)
    @type port: int
    @param port: the port number of the remote host (overriden by url)
    @type transport: str
    @param transport: one of tcp, tcp+tls, or ssl (alias for tcp+tls)
    @type heartbeat: int
    @param heartbeat: heartbeat interval in seconds

    @type username: str
    @param username: the username for authentication (overriden by url)
    @type password: str
    @param password: the password for authentication (overriden by url)
    @type sasl_mechanisms: str
    @param sasl_mechanisms: space separated list of permitted sasl mechanisms
    @type sasl_service: str
    @param sasl_service: the service name if needed by the SASL mechanism in use
    @type sasl_min_ssf: int
    @param sasl_min_ssf: the minimum acceptable security strength factor
    @type sasl_max_ssf: int
    @param sasl_max_ssf: the maximum acceptable security strength factor

    @type reconnect: bool
    @param reconnect: enable/disable automatic reconnect
    @type reconnect_timeout: float
    @param reconnect_timeout: total time to attempt reconnect
    @type reconnect_interval_min: float
    @param reconnect_interval_min: minimum interval between reconnect attempts
    @type reconnect_interval_max: float
    @param reconnect_interval_max: maximum interval between reconnect attempts
    @type reconnect_interval: float
    @param reconnect_interval: set both min and max reconnect intervals
    @type reconnect_limit: int
    @param reconnect_limit: limit the total number of reconnect attempts
    @type reconnect_urls: list[str]
    @param reconnect_urls: list of backup hosts specified as urls

    @type address_ttl: float
    @param address_ttl: time until cached address resolution expires

    @type ssl_keyfile: str
    @param ssl_keyfile: file with client's private key (PEM format)
    @type ssl_certfile: str
    @param ssl_certfile: file with client's public (eventually priv+pub) key (PEM format)
    @type ssl_trustfile: str
    @param ssl_trustfile: file trusted certificates to validate the server
    @type ssl_skip_hostname_check: bool
    @param ssl_skip_hostname_check: disable verification of hostname in
    certificate. Use with caution - disabling hostname checking leaves you
    vulnerable to Man-in-the-Middle attacks.

    @rtype: Connection
    @return: a disconnected Connection
    """
        if url is None:
            url = options.get("host")
        if isinstance(url, basestring):
            url = URL(url)
        self.host = url.host
        if options.has_key("transport"):
            self.transport = options.get("transport")
        elif url.scheme == url.AMQP:
            self.transport = "tcp"
        elif url.scheme == url.AMQPS:
            self.transport = "ssl"
        else:
            self.transport = "tcp"
        if self.transport in ("ssl", "tcp+tls"):
            self.port = default(url.port, options.get("port", AMQPS_PORT))
        else:
            self.port = default(url.port, options.get("port", AMQP_PORT))
        self.heartbeat = options.get("heartbeat")
        self.username = default(url.user, options.get("username", None))
        self.password = default(url.password, options.get("password", None))
        self.auth_username = None

        self.sasl_mechanisms = options.get("sasl_mechanisms")
        self.sasl_service = options.get("sasl_service", "qpidd")
        self.sasl_min_ssf = options.get("sasl_min_ssf")
        self.sasl_max_ssf = options.get("sasl_max_ssf")

        self.reconnect = options.get("reconnect", False)
        self.reconnect_timeout = options.get("reconnect_timeout")
        reconnect_interval = options.get("reconnect_interval")
        self.reconnect_interval_min = options.get("reconnect_interval_min", default(reconnect_interval, 1))
        self.reconnect_interval_max = options.get("reconnect_interval_max", default(reconnect_interval, 2 * 60))
        self.reconnect_limit = options.get("reconnect_limit")
        self.reconnect_urls = options.get("reconnect_urls", [])
        self.reconnect_log = options.get("reconnect_log", True)

        self.address_ttl = options.get("address_ttl", 60)
        self.tcp_nodelay = options.get("tcp_nodelay", False)

        self.ssl_keyfile = options.get("ssl_keyfile", None)
        self.ssl_certfile = options.get("ssl_certfile", None)
        self.ssl_trustfile = options.get("ssl_trustfile", None)
        self.ssl_skip_hostname_check = options.get("ssl_skip_hostname_check", False)
        self.client_properties = options.get("client_properties", {})

        self.options = options

        self.id = str(uuid4())
        self.session_counter = 0
        self.sessions = {}
        self._open = False
        self._connected = False
        self._transport_connected = False
        self._lock = RLock()
        self._condition = Condition(self._lock)
        self._waiter = Waiter(self._condition)
        self._modcount = Serial(0)
        self.error = None
        from driver import Driver

        self._driver = Driver(self)

    def _wait(self, predicate, timeout=None):
        return self._waiter.wait(predicate, timeout=timeout)

    def _wakeup(self):
        self._modcount += 1
        self._driver.wakeup()

    def check_error(self):
        if self.error:
            self._condition.gc()
            e = self.error
            if isinstance(e, ContentError):
                """ forget the content error. It will be
          raised this time but won't block future calls
          """
                self.error = None
            raise e

    def get_error(self):
        return self.error

    def _ewait(self, predicate, timeout=None):
        result = self._wait(lambda: self.error or predicate(), timeout)
        self.check_error()
        return result

    def check_closed(self):
        if not self._connected:
            self._condition.gc()
            raise ConnectionClosed()

    @synchronized
    def session(self, name=None, transactional=False):
        """
    Creates or retrieves the named session. If the name is omitted or
    None, then a unique name is chosen based on a randomly generated
    uuid.

    @type name: str
    @param name: the session name
    @rtype: Session
    @return: the named Session
    """

        if name is None:
            name = "%s:%s" % (self.id, self.session_counter)
            self.session_counter += 1
        else:
            name = "%s:%s" % (self.id, name)

        if self.sessions.has_key(name):
            return self.sessions[name]
        else:
            ssn = Session(self, name, transactional)
            self.sessions[name] = ssn
            self._wakeup()
            return ssn

    @synchronized
    def _remove_session(self, ssn):
        self.sessions.pop(ssn.name, 0)

    @synchronized
    def open(self, timeout=None):
        """
    Opens a connection.
    """
        if self._open:
            raise ConnectionError("already open")
        self._open = True
        if self.reconnect and self.reconnect_timeout > 0:
            timeout = self.reconnect_timeout
        self.attach(timeout=timeout)

    @synchronized
    def opened(self):
        """
    Return true if the connection is open, false otherwise.
    """
        return self._open

    @synchronized
    def attach(self, timeout=None):
        """
    Attach to the remote endpoint.
    """
        if not self._connected:
            self._connected = True
            self._driver.start()
            self._wakeup()
        if not self._ewait(lambda: self._transport_connected and not self._unlinked(), timeout=timeout):
            self.reconnect = False
            raise Timeout("Connection attach timed out")

    def _unlinked(self):
        return [
            l
            for ssn in self.sessions.values()
            if not (ssn.error or ssn.closed)
            for l in ssn.senders + ssn.receivers
            if not (l.linked or l.error or l.closed)
        ]

    @synchronized
    def detach(self, timeout=None):
        """
    Detach from the remote endpoint.
    """
        if self._connected:
            self._connected = False
            self._wakeup()
            cleanup = True
        else:
            cleanup = False
        try:
            if not self._wait(lambda: not self._transport_connected, timeout=timeout):
                raise Timeout("detach timed out")
        finally:
            if cleanup:
                self._driver.stop()
            self._condition.gc()

    @synchronized
    def attached(self):
        """
    Return true if the connection is attached, false otherwise.
    """
        return self._connected

    @synchronized
    def close(self, timeout=None):
        """
    Close the connection and all sessions.
    """
        try:
            for ssn in self.sessions.values():
                ssn.close(timeout=timeout)
        finally:
            self.detach(timeout=timeout)
            self._open = False
Example #4
0
class Connection(Endpoint):

  """
  A Connection manages a group of L{Sessions<Session>} and connects
  them with a remote endpoint.
  """

  @static
  def establish(url=None, **options):
    """
    Constructs a L{Connection} with the supplied parameters and opens
    it.
    """
    conn = Connection(url, **options)
    conn.open()
    return conn

  def __init__(self, url=None, **options):
    """
    Creates a connection. A newly created connection must be connected
    with the Connection.connect() method before it can be used.

    @type url: str
    @param url: [ <username> [ / <password> ] @ ] <host> [ : <port> ]
    @type host: str
    @param host: the name or ip address of the remote host (overriden by url)
    @type port: int
    @param port: the port number of the remote host (overriden by url)
    @type transport: str
    @param transport: one of tcp, tcp+tls, or ssl (alias for tcp+tls)
    @type heartbeat: int
    @param heartbeat: heartbeat interval in seconds

    @type username: str
    @param username: the username for authentication (overriden by url)
    @type password: str
    @param password: the password for authentication (overriden by url)

    @type sasl_mechanisms: str
    @param sasl_mechanisms: space separated list of permitted sasl mechanisms
    @type sasl_service: str
    @param sasl_service: ???
    @type sasl_min_ssf: ???
    @param sasl_min_ssf: ???
    @type sasl_max_ssf: ???
    @param sasl_max_ssf: ???

    @type reconnect: bool
    @param reconnect: enable/disable automatic reconnect
    @type reconnect_timeout: float
    @param reconnect_timeout: total time to attempt reconnect
    @type reconnect_internal_min: float
    @param reconnect_internal_min: minimum interval between reconnect attempts
    @type reconnect_internal_max: float
    @param reconnect_internal_max: maximum interval between reconnect attempts
    @type reconnect_internal: float
    @param reconnect_interval: set both min and max reconnect intervals
    @type reconnect_limit: int
    @param reconnect_limit: limit the total number of reconnect attempts
    @type reconnect_urls: list[str]
    @param reconnect_urls: list of backup hosts specified as urls

    @type address_ttl: float
    @param address_ttl: time until cached address resolution expires

    @type ssl_keyfile: str
    @param ssl_keyfile: file with client's private key (PEM format)
    @type ssl_certfile: str
    @param ssl_certfile: file with client's public (eventually priv+pub) key (PEM format)
    @type ssl_trustfile: str
    @param ssl_trustfile: file trusted certificates to validate the server

    @rtype: Connection
    @return: a disconnected Connection
    """
    if url is None:
      url = options.get("host")
    if isinstance(url, basestring):
      url = URL(url)
    self.host = url.host
    if options.has_key("transport"):
      self.transport = options.get("transport")
    elif url.scheme == url.AMQP:
      self.transport = "tcp"
    elif url.scheme == url.AMQPS:
      self.transport = "ssl"
    else:
      self.transport = "tcp"
    if self.transport in ("ssl", "tcp+tls"):
      self.port = default(url.port, options.get("port", AMQPS_PORT))
    else:
      self.port = default(url.port, options.get("port", AMQP_PORT))
    self.heartbeat = options.get("heartbeat")
    self.username = default(url.user, options.get("username", None))
    self.password = default(url.password, options.get("password", None))
    self.auth_username = None

    self.sasl_mechanisms = options.get("sasl_mechanisms")
    self.sasl_service = options.get("sasl_service", "qpidd")
    self.sasl_min_ssf = options.get("sasl_min_ssf")
    self.sasl_max_ssf = options.get("sasl_max_ssf")

    self.reconnect = options.get("reconnect", False)
    self.reconnect_timeout = options.get("reconnect_timeout")
    reconnect_interval = options.get("reconnect_interval")
    self.reconnect_interval_min = options.get("reconnect_interval_min",
                                              default(reconnect_interval, 1))
    self.reconnect_interval_max = options.get("reconnect_interval_max",
                                              default(reconnect_interval, 2*60))
    self.reconnect_limit = options.get("reconnect_limit")
    self.reconnect_urls = options.get("reconnect_urls", [])
    self.reconnect_log = options.get("reconnect_log", True)

    self.address_ttl = options.get("address_ttl", 60)
    self.tcp_nodelay = options.get("tcp_nodelay", False)

    self.ssl_keyfile = options.get("ssl_keyfile", None)
    self.ssl_certfile = options.get("ssl_certfile", None)
    self.ssl_trustfile = options.get("ssl_trustfile", None)
    self.client_properties = options.get("client_properties", {})

    self.options = options


    self.id = str(uuid4())
    self.session_counter = 0
    self.sessions = {}
    self._open = False
    self._connected = False
    self._transport_connected = False
    self._lock = RLock()
    self._condition = Condition(self._lock)
    self._waiter = Waiter(self._condition)
    self._modcount = Serial(0)
    self.error = None
    from driver import Driver
    self._driver = Driver(self)

  def _wait(self, predicate, timeout=None):
    return self._waiter.wait(predicate, timeout=timeout)

  def _wakeup(self):
    self._modcount += 1
    self._driver.wakeup()

  def check_error(self):
    if self.error:
      self._condition.gc()
      raise self.error

  def get_error(self):
    return self.error

  def _ewait(self, predicate, timeout=None):
    result = self._wait(lambda: self.error or predicate(), timeout)
    self.check_error()
    return result

  def check_closed(self):
    if not self._connected:
      self._condition.gc()
      raise ConnectionClosed()

  @synchronized
  def session(self, name=None, transactional=False):
    """
    Creates or retrieves the named session. If the name is omitted or
    None, then a unique name is chosen based on a randomly generated
    uuid.

    @type name: str
    @param name: the session name
    @rtype: Session
    @return: the named Session
    """

    if name is None:
      name = "%s:%s" % (self.id, self.session_counter)
      self.session_counter += 1
    else:
      name = "%s:%s" % (self.id, name)

    if self.sessions.has_key(name):
      return self.sessions[name]
    else:
      ssn = Session(self, name, transactional)
      self.sessions[name] = ssn
      self._wakeup()
      return ssn

  @synchronized
  def _remove_session(self, ssn):
    self.sessions.pop(ssn.name, 0)

  @synchronized
  def open(self):
    """
    Opens a connection.
    """
    if self._open:
      raise ConnectionError("already open")
    self._open = True
    self.attach()

  @synchronized
  def opened(self):
    """
    Return true if the connection is open, false otherwise.
    """
    return self._open

  @synchronized
  def attach(self):
    """
    Attach to the remote endpoint.
    """
    if not self._connected:
      self._connected = True
      self._driver.start()
      self._wakeup()
    self._ewait(lambda: self._transport_connected and not self._unlinked())

  def _unlinked(self):
    return [l
            for ssn in self.sessions.values()
            if not (ssn.error or ssn.closed)
            for l in ssn.senders + ssn.receivers
            if not (l.linked or l.error or l.closed)]

  @synchronized
  def detach(self, timeout=None):
    """
    Detach from the remote endpoint.
    """
    if self._connected:
      self._connected = False
      self._wakeup()
      cleanup = True
    else:
      cleanup = False
    try:
      if not self._wait(lambda: not self._transport_connected, timeout=timeout):
        raise Timeout("detach timed out")
    finally:
      if cleanup:
        self._driver.stop()
      self._condition.gc()

  @synchronized
  def attached(self):
    """
    Return true if the connection is attached, false otherwise.
    """
    return self._connected

  @synchronized
  def close(self, timeout=None):
    """
    Close the connection and all sessions.
    """
    try:
      for ssn in self.sessions.values():
        ssn.close(timeout=timeout)
    finally:
      self.detach(timeout=timeout)
      self._open = False