Debug mode now has an off mode to silence tracebacks on 500, per redbo's suggestion.

This commit is contained in:
Ryan Williams
2011-02-04 09:40:40 -08:00
parent 8c808e9184
commit 00ba55aaaf
2 changed files with 70 additions and 30 deletions

View File

@@ -359,13 +359,16 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
write('')
except Exception:
self.close_connection = 1
exc = traceback.format_exc()
self.server.log_message(exc)
tb = traceback.format_exc()
self.server.log_message(tb)
if not headers_set:
err_body = ""
if(self.server.debug):
err_body = tb
start_response("500 Internal Server Error",
[('Content-type', 'text/plain'),
('Content-length', len(exc))])
write(exc)
('Content-length', len(err_body))])
write(err_body)
finally:
if hasattr(result, 'close'):
result.close()
@@ -455,6 +458,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
self.connection.close()
class Server(BaseHTTPServer.HTTPServer):
def __init__(self,
socket,
@@ -467,7 +471,8 @@ class Server(BaseHTTPServer.HTTPServer):
minimum_chunk_size=None,
log_x_forwarded_for=True,
keepalive=True,
log_format=DEFAULT_LOG_FORMAT):
log_format=DEFAULT_LOG_FORMAT,
debug=True):
self.outstanding_requests = 0
self.socket = socket
@@ -486,6 +491,7 @@ class Server(BaseHTTPServer.HTTPServer):
protocol.minimum_chunk_size = minimum_chunk_size
self.log_x_forwarded_for = log_x_forwarded_for
self.log_format = log_format
self.debug = debug
def get_environ(self):
d = {
@@ -531,7 +537,8 @@ def server(sock, site,
log_x_forwarded_for=True,
custom_pool=None,
keepalive=True,
log_format=DEFAULT_LOG_FORMAT):
log_format=DEFAULT_LOG_FORMAT,
debug=True):
""" Start up a wsgi server handling requests from the supplied server
socket. This function loops forever. The *sock* object will be closed after server exits,
but the underlying file descriptor will remain open, so if you have a dup() of *sock*,
@@ -550,6 +557,7 @@ def server(sock, site,
:param custom_pool: A custom GreenPool instance which is used to spawn client green threads. If this is supplied, max_size is ignored.
:param keepalive: If set to False, disables keepalives on the server; all connections will be closed after serving one request.
:param log_format: A python format string that is used as the template to generate log lines. The following values can be formatted into it: client_ip, date_time, request_line, status_code, body_length, wall_seconds. The default is a good example of how to use it.
:param debug: True if the server should send exception tracebacks to the clients on 500 errors. If False, the server will respond with empty bodies.
"""
serv = Server(sock, sock.getsockname(),
site, log,
@@ -559,7 +567,8 @@ def server(sock, site,
minimum_chunk_size=minimum_chunk_size,
log_x_forwarded_for=log_x_forwarded_for,
keepalive=keepalive,
log_format=log_format)
log_format=log_format,
debug=debug)
if server_event is not None:
server_event.send(serv)
if max_size is None:

View File

@@ -124,7 +124,7 @@ def read_http(sock):
raise ConnectionClosed
raise
if not response_line:
raise ConnectionClosed
raise ConnectionClosed(response_line)
header_lines = []
while True:
@@ -174,7 +174,6 @@ class _TestBase(LimitedTestCase):
eventlet.sleep(0) # give previous server a chance to start
if self.killer:
greenthread.kill(self.killer)
eventlet.sleep(0) # give killer a chance to kill
new_kwargs = dict(max_size=128,
log=self.logfile,
@@ -952,6 +951,58 @@ class TestHttpd(_TestBase):
self.assertEqual(headers['connection'], 'close')
self.assert_('unicode' in body)
def test_ipv6(self):
try:
sock = eventlet.listen(('::1', 0), family=socket.AF_INET6)
except (socket.gaierror, socket.error): # probably no ipv6
return
log = StringIO()
# first thing the server does is try to log the IP it's bound to
def run_server():
try:
server = wsgi.server(sock=sock, log=log, site=Site())
except ValueError:
log.write('broked')
eventlet.spawn_n(run_server)
logval = log.getvalue()
while not logval:
eventlet.sleep(0.0)
logval = log.getvalue()
if 'broked' in logval:
self.fail('WSGI server raised exception with ipv6 socket')
def test_debug(self):
self.spawn_server(debug=False)
def crasher(env, start_response):
raise RuntimeError("intentional crash")
self.site.application = crasher
sock = eventlet.connect(('localhost', self.port))
fd = sock.makefile('w')
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_(response_line.startswith('HTTP/1.1 500 Internal Server Error'))
self.assertEqual(body, '')
self.assertEqual(headers['connection'], 'close')
self.assert_('transfer-encoding' not in headers)
# verify traceback when debugging enabled
self.spawn_server(debug=True)
self.site.application = crasher
sock = eventlet.connect(('localhost', self.port))
fd = sock.makefile('w')
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
fd.flush()
response_line, headers, body = read_http(sock)
self.assert_(response_line.startswith('HTTP/1.1 500 Internal Server Error'))
self.assert_('intentional crash' in body)
self.assert_('RuntimeError' in body)
self.assert_('Traceback' in body)
self.assertEqual(headers['connection'], 'close')
self.assert_('transfer-encoding' not in headers)
def read_headers(sock):
fd = sock.makefile()
try:
@@ -1136,27 +1187,7 @@ class TestChunkedInput(_TestBase):
signal.alarm(0)
signal.signal(signal.SIGALRM, signal.SIG_DFL)
assert not got_signal, "caught alarm signal. infinite loop detected."
def test_ipv6(self):
try:
sock = eventlet.listen(('::1', 0), family=socket.AF_INET6)
except (socket.gaierror, socket.error): # probably no ipv6
return
log = StringIO()
# first thing the server does is try to log the IP it's bound to
def run_server():
try:
server = wsgi.server(sock=sock, log=log, site=Site())
except ValueError:
log.write('broked')
eventlet.spawn_n(run_server)
logval = log.getvalue()
while not logval:
eventlet.sleep(0.0)
logval = log.getvalue()
if 'broked' in logval:
self.fail('WSGI server raised exception with ipv6 socket')
assert not got_signal, "caught alarm signal. infinite loop detected."
if __name__ == '__main__':