commit 7e21184d4432b4c25f775eb27aa8b9de68d650b5
Author: Isis Lovecruft <isis(a)torproject.org>
Date: Tue Jan 28 17:01:57 2014 +0000
Implement a simple error page to replace all webserver tracebacks.
* ADD HTTPServer.replaceErrorPage() function for catching templating
errors and sending them to the logger. The user is served a very
simply "Somthing went wrong page".
* CHANGE all calls to mako.templates.Template.render() to be wrapped in
a try/except block which directs to the new
HTTPServer.replaceErrorPage() function.
---
lib/bridgedb/HTTPServer.py | 87 +++++++++++++++++++++++++++++++++-----------
1 file changed, 66 insertions(+), 21 deletions(-)
diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index 104e924..5794be7 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -34,6 +34,7 @@ from bridgedb.Filters import filterBridgesByNotBlockedIn
from bridgedb.parse import headers
from ipaddr import IPv4Address, IPv6Address
from random import randint
+import mako.exceptions
from mako.template import Template
from mako.lookup import TemplateLookup
from zope.interface import Interface, Attribute, implements
@@ -66,6 +67,39 @@ else:
logging.info("GeoIP database loaded")
+def replaceErrorPage(error, template_name=None):
+ """Create a general error page for displaying in place of tracebacks.
+
+ Log the error to BridgeDB's logger, and then display a very plain "Sorry!
+ Something went wrong!" page to the client.
+
+ :type error: :exc:`Exception`
+ :param error: Any exeption which has occurred while attempting to retrieve
+ a template, render a page, or retrieve a resource.
+ :param str template_name: A string describing which template/page/resource
+ was being used when the exception occurred,
+ i.e. ``'index.html'``.
+ :returns: A string containing HTML to serve to the client (rather than
+ serving a traceback).
+ """
+ logging.error("Error while attempting to render %s: %s"
+ % (template_name or 'template',
+ mako.exceptions.text_error_template().render()))
+
+ errmsg = _("Sorry! Something went wrong with your request.")
+ rendered = """<html>
+ <head>
+ <link href="/assets/bootstrap.min.css" rel="stylesheet">
+ <link href="/assets/custom.css" rel="stylesheet">
+ </head>
+ <body>
+ <p>{0}</p>
+ </body>
+ </html>""".format(errmsg)
+
+ return rendered
+
+
class CaptchaProtectedResource(twisted.web.resource.Resource):
def __init__(self, useRecaptcha=False, recaptchaPrivKey='',
recaptchaPubKey='', useForwardedHeader=False, resource=None):
@@ -97,18 +131,17 @@ class CaptchaProtectedResource(twisted.web.resource.Resource):
logging.fatal("Connection to Recaptcha server failed: %s" % error)
if c.image is None:
- # TODO: We should have a general "Something went wrong!" page
- # which displays here, rather than displaying the img alt text
- # (which says "Upgrade your browser to Firefox"), so that users
- # don't think the problem is on their end when it's actually a
- # problem with ReCaptcha or BridgeDB.
logging.warn("No CAPTCHA image received from ReCaptcha server!")
- c.image = ''
- # TODO: this does not work for versions of IE < 8.0
- imgstr = 'data:image/jpeg;base64,%s' % base64.b64encode(c.image)
- template = lookup.get_template('captcha.html')
- rendered = template.render(imgstr=imgstr, challenge_field=c.challenge)
+ try:
+ # TODO: this does not work for versions of IE < 8.0
+ imgstr = 'data:image/jpeg;base64,%s' % base64.b64encode(c.image)
+ template = lookup.get_template('captcha.html')
+ rendered = template.render(imgstr=imgstr,
+ challenge_field=c.challenge)
+ except Exception as err:
+ rendered = replaceErrorPage(err, 'captcha.html')
+
return rendered
def render_POST(self, request):
@@ -135,21 +168,27 @@ class CaptchaProtectedResource(twisted.web.resource.Resource):
randint(1,255),randint(1,255))
recaptcha_response = captcha.submit(challenge, response,
- self.recaptchaPrivKey, remote_ip)
+ self.recaptchaPrivKey, remote_ip)
+ logging.debug("Captcha from client with masked IP %r. Parameters:\n%r"
+ % (remote_ip, request.args))
+
if recaptcha_response.is_valid:
- logging.info("Valid recaptcha from %s. Parameters were %r"
- % (remote_ip, request.args))
- return self.resource.render(request)
+ logging.info("Valid recaptcha from client with masked IP %r."
+ % remote_ip)
+ try:
+ rendered = self.resource.render(request)
+ except Exception as err:
+ rendered = replaceErrorPage(err)
+ return rendered
else:
- logging.info("Invalid recaptcha from %s. Parameters were %r"
- % (remote_ip, request.args))
- logging.info("Recaptcha error code: %r"
- % recaptcha_response.error_code)
+ logging.info("Invalid recaptcha from client with masked IP %r: %r"
+ % (remote_ip, recaptcha_response.error_code))
logging.debug("Client failed a recaptcha; returning redirect to %s"
% request.uri)
return redirectTo(request.uri, request)
+
class WebResource(twisted.web.resource.Resource):
"""This resource is used by Twisted Web to give a web page with some
bridges in response to a request."""
@@ -312,11 +351,17 @@ class WebResource(twisted.web.resource.Resource):
"""
if format == 'plain':
request.setHeader("Content-Type", "text/plain")
- return answer
+ rendered = bridgeLines
else:
request.setHeader("Content-Type", "text/html; charset=utf-8")
- return lookup.get_template('bridges.html').render(answer=bridgeLines,
- rtl=rtl)
+ try:
+ template = lookup.get_template('bridges.html')
+ rendered = template.render(answer=bridgeLines, rtl=rtl)
+ except Exception as err:
+ rendered = replaceErrorPage(err)
+
+ return rendered
+
class WebRoot(twisted.web.resource.Resource):
"""The parent resource of all other documents hosted by the webserver."""