[tor-commits] [bridgedb/master] Implement a simple error page to replace all webserver tracebacks.

isis at torproject.org isis at torproject.org
Tue Feb 4 00:28:47 UTC 2014


commit 7e21184d4432b4c25f775eb27aa8b9de68d650b5
Author: Isis Lovecruft <isis at 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."""





More information about the tor-commits mailing list