def send_data(self, data, callback=None): """Send data out to the socket when it becomes ready. NOTE: currently we have no way of detecting when the data gets sent out, or if errors happen. """ if not self.isOpen(): raise ValueError("Socket not connected") self.toSend.append(_Packet(data, callback)) self.to_send_length += len(data) eventloop.add_write_callback(self.socket, self.onWriteReady)
def handleSocketError(self, code, msg, operation): if code in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): if self.interruptedOperation is None: self.interruptedOperation = operation elif self.interruptedOperation != operation: signals.system.failed("When talking to the network", details="socket error for the wrong SSL operation") self.close_connection() return eventloop.stop_handling_socket(self.socket) if code == socket.SSL_ERROR_WANT_READ: eventloop.add_read_callback(self.socket, self.onReadReady) else: eventloop.add_write_callback(self.socket, self.onWriteReady) elif code in (socket.SSL_ERROR_ZERO_RETURN, socket.SSL_ERROR_SSL, socket.SSL_ERROR_SYSCALL, socket.SSL_ERROR_EOF): self.handleEarlyClose(operation) else: super(AsyncSSLStream, self).handleSocketError(code, msg, operation)
def add_write_callback(self, socket, callback): eventloop.add_write_callback(socket, callback)
def resumeNormalCallbacks(self): if self.readCallback is not None: eventloop.add_read_callback(self.socket, self.onReadReady) if len(self.toSend) != 0: eventloop.add_write_callback(self.socket, self.onWriteReady)
class AsyncSocket(object): """Socket class that uses the eventloop module. """ MEMORY_ERROR_LIMIT = 5 def __init__(self, closeCallback=None): """Create an AsyncSocket. If closeCallback is given, it will be called if we detect that the socket has been closed durring a read/write operation. The arguments will be the AsyncSocket object and either socket.SHUT_RD or socket.SHUT_WR. """ self.toSend = [] self.to_send_length = 0 self.readSize = 4096 self.socket = None self.readCallback = None self.closeCallback = closeCallback self.readTimeout = None self.timedOut = False self.connectionErrback = None self.disable_read_timeout = False self.readSomeData = False self.name = "" self.lastClock = None self.memoryErrors = 0 def __str__(self): if self.name: return "%s: %s" % (type(self).__name__, self.name) else: return "Unknown %s" % (type(self).__name__,) # The complication in the timeout code is because creating and # cancelling a timeout costs some memory (timeout is in memory # until it goes off, even if cancelled.) def startReadTimeout(self): if self.disable_read_timeout: return self.lastClock = clock() if self.readTimeout is not None: return self.readTimeout = eventloop.add_timeout(SOCKET_INITIAL_READ_TIMEOUT, self.onReadTimeout, "AsyncSocket.onReadTimeout") def stopReadTimeout(self): if self.readTimeout is not None: self.readTimeout.cancel() self.readTimeout = None def _pick_address(self, addresses): """Pick the best entry to use from a list of addresses :param addresses: list of address tuples returned by getaddrinfo() :returns: one of the tuples, or None if no address could be found """ if not app.config.get(prefs.DISABLE_IPV6) and util.use_ipv6(): # prefer ipv6 if possible for entry in addresses: if entry[0] == socket.AF_INET6: return entry # fall back on ipv4 for entry in addresses: if entry[0] == socket.AF_INET: return entry return None def open_connection(self, host, port, callback, errback, disable_read_timeout=None): """Open a connection. On success, callback will be called with this object. """ if disable_read_timeout is not None: self.disable_read_timeout = disable_read_timeout self.name = "Outgoing %s:%s" % (host, port) self.connectionErrback = errback def handleGetAddrInfoException(e): if self.connectionErrback is None: # called connectionErrback while we were waiting for # getaddrinfo return trap_call(self, errback, ConnectionError(e[1] + " (host: %s)" % host)) def createSocketHandle(family): try: self.socket = socket.socket(family, socket.SOCK_STREAM) except socket.error, e: trap_call(self, errback, ConnectionError(e[1])) return self.socket.setblocking(0) return self.socket def onAddressLookup(addresses): if self.connectionErrback is None: # called connectionErrback while we were waiting for # getaddrinfo return entry = self._pick_address(addresses) if entry is None: # FIXME - wtf kind of user message is this? it's too # technical and there's no way a user would know what # to do about it. msg = _("Couldn't find address family to use") trap_call(self, errback, ConnectionError(msg)) return try: self.socket = socket.socket(entry[0], socket.SOCK_STREAM) except socket.error, e: trap_call(self, errback, ConnectionError(e[1])) return self.socket.setblocking(0) try: rv = self.socket.connect_ex(entry[4]) except socket.gaierror: trap_call(self, errback, ConnectionError('gaierror')) return if rv in (0, errno.EINPROGRESS, errno.EWOULDBLOCK): eventloop.add_write_callback(self.socket, onWriteReady) self.socketConnectTimeout = eventloop.add_timeout( SOCKET_CONNECT_TIMEOUT, onWriteTimeout, "socket connect timeout") else: fullmsg = "Connection failed" trap_call(self, errback, ConnectionError(fullmsg))