From f59659f66dc3a2443f8f4b31ae9c4ceff19dc365 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Thu, 31 Dec 2009 14:16:15 -0800 Subject: [PATCH 1/3] Custom pool patch from gholt. --- AUTHORS | 1 + eventlet/wsgi.py | 8 ++++++-- tests/wsgi_test.py | 28 +++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7d4a356..5ca692d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,6 +23,7 @@ Linden Lab Contributors Thanks To --------- +* gholt, wsgi patch for custom pool * Luke Tucker, bug report regarding wsgi + webob * Chuck Thier, reporting a bug in processes.py * Brantley Harris, reporting bug #4 diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 440367b..26c5ea2 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -421,7 +421,8 @@ def server(sock, site, protocol=HttpProtocol, server_event=None, minimum_chunk_size=None, - log_x_forwarded_for=True): + log_x_forwarded_for=True, + custom_pool=None): """ Start up a wsgi server handling requests from the supplied server socket. This function loops forever. @@ -438,7 +439,10 @@ def server(sock, site, server_event.send(serv) if max_size is None: max_size = DEFAULT_MAX_SIMULTANEOUS_REQUESTS - pool = Pool(max_size=max_size) + if custom_pool is not None: + pool = custom_pool + else: + pool = Pool(max_size=max_size) try: host, port = sock.getsockname() port = ':%s' % (port, ) diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 88b84ac..052ebae 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -490,7 +490,33 @@ class TestHttpd(LimitedTestCase): 'Connection: close\r\n' '\r\n\r\n') self.assert_('200 OK' in fd.read()) - + + def test_022_custom_pool(self): + # just test that it accepts the parameter for now + # TODO: test that it uses the pool and that you can waitall() to + # ensure that all clients finished + from eventlet import pool + p = pool.Pool(max_size=5) + api.kill(self.killer) + listener = api.tcp_listener(('localhost', 0)) + self.port = listener.getsockname()[1] + self.killer = api.spawn( + wsgi.server, + listener, + self.site, + max_size=128, + log=self.logfile, + custom_pool=p) + + # this stuff is copied from test_001_server, could be better factored + sock = api.connect_tcp( + ('localhost', self.port)) + fd = sock.makeGreenFile() + fd.write('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n') + result = fd.read() + fd.close() + self.assert_(result.startswith('HTTP'), result) + self.assert_(result.endswith('hello world')) if __name__ == '__main__': main() From 9a7a96b5e385323a768610829bfeaf788b764364 Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Thu, 31 Dec 2009 14:32:49 -0800 Subject: [PATCH 2/3] Invalid content-length patch from gholt. --- AUTHORS | 2 +- eventlet/wsgi.py | 11 +++++++++++ tests/wsgi_test.py | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 5ca692d..efe4a6f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,7 +23,7 @@ Linden Lab Contributors Thanks To --------- -* gholt, wsgi patch for custom pool +* gholt, wsgi patches for accepting a custom pool, and returning 400 if content-length is invalid * Luke Tucker, bug report regarding wsgi + webob * Chuck Thier, reporting a bug in processes.py * Brantley Harris, reporting bug #4 diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 26c5ea2..9906e84 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -165,6 +165,17 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): if not self.parse_request(): return + content_length = self.headers.getheader('content-length') + if content_length: + try: + int(content_length) + except ValueError: + self.wfile.write( + "HTTP/1.0 400 Bad Request\r\n" + "Connection: close\r\nContent-length: 0\r\n\r\n") + self.close_connection = 1 + return + self.environ = self.get_environ() self.application = self.server.app try: diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index 052ebae..e72641c 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -517,6 +517,18 @@ class TestHttpd(LimitedTestCase): fd.close() self.assert_(result.startswith('HTTP'), result) self.assert_(result.endswith('hello world')) + + def test_023_bad_content_length(self): + sock = api.connect_tcp( + ('localhost', self.port)) + fd = sock.makeGreenFile() + fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nContent-length: argh\r\n\r\n') + result = fd.read() + fd.close() + self.assert_(result.startswith('HTTP'), result) + self.assert_('400 Bad Request' in result) + self.assert_('500' not in result) + if __name__ == '__main__': main() From fb0c2c429c14edd3fe33292f502bbcbe6f05866c Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Thu, 31 Dec 2009 17:59:04 -0800 Subject: [PATCH 3/3] Fixed a bug where patcher was leaving crap in sys.modules, added a unit test that is huuuge but works correctly. --- eventlet/patcher.py | 2 ++ tests/patcher_test.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/patcher_test.py diff --git a/eventlet/patcher.py b/eventlet/patcher.py index b43fd93..e10ac4f 100644 --- a/eventlet/patcher.py +++ b/eventlet/patcher.py @@ -33,6 +33,8 @@ def inject(module_name, new_globals, *additional_modules): for name, mod in additional_modules: if saved[name] is not None: sys.modules[name] = saved[name] + else: + del sys.modules[name] return module diff --git a/tests/patcher_test.py b/tests/patcher_test.py new file mode 100644 index 0000000..401e11e --- /dev/null +++ b/tests/patcher_test.py @@ -0,0 +1,72 @@ +import os +import tempfile +import subprocess +import sys + +from tests import LimitedTestCase + +base_module_contents = """ +import socket +import urllib +print "base", socket, urllib +""" + +patching_module_contents = """ +from eventlet.green import socket +from eventlet.green import urllib +from eventlet import patcher +print 'patcher', socket, urllib +patcher.inject('%s', globals(), ('socket', socket), ('urllib', urllib)) +del patcher +""" + +import_module_contents = """ +import %(mod)s +import httplib +print "importing", %(mod)s, httplib, %(mod)s.socket, %(mod)s.urllib +""" + +class Patcher(LimitedTestCase): + TEST_TIMEOUT=3 # starting processes is time-consuming + def setUp(self): + self._saved_syspath = sys.path + self.tempfiles = [] + + def tearDown(self): + sys.path = self._saved_syspath + for tf in self.tempfiles: + os.remove(tf) + + def write_to_tempfile(self, contents): + fn, filename = tempfile.mkstemp('_patcher_test.py') + fd = os.fdopen(fn, 'w') + fd.write(contents) + fd.close() + self.tempfiles.append(filename) + return os.path.dirname(filename), os.path.basename(filename) + + def test_patch_a_module(self): + base = self.write_to_tempfile(base_module_contents) + base_modname = os.path.splitext(base[1])[0] + patching = self.write_to_tempfile(patching_module_contents % base_modname) + patching_modname = os.path.splitext(patching[1])[0] + importing = self.write_to_tempfile( + import_module_contents % dict(mod=patching_modname)) + + python_path = os.pathsep.join(sys.path) + python_path += os.pathsep.join((base[0], patching[0], importing[0])) + new_env = os.environ.copy() + new_env['PYTHONPATH'] = python_path + p = subprocess.Popen([sys.executable, + os.path.join(importing[0], importing[1])], + stdout=subprocess.PIPE, env=new_env) + output = p.communicate() + lines = output[0].split("\n") + self.assert_(lines[0].startswith('patcher')) + self.assert_(lines[1].startswith('base')) + self.assert_(lines[2].startswith('importing')) + self.assert_('eventlet.green.socket' in lines[1]) + self.assert_('eventlet.green.urllib' in lines[1]) + self.assert_('eventlet.green.socket' in lines[2]) + self.assert_('eventlet.green.urllib' in lines[2]) + self.assert_('eventlet.green.httplib' not in lines[2]) \ No newline at end of file