diff --git a/gunicorn/util.py b/gunicorn/util.py index 4d7d7806..c27dc8c9 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -45,7 +45,7 @@ hop_headers = set(""" te trailers transfer-encoding upgrade server date """.split()) - + try: from setproctitle import setproctitle def _setproctitle(title): @@ -178,27 +178,28 @@ def writelines(sock, lines, chunked=False): for line in list(lines): write(sock, line, chunked) -def write_error(sock, msg): +def write_error(sock, msg, status_int=500, + reason="Internal Server Error"): html = textwrap.dedent("""\ - Internal Server Error + %(reason)s -

Internal Server Error

-

WSGI Error Report:

-
%s
+

%(reason)s

+ %(msg)s - """) % msg + """) % {"reason": reason, "msg": msg} + http = textwrap.dedent("""\ - HTTP/1.1 500 Internal Server Error\r + HTTP/1.1 %s %s\r Connection: close\r Content-Type: text/html\r Content-Length: %d\r \r %s - """) % (len(html), html) + """) % (str(status_int), reason, len(html), html) write_nonblock(sock, http) def normalize_name(name): diff --git a/gunicorn/workers/async.py b/gunicorn/workers/async.py index 8669a6ee..594c72bd 100644 --- a/gunicorn/workers/async.py +++ b/gunicorn/workers/async.py @@ -48,13 +48,7 @@ class AsyncWorker(base.Worker): self.log.debug("Ignoring EPIPE") except Exception, e: self.log.exception("General error processing request.") - try: - # Last ditch attempt to notify the client of an error. - mesg = "HTTP/1.0 500 Internal Server Error\r\n\r\n" - util.write_nonblock(client, mesg) - except: - pass - return + self.handle_error(client, e) finally: util.close(client) @@ -82,9 +76,7 @@ class AsyncWorker(base.Worker): raise except Exception, e: #Only send back traceback in HTTP in debug mode. - if not self.debug: - raise - util.write_error(sock, traceback.format_exc()) + self.handle_error(sock, e) return False finally: try: diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index ff1c6e0f..e6cea2b4 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -10,10 +10,16 @@ import random import signal import sys import tempfile +import traceback + from gunicorn import util from gunicorn.workers.workertmp import WorkerTmp +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \ +InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion + + class Worker(object): SIGNALS = map( @@ -110,6 +116,37 @@ class Worker(object): self.alive = False sys.exit(0) + + def handle_error(self, client, exc): + + if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod, + InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,)): + + if isinstance(exc, InvalidRequestLine): + mesg = "

Invalid Request Line '%s'

" % str(exc) + elif isinstance(exc, InvalidRequestMethod): + mesg = "

Invalid Method'%s'

" % str(exc) + elif isinstance(exc, InvalidHTTPVersion): + mesg = "

Invalid HTTP Version '%s'

" % str(exc) + elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)): + mesg = "

Invalid Header'%s'

" % str(exc) + reason = "Bad Request" + status_int = 400 + else: + mesg = reason = "Internal Server reason" + status_int = 500 + + if self.debug: + tb = traceback.format_exc() + mesg += "

Traceback:
%s
" % tb + + try: + util.write_error(client, mesg, status_int=status_int, + reason=reason) + except: + self.log.warning("Unexpected error" % traceback.format_exc()) + pass + def handle_winch(self, sig, fname): # Ignore SIGWINCH in worker. Fixes a crash on OpenBSD. return diff --git a/gunicorn/workers/ggevent.py b/gunicorn/workers/ggevent.py index e9e1d9fa..2407fd20 100644 --- a/gunicorn/workers/ggevent.py +++ b/gunicorn/workers/ggevent.py @@ -72,7 +72,6 @@ class GeventWorker(AsyncWorker): worker=self) server.start() - try: while self.alive: self.notify() diff --git a/gunicorn/workers/sync.py b/gunicorn/workers/sync.py index e3c9e62d..b92e7765 100644 --- a/gunicorn/workers/sync.py +++ b/gunicorn/workers/sync.py @@ -8,7 +8,6 @@ import errno import os import select import socket -import traceback import gunicorn.http as http import gunicorn.http.wsgi as wsgi @@ -63,7 +62,7 @@ class SyncWorker(base.Worker): else: return raise - + def handle(self, client, addr): try: parser = http.RequestParser(client) @@ -78,12 +77,7 @@ class SyncWorker(base.Worker): self.log.debug("Ignoring EPIPE") except Exception, e: self.log.exception("Error processing request.") - try: - # Last ditch attempt to notify the client of an error. - mesg = "HTTP/1.1 500 Internal Server Error\r\n\r\n" - util.write_nonblock(client, mesg) - except: - pass + self.handle_error(client, e) finally: util.close(client) @@ -111,9 +105,7 @@ class SyncWorker(base.Worker): raise except Exception, e: # Only send back traceback in HTTP in debug mode. - if not self.debug: - raise - util.write_error(client, traceback.format_exc()) + self.handle_error(client, e) return finally: try: