Escape untrusted content before displaying it.

Reviewed in https://codereview.appspot.com/6460120/.
diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py
index e9cb17e..5439a35 100644
--- a/oauth2client/appengine.py
+++ b/oauth2client/appengine.py
@@ -20,6 +20,7 @@
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
 import base64
+import cgi
 import httplib2
 import logging
 import os
@@ -51,6 +52,18 @@
 XSRF_MEMCACHE_ID = 'xsrf_secret_key'
 
 
+def _safe_html(s):
+  """Escape text to make it safe to display.
+
+  Args:
+    s: string, The text to escape.
+
+  Returns:
+    The escaped text as a string.
+  """
+  return cgi.escape(s, quote=1).replace("'", ''')
+
+
 class InvalidClientSecretsError(Exception):
   """The client_secrets.json file is malformed or missing required fields."""
 
@@ -417,7 +430,7 @@
 
   def _display_error_message(self, request_handler):
     request_handler.response.out.write('<html><body>')
-    request_handler.response.out.write(self._message)
+    request_handler.response.out.write(_safe_html(self._message))
     request_handler.response.out.write('</body></html>')
 
   def oauth_required(self, method):
@@ -578,7 +591,7 @@
         if error:
           errormsg = self.request.get('error_description', error)
           self.response.out.write(
-              'The authorization request failed: %s' % errormsg)
+              'The authorization request failed: %s' % _safe_html(errormsg))
         else:
           user = users.get_current_user()
           decorator._create_flow(self)
diff --git a/tests/test_oauth2client_appengine.py b/tests/test_oauth2client_appengine.py
index 1c2d17a..6039a0d 100644
--- a/tests/test_oauth2client_appengine.py
+++ b/tests/test_oauth2client_appengine.py
@@ -331,7 +331,10 @@
         webapp2.Route(r'/bar_path/<year:\d{4}>/<month:\d{2}>',
           handler=TestAwareHandler, name='bar')],
       debug=True)
-    self.app = TestApp(application)
+    self.app = TestApp(application, extra_environ={
+        'wsgi.url_scheme': 'http',
+        'HTTP_HOST': 'localhost',
+        })
     users.get_current_user = user_mock()
     self.httplib2_orig = httplib2.Http
     httplib2.Http = Http2Mock
@@ -343,7 +346,7 @@
   def test_required(self):
     # An initial request to an oauth_required decorated path should be a
     # redirect to start the OAuth dance.
-    response = self.app.get('/foo_path')
+    response = self.app.get('http://localhost/foo_path')
     self.assertTrue(response.status.startswith('302'))
     q = parse_qs(response.headers['Location'].split('?', 1)[1])
     self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0])
@@ -425,7 +428,7 @@
 
   def test_aware(self):
     # An initial request to an oauth_aware decorated path should not redirect.
-    response = self.app.get('/bar_path/2012/01')
+    response = self.app.get('http://localhost/bar_path/2012/01')
     self.assertEqual('Hello World!', response.body)
     self.assertEqual('200 OK', response.status)
     self.assertEqual(False, self.decorator.has_credentials())
@@ -471,10 +474,10 @@
     response = self.app.get('/bar_path/2012/01')
     url = self.decorator.authorize_url()
     response = self.app.get('/oauth2callback', {
-        'error': 'BadStuffHappened'
+        'error': 'Bad<Stuff>Happened\''
         })
     self.assertEqual('200 OK', response.status)
-    self.assertTrue('BadStuffHappened' in response.body)
+    self.assertTrue('Bad&lt;Stuff&gt;Happened&#39;' in response.body)
 
   def test_kwargs_are_passed_to_underlying_flow(self):
     decorator = OAuth2Decorator(client_id='foo_client_id',