First import of Sergey's serve method. Hacked to fit, a few tests written.

This commit is contained in:
Ryan Williams
2010-02-25 07:11:14 -05:00
parent 7f9a59aa52
commit 4d9ef644c2
2 changed files with 90 additions and 6 deletions

View File

@@ -561,14 +561,25 @@ def wrap_ssl(sock, keyfile=None, certfile=None, server_side=False,
pass
class StopServe(Exception): pass
def serve(sock, handle, concurrency=1000):
"""Runs a server on the supplied socket. Calls the function
*handle* in a separate greenthread for every incoming request.
This function blocks the calling greenthread; it won't return until
*handle* in a separate greenthread for every incoming request with
two arguments: the client socket object, and the client address::
def myhandle(client_sock, client_addr):
print "client connected", client_addr
eventlet.serve(eventlet.listen(('127.0.0.1', 9999)), myhandle)
Returning from *handle* closes the client socket.
:func:`serve` blocks the calling greenthread; it won't return until
the server completes. If you desire an immediate return,
spawn a new greenthread for :func:`serve`.
The *handle* function must raise an EndServerException to
The *handle* function must raise a StopServe exception to
gracefully terminate the server -- that's the only way to get the
server() function to return. Any other uncaught exceptions raised
in *handle* are raised as exceptions from :func:`serve`, so be
@@ -580,4 +591,24 @@ def serve(sock, handle, concurrency=1000):
the server hits the concurrency limit, it stops accepting new
connections until the existing ones complete.
"""
from eventlet import greenpool
from eventlet import greenthread
pool = greenpool.GreenPool(concurrency)
server_thread = greenthread.getcurrent()
def stop_checker(t, server_thread, conn):
try:
t.wait()
except greenthread.greenlet.GreenletExit:
pass
except Exception:
conn.close()
server_thread.throw(*sys.exc_info())
while True:
try:
conn, addr = sock.accept()
pool.spawn(handle, conn, addr).link(stop_checker, server_thread, conn)
conn, addr = None, None
except StopServe:
return

View File

@@ -524,5 +524,58 @@ class TestGreenIoLong(LimitedTestCase):
self.assert_(len(results2) > 0)
class TestServe(LimitedTestCase):
def setUp(self):
super(TestServe, self).setUp()
from eventlet import debug
debug.hub_exceptions(False)
def tearDown(self):
super(TestServe, self).tearDown()
from eventlet import debug
debug.hub_exceptions(True)
def test_exiting_server(self):
# tests that the server closes the client sock on handle() exit
def closer(sock,addr):
pass
l = eventlet.listen(('localhost', 0))
gt = eventlet.spawn(greenio.serve, l, closer)
client = eventlet.connect(('localhost', l.getsockname()[1]))
client.sendall('a')
self.assertEqual('', client.recv(100))
gt.kill()
def test_excepting_server(self):
# tests that the server closes the client sock on handle() exception
def crasher(sock,addr):
x = sock.recv(1024)
0/0
l = eventlet.listen(('localhost', 0))
gt = eventlet.spawn(greenio.serve, l, crasher)
client = eventlet.connect(('localhost', l.getsockname()[1]))
client.sendall('a')
self.assertRaises(ZeroDivisionError, gt.wait)
self.assertEqual('', client.recv(100))
def test_excepting_server_already_closed(self):
# tests that the server closes the client sock on handle() exception
def crasher(sock,addr):
x = sock.recv(1024)
sock.close()
0/0
l = eventlet.listen(('localhost', 0))
gt = eventlet.spawn(greenio.serve, l, crasher)
client = eventlet.connect(('localhost', l.getsockname()[1]))
client.sendall('a')
self.assertRaises(ZeroDivisionError, gt.wait)
self.assertEqual('', client.recv(100))
if __name__ == '__main__':
main()