[Checkins] SVN: zc.resumelb/trunk/src/zc/resumelb/ Added HTTP socket timeout to avoid "leaking" connections.

jim cvs-admin at zope.org
Fri Jun 15 22:11:28 UTC 2012


Log message for revision 126880:
  Added HTTP socket timeout to avoid "leaking" connections.
  
  Also changed some backlog/pool tests to be less brittle.
  

Changed:
  U   zc.resumelb/trunk/src/zc/resumelb/README.txt
  U   zc.resumelb/trunk/src/zc/resumelb/tests.py
  U   zc.resumelb/trunk/src/zc/resumelb/zk.py
  U   zc.resumelb/trunk/src/zc/resumelb/zk.test

-=-
Modified: zc.resumelb/trunk/src/zc/resumelb/README.txt
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/README.txt	2012-06-15 22:11:20 UTC (rev 126879)
+++ zc.resumelb/trunk/src/zc/resumelb/README.txt	2012-06-15 22:11:24 UTC (rev 126880)
@@ -242,6 +242,11 @@
 Change History
 ==============
 
+0.6.2 (2012-06-15)
+------------------
+
+- Fixed: a lack of socket timeout could cause requests to leak.
+
 0.6.0 (2012-05-11)
 ------------------
 

Modified: zc.resumelb/trunk/src/zc/resumelb/tests.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/tests.py	2012-06-15 22:11:20 UTC (rev 126879)
+++ zc.resumelb/trunk/src/zc/resumelb/tests.py	2012-06-15 22:11:24 UTC (rev 126880)
@@ -247,6 +247,56 @@
 
     """
 
+def zk_wsgi_server_output_timeout():
+    r"""
+
+    >>> import zc.resumelb.zk, zc.resumelb.tests, zc.zk
+    >>> zk = zc.zk.ZooKeeper('zookeeper.example.com:2181')
+    >>> zk.import_tree('''
+    ... /test
+    ...   /lb
+    ...     /providers
+    ...     /workers
+    ...       /providers
+    ... ''')
+
+    >>> app = zc.resumelb.tests.app()
+    >>> worker = zc.resumelb.zk.worker(
+    ...     app, None, address='127.0.0.1:0', run=False,
+    ...     zookeeper='zookeeper.example.com:2181', path='/test/lb/workers')
+
+    >>> lb, server = zc.resumelb.zk.lbmain(
+    ...     'zookeeper.example.com:2181 /test/lb -t.2')
+
+    >>> [addr] = map(zc.parse_addr.parse_addr,
+    ...              zk.get_children('/test/lb/providers'))
+
+    Now we'll make a request, but not consume output.  It should
+    timeout after .2 seconds:
+
+    >>> with mock.patch('sys.stderr'):
+    ...     with mock.patch('sys.stdout'):
+    ...         sock = gevent.socket.create_connection(addr)
+    ...         sock.sendall('GET /gen.html?size=999 HTTP/1.0\r\n\r\n')
+    ...         gevent.sleep(1)
+
+    The various output that get's generated in the timeout case is
+    sub-optimal and, hopefully, likely to change, so we won't show it
+    here.  The only clue we have that the timeout worked is that the
+    output is less than we expect:
+
+    >>> f = sock.makefile()
+    >>> data = f.read()
+    >>> len(data) < 999*12000
+    True
+
+    >>> worker.stop()
+    >>> server.stop()
+    >>> lb.stop()
+    >>> zk.close()
+
+    """
+
 def test_classifier(env):
     return "yup, it's a test"
 
@@ -315,6 +365,6 @@
             'zk.test',
             setUp=zkSetUp, tearDown=zkTearDown),
         doctest.DocTestSuite(
-            setUp=setUp, tearDown=zope.testing.setupstack.tearDown),
+            setUp=zkSetUp, tearDown=zope.testing.setupstack.tearDown),
         ))
 

Modified: zc.resumelb/trunk/src/zc/resumelb/zk.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/zk.py	2012-06-15 22:11:20 UTC (rev 126879)
+++ zc.resumelb/trunk/src/zc/resumelb/zk.py	2012-06-15 22:11:24 UTC (rev 126880)
@@ -85,6 +85,16 @@
         gevent.sleep(.01)
         return worker
 
+class WSGIServer(gevent.pywsgi.WSGIServer):
+
+    def __init__(self, *args, **kw):
+        self.__socket_timeout = kw.pop('socket_timeout')
+        gevent.pywsgi.WSGIServer.__init__(self, *args, **kw)
+
+    def handle(self, socket, address):
+        socket.settimeout(self.__socket_timeout)
+        return gevent.pywsgi.WSGIServer.handle(self, socket, address)
+
 def lbmain(args=None, run=True):
     """%prog [options] zookeeper_connection path
 
@@ -116,6 +126,9 @@
         help=("Run a status server for getting pool information. "
               "The argument is a unix-domain socket path to listen on."))
     parser.add_option(
+        '-t', '--socket-timeout', type='float', default=99.,
+        help=('HTTP socket timeout.'))
+    parser.add_option(
         '-L', '--logger-configuration',
         help=
         "Read logger configuration from the given configuration file path.\n"
@@ -203,9 +216,9 @@
     else:
         accesslog = None
 
-    server = gevent.pywsgi.WSGIServer(
-        addr, lb.handle_wsgi, backlog = options.backlog,
-        spawn = spawn, log = accesslog)
+    server = WSGIServer(
+        addr, lb.handle_wsgi, backlog=options.backlog,
+        spawn=spawn, log=accesslog, socket_timeout=options.socket_timeout)
     server.start()
 
     registration_data = {}

Modified: zc.resumelb/trunk/src/zc/resumelb/zk.test
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/zk.test	2012-06-15 22:11:20 UTC (rev 126879)
+++ zc.resumelb/trunk/src/zc/resumelb/zk.test	2012-06-15 22:11:24 UTC (rev 126880)
@@ -202,6 +202,8 @@
                             Run a status server for getting pool
                             information. The argument is a unix-domain
                             socket path to listen on.
+      -t SOCKET_TIMEOUT, --socket-timeout=SOCKET_TIMEOUT
+                            HTTP socket timeout.
       -L LOGGER_CONFIGURATION, --logger-configuration=LOGGER_CONFIGURATION
                             Read logger configuration from the given
                             configuration file path.  The configuration
@@ -359,12 +361,18 @@
     >>> with mock.patch('ZConfig.configureLoggers') as configureLoggers:
     ...     lb, server = zc.resumelb.zk.lbmain(
     ...         'zookeeper.example.com:2181 /test/lb'
-    ...         ' -alocalhost:0 -laccess -b1 -m1'
+    ...         ' -alocalhost:0 -laccess -b1 -m2'
     ...         ' --logger-configuration log.conf '
     ...         ' -rzc.resumelb.tests:test_classifier -eoops.html'
     ...         )
     ...     configureLoggers.assert_called_with('loggers')
 
+    >>> server.backlog
+    1
+
+    >>> server.pool.size
+    2
+
     >>> lb.disconnect_message
     'oops'
 
@@ -377,12 +385,6 @@
     >>> len(lb.pool.workers)
     2
 
-    >>> sock = gevent.socket.create_connection(addr)
-    >>> sock2 = gevent.socket.create_connection(addr)
-    >>> sock3 = gevent.socket.create_connection(addr, .00001)
-    Traceback (most recent call last):
-    ...
-    timeout: timed out
 
 The 3rd collection failed because we said to only accept one
 connection at a time and set the backlog to 1.
@@ -395,6 +397,7 @@
 
 Let's do a request.
 
+    >>> sock = gevent.socket.create_connection(addr)
     >>> sock.sendall('''GET /hi.html HTTP/1.0\r
     ... Host: h1.com\r
     ... Content-Length: 0\r
@@ -404,7 +407,6 @@
     HTTP/1.0 200 OK...
 
     >>> sock.close()
-    >>> sock2.close()
 
 We now have access-log records in the access-log handler:
 



More information about the checkins mailing list