def server(sock, site, log=None, max_size=512, serv=None, max_http_version=DEFAULT_MAX_HTTP_VERSION): pool = Pool(max_size=max_size) if serv is None: serv = Server(sock, sock.getsockname(), site, log, max_http_version=max_http_version) try: serv.log.write("httpd starting up on %s\n" % (sock.getsockname(), )) while True: try: new_sock, address = sock.accept() proto = HttpProtocol(new_sock, address, serv) pool.execute_async(proto.handle) api.sleep(0) # sleep to allow other coros to run except KeyboardInterrupt: api.get_hub().remove_descriptor(sock.fileno()) serv.log.write("httpd exiting\n") break finally: try: sock.close() except socket.error: pass
def server(sock, site, log=None, environ=None, max_size=None, max_http_version=DEFAULT_MAX_HTTP_VERSION, protocol=HttpProtocol, server_event=None, minimum_chunk_size=None): serv = Server(sock, sock.getsockname(), site, log, environ=None, max_http_version=max_http_version, protocol=protocol, minimum_chunk_size=minimum_chunk_size) if server_event is not None: server_event.send(serv) if max_size is None: max_size = DEFAULT_MAX_SIMULTANEOUS_REQUESTS pool = Pool(max_size=max_size) try: host, port = sock.getsockname() port = ':%s' % (port, ) if sock.is_secure: scheme = 'https' if port == ':443': port = '' else: scheme = 'http' if port == ':80': port = '' print "(%s) wsgi starting up on %s://%s%s/" % (os.getpid(), scheme, host, port) while True: try: try: client_socket = sock.accept() except socket.error, e: if e[0] != errno.EPIPE and e[0] != errno.EBADF: raise pool.execute_async(serv.process_request, client_socket) except KeyboardInterrupt: api.get_hub().remove_descriptor(sock.fileno()) print "wsgi exiting" break
def __call__(self, *args): if not self.called: self.called = True cb, args, kw = self.tpl try: cb(*args, **kw) finally: get_hub().timer_finished(self)
def test_explicit_hub(self): oldhub = api.get_hub() try: api.use_hub(Foo) assert isinstance(api.get_hub(), Foo), api.get_hub() finally: api._threadlocal.hub = oldhub check_hub()
def spawn_greenlet(function, *args): """Create a new greenlet that will run `function(*args)'. The current greenlet won't be unscheduled. Keyword arguments aren't supported (limitation of greenlet), use spawn() to work around that. """ g = api.Greenlet(function) g.parent = api.get_hub().greenlet api.get_hub().schedule_call_global(0, g.switch, *args) return g
def __call__(self, *args): if not self.called: self.called = True if self.greenlet is not None and self.greenlet.dead: return cb, args, kw = self.tpl try: cb(*args, **kw) finally: get_hub().timer_finished(self)
def cancel(self): """Prevent this timer from being called. If the timer has already been called, has no effect. """ self._cancelled = True self.called = True get_hub().timer_canceled(self) try: del self.tpl except AttributeError: pass
def server(sock, site, log=None, environ=None, max_size=None, max_http_version=DEFAULT_MAX_HTTP_VERSION, protocol=HttpProtocol, server_event=None, minimum_chunk_size=None): serv = Server(sock, sock.getsockname(), site, log, environ=None, max_http_version=max_http_version, protocol=protocol, minimum_chunk_size=minimum_chunk_size) if server_event is not None: server_event.send(serv) if max_size is None: max_size = DEFAULT_MAX_SIMULTANEOUS_REQUESTS pool = Pool(max_size=max_size) try: host, port = sock.getsockname() port = ':%s' % (port, ) if sock.is_secure: scheme = 'https' if port == ':443': port = '' else: scheme = 'http' if port == ':80': port = '' print("(%s) wsgi starting up on %s://%s%s/" % (os.getpid(), scheme, host, port)) while True: try: try: client_socket = sock.accept() except socket.error as e: if e[0] != errno.EPIPE and e[0] != errno.EBADF: raise pool.execute_async(serv.process_request, client_socket) except KeyboardInterrupt: api.get_hub().remove_descriptor(sock.fileno()) print("wsgi exiting") break finally: try: sock.close() except socket.error as e: if e[0] != errno.EPIPE: raise
def acquire(self, blocking=True): if not blocking and self.locked(): return False while self.counter <= 0: self._waiters[api.getcurrent()] = None try: api.get_hub().switch() finally: self._waiters.pop(api.getcurrent(), None) self.counter -= 1 return True
def killall(procs, *throw_args, **kwargs): if not throw_args: throw_args = (ProcExit, ) wait = kwargs.pop('wait', False) if kwargs: raise TypeError('Invalid keyword argument for proc.killall(): %s' % ', '.join(kwargs.keys())) for g in procs: if not g.dead: api.get_hub().schedule_call_global(0, g.throw, *throw_args) if wait and api.getcurrent() is not api.get_hub().greenlet: api.sleep(0)
def check_hub(): # Clear through the descriptor queue api.sleep(0) api.sleep(0) hub = api.get_hub() for nm in 'get_readers', 'get_writers', 'get_excs': dct = getattr(hub, nm)() assert not dct, "hub.%s not empty: %s" % (nm, dct) # Stop the runloop (unless it's twistedhub which does not support that) if not getattr(api.get_hub(), 'uses_twisted_reactor', None): api.get_hub().abort() api.sleep(0)
def killall(procs, *throw_args, **kwargs): if not throw_args: throw_args = (ProcExit, ) wait = kwargs.pop('wait', False) if kwargs: raise TypeError('Invalid keyword argument for proc.killall(): %s' % ', '.join(list(kwargs.keys()))) for g in procs: if not g.dead: api.get_hub().schedule_call_global(0, g.throw, *throw_args) if wait and api.getcurrent() is not api.get_hub().greenlet: api.sleep(0)
def wait(self): """Wait until send or send_exception is called. Return value passed into send() or raise exception passed into send_exception(). """ assert self.greenlet is None current = api.getcurrent() assert current is not api.get_hub().greenlet self.greenlet = current try: return api.get_hub().switch() finally: self.greenlet = None
def _do_send(self, links, consult): while links: listener, link = links.pop() try: if listener in consult: try: link(self) finally: consult.pop(listener, None) except: api.get_hub().schedule_call_global(0, self._do_send, links, consult) raise
def kill(self, *throw_args): """Raise an exception in the greenlet. Unschedule the current greenlet so that this Proc can handle the exception (or die). The exception can be specified with throw_args. By default, ProcExit is raised. """ if not self.dead: if not throw_args: throw_args = (ProcExit, ) api.get_hub().schedule_call_global(0, self.greenlet.throw, *throw_args) if api.getcurrent() is not api.get_hub().greenlet: api.sleep(0)
def send(self, result=None, exc=None): """Makes arrangements for the waiters to be woken with the result and then returns immediately to the parent. >>> from eventlib import coros, api >>> evt = coros.event() >>> def waiter(): ... print 'about to wait' ... result = evt.wait() ... print 'waited for', result >>> _ = api.spawn(waiter) >>> api.sleep(0) about to wait >>> evt.send('a') >>> api.sleep(0) waited for a It is an error to call send() multiple times on the same event. >>> evt.send('whoops') Traceback (most recent call last): ... AssertionError: Trying to re-send() an already-triggered event. Use reset() between send()s to reuse an event object. """ assert self._result is NOT_USED, "Trying to re-send() an already-triggered event." self._result = result if exc is not None and not isinstance(exc, tuple): exc = (exc,) self._exc = exc hub = api.get_hub() if self._waiters: hub.schedule_call_global(0, self._do_send, self._result, self._exc, self._waiters.keys())
def _write_chunk(self, chunk, response_cb=None): assert chunk.transaction_id not in self.expected_responses, "MSRP transaction %r is already in progress" % chunk.transaction_id self.msrp.write_chunk(chunk) if response_cb is not None: timer = api.get_hub().schedule_call_global(self.RESPONSE_TIMEOUT, self._response_timeout, chunk.transaction_id, Response408Timeout) self.expected_responses[chunk.transaction_id] = (response_cb, timer) self.last_expected_response = time() + self.RESPONSE_TIMEOUT
def wait(self): """Wait until another coroutine calls send. Returns the value the other coroutine passed to send. >>> from eventlib import coros, api >>> evt = coros.event() >>> def wait_on(): ... retval = evt.wait() ... print "waited for", retval >>> _ = api.spawn(wait_on) >>> evt.send('result') >>> api.sleep(0) waited for result Returns immediately if the event has already occured. >>> evt.wait() 'result' """ if self._result is NOT_USED: self._waiters[api.getcurrent()] = True try: return api.get_hub().switch() finally: self._waiters.pop(api.getcurrent(), None) if self._exc is not None: api.getcurrent().throw(*self._exc) return self._result
def block_on(deferred): cur = [getcurrent()] synchronous = [] def cb(value): if cur: if getcurrent() is cur[0]: synchronous.append((value, None)) else: cur[0].switch(value) return value def eb(failure): if cur: if getcurrent() is cur[0]: synchronous.append((None, failure)) else: failure.throwExceptionIntoGenerator(cur[0]) deferred.addCallbacks(cb, eb) if synchronous: result, failure = synchronous[0] if failure is not None: failure.raiseException() return result try: return get_hub().switch() finally: del cur[0]
def send_exception(self, *throw_args): """Make greenlet calling wait() wake up (if there is a wait()). Can only be called from get_hub().greenlet. """ assert api.getcurrent() is api.get_hub().greenlet if self.greenlet is not None: self.greenlet.throw(*throw_args)
def send(self, value): """Wake up the greenlet that is calling wait() currently (if there is one). Can only be called from get_hub().greenlet. """ assert api.getcurrent() is api.get_hub().greenlet if self.greenlet is not None: self.greenlet.switch(value)
def send(self, result=None, exc=None): """Makes arrangements for the waiters to be woken with the result and then returns immediately to the parent. >>> from eventlib import coros, api >>> evt = coros.event() >>> def waiter(): ... print 'about to wait' ... result = evt.wait() ... print 'waited for', result >>> _ = api.spawn(waiter) >>> api.sleep(0) about to wait >>> evt.send('a') >>> api.sleep(0) waited for a It is an error to call send() multiple times on the same event. >>> evt.send('whoops') Traceback (most recent call last): ... AssertionError: Trying to re-send() an already-triggered event. Use reset() between send()s to reuse an event object. """ assert self._result is NOT_USED, 'Trying to re-send() an already-triggered event.' self._result = result if exc is not None and not isinstance(exc, tuple): exc = (exc, ) self._exc = exc hub = api.get_hub() if self._waiters: hub.schedule_call_global(0, self._do_send, self._result, self._exc, list(self._waiters.keys()))
def close(self, *args, **kw): if self.closed: return self.closed = True if self.is_secure: # *NOTE: This is not quite the correct SSL shutdown sequence. # We should actually be checking the return value of shutdown. # Note also that this is not the same as calling self.shutdown(). self.fd.shutdown() fn = self.close = self.fd.close try: res = fn(*args, **kw) finally: # This will raise socket.error(32, 'Broken pipe') if there's # a caller waiting on trampoline (e.g. server on .accept()) get_hub().exc_descriptor(self._fileno) return res
def test_schedule(self): hub = api.get_hub() # clean up the runloop, preventing side effects from previous tests # on this thread if hub.running: hub.abort() api.sleep(0) called = [] #t = timer.Timer(0, lambda: (called.append(True), hub.abort())) #t.schedule() # let's have a timer somewhere in the future; make sure abort() still works # (for libevent, its dispatcher() does not exit if there is something scheduled) # XXX libevent handles this, other hubs do not #api.get_hub().schedule_call_global(10000, lambda: (called.append(True), hub.abort())) api.get_hub().schedule_call_global(0, lambda: (called.append(True), hub.abort())) hub.default_sleep = lambda: 0.0 hub.switch() assert called assert not hub.running
def testInterruptedTimeout(self): # XXX I don't know how to do this test on MSWindows or any other # plaform that doesn't support signal.alarm() or os.kill(), though # the bug should have existed on all platforms. if not hasattr(signal, "alarm"): return # can only test on *nix self.serv.settimeout(5.0) # must be longer than alarm class Alarm(Exception): pass def alarm_handler(signal, frame): raise Alarm from eventlib.api import get_hub if hasattr(get_hub(), 'signal'): myalarm = get_hub().signal(signal.SIGALRM, alarm_handler) else: old_alarm = signal.signal(signal.SIGALRM, alarm_handler) try: try: signal.alarm( 2) # POSIX allows alarm to be up to 1 second early try: foo = self.serv.accept() except socket.timeout: self.fail("caught timeout instead of Alarm") except Alarm: pass except: self.fail("caught other exception instead of Alarm") else: self.fail("nothing caught") signal.alarm(0) # shut off alarm except Alarm: self.fail("got Alarm in wrong place") finally: # no alarm can be pending. Safe to restore old handler. if hasattr(get_hub(), 'signal'): myalarm.cancel() else: signal.signal(signal.SIGALRM, old_alarm)
def test_sleep(self): # even if there was an error in the mainloop, the hub should continue to work start = time.time() api.sleep(DELAY) delay = time.time() - start assert delay >= DELAY * 0.9, 'sleep returned after %s seconds (was scheduled for %s)' % ( delay, DELAY) def fail(): 1 / 0 api.get_hub().schedule_call_global(0, fail) start = time.time() api.sleep(DELAY) delay = time.time() - start assert delay >= DELAY * 0.9, 'sleep returned after %s seconds (was scheduled for %s)' % ( delay, DELAY)
def backdoor(connection, locals=None): """ Use this with tcp_server like so: api.tcp_server( api.tcp_listener(('127.0.0.1', 9000)), backdoor.backdoor, {}) """ (conn, addr) = connection host, port = addr print("backdoor to %s:%s" % (host, port)) fl = conn.makeGreenFile("rw") fl.newlines = '\n' greenlet = SocketConsole(fl, (host, port), locals) hub = api.get_hub() hub.schedule_call_global(0, greenlet.switch)
def backdoor_server(server, locals=None): print "backdoor listening on %s:%s" % server.getsockname() try: try: while True: (conn, (host, port)) = server.accept() print "backdoor connected to %s:%s" % (host, port) fl = conn.makeGreenFile("rw") fl.newlines = '\n' greenlet = SocketConsole(fl, (host, port), locals) hub = api.get_hub() hub.schedule_call_global(0, greenlet.switch) except socket.error, e: # Broken pipe means it was shutdown if e[0] != 32: raise finally: server.close()
def backdoor_server(server, locals=None): print("backdoor listening on %s:%s" % server.getsockname()) try: try: while True: (conn, (host, port)) = server.accept() print("backdoor connected to %s:%s" % (host, port)) fl = conn.makeGreenFile("rw") fl.newlines = '\n' greenlet = SocketConsole(fl, (host, port), locals) hub = api.get_hub() hub.schedule_call_global(0, greenlet.switch) except socket.error as e: # Broken pipe means it was shutdown if e[0] != 32: raise finally: server.close()
def _tasklet_loop(self): deque = self.deque = collections.deque() hub = api.get_hub() current = greenlet.getcurrent() def switch(g, value=None, exc=None): if exc is None: return g.switch(value) else: return g.throw(exc) direction, caller, args = switch(current.parent or current) try: while True: if direction == -1: # waiting to receive if self.balance > 0: sender, args = deque.popleft() hub.schedule_call(0, switch, sender) hub.schedule_call(0, switch, caller, *args) else: deque.append(caller) else: # waiting to send if self.balance < 0: receiver = deque.popleft() hub.schedule_call(0, switch, receiver, *args) hub.schedule_call(0, switch, caller) else: deque.append((caller, args)) self.balance += direction direction, caller, args = hub.switch() finally: deque.clear() del self.deque self.balance = 0
else: r.install() use_hub('twistedr') def parse_args(): hub = None reactor = None del sys.argv[0] # kill with_eventlib.py if sys.argv[0] == '--hub': del sys.argv[0] hub = sys.argv[0] del sys.argv[0] if sys.argv[0] == '--reactor': del sys.argv[0] reactor = sys.argv[0] del sys.argv[0] return hub, reactor if __name__ == '__main__': hub, reactor = parse_args() setup_hub(hub, reactor) from eventlib.api import get_hub hub = get_hub() # set up the hub now print('===HUB=%r' % hub) if 'twisted.internet.reactor' in sys.modules: print('===REACTOR=%r' % sys.modules['twisted.internet.reactor']) sys.stdout.flush() exec(compile(open(sys.argv[0]).read(), sys.argv[0], 'exec'))
class TestApi(TestCase): mode = 'static' certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') def test_tcp_listener(self): socket = api.tcp_listener(('0.0.0.0', 0)) assert socket.getsockname()[0] == '0.0.0.0' socket.close() check_hub() def test_connect_tcp(self): def accept_once(listenfd): try: conn, addr = listenfd.accept() fd = conn.makeGreenFile() conn.close() fd.write('hello\n') fd.close() finally: listenfd.close() server = api.tcp_listener(('0.0.0.0', 0)) api.spawn(accept_once, server) client = api.connect_tcp(('127.0.0.1', server.getsockname()[1])) fd = client.makeGreenFile() client.close() assert fd.readline() == b'hello\n' assert fd.read() == '' fd.close() check_hub() def test_connect_ssl(self): def accept_once(listenfd): try: listenfd.do_handshake_on_connect=False conn, addr = listenfd.accept() fl = conn.makeGreenFile('w') fl.write('hello\r\n') fl.close() conn.close() finally: listenfd.close() server = api.ssl_listener(('0.0.0.0', 0), self.certificate_file, self.private_key_file) api.spawn(accept_once, server) client = util.wrap_ssl( api.connect_tcp(('127.0.0.1', server.getsockname()[1])), self.certificate_file, self.private_key_file ) #client = client.makeGreenFile() #assert client.readline() == b'hello\r\n' assert client.read() == '' client.close() def test_server(self): connected = [] server = api.tcp_listener(('0.0.0.0', 0)) bound_port = server.getsockname()[1] def accept_twice(client): (conn, addr) = client connected.append(True) conn.close() if len(connected) == 2: server.close() api.call_after(0, api.connect_tcp, ('127.0.0.1', bound_port)) api.call_after(0, api.connect_tcp, ('127.0.0.1', bound_port)) try: api.tcp_server(server, accept_twice) except: api.sleep(0.1) raise assert len(connected) == 2 check_hub() def test_001_trampoline_timeout(self): server = api.tcp_listener(('0.0.0.0', 0)) bound_port = server.getsockname()[1] try: desc = greenio.GreenSocket(util.tcp_socket()) desc.connect(('127.0.0.1', bound_port)) api.trampoline(desc, read=True, write=False, timeout=0.1) except api.TimeoutError: pass # test passed else: assert False, "Didn't timeout" check_hub() def test_timeout_cancel(self): server = api.tcp_listener(('0.0.0.0', 0)) bound_port = server.getsockname()[1] def client_connected(server): (conn, addr) = server conn.close() def go(): client = util.tcp_socket() desc = greenio.GreenSocket(client) desc.connect(('127.0.0.1', bound_port)) try: api.trampoline(desc, read=True, write=True, timeout=0.1) except api.TimeoutError: assert False, "Timed out" server.close() client.close() api.call_after(0, go) api.tcp_server(server, client_connected) check_hub() if not getattr(api.get_hub(), 'uses_twisted_reactor', None): def test_explicit_hub(self): oldhub = api.get_hub() try: api.use_hub(Foo) assert isinstance(api.get_hub(), Foo), api.get_hub() finally: api._threadlocal.hub = oldhub check_hub() def test_named(self): named_foo = api.named('api_test.Foo') self.assertEqual( named_foo.__name__, "Foo") def test_naming_missing_class(self): self.assertRaises( ImportError, api.named, 'this_name_should_hopefully_not_exist.Foo') def test_timeout_and_final_write(self): # This test verifies that a write on a socket that we've # stopped listening for doesn't result in an incorrect switch rpipe, wpipe = os.pipe() rfile = os.fdopen(rpipe,"rb",1) wrap_rfile = greenio.GreenPipe(rfile) wfile = os.fdopen(wpipe,"wb",1) wrap_wfile = greenio.GreenPipe(wfile) def sender(evt): api.sleep(0.02) wrap_wfile.write('hi') evt.send('sent via event') from eventlib import coros evt = coros.event() api.spawn(sender, evt) try: # try and get some data off of this pipe # but bail before any is sent api.exc_after(0.01, api.TimeoutError) _c = wrap_rfile.read(1) self.fail() except api.TimeoutError: pass result = evt.wait() self.assertEqual(result, 'sent via event')
def release(self, blocking=True): # `blocking' parameter is for consistency with BoundedSemaphore and is ignored self.counter += 1 if self._waiters: api.get_hub().schedule_call_global(0, self._do_acquire) return True
while True: (conn, (host, port)) = server.accept() print "backdoor connected to %s:%s" % (host, port) fl = conn.makeGreenFile("rw") fl.newlines = '\n' greenlet = SocketConsole(fl, (host, port), locals) hub = api.get_hub() hub.schedule_call_global(0, greenlet.switch) except socket.error, e: # Broken pipe means it was shutdown if e[0] != 32: raise finally: server.close() def backdoor((conn, addr), locals=None): """ Use this with tcp_server like so: api.tcp_server( api.tcp_listener(('127.0.0.1', 9000)), backdoor.backdoor, {}) """ host, port = addr print "backdoor to %s:%s" % (host, port) fl = conn.makeGreenFile("rw") fl.newlines = '\n' greenlet = SocketConsole(fl, (host, port), locals) hub = api.get_hub() hub.schedule_call_global(0, greenlet.switch)
def gethostbyname(name): if getattr(get_hub(), 'uses_twisted_reactor', None): globals()['gethostbyname'] = _gethostbyname_twisted else: globals()['gethostbyname'] = _gethostbyname_tpool return globals()['gethostbyname'](name)
def schedule(self): """Schedule this timer to run in the current runloop. """ self.called = False self.scheduled_time = get_hub().add_timer(self) return self
def _start_send(self): api.get_hub().schedule_call_global(0, self._do_send, self._value_links.items(), self._value_links)
def _start_send_exception(self): api.get_hub().schedule_call_global(0, self._do_send, self._exception_links.items(), self._exception_links)
def getaddrinfo(*args, **kw): if getattr(get_hub(), 'uses_twisted_reactor', None): globals()['getaddrinfo'] = _getaddrinfo_twisted else: globals()['getaddrinfo'] = _getaddrinfo_tpool return globals()['getaddrinfo'](*args, **kw)