def test_handle_error_with_handler_and_no_err_msg(self):
    sock = EventSocket()
    sock._parent_error_cb = mock()

    expect(sock._parent_error_cb).args( sock, 'unknown error', 'exception' )

    sock._handle_error( 'exception' )
  def test_handle_error_no_handler_and_logger_and_no_err_msg(self):
    sock = EventSocket()
    sock._logger = mock()

    expect(sock._logger.error).args( 'unhandled unknown error', exc_info=True )

    sock._handle_error( 'exception' )
 def test_connect_sets_default_timeout_from_socket(self):
   now = time.time()
   sock = EventSocket()
   expect( time, 'time' ).returns( now )
   sock._sock.settimeout( 8.67 )
   expect( sock._connect_cb ).args( now+8.67, ('localhost',5309), immediate_raise=True )
   sock.connect( ('localhost',5309) )
 def test_parent_read_timer_cb_when_read_cb_reset(self):
   sock = EventSocket()
   sock._pending_read_cb_event = 'foo'
   
   sock._parent_read_timer_cb()
   assert_equals( 'error processing socket input buffer', sock._error_msg )
   assert_equals( None, sock._pending_read_cb_event )
  def test_handle_error_no_handler_and_no_logger(self):
    sock = EventSocket()
    mock( eventsocket, 'traceback' )

    expect(eventsocket.traceback.print_exc)

    sock._handle_error( 'exception' )
  def test_protected_cb_when_no_error(self):
    sock = EventSocket()
    cb = mock()

    expect( cb ).args( 'arg1', 'arg2', arg3='foo' ).returns( 'result' )

    assert_equals( 'result', 
      sock._protected_cb( cb, 'arg1', 'arg2', arg3='foo' ) )
  def test_connect_sets_timeoutat_from_kwargs(self):
    now = time.time()
    sock = EventSocket()
    expect( time, 'time' ).returns( now )
    expect( sock._connect_cb ).args( now+5.3, ('localhost',5309), immediate_raise=True )
    sock.connect( ('localhost',5309), timeout=5.3 )

    expect( sock._connect_cb ).args( now+5.7, ('localhost',5309), immediate_raise=True )
    sock.connect( ('localhost',5309), timeout_at=now+5.7 )
 def test_set_inactive_timeout_when_turning_off(self):
   sock = EventSocket()
   sock._inactive_event = mock()
   
   expect( sock._inactive_event.delete )
   
   sock.set_inactive_timeout(0)
   assert_equals( None, sock._inactive_event )
   assert_equals( 0, sock._inactive_timeout )
  def test_parent_read_timer_cb(self):
    sock = EventSocket()
    sock._pending_read_cb_event = 'foo'
    sock._parent_read_cb = mock()
    expect( sock._parent_read_cb ).args( sock )

    sock._parent_read_timer_cb()
    assert_equals( 'error processing socket input buffer', sock._error_msg )
    assert_equals( None, sock._pending_read_cb_event )
  def test_write_when_write_event_and_not_pending(self):
    sock = EventSocket()
    sock._write_event = mock()
    
    expect( sock._write_event.pending ).returns( False )
    expect( sock._write_event.add )
    expect( sock._flag_activity )

    sock.write( 'foo' )
    assert_equals( deque(['foo']), sock._write_buf )
  def test_bind_without_debugging(self):
    sock = EventSocket()
    sock._sock = mock()
    mock(sock, 'getsockname')
    
    expect(sock._sock.bind).args( 'arg1', 'arg2' )
    expect(sock.getsockname).returns( ('foo',1234) )
    expect(eventsocket.event.read).args( sock, sock._protected_cb, sock._accept_cb )

    sock.bind( 'arg1', 'arg2' )
  def test_set_inactive_timeout_when_turning_off(self):
    sock = EventSocket()
    sock._inactive_event = mock()
    
    expect( sock._inactive_event.delete )
    expect( eventsocket.event.timeout ).args( 32, sock._inactive_cb ).returns( 'new_timeout' )

    sock.set_inactive_timeout(32)
    assert_equals( 'new_timeout', sock._inactive_event )
    assert_equals( 32, sock._inactive_timeout )
  def test_connect_cb_when_fail_and_not_immediateraise(self):
    sock = EventSocket()
    mock( sock, '_sock' )
    mock( sock, '_connect_event' )
    
    expect( sock._sock.connect_ex ).args( ('.com', 1234) ).returns( errno.ECONNREFUSED )
    expect( sock._connect_event.delete )
    expect( sock._handle_error ).args( socket.error )

    sock._connect_cb( time.time(), ('.com',1234) )
  def test_connect_cb_when_ealready_and_timeout(self):
    sock = EventSocket()
    mock( sock, '_sock' )
    mock( sock, '_connect_event' ) # assert delete() not called

    timeout_at = time.time()-3
    expect( sock._sock.connect_ex ).args( ('.com', 1234) ).returns( errno.EALREADY )
    expect( sock.close )

    sock._connect_cb( timeout_at, ('.com',1234) )
  def test_connect_cb_when_einprogress_and_notimeout_and_no_pending_connect(self):
    sock = EventSocket()
    mock( sock, '_sock' )
    
    timeout_at = time.time()+3
    expect( sock._sock.connect_ex ).args( ('.com', 1234) ).returns( errno.EINPROGRESS )
    expect( eventsocket.event.timeout ).args( 0.1, sock._connect_cb, timeout_at, ('.com', 1234) ).returns( 'connectev' )

    sock._connect_cb( timeout_at, ('.com',1234) )
    assert_equals( 'connectev', sock._connect_event )
  def test_flag_activity(self):
    sock = EventSocket()
    sock._inactive_event = mock()
    sock._inactive_timeout = 42
    
    expect( sock._inactive_event.delete )
    expect( eventsocket.event.timeout ).args( 42, sock._protected_cb, sock._inactive_cb ).returns( 'doitagain' )

    sock._flag_activity()
    assert_equals( 'doitagain', sock._inactive_event )
 def test_read_cb_when_no_data(self):
   sock = EventSocket()
   sock._sock = mock()
   mock( sock, 'getsockopt' )
   mock( sock, 'close' )
   
   expect( sock.getsockopt ).args( socket.SOL_SOCKET, socket.SO_RCVBUF ).returns( 42 )
   expect( sock._sock.recv ).args( 42 ).returns( '' )
   expect( sock.close )
   
   assert_equals( None, sock._read_cb() )
 def test_read_cb_simplest_case(self):
   sock = EventSocket()
   sock._sock = mock()
   mock( sock, 'getsockopt' )
   
   expect( sock.getsockopt ).args( socket.SOL_SOCKET, socket.SO_RCVBUF ).returns( 42 )
   expect( sock._sock.recv ).args( 42 ).returns( 'sumdata' )
   expect( sock._flag_activity )
   
   assert_true( sock._read_cb() )
   assert_equals( bytearray('sumdata'), sock._read_buf )
   assert_equals( 'error reading from socket', sock._error_msg )
  def test_write_cb_when_not_all_data_sent(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_output_empty_cb = mock()  # assert not called
    sock._write_buf = deque(['data1','data2'])

    expect( sock._sock.send ).args( 'data1' ).returns( 5 )
    expect( sock._sock.send ).args( 'data2' ).returns( 2 )
    expect( sock._flag_activity )

    assert_true( sock._write_cb() )
    assert_equals( deque(['ta2']), sock._write_buf )
 def test_connect_cb_when_no_err_and_pending_connect_event(self):
   sock = EventSocket()
   mock( sock, '_sock' )
   mock( sock, '_connect_event' )
   expect( sock._sock.connect_ex ).args( ('.com', 1234) )
   expect( sock._sock.getpeername ).returns( ('.com', 1234) )
   expect( eventsocket.event.read ).any_args()
   expect( eventsocket.event.write ).any_args()
   expect( sock._connect_event.delete )
   
   sock._connect_cb( 3.14, ('.com',1234) )
   assert_equals( None, sock._connect_event )
  def test_write_cb_when_eagain_raised(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_output_empty_cb = mock()  # assert not called
    sock._write_buf = deque(['data1','data2'])

    expect( sock._sock.send ).args( 'data1' ).raises(
      EnvironmentError(errno.EAGAIN,'try again') )
    expect( sock._flag_activity )

    assert_true( sock._write_cb() )
    assert_equals( deque(['data1','data2']), sock._write_buf )
  def test_protected_cb_when_an_error(self):
    sock = EventSocket()
    #mock( sock, '_handle_error' )
    cb = mock()
    sock._error_msg = 'it broked'

    exc = RuntimeError('fale')
    expect( cb ).args( 'arg1', 'arg2', arg3='foo' ).raises( exc )
    expect( sock._handle_error ).args( exc )

    assert_equals( None,
      sock._protected_cb( cb, 'arg1', 'arg2', arg3='foo' ) )
    assert_equals( None, sock._error_msg )
 def test_connect_cb_when_no_err(self):
   sock = EventSocket()
   mock( sock, '_sock' )
   expect( sock._sock.connect_ex ).args( ('.com', 1234) )
   expect( sock._sock.getpeername ).returns( ('.com', 1234) )
   expect( eventsocket.event.read ).args( sock._sock, sock._protected_cb, sock._read_cb ).returns( 'readev' )
   expect( eventsocket.event.write ).args( sock._sock, sock._protected_cb, sock._write_cb ).returns( 'writeev' )
   
   sock._connect_cb( 3.14, ('.com',1234) )
   assert_equals( '.com:1234', sock._peername )
   assert_equals( 'readev', sock._read_event )
   assert_equals( 'writeev', sock._write_event )
   assert_equals( None, sock._connect_event )
  def test_write_cb_sends_all_data_and_theres_an_output_empty_cb(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_output_empty_cb = mock()
    sock._write_buf = deque(['data1','data2'])

    expect( sock._sock.send ).args( 'data1' ).returns( 5 )
    expect( sock._sock.send ).args( 'data2' ).returns( 5 )
    expect( sock._flag_activity )
    expect( sock._parent_output_empty_cb ).args( sock )

    assert_equals( None, sock._write_cb() )
    assert_equals( 0, len(sock._write_buf) )
  def test_accept_cb_when_logger_and_parent_cb(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_accept_cb = 'p_accept_cb'
    sock._parent_read_cb = 'p_read_cb'
    sock._parent_error_cb = 'p_error_cb'
    sock._parent_close_cb = 'p_close_cb'
    sock._debug = True
    sock._logger = mock()
    sock._max_read_buffer = 42

    expect(sock._sock.accept).returns( ('connection', 'address') )
    expect(sock._logger.debug).args( "accepted connection from address" )
    expect(EventSocket.__init__).args( read_cb='p_read_cb', error_cb='p_error_cb',
      close_cb='p_close_cb', sock='connection', debug=True,
      logger=sock._logger, max_read_buffer=42 )
    expect(sock._protected_cb).args( 'p_accept_cb', is_a(EventSocket) )

    assert_true( sock._accept_cb() )
  def test_accept_cb_when_no_logger_and_no_parent_cb(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_read_cb = 'p_read_cb'
    sock._parent_error_cb = 'p_error_cb'
    sock._parent_close_cb = 'p_close_cb'
    sock._debug = False
    sock._logger = None
    sock._max_read_buffer = 42

    expect(sock._sock.accept).returns( ('connection', 'address') )
    expect(EventSocket.__init__).args( read_cb='p_read_cb', error_cb='p_error_cb',
      close_cb='p_close_cb', sock='connection', debug=False,
      logger=None, max_read_buffer=42 )

    assert_true( sock._accept_cb() )
    assert_equals( 'error accepting new socket', sock._error_msg )
  def test_write_when_write_event_is_pending_and_debugging(self):
    sock = EventSocket()
    sock._write_event = mock()
    sock._peername = 'peername'
    sock._write_buf = deque(['data'])
    sock._debug = 2
    sock._logger = mock()
    
    expect( sock._write_event.pending ).returns( True )
    expect( sock._logger.debug ).args(str, 3, 7, 'peername')
    expect( sock._flag_activity )

    sock.write( 'foo' )
    assert_equals( deque(['data', 'foo']), sock._write_buf )
Beispiel #28
0
    def connect(self, host, port):
        '''
    Connect to a host and port. Can be called directly, or is called by the
    strategy as it tries to find and connect to hosts.
    '''
        # Clear the connect state immediately since we're no longer connected
        # at this point.
        self._connected = False

        # NOTE: purposefully leave output_frame_buffer alone so that pending writes can
        # still occur.  this allows the reconnect to occur silently without
        # completely breaking any pending data on, say, a channel that was just
        # opened.
        self._sock = EventSocket(read_cb=self._sock_read_cb,
                                 close_cb=self._sock_close_cb,
                                 error_cb=self._sock_error_cb,
                                 debug=self._debug,
                                 logger=self._logger)
        self._sock.settimeout(self._connect_timeout)
        if self._sock_opts:
            for k, v in self._sock_opts.iteritems():
                family, type = k
                self._sock.setsockopt(family, type, v)
        self._sock.connect((host, port))
        self._sock.setblocking(False)

        # Only after the socket has connected do we clear this state; closed must
        # be False so that writes can be buffered in writePacket().  The closed
        # state might have been set to True due to a socket error or a redirect.
        self._host = "%s:%d" % (host, port)
        self._closed = False
        self._close_info = {
            'reply_code': 0,
            'reply_text': 'failed to connect to %s' % (self._host),
            'class_id': 0,
            'method_id': 0
        }

        self._sock.write(PROTOCOL_HEADER)
  def test_write_cb_with_no_data(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._parent_output_empty_cb = mock()
    sock._debug = True
    sock._logger = mock()
    mock( sock, '_flag_activity' )
    sock._write_buf = deque()

    assert_equals( None, sock._write_cb() )
    assert_equals( sock._error_msg, "error writing socket output buffer" )
  def test_write_cb_when_not_all_data_sent_and_logging(self):
    sock = EventSocket()
    sock._sock = mock()
    sock._peername = 'peer'
    sock._logger = mock()
    sock._debug = True
    sock._parent_output_empty_cb = mock()  # assert not called
    sock._write_buf = deque(['data1','data2'])

    expect( sock._sock.send ).args( 'data1' ).returns( 5 )
    expect( sock._sock.send ).args( 'data2' ).returns( 2 )
    expect( sock._logger.debug ).args( str, 7, 10, 'peer' )
    expect( sock._flag_activity )

    assert_true( sock._write_cb() )
    assert_equals( deque(['ta2']), sock._write_buf )
  def test_set_read_cb_when_data_to_flush_but_pending_read_event(self):
    sock = EventSocket()
    sock._read_buf = bytearray('somedata')
    sock._parent_read_cb = None
    sock._pending_read_cb_event = 'pending_event'

    sock._set_read_cb( 'parent_read_cb' )
    assert_equals( 'parent_read_cb', sock._parent_read_cb )
    assert_equals( 'pending_event', sock._pending_read_cb_event )
Beispiel #32
0
class EventTransport(Transport):
  '''
  Transport using libevent-based EventSocket.
  '''
  
  def __init__(self, *args):
    super(EventTransport,self).__init__(*args)
    self._synchronous = False

  ###
  ### EventSocket callbacks
  ###
  def _sock_close_cb(self, sock):
    self._connection.transport_closed(
      msg='socket to %s closed unexpectedly'%(self._host),
    )

  def _sock_error_cb(self, sock, msg, exception=None):
    self._connection.transport_closed(
      msg='error on connection to %s: %s'%(self._host, msg)
    )

  def _sock_read_cb(self, sock):
    self.connection.read_frames()
  
  ###
  ### Transport API
  ###
  def connect(self, (host,port)):
    '''
    Connect assuming a host and port tuple. Implemented as non-blocking, and
    will close the transport if there's an error
    '''
    self._host = "%s:%s"%(host,port)
    self._sock = EventSocket(
      read_cb=self._sock_read_cb,
      close_cb=self._sock_close_cb, 
      error_cb=self._sock_error_cb,
      debug=self.connection.debug, 
      logger=self.connection.logger )
    if self.connection._sock_opts:
      for k,v in self.connection._sock_opts.iteritems():
        family,type = k
        self._sock.setsockopt(family, type, v)
    self._sock.setblocking( False )
    self._sock.connect( (host,port), timeout=self.connection._connect_timeout )
    self._heartbeat_timeout = None
Beispiel #33
0
class Connection(object):
    class TooManyChannels(ConnectionError):
        '''This connection has too many channels open.  Non-fatal.'''

    class InvalidChannel(ConnectionError):
        '''The channel id does not correspond to an existing channel.  Non-fatal.'''

    def __init__(self, **kwargs):
        '''
    Initialize the connection.
    '''
        self._debug = kwargs.get('debug', False)
        self._logger = kwargs.get('logger', root_logger)

        self._user = kwargs.get('user', 'guest')
        self._password = kwargs.get('password', 'guest')
        self._host = kwargs.get('host', 'localhost')
        self._vhost = kwargs.get('vhost', '/')

        self._connect_timeout = kwargs.get('connect_timeout', 5)
        self._sock_opts = kwargs.get('sock_opts')
        self._sock = None
        self._heartbeat = kwargs.get('heartbeat')
        self._reconnect_cb = kwargs.get('reconnect_cb')
        self._close_cb = kwargs.get('close_cb')

        self._login_method = kwargs.get('login_method', 'AMQPLAIN')
        self._locale = kwargs.get('locale', 'en_US')
        self._client_properties = kwargs.get('client_properties')

        self._properties = LIBRARY_PROPERTIES.copy()
        if self._client_properties:
            self._properties.update(self._client_properties)

        self._closed = False
        self._connected = False
        self._close_info = {
            'reply_code': 0,
            'reply_text': 'first connect',
            'class_id': 0,
            'method_id': 0
        }

        self._channels = {0: ConnectionChannel(self, 0)}

        login_response = Writer()
        login_response.write_table({
            'LOGIN': self._user,
            'PASSWORD': self._password
        })
        #stream = BytesIO()
        #login_response.flush(stream)
        #self._login_response = stream.getvalue()[4:]  #Skip the length
        #at the beginning
        self._login_response = login_response.buffer()[4:]

        self._channel_counter = 0
        self._channel_max = 65535
        self._frame_max = 65535

        self._frames_read = 0
        self._frames_written = 0

        self._strategy = kwargs.get('connection_strategy')
        if not self._strategy:
            self._strategy = ConnectionStrategy(
                self, self._host, reconnect_cb=self._reconnect_cb)
        self._strategy.connect()

        self._output_frame_buffer = []

    @property
    def logger(self):
        return self._logger

    @property
    def debug(self):
        return self._debug

    @property
    def frame_max(self):
        return self._frame_max

    @property
    def channel_max(self):
        return self._channel_max

    @property
    def frames_read(self):
        '''Number of frames read in the lifetime of this connection.'''
        return self._frames_read

    @property
    def frames_written(self):
        '''Number of frames written in the lifetime of this connection.'''
        return self._frames_written

    @property
    def close_info(self):
        '''Return dict with information on why this connection is closed.  Will
    return None if the connections is open.'''
        return self._close_info if self._closed else None

    def reconnect(self):
        '''Reconnect to the configured host and port.'''
        self._strategy.connect()

    def connect(self, host, port):
        '''
    Connect to a host and port. Can be called directly, or is called by the
    strategy as it tries to find and connect to hosts.
    '''
        # Clear the connect state immediately since we're no longer connected
        # at this point.
        self._connected = False

        # NOTE: purposefully leave output_frame_buffer alone so that pending writes can
        # still occur.  this allows the reconnect to occur silently without
        # completely breaking any pending data on, say, a channel that was just
        # opened.
        self._sock = EventSocket(read_cb=self._sock_read_cb,
                                 close_cb=self._sock_close_cb,
                                 error_cb=self._sock_error_cb,
                                 debug=self._debug,
                                 logger=self._logger)
        self._sock.settimeout(self._connect_timeout)
        if self._sock_opts:
            for k, v in self._sock_opts.iteritems():
                family, type = k
                self._sock.setsockopt(family, type, v)
        self._sock.connect((host, port))
        self._sock.setblocking(False)

        # Only after the socket has connected do we clear this state; closed must
        # be False so that writes can be buffered in writePacket().  The closed
        # state might have been set to True due to a socket error or a redirect.
        self._host = "%s:%d" % (host, port)
        self._closed = False
        self._close_info = {
            'reply_code': 0,
            'reply_text': 'failed to connect to %s' % (self._host),
            'class_id': 0,
            'method_id': 0
        }

        self._sock.write(PROTOCOL_HEADER)

    def disconnect(self):
        '''
    Disconnect from the current host, but otherwise leave this object "open"
    so that it can be reconnected.
    '''
        self._connected = False
        if self._sock != None:
            self._sock.close_cb = None
            try:
                self._sock.close()
            except:
                self.logger.error("Failed to disconnect socket to %s",
                                  self._host,
                                  exc_info=True)
            self._sock = None

    def add_reconnect_callback(self, callback):
        '''Adds a reconnect callback to the strategy.  This can be used to
    resubscribe to exchanges, etc.'''
        self._strategy.reconnect_callbacks.append(callback)

    ###
    ### EventSocket callbacks
    ###
    def _sock_read_cb(self, sock):
        '''
    Callback when there's data to read on the socket.
    '''
        try:
            self._read_frames()
        except:
            self.logger.error("Failed to read frames from %s",
                              self._host,
                              exc_info=True)
            self.close(reply_code=501, reply_text='Error parsing frames')

    def _sock_close_cb(self, sock):
        """
    Callback when socket closed.  This is intended to be the callback when the
    closure is unexpected.
    """
        self.logger.warning('socket to %s closed unexpectedly', self._host)
        self._close_info = {
            'reply_code': 0,
            'reply_text': 'socket closed unexpectedly to %s' % (self._host),
            'class_id': 0,
            'method_id': 0
        }

        # We're not connected any more (we're not closed but we're definitely not
        # connected)
        self._connected = False
        self._sock = None

        # Call back to a user-provided close function
        self._callback_close()

        # Fail and do nothing. If you haven't configured permissions and that's
        # why the socket is closing, this keeps us from looping.
        self._strategy.fail()

    def _sock_error_cb(self, sock, msg, exception=None):
        """
    Callback when there's an error on the socket.
    """
        self.logger.error('error on connection to %s: %s', self._host, msg)
        self._close_info = {
            'reply_code': 0,
            'reply_text': 'socket error on host %s: %s' % (self._host, msg),
            'class_id': 0,
            'method_id': 0
        }

        # we're not connected any more (we're not closed but we're definitely not
        # connected)
        self._connected = False
        self._sock = None

        # Call back to a user-provided close function
        self._callback_close()

        # Fail and try to reconnect, because this is expected to be a transient error.
        self._strategy.fail()
        self._strategy.next_host()

    ###
    ### Connection methods
    ###
    def _next_channel_id(self):
        '''Return the next possible channel id.  Is a circular enumeration.'''
        self._channel_counter += 1
        if self._channel_counter >= self._channel_max:
            self._channel_counter = 1
        return self._channel_counter

    def channel(self, channel_id=None):
        """
    Fetch a Channel object identified by the numeric channel_id, or
    create that object if it doesn't already exist.  If channel_id is not
    None but no channel exists for that id, will raise InvalidChannel.  If
    there are already too many channels open, will raise TooManyChannels.
    """
        if channel_id is None:
            # adjust for channel 0
            if len(self._channels) - 1 >= self._channel_max:
                raise Connection.TooManyChannels(
                    "%d channels already open, max %d",
                    len(self._channels) - 1, self._channel_max)
            channel_id = self._next_channel_id()
            while channel_id in self._channels:
                channel_id = self._next_channel_id()
        elif channel_id in self._channels:
            return self._channels[channel_id]
        else:
            raise Connection.InvalidChannel("%s is not a valid channel id",
                                            channel_id)

        # Call open() here so that ConnectionChannel doesn't have it called.  Could
        # also solve this other ways, but it's a HACK regardless.
        rval = Channel(self, channel_id)
        self._channels[channel_id] = rval
        rval.open()
        return rval

    def close(self, reply_code=0, reply_text='', class_id=0, method_id=0):
        '''
    Close this connection.
    '''
        self._close_info = {
            'reply_code': reply_code,
            'reply_text': reply_text,
            'class_id': class_id,
            'method_id': method_id
        }
        self._channels[0].close()

    def _close_socket(self):
        '''Close the socket.'''
        # The assumption here is that we don't want auto-reconnect to kick in if
        # the socket is purposefully closed.
        self._closed = True

        # By the time we hear about the protocol-level closure, the socket may
        # have already gone away.
        if self._sock != None:
            self._sock.close_cb = None
            try:
                self._sock.close()
            except:
                self.logger.error('error closing socket')
            self._sock = None

    def _callback_close(self):
        '''Callback to any close handler.'''
        if self._close_cb:
            try:
                self._close_cb()
            except SystemExit:
                raise
            except:
                self.logger.error('error calling close callback')

    def _read_frames(self):
        '''
    Read frames from the socket.
    '''
        # Because of the timer callback to dataRead when we re-buffered, there's a
        # chance that in between we've lost the socket.  If that's the case, just
        # silently return as some code elsewhere would have already notified us.
        # That bug could be fixed by improving the message reading so that we consume
        # all possible messages and ensure that only a partial message was rebuffered,
        # so that we can rely on the next read event to read the subsequent message.
        if self._sock is None:
            return

        data = self._sock.read()
        reader = Reader(data)
        p_channels = set()

        for frame in Frame.read_frames(reader):
            if self._debug > 1:
                self.logger.debug("READ: %s", frame)
            self._frames_read += 1
            ch = self.channel(frame.channel_id)
            ch.buffer_frame(frame)
            p_channels.add(ch)

        # Still not clear on what's the best approach here. It seems there's a
        # slight speedup by calling this directly rather than delaying, but the
        # delay allows for pending IO with higher priority to execute.
        self._process_channels(p_channels)
        #event.timeout(0, self._process_channels, p_channels)

        # HACK: read the buffer contents and re-buffer.  Would prefer to pass
        # buffer back, but there's no good way of asking the total size of the
        # buffer, comparing to tell(), and then re-buffering.  There's also no
        # ability to clear the buffer up to the current position.
        # NOTE: This will be cleared up once eventsocket supports the
        # uber-awesome buffering scheme that will utilize mmap.
        if reader.tell() < len(data):
            self._sock.buffer(data[reader.tell():])

    def _process_channels(self, channels):
        '''
    Walk through a set of channels and process their frame buffer. Will
    collect all socket output and flush in one write.
    '''
        for channel in channels:
            channel.process_frames()

    def _flush_buffered_frames(self):
        # In the rare case (a bug) where this is called but send_frame thinks
        # they should be buffered, don't clobber.
        frames = self._output_frame_buffer
        self._output_frame_buffer = []
        for frame in frames:
            self.send_frame(frame)

    def send_frame(self, frame):
        '''
    Send a single frame. If there is an output buffer, write to that, else send
    immediately to the socket.
    '''
        if self._closed:
            if self._close_info and len(self._close_info['reply_text']) > 0:
                raise ConnectionClosed("connection is closed: %s : %s"%\
                  (self._close_info['reply_code'],self._close_info['reply_text']) )
            raise ConnectionClosed("connection is closed")

        if self._sock == None or (not self._connected
                                  and frame.channel_id != 0):
            self._output_frame_buffer.append(frame)
            return

        if self._debug > 1:
            self.logger.debug("WRITE: %s", frame)

        buf = bytearray()
        frame.write_frame(buf)
        self._sock.write(buf)

        self._frames_written += 1