def connection_ready(sock, fd, events): while True: try: connection, address = sock.accept() except socket.error: exc = exception().exception if exc.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): raise return connection.setblocking(0) stream = iostream.IOStream(connection) def read_request(stream, request): def write_chunk(stream, chunks_written=0): if chunks_written == nchunks: stream.close() return stream.write(chunk, functools.partial(write_chunk, stream, chunks_written+1)) stream.write(b"HTTP/1.0 200 OK\r\nContent-Length: %s\r\n\r\n" % (clength, ), functools.partial(write_chunk, stream)) stream.read_bytes(82, functools.partial(read_request, stream))
def __call__(self, request): """Parse the JSON body and dispatch.""" if request.method != 'POST': raise HTTPMethodNotAllowed("POST required.") try: json = loads(request.body) except ValueError: raise HTTPBadRequest("Unable to parse JSON request.") for key in ('method', 'id'): if key not in json: raise HTTPBadRequest("Missing required JSON-RPC value: " + key) method = json['method'] args = json.get('params', []) id_ = json['id'] if not isinstance(args, list): raise HTTPBadRequest("Bad parameters, must be a list: {0!r}".format(args)) log.debug("JSON-RPC Call: %s%r", method, args) func, parent = route(self, method, JSONRPCController) log.debug("Found method: %r", func) try: callback = getattr(parent, '__before__', None) if callback: args = callback(*args) result = func(*args) error = None callback = getattr(parent, '__after__', None) if callback: result = callback(result, *args) except: log.exception("Error calling RPC mechanism.") exc = exception() web.core.response.status_int = 500 result = None error = dict( name='JSONRPCError', code=100, message=str(exc.exception), error="Not disclosed." if not web.core.config.get('debug', False) else exc.formatted ) else: log.debug("Got result: %r", result) if id_ is not None: web.core.response.content_type = 'application/json' return 'json:', dict(result=result, error=error, id=id_)
def read(self): """Emulate a file descriptors read method""" try: return self.reader.recv(1) except socket.error: ex = exception().exception if ex.args[0] == errno.EWOULDBLOCK: raise IOError raise
def test_exception(self): try: 1/0 except: exc = compat.exception() self.assertEquals(exc.name, 'ZeroDivisionError') self.assertEquals(exc.cls, ZeroDivisionError) self.assertTrue('division or modulo by zero' in exc.args[0]) try: raise Exception('foo', 1) except: exc = compat.exception() self.assertEquals(exc.name, 'Exception') self.assertEquals(exc.cls, Exception) self.assertEquals(exc.args, ('foo', 1))
def test_exception(self): try: 1 / 0 except: exc = compat.exception() self.assertEquals(exc.name, 'ZeroDivisionError') self.assertEquals(exc.cls, ZeroDivisionError) self.assertTrue('division or modulo by zero' in exc.args[0]) try: raise Exception('foo', 1) except: exc = compat.exception() self.assertEquals(exc.name, 'Exception') self.assertEquals(exc.cls, Exception) self.assertEquals(exc.args, ('foo', 1))
def __init__(self): # Based on Zope async.py: http://svn.zope.org/zc.ngi/trunk/src/zc/ngi/async.py self.writer = socket.socket() # Disable buffering -- pulling the trigger sends 1 byte, # and we want that sent immediately, to wake up ASAP. self.writer.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) count = 0 while 1: count += 1 # Bind to a local port; for efficiency, let the OS pick # a free port for us. # Unfortunately, stress tests showed that we may not # be able to connect to that port ("Address already in # use") despite that the OS picked it. This appears # to be a race bug in the Windows socket implementation. # So we loop until a connect() succeeds (almost always # on the first try). See the long thread at # http://mail.zope.org/pipermail/zope/2005-July/160433.html # for hideous details. a = socket.socket() a.bind(("127.0.0.1", 0)) connect_address = a.getsockname() # assigned (host, port) pair a.listen(1) try: self.writer.connect(connect_address) break # success except socket.error: detail = exception().exception if detail[0] != errno.WSAEADDRINUSE: # "Address already in use" is the only error # I've seen on two WinXP Pro SP2 boxes, under # Pythons 2.3.5 and 2.4.1. raise # (10048, 'Address already in use') # assert count <= 2 # never triggered in Tim's tests if count >= 10: # I've never seen it go above 2 a.close() self.writer.close() raise socket.error("Cannot bind trigger!") # Close `a` and try again. Note: I originally put a short # sleep() here, but it didn't appear to help or hurt. a.close() self.reader, addr = a.accept() self.reader.setblocking(0) self.writer.setblocking(0) a.close() self.reader_fd = self.reader.fileno()
def _do_ssl_handshake(self): # Based on code from test_ssl.py in the python stdlib try: self.socket.do_handshake() except ssl.SSLError: err = exception().exception if err.args[0] == ssl.SSL_ERROR_WANT_READ: self._add_io_state(self.io_loop.READ) return elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: self._add_io_state(self.io_loop.WRITE) return elif err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN): return self.close() elif err.args[0] == ssl.SSL_ERROR_SSL: self.close() raise except socket.error: err = exception().exception if err.args[0] == errno.ECONNABORTED: return self.close() else: self._ssl_accepting = False
def _accept(self, sock, fd, events): # TODO: Move this into the Server class. # Work that needs to be done can be issued within the real accept method. try: connection, address = sock.accept() except socket.error: exc = exception().exception if exc.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): raise return client = self.client(connection, io_loop=self.testing or None) self.accept(client)
def _handle_read(self): try: chunk = self.socket.recv(self.read_chunk_size) except socket.error: e = exception().exception if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): return else: log.warning("Read error on %d: %s", self.socket.fileno(), e) self.close() return if not chunk: self.close() return self._read_buffer += chunk rblen = len(self._read_buffer) if rblen >= self.max_buffer_size: log.error("Connection %d reached maximum read buffer size.", self.socket.fileno()) self.close() return if self._read_bytes: if rblen >= self._read_bytes: num_bytes = self._read_bytes callback = self._read_callback self._read_callback = None self._read_bytes = None self._run_callback(callback, self._consume(num_bytes)) elif self._read_delimiter: loc = self._read_buffer.find(self._read_delimiter) if loc != -1: callback = self._read_callback delimiter_len = len(self._read_delimiter) self._read_callback = None self._read_delimiter = None self._run_callback(callback, self._consume(loc + delimiter_len))
def __call__(self, environ, start_response=None): req = Request(environ) req.response = Response(req) try: resp = self.call(req, *self.args, **self.kw) except exc.HTTPException: resp = exception().exception if resp is None: return req.response(environ, start_response) # Handle None # Handle byte string # Handle unicode string # Handle file handle # Handle iterable # Handle exception # Handle response return resp(environ, start_response)
def _handle_write(self): while self._write_buffer: try: num_bytes = self.socket.send(self._write_buffer[:128 * 1024]) self._write_buffer = self._write_buffer[num_bytes:] except socket.error: e = exception().exception if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN): break else: log.warning("Write error on %d: %s", self.socket.fileno(), e) self.close() return # TODO: Allow multiple callbacks. if not self._write_buffer and self._write_callback: callback = self._write_callback self._write_callback = None self._run_callback(callback)
def start(self): """Starts the I/O loop. The loop will run until one of the I/O handlers calls stop(), which will make the loop stop after the current event iteration completes. """ log.debug("IOLoop reactor starting.") if self._stopped: self._stopped = False return self._running = True while True: # log.debug("Starting reactor tick.") # Never use an infinite timeout here - it can stall epoll poll_timeout = 0.2 # Prevent IO event starvation by delaying new callbacks # to the next iteration of the event loop. callbacks = list(self._callbacks) for callback in callbacks: # A callback can add or remove other callbacks if callback in self._callbacks: self._callbacks.remove(callback) self._run_callback(callback) if self._callbacks: # log.debug("Callbacks waiting, reducing timeout.") poll_timeout = 0.0 if self._timeouts: # log.debug("Timeouts pending.") now = time() while self._timeouts and self._timeouts[0].deadline <= now: timeout = self._timeouts.pop(0) self._run_callback(timeout.callback) if self._timeouts: milliseconds = self._timeouts[0].deadline - now poll_timeout = min(milliseconds, poll_timeout) if not self._running: log.debug("IOLoop reactor exiting.") break if self._blocking_log_threshold is not None: # clear alarm so it doesn't fire while poll is waiting for events. signal.setitimer(signal.ITIMER_REAL, 0, 0) try: event_pairs = self._impl.poll(poll_timeout) except Exception: exc = exception().exception # Depending on python version and IOLoop implementation, # different exception types may be thrown and there are # two ways EINTR might be signaled: # * e.errno == errno.EINTR # * e.args is like (errno.EINTR, 'Interrupted system call') # TODO: Determine if this can be simplified to: # if e.args == (4, "Interrupted system call"): if (getattr(exc, 'errno', None) == errno.EINTR or (isinstance(getattr(exc, 'args'), tuple) and len(exc.args) == 2 and exc.args[0] == errno.EINTR)): log.warning("Interrupted system call", exc_info=True) continue else: log.exception("Error polling.") raise if self._blocking_log_threshold is not None: signal.setitimer(signal.ITIMER_REAL, self._blocking_log_threshold, 0) # Pop one fd at a time from the set of pending fds and run # its handler. Since that handler may perform actions on # other file descriptors, there may be reentrant calls to # this IOLoop that update self._events self._events.update(event_pairs) while self._events: fd, events = self._events.popitem() try: self._handlers[fd](fd, events) except (KeyboardInterrupt, SystemExit): raise except (OSError, IOError): exc = exception().exception if exc.args[0] == errno.EPIPE: # Happens when the client closes the connection pass else: log.error("Exception in I/O handler for fd %d", fd, exc_info=True) except: log.error("Exception in I/O handler for fd %d", fd, exc_info=True) # reset the stopped flag so another start/stop pair can be issued self._stopped = False if self._blocking_log_threshold is not None: signal.setitimer(signal.ITIMER_REAL, 0, 0)