[Checkins] SVN: zc.resumelb/trunk/src/zc/resumelb/ Fixed: Poorly-behaved WSGI applications that fail to catch errors
jim
cvs-admin at zope.org
Mon May 7 18:07:19 UTC 2012
Log message for revision 125693:
Fixed: Poorly-behaved WSGI applications that fail to catch errors
caused requests to hang rather than return 500 responses.
Moved helper functions from worker.test into tests module so they can
be used in other tests.
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/worker.py
U zc.resumelb/trunk/src/zc/resumelb/worker.test
-=-
Modified: zc.resumelb/trunk/src/zc/resumelb/README.txt
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/README.txt 2012-05-07 15:53:29 UTC (rev 125692)
+++ zc.resumelb/trunk/src/zc/resumelb/README.txt 2012-05-07 18:07:15 UTC (rev 125693)
@@ -252,6 +252,9 @@
- Fixed: worker errors were written to standard out rather than being
logged.
+- Fixed: Poorly-behaved WSGI applications that fail to catch errors
+ caused requests to hang rather than return 500 responses.
+
0.5.0 (2012-05-03)
------------------
Modified: zc.resumelb/trunk/src/zc/resumelb/tests.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/tests.py 2012-05-07 15:53:29 UTC (rev 125692)
+++ zc.resumelb/trunk/src/zc/resumelb/tests.py 2012-05-07 18:07:15 UTC (rev 125693)
@@ -14,6 +14,7 @@
import bobo
import doctest
import gevent
+import gevent.socket
import hashlib
import manuel.capture
import manuel.doctest
@@ -24,11 +25,13 @@
import pprint
import re
import time
+import traceback
import unittest
import webob
import zc.resumelb.util
import zc.resumelb.worker
import zc.zk.testing
+import zope.testing.loggingsupport
import zope.testing.setupstack
import zope.testing.wait
import zope.testing.renormalizing
@@ -86,6 +89,42 @@
#
###############################################################################
+def newenv(rclass, *a, **kw):
+ r = webob.Request.blank(*a, **kw)
+ env = r.environ.copy()
+ inp = env.pop('wsgi.input')
+ del env['wsgi.errors']
+ env['zc.resumelb.request_class'] = rclass
+ return env
+
+def print_response(worker_socket, rno, size_only=False):
+ d = zc.resumelb.util.read_message(worker_socket)
+ try: rn, (status, headers) = d
+ except:
+ print 'wtf', `d`
+ return
+ #rn, (status, headers) = read_message(worker_socket)
+ if rn != rno:
+ raise AssertionError("Bad request numbers", rno, rn)
+ print rno, status
+ for h in sorted(headers):
+ print "%s: %s" % h
+ print
+ size = 0
+ while 1:
+ rn, data = zc.resumelb.util.read_message(worker_socket)
+ if rn != rno:
+ raise AssertionError("Bad request numbers", rno, rn)
+ if data:
+ if size_only:
+ size += len(data)
+ else:
+ print data,
+ else:
+ break
+ if size_only:
+ print size
+
def test_loading_recipes_with_no_history_argument():
"""A bug as introduced that caused resumes to be loaded
incorrectly when no history was given to the constructor. It
@@ -100,9 +139,50 @@
>>> pprint.pprint(worker.perf_data)
{'a': (0, 1.0, 9999), 'b': (0, 0.5, 9999)}
+
+ >>> worker.stop()
"""
+def workers_generate_500s_for_bad_apps():
+ """If an app is poorly behaved and raises exceptions, a worker
+ will generate a 500.
+ >>> def baddapp(*args):
+ ... raise Exception("I'm a bad-app")
+
+ >>> worker = zc.resumelb.worker.Worker(baddapp, ('127.0.0.1', 0))
+ >>> worker_socket = gevent.socket.create_connection(worker.addr)
+ >>> zc.resumelb.util.read_message(worker_socket)
+ (0, {})
+
+ >>> env = newenv('', '/hi.html')
+ >>> handler = zope.testing.loggingsupport.InstalledHandler(
+ ... 'zc.resumelb.worker')
+ >>> zc.resumelb.util.write_message(worker_socket, 1, env, '')
+ >>> print_response(worker_socket, 1)
+ 1 500 Internal Server Error
+ Content-Length: 23
+ Content-Type: text/html; charset=UTF-8
+ <BLANKLINE>
+ A system error occurred
+
+ >>> for record in handler.records:
+ ... print record.name, record.levelname
+ ... print record.getMessage()
+ ... if record.exc_info:
+ ... traceback.print_exception(*record.exc_info)
+ ... # doctest: +ELLIPSIS
+ zc.resumelb.worker ERROR
+ Uncaught application exception for 1
+ Traceback (most recent call last):
+ ...
+ Exception: I'm a bad-app
+
+ >>> handler.uninstall()
+ >>> worker.stop()
+ """
+
+
def test_classifier(env):
return "yup, it's a test"
@@ -121,6 +201,8 @@
zope.testing.setupstack.register(
test, setattr, zc.resumelb.util, 'queue_size_bytes', old)
zc.resumelb.util.queue_size_bytes = 999
+ test.globs['newenv'] = newenv
+ test.globs['print_response'] = print_response
def zkSetUp(test):
setUp(test)
Modified: zc.resumelb/trunk/src/zc/resumelb/worker.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/worker.py 2012-05-07 15:53:29 UTC (rev 125692)
+++ zc.resumelb/trunk/src/zc/resumelb/worker.py 2012-05-07 18:07:15 UTC (rev 125693)
@@ -66,7 +66,15 @@
def start_response(status, headers, exc_info=None):
assert not exc_info # XXX
response[0] = (status, headers)
- body = app(env, start_response)
+
+ try:
+ body = app(env, start_response)
+ except Exception:
+ error("Uncaught application exception for %s" % trno)
+ import webob
+ body = webob.Response(
+ 'A system error occurred', status=500)(env, start_response)
+
return response[0], body
if tracelog:
@@ -200,7 +208,7 @@
url += '?' + query_string
self.tracelog(trno, 'B', '%s %s' % (env['REQUEST_METHOD'], url))
else:
- trno = 0
+ trno = rno
env['wsgi.errors'] = sys.stderr
Modified: zc.resumelb/trunk/src/zc/resumelb/worker.test
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/worker.test 2012-05-07 15:53:29 UTC (rev 125692)
+++ zc.resumelb/trunk/src/zc/resumelb/worker.test 2012-05-07 18:07:15 UTC (rev 125693)
@@ -44,17 +44,8 @@
When the worker sends its resume, it sends 0 as the request number.
Now, let's send a request to the worker. Requests are based on wsgi
-environments. We'll use webob to help us set this up.
+environments. We have a helper, newenv, that helps us create one.
- >>> import webob
- >>> def newenv(rclass, *a, **kw):
- ... r = webob.Request.blank(*a, **kw)
- ... env = r.environ.copy()
- ... inp = env.pop('wsgi.input')
- ... del env['wsgi.errors']
- ... env['zc.resumelb.request_class'] = rclass
- ... return env
-
>>> env = newenv('', '/hi.html')
The newenv helper:
@@ -76,33 +67,7 @@
- response body, and
- an empty end-of-body message.
- >>> def print_response(worker_socket, rno, size_only=False):
- ... d = read_message(worker_socket)
- ... try: rn, (status, headers) = d
- ... except:
- ... print 'wtf', `d`
- ... return
- ... #rn, (status, headers) = read_message(worker_socket)
- ... if rn != rno:
- ... raise AssertionError("Bad request numbers", rno, rn)
- ... print rno, status
- ... for h in sorted(headers):
- ... print "%s: %s" % h
- ... print
- ... size = 0
- ... while 1:
- ... rn, data = read_message(worker_socket)
- ... if rn != rno:
- ... raise AssertionError("Bad request numbers", rno, rn)
- ... if data:
- ... if size_only:
- ... size += len(data)
- ... else:
- ... print data,
- ... else:
- ... break
- ... if size_only:
- ... print size
+We also have a helper that consumes and prints a response:
>>> print_response(worker_socket, 1)
1 200 OK
More information about the checkins
mailing list