def _read_line_coro(self, max_len, delimiter): """Helper coroutine created by `read_line` if data is not immediately available.""" while True: # Wait for the socket to be readable yield reactor.wait_for_readable(self.remote_sock) # Read more data try: new_data = self.remote_sock.recv(max_len) except socket.error, exc: if exc[0] in _AGAIN: # Nope, still not readable. continue else: raise exc if not new_data: raise ConnectionClosedException() self._buffer += new_data # Check if the delimiter is already in the buffer. if delimiter in self._buffer[:max_len]: out, self._buffer = self._buffer.split(delimiter, 1) raise StopIteration(out) # If not, and the buffer's longer than our expected line, # we've had an overflow if len(self._buffer) > max_len: raise ConnectionOverflowException()
def recv(self, buflen): """ Read data from the socket. This behaves analogously to the ``recv`` system call, but will read data from the internal buffer if available. """ if self._buffer: if len(self._buffer) > buflen: out = self._buffer[:buflen] self._buffer = self._buffer[buflen:] else: out = self._buffer self._buffer = "" raise StopIteration(out) while True: # Try reading the data. try: res = self.remote_sock.recv(buflen) except socket.error, exc: # If we would have blocked, try again later. if exc[0] not in _AGAIN: raise exc else: raise StopIteration(res) yield reactor.wait_for_readable(self.remote_sock)
def _sendall_coro(self, data): """Helper coroutine created by `sendall` if not all data could be sent.""" while data: yield reactor.wait_for_readable(self.remote_sock) try: res = self.remote_sock.send(data) except socket.error, exc: # If the write would block, just loop around and try later. if exc[0] not in _AGAIN: raise exc data = data[res:]
def read_exactly(self, length, read_increment = 32768): """ Read and return exactly ``length`` bytes. :param length: Number of bytes to return. :param read_increment: Number of bytes to low-level read from the socket at a time. """ # If we have enough bytes already, just return them if len(self._buffer) >= length: out = self._buffer[:length] self._buffer = self._buffer[length:] raise StopIteration(out) while True: # If not, attempt to recv() bytes_left = length - len(self._buffer) try: new_data = self.remote_sock.recv(min(bytes_left, read_increment)) except socket.error, exc: # Reraise any unexpected errors. if exc[0] not in _AGAIN: raise exc else: # We successfully recv()'d; add the data to the buffer # and then check if it's enough. if not new_data: raise ConnectionClosedException() self._buffer += new_data if len(self._buffer) >= length: out = self._buffer[:length] self._buffer = self._buffer[length:] raise StopIteration(out) # Wait for readability, then try the recv again. yield reactor.wait_for_readable(self.remote_sock)
def acceptor(self): """Main coroutine function. Continously calls accept() and creates new connection objects. """ # Continuously accept new connections while True: # Keep trying to accept() until we get a socket while True: try: client_socket, client_addr = self.master_socket.accept() except socket.error, exc: if exc[0] not in _AGAIN: print "Error in accept(): %s" % exc yield reactor.wait_for_readable(self.master_socket) else: break # Create a new TCPConnection for the socket new_conn = self.connection_class(client_addr, client_socket, self) self.connections[id(new_conn)] = new_conn new_conn.start()