Ejemplo n.º 1
0
 def read_some(self, max_len):
     """See :meth:`versile.reactor.io.IVByteInput.read_some`"""
     if not self.was_connected:
         raise VIOError('Socket was not connected')
     if self._sock_in_closed:
         if isinstance(self._sock_in_closed_reason, VFIOCompleted):
             raise VIOCompleted()
         else:
             raise VIOLost()
     if not self.sock:
         raise VIOError('No socket')
     try:
         if _pyver == 2:
             data = _s2b(self.sock.recv(max_len))
         else:
             data = self.sock.recv(max_len)
     except IOError as e:
         if e.errno in _errno_block:
             return b''
         elif (e.errno in (errno.EPIPE, errno.ENOTCONN)
               and not self._sock_verified):
             # ISSUE - these have been seen to be raised after
             # socket thinks it is connected due to select() event,
             # however ignoring it causes the connection to be
             # 'resumed'. For now we log a message and resume.
             self.log.debug('Ignoring post-connect read errno %s' % e.errno)
             return b''
         else:
             self.log.debug('Read got errno %s' % e.errno)
             raise VIOError('Socket read error, errno %s' % e.errno)
     else:
         if not data:
             raise VIOCompleted()
         self._sock_verified = True
         return data
Ejemplo n.º 2
0
    def _tc_attach(self, producer, rthread=False):
        # Ensure 'attach' is performed in reactor thread
        if not rthread:
            self.reactor.execute(self._tc_attach, producer, rthread=True)
            return

        if self._handshake_error:
            raise VIOError('Earlier error during handshaking')
        elif self._tc_producer is producer:
            return
        elif self._tc_producer:
            raise VIOError('Producer already connected')

        self.__tc_producer = producer
        self._tc_cons_lim = 0
        producer.attach(self._transport_consume)

        if not self._handshaking:
            _lim = self._ec_cons_lim
            if _lim >= 0:
                _lim -= self._handshake_consumed
            producer.can_produce(_lim)

        try:
            producer.control.notify_consumer_attached(self._transport_consume)
        except VIOMissingControl:
            pass
Ejemplo n.º 3
0
    def _bc_consume(self, data, clim):
        if self.__bc_eod:
            raise VIOError('Consumer already received end-of-data')
        elif not self._bc_producer:
            raise VIOError('No connected producer')
        elif not data:
            raise VIOError('No data to consume')
        max_cons = self.__lim(self.__bc_consumed, self.__bc_consume_lim)
        if max_cons == 0:
            raise VIOError('Consume limit exceeded')
        if clim is not None and clim > 0:
            max_cons = min(max_cons, clim)

        with self.__bc_cond:
            buf_len = len(self.__bc_rbuf)
            self.__bc_rbuf.append_list(data.pop_list(max_cons))
            self.__bc_consumed += len(self.__bc_rbuf) - buf_len

            # Update consume limit
            max_add = self.__lim(len(self.__bc_rbuf), self.__bc_rbuf_len)
            if max_add >= 0:
                self.__bc_consume_lim = self.__bc_consumed + max_add
            else:
                self.__bc_consume_lim = -1

            # Notify data is available
            self.__bc_cond.notify_all()

        return self.__bc_consume_lim
Ejemplo n.º 4
0
 def write_some(self, data):
     """See :meth:`versile.reactor.io.IVByteOutput.write_some`\ ."""
     if not self.was_connected:
         raise VIOError('Socket not connected')
     if self._sock_out_closed:
         if isinstance(self._sock_out_closed_reason, VFIOCompleted):
             raise VIOCompleted()
         else:
             raise VIOLost()
     if not self.sock:
         raise VIOError('No socket')
     try:
         if _pyver == 2:
             num_written = self.sock.send(_b2s(data))
         else:
             num_written = self.sock.send(data)
     except IOError as e:
         if e.errno in _errno_block:
             return 0
         elif (e.errno in (errno.EPIPE, errno.ENOTCONN)
               and not self._sock_verified):
             # ISSUE - these have been seen to be raised after
             # socket thinks it is connected due to select() event,
             # however ignoring it causes the connection to be
             # 'resumed'. For now we log a message and resume.
             self.log.debug('Ignoring post-connect write errno %s' %
                            e.errno)
             return 0
         else:
             self.log.debug('Write got errno %s' % e.errno)
             raise VIOError('Socket write error, errno %s' % e.errno)
     else:
         self._sock_verified = True
         return num_written
Ejemplo n.º 5
0
    def create_native_pair(cls, interface='127.0.0.1'):
        """Creates a native TCP socket pair

        :param interface: the interface to use when not using built-in
        :returns:         two paired native sockets (sock1, sock2)

        """
        if hasattr(socket, 'socketpair'):
            try:
                s1, s2 = socket.socketpair(socket.AF_INET)
                s1.setblocking(0)
                s2.setblocking(0)
            except socket.error:
                pass
            else:
                return (s1, s2)
        try:
            s = socket.socket(socket.AF_INET)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind((interface, 0))
            s.listen(1)

            cs1 = socket.socket(socket.AF_INET)
            cs1.connect(s.getsockname())
            cs2, host2 = s.accept()
            if host2 == cs1.getsockname():
                s.close()
                return (cs1, cs2)
            else:
                s.close()
                raise VIOError('Could not create socket pair')
        except socket.error:
            raise VIOError('Could not create socket pair')
Ejemplo n.º 6
0
    def read_some(self, max_len):
        """See :meth:`versile.reactor.io.IVByteInput.read_some`.

        .. note::

            Whereas :meth:`versile.reactor.io.sock.VSocket.read_some`
            is non-blocking, reading on a TLS socket is a blocking
            operation. This is due to limitations of the python 2.6
            ssl module. If non-blocking behavior is required, then
            :func:`select.select` or similar should be called to check
            whether the socket has data.

        This method may not return data even though data was available
        on the underlying socket when the method was called. This is
        because the TLS layer can only provide data when a full
        encrypted frame has been received.

        """
        if not self.was_connected:
            raise VIOError('Socket was not connected')
        if self._sock_in_closed:
            if isinstance(self._sock_in_closed_reason, VFIOCompleted):
                raise VIOCompleted()
            else:
                raise VIOLost()
        if not self.sock:
            raise VIOError('No socket')
        self._tls_read_pending = False
        try:
            data = self.sock.read(max_len)
        except IOError as e:
            if e.errno in _errno_wouldblock:
                return b''
            elif (e.errno in (errno.EPIPE, errno.ENOTCONN)
                  and not self._sock_verified):
                # ISSUE - see VSocket.read_some comments
                self.log.debug('Ignoring post-connect read errno %s' % e.errno)
                return b''
            else:
                self.log.debug('Read got errno %s' % e.errno)
                raise VIOError('Socket read error, errno %s' % e.errno)
        except ssl.SSLError as e:
            if e.args[0] in (ssl.SSL_ERROR_WANT_READ,
                             ssl.SSL_ERROR_WANT_WRITE):
                # Full frame of decrypted data not yet available
                return b''
        else:
            if not data:
                raise VIOCompleted()
            if self.sock.pending():
                self.log.debug('SSL data pending')
                self._tls_read_pending = True
                self.reactor.schedule(0.0, self.do_read)
            self._sock_verified = True
            return data
Ejemplo n.º 7
0
    def _tp_can_produce(self, limit):
        if self._handshake_error:
            raise VIOError('Earlier error during handshaking')
        elif not self._tp_consumer:
            raise VIOError('No attached consumer')

        self._tp_prod_lim = limit

        if not self._handshaking and self._ec_producer:
            if limit >= 0:
                limit += self._handshake_consumed
            self._ec_producer.can_produce(limit)
Ejemplo n.º 8
0
    def recv(self, max_read, timeout=None):
        """Receive input data from byte channel.

        :param max_read: max bytes to read (unlimited if None)
        :type  max_read: int
        :param timeout:  timeout in seconds (blocking if None)
        :type  timeout:  float
        :returns:        data read (empty if input was closed)
        :rtype:          bytes
        :raises:         :exc:`versile.reactor.io.VIOTimeout`\ ,
                         :exc:`versile.reactor.io.VIOError`

        """
        if timeout:
            start_time = time.time()
        with self.__bc_cond:
            while True:
                if self.__bc_rbuf:
                    if max_read is None:
                        result = self.__bc_rbuf.pop()
                    elif max_read > 0:
                        result = self.__bc_rbuf.pop(max_read)
                    else:
                        result = b''

                    # Trigger updating can_produce in reactor thread
                    if not self.__bc_scheduled_lim_update:
                        self.__bc_scheduled_lim_update = True
                        self.reactor.schedule(0.0, self.__bc_lim_update)

                    return result
                elif self.__bc_aborted:
                    raise VIOError('Byte input was aborted')
                elif self.__bc_eod:
                    if self.__bc_eod_clean:
                        return b''
                    else:
                        raise VIOError('Byte input was closed but not cleanly')

                if timeout == 0.0:
                    raise VIOTimeout()
                elif timeout is not None and timeout > 0.0:
                    current_time = time.time()
                    if current_time > start_time + timeout:
                        raise VIOTimeout()
                    wait_time = start_time + timeout - current_time
                    self.__bc_cond.wait(wait_time)
                else:
                    self.__bc_cond.wait()
Ejemplo n.º 9
0
    def _ep_can_produce(self, limit):
        if self._handshake_error:
            raise VIOError('Earlier error during handshaking')
        elif not self._ep_consumer:
            raise VIOError('No attached consumer')

        self._ep_prod_lim = limit

        if self._handshaking:
            self._handshake_can_produce()
        else:
            if self._tc_producer:
                if limit >= 0:
                    limit = max(limit - self._handshake_produced, 0)
                self._tc_cons_lim = limit
                self._tc_producer.can_produce(limit)
Ejemplo n.º 10
0
    def listen(self, num_listen):
        """Start listening on bound network interface.

        :param num_listen: max pending connection requests
        :type  num_listen: int

        """
        if not self._bound:
            raise VIOError('Socket not bound')
        if self._listening:
            raise VIOError('Socket already listening')
        self.sock.listen(num_listen)
        self._listening = True
        self._set_sock_enabled()
        self._set_sock_connected()
        self._set_sock_active()
Ejemplo n.º 11
0
    def _c_attach(self, producer, rthread=False):
        # Ensure 'attach' is performed in reactor thread
        if not rthread:
            self.reactor.execute(self._c_attach, producer, rthread=True)
            return

        if self._ci_producer is producer:
            return
        elif self._ci_producer:
            raise VIOError('Producer already attached')

        self._ci_producer = producer
        if self._sock_out_closed:
            self.reactor.schedule(0.0, self._c_abort, True)
        else:
            self._ci_consumed = self._ci_lim_sent = 0
            producer.attach(self.byte_consume)
            self._ci_lim_sent = self._wbuf_len
            producer.can_produce(self._ci_lim_sent)

        # Notify attached chain
        try:
            producer.control.notify_consumer_attached(self.byte_consume)
        except VIOMissingControl:
            pass
Ejemplo n.º 12
0
    def send(self, data, timeout=None):
        """Receive input data from byte channel.

        :param data:     data to write
        :type  data:     bytes
        :type  max_read: int
        :param timeout:  timeout in seconds (blocking if None)
        :type  timeout:  float
        :returns:        number bytes written
        :rtype:          int
        :raises:         :exc:`versile.reactor.io.VIOTimeout`\ ,
                         :exc:`versile.reactor.io.VIOError`

        """
        if timeout:
            start_time = time.time()
        with self.__bp_cond:
            while True:
                if self.__bp_aborted:
                    raise VIOError('Byte output was aborted')
                elif self.__bp_eod:
                    raise VIOError('Byte output was closed')
                if not data:
                    return 0
                max_write = self.__bp_wbuf_len - len(self.__bp_wbuf)
                if max_write > 0:
                    write_data = data[:max_write]
                    self.__bp_wbuf.append(write_data)

                    # Trigger reactor production
                    if not self.__bp_scheduled_produce:
                        self.__bp_scheduled_produce = True
                        self.reactor.schedule(0.0, self.__bp_do_produce)

                    return len(write_data)

                if timeout == 0.0:
                    raise VIOTimeout()
                elif timeout is not None and timeout > 0.0:
                    current_time = time.time()
                    if current_time > start_time + timeout:
                        raise VIOTimeout()
                    wait_time = start_time + timeout - current_time
                    self.__bc_cond.wait(wait_time)
                else:
                    self.__bc_cond.wait()
Ejemplo n.º 13
0
 def _data_received(self, data):
     if not self._active:
         # Link no longer active - handle silently by performing
         # (another) shutdown of the input
         self.__shutdown_input()
         return
     for obj in data:
         if self._protocol_handshake:
             try:
                 self._recv_handshake(obj)
             except VLinkError as e:
                 raise VIOError('VLink handshake error', e.args)
         else:
             try:
                 self._recv_msg(obj)
             except VLinkError as e:
                 raise VIOError('VLink protocol error', e.args)
Ejemplo n.º 14
0
    def _tc_consume(self, data, clim):
        if not self._tc_producer:
            raise VIOError('No connected producer')
        elif not data:
            raise VIOError('No data to consume')
        elif self._handshake_error:
            raise VIOError('Error during handshaking')

        if self._handshaking:
            raise VIOError('Handshaking not completed')

        if self._ep_consumer:
            _lim = self._ep_consumer.consume(data, clim)
            self._ep_prod_lim = _lim
            if _lim >= 0:
                _lim = max(_lim - self._handshake_produced, 0)
            self._tc_cons_lim = _lim

        return self._tc_cons_lim
Ejemplo n.º 15
0
 def fileno(self):
     """See :meth:`versile.reactor.io.IVSelectable.fileno`\ ."""
     if not self.__sock:
         raise VIOError('No socket')
     try:
         fd = self.__sock.fileno()
     except socket.error:
         return -1
     else:
         return fd
Ejemplo n.º 16
0
    def _bp_attach(self, consumer, rthread=False):
        # Ensure 'attach' is performed in reactor thread
        if not rthread:
            self.reactor.execute(self._bp_attach, consumer, rthread=True)
            return

        if self.__bp_consumer is consumer:
            return
        if self.__bp_consumer:
            raise VIOError('Consumer already attached')
        elif self.__bp_eod:
            raise VIOError('Producer already reached end-of-data')
        self.__bp_consumer = consumer
        self.__bp_produced = self.__bp_produce_lim = 0
        consumer.attach(self.byte_produce)

        # Notify attached chain
        try:
            consumer.control.notify_producer_attached(self.byte_produce)
        except VIOMissingControl:
            pass
Ejemplo n.º 17
0
    def bind(self, host):
        """Bind to network interface.

        :param host: native socket address

        This method should not be called if the socket is already bound.

        """
        if self._bound:
            raise VIOError('Socket already bound')
        self.sock.bind(host)
        self._bound = True
Ejemplo n.º 18
0
    def _ep_attach(self, consumer, rthread=False):
        # Ensure 'attach' is performed in reactor thread
        if not rthread:
            self.reactor.execute(self._ep_attach, consumer, rthread=True)
            return

        if self._handshake_error:
            raise VIOError('Earlier error during handshaking')
        elif self._ep_consumer is consumer:
            return
        elif self._ep_consumer:
            raise VIOError('Consumer already attached')

        self.__ep_consumer = consumer
        self._ep_prod_lim = 0
        consumer.attach(self.external_produce)

        try:
            consumer.control.notify_producer_attached(self.external_produce)
        except VIOMissingControl:
            pass
Ejemplo n.º 19
0
    def create_native_pair(cls):
        """Create native Unix socket pair

        :returns: two paired native sockets (sock1, sock2)

        """
        if hasattr(socket, 'socketpair'):
            try:
                s1, s2 = socket.socketpair(socket.AF_UNIX)
                s1.setblocking(0)
                s2.setblocking(0)
            except socket.error:
                pass
            else:
                return (s1, s2)
        try:
            s = socket.socket(socket.AF_UNIX)
            cs1 = socket.socket(socket.AF_UNIX)
        except socket.error:
            raise VIOError('Could not create Unix socket')
        try:
            tempdir = tempfile.mkdtemp()
            s_file = os.path.join(tempdir, 's_file.socket')
            c_file = os.path.join(tempdir, 'c_file.socket')
            s.bind(s_file)
            cs1.bind(c_file)
            s.listen(1)
            cs1.connect(s_file)
            cs2, h = s.accept()
            if cs1.getpeername() == s_file and cs2.getpeername() == c_file:
                return (cs1, cs2)
            else:
                raise VIOError('Could not create socket pair')
        except socket.error:
            raise VIOError('Could not create socket pair')
        finally:
            s.close()
            os.remove(s_file)
            os.remove(c_file)
            os.rmdir(tempdir)
Ejemplo n.º 20
0
    def write_some(self, data):
        """See :meth:`versile.reactor.io.IVByteOutput.write_some`\ .

        .. note::

            Whereas :meth:`versile.reactor.io.sock.VSocket.write_some`
            is non-blocking, writing to a TLS socket is a blocking
            operation. This is due to limitations of the python 2.6
            ssl module. If non-blocking behavior is required then
            :func:`select.select` or similar should be called to check
            whether the socket is ready for writing.

        """
        if not self.was_connected:
            raise VIOError('Socket not connected')
        if self._sock_out_closed:
            if isinstance(self._sock_out_closed_reason, VFIOCompleted):
                raise VIOCompleted()
            else:
                raise VIOLost()
        if not self.sock:
            raise VIOError('No socket')
        try:
            num_written = self.sock.write(data)
        except IOError as e:
            if e.errno in _errno_wouldblock:
                return 0
            elif (e.errno in (errno.EPIPE, errno.ENOTCONN)
                  and not self._sock_verified):
                # ISSUE - see VSocket.write_some comments
                self.log.debug('Ignoring post-connect write errno %s' %
                               e.errno)
                return 0
            else:
                self.log.debug('Write got errno %s' % e.errno)
                raise VIOError('Socket write error, errno %s' % e.errno)
        else:
            self._sock_verified = True
            return num_written
Ejemplo n.º 21
0
    def _c_consume(self, buf, clim):
        if self._ci_eod:
            raise VIOClosed('Consumer already reached end-of-data')
        elif not self._ci_producer:
            raise VIOError('No connected producer')
        elif self._ci_consumed >= self._ci_lim_sent:
            raise VIOError('Consume limit exceeded')
        elif not buf:
            raise VIOError('No data to consume')

        max_cons = self._wbuf_len - len(self._wbuf)
        max_cons = min(max_cons, self._ci_lim_sent - self._ci_consumed)
        if clim is not None and clim > 0:
            max_cons = min(max_cons, clim)

        was_empty = not self._wbuf
        indata = buf.pop(max_cons)
        self._wbuf.append(indata)
        self._ci_consumed += len(indata)
        if was_empty:
            self.start_writing(internal=True)
        return self._ci_lim_sent
Ejemplo n.º 22
0
 def write_some(self, data):
     """See :meth:`versile.reactor.io.IVByteOutput.write_some`\ ."""
     if self._out_closed:
         if isinstance(self._out_closed_reason, VFIOCompleted):
             raise VIOCompleted()
         else:
             raise VIOLost()
     try:
         if _pyver == 2:
             data = _b2s(data)
         num_written = os.write(self._fd, data)
     except OSError as e:
         if e.errno in _errno_block:
             return 0
         else:
             self.log.debug('Write got errno %s' % e.errno)
             raise VIOError('Pipe read error')
     else:
         if num_written > 0:
             return num_written
         else:
             self.log.debug('Pipe write error')
             raise VIOError('Pipe write error')
Ejemplo n.º 23
0
 def read_some(self, max_len):
     """See :meth:`versile.reactor.io.IVByteInput.read_some`"""
     if self._in_closed:
         if isinstance(self._in_closed_reason, VFIOCompleted):
             raise VIOCompleted()
         else:
             raise VIOLost()
     try:
         data = os.read(self._fd, max_len)
         if _pyver == 2:
             data = _s2b(data)
     except OSError as e:
         if e.errno in _errno_block:
             return b''
         else:
             self.log.debug('Read got errno %s' % e.errno)
             raise VIOError('Pipe read error')
     else:
         if data:
             return data
         else:
             self.log.debug('Pipe read error')
             raise VIOError('Pipe read error')
Ejemplo n.º 24
0
    def _ec_consume(self, data, clim):
        if not self._ec_producer:
            raise VIOError('No connected external producer')
        elif not data:
            raise VIOError('No data to consume')
        elif self._handshake_error:
            raise VIOError('Earlier handshake error')

        # Handle handshake
        if self._handshaking:
            _len = len(data)
            self._handshake_consume(data, clim)
            if clim is not None:
                clim -= _len - len(data)

        # Handle post-handshake pass-through to transport
        if (not self._handshaking and self._tp_consumer and data
                and (clim is None or clim > 0)):
            _lim = self._tp_consumer.consume(data, clim)
            if _lim >= 0:
                _lim += self._handshake_consumed
            self._ec_cons_lim = _lim

        return self._ec_cons_lim
Ejemplo n.º 25
0
    def _ec_attach(self, producer, rthread=False):
        # Ensure 'attach' is performed from reactor thread
        if not rthread:
            self.reactor.execute(self._ec_attach, producer, rthread=True)
            return

        if self._handshake_error:
            raise VIOError('Earlier error during handshaking')
        elif self._ec_producer is producer:
            return
        elif self._ec_producer:
            raise VIOError('Producer already connected')

        self.__ec_producer = producer
        self._ec_cons_lim = 0
        producer.attach(self.external_consume)

        try:
            producer.control.notify_consumer_attached(self.external_consume)
        except VIOMissingControl:
            pass

        # Trigger any handshake actions
        self._handshake_producer_attached()
Ejemplo n.º 26
0
    def _bc_attach(self, producer, rthread=False):
        # Ensure 'attach' is performed in reactor thread
        if not rthread:
            self.reactor.execute(self._bc_attach, producer, rthread=True)
            return

        if self.__bc_producer is producer:
            return
        if self.__bc_eod:
            raise VIOError('Consumer already received end-of-data')
        elif self.__bc_producer:
            raise VIOError('Producer already connected')
        self.__bc_producer = producer
        self.__bc_consumed = 0
        self.__bc_consume_lim = self.__lim(len(self.__bc_rbuf),
                                           self.__bc_rbuf_len)
        producer.attach(self.byte_consume)
        producer.can_produce(self.__bc_consume_lim)

        # Notify attached chain
        try:
            producer.control.notify_consumer_attached(self.byte_consume)
        except VIOMissingControl:
            pass
Ejemplo n.º 27
0
    def _p_can_produce(self, limit):
        if not self._pi_consumer:
            raise VIOError('No connected consumer')

        if limit is None or limit < 0:
            if (not self._pi_prod_lim is None and not self._pi_prod_lim < 0):
                if self._pi_produced >= self._pi_prod_lim:
                    self.start_reading(internal=True)
                self._pi_prod_lim = limit
        else:
            if (self._pi_prod_lim is not None
                    and 0 <= self._pi_prod_lim < limit):
                if self._pi_produced >= self._pi_prod_lim:
                    self.start_reading(internal=True)
                self._pi_prod_lim = limit
Ejemplo n.º 28
0
 def _bp_can_produce(self, limit):
     if not self.__bp_consumer:
         raise VIOError('No attached consumer')
     if limit is None or limit < 0:
         if (not self.__bp_produce_lim is None
                 and not self.__bp_produce_lim < 0):
             self.__bp_produce_lim = limit
             if not self.__bp_scheduled_produce:
                 self.__bp_scheduled_produce = True
                 self.reactor.schedule(0.0, self.__bp_do_produce)
     else:
         if (self.__bp_produce_lim is not None
                 and 0 <= self.__bp_produce_lim < limit):
             self.__bp_produce_lim = limit
             if not self.__bp_scheduled_produce:
                 self.__bp_scheduled_produce = True
                 self.reactor.schedule(0.0, self.__bp_do_produce)
Ejemplo n.º 29
0
    def create_native_pair(cls):
        """Returns two native paired (connected) sockets.

        :returns: two paired native sockets
        :rtype:   :class:`socket.socket`\ , :class:`socket.socket`
        :raises:  :exc:`versile.reactor.io.VIOError`

        """
        # Use native socket.socketpair() if available
        if hasattr(socket, 'socketpair'):
            try:
                pair = socket.socketpair()
            except:
                raise VIOError('Could not create socket pair')
            else:
                return pair
        # Use local TCP socket implementation as a workaround
        from versile.reactor.io.tcp import VTCPSocket
        return VTCPSocket.create_native_pair()
Ejemplo n.º 30
0
    def connect(self, peer):
        """Connects to a peer.

        :param peer: peer to connect to
        :type  peer: :class:`versile.common.peer.VSocketPeer`
        :raises:     :exc:`versile.reactor.io.VIOError`

        Default creates a new native socket which is set up with
        *peer* socket parameters and performs :func:`sock.connect` on
        *peer.address*. The :class:`VClientSocket` then registers
        itself with the the reactor for writing to detect connect
        success. Upon immediate failure the socket is closed. Derived
        classes can override.

        """
        try:
            sock = peer.create_socket()
            sock.setblocking(False)
            # The discarded socket is discarded, so we close before replacing
            if self.sock:
                try:
                    self.sock.close()
                except socket.error as e:
                    _v_silent(e)
            self._sock_replace(sock)
            if not self._can_connect(peer):
                self.close_io(VFIOLost())
                raise VIOError('Not allowed to connect to host')
            self._sock_peer = peer
            self._set_sock_enabled()
            self.sock.connect(peer.address)
        except IOError as e:
            if e.errno in _errno_connect:
                self.start_writing()
            else:
                raise
        except Exception as e:
            self.log.debug('Connect exception')
            self.close_io(VFIOLost())
        else:
            self._set_sock_connected()