PageRenderTime 38ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/r2/r2/controllers/error.py

https://github.com/stevewilber/reddit
Python | 223 lines | 186 code | 6 blank | 31 comment | 4 complexity | e27c893d04017ca42f969cc071d0e558 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0
  1. # The contents of this file are subject to the Common Public Attribution
  2. # License Version 1.0. (the "License"); you may not use this file except in
  3. # compliance with the License. You may obtain a copy of the License at
  4. # http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
  5. # License Version 1.1, but Sections 14 and 15 have been added to cover use of
  6. # software over a computer network and provide for limited attribution for the
  7. # Original Developer. In addition, Exhibit A has been modified to be consistent
  8. # with Exhibit B.
  9. #
  10. # Software distributed under the License is distributed on an "AS IS" basis,
  11. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. # the specific language governing rights and limitations under the License.
  13. #
  14. # The Original Code is reddit.
  15. #
  16. # The Original Developer is the Initial Developer. The Initial Developer of
  17. # the Original Code is reddit Inc.
  18. #
  19. # All portions of the code written by reddit are Copyright (c) 2006-2012 reddit
  20. # Inc. All Rights Reserved.
  21. ###############################################################################
  22. import os.path
  23. import pylons
  24. import paste.fileapp
  25. from paste.httpexceptions import HTTPFound, HTTPMovedPermanently
  26. from pylons.middleware import error_document_template, media_path
  27. from pylons import c, request, g
  28. from pylons.i18n import _
  29. import random as rand
  30. from r2.controllers.errors import ErrorSet
  31. from r2.lib.filters import safemarkdown, unsafe
  32. import json
  33. try:
  34. # place all r2 specific imports in here. If there is a code error, it'll get caught and
  35. # the stack trace won't be presented to the user in production
  36. from reddit_base import RedditController, Cookies
  37. from r2.models.subreddit import DefaultSR, Subreddit
  38. from r2.models.link import Link
  39. from r2.lib import pages
  40. from r2.lib.strings import strings, rand_strings
  41. from r2.lib.template_helpers import static
  42. except Exception, e:
  43. if g.debug:
  44. # if debug mode, let the error filter up to pylons to be handled
  45. raise e
  46. else:
  47. # production environment: protect the code integrity!
  48. print "HuffmanEncodingError: make sure your python compiles before deploying, stupid!"
  49. # kill this app
  50. import os
  51. os._exit(1)
  52. NUM_FAILIENS = 3
  53. redditbroke = \
  54. '''<html>
  55. <head>
  56. <title>reddit broke!</title>
  57. </head>
  58. <body>
  59. <div style="margin: auto; text-align: center">
  60. <p>
  61. <a href="/">
  62. <img border="0" src="%s" alt="you broke reddit" />
  63. </a>
  64. </p>
  65. <p>
  66. %s
  67. </p>
  68. </body>
  69. </html>
  70. '''
  71. class ErrorController(RedditController):
  72. """Generates error documents as and when they are required.
  73. The ErrorDocuments middleware forwards to ErrorController when error
  74. related status codes are returned from the application.
  75. This behaviour can be altered by changing the parameters to the
  76. ErrorDocuments middleware in your config/middleware.py file.
  77. """
  78. allowed_render_styles = ('html', 'xml', 'js', 'embed', '', "compact", 'api')
  79. # List of admins to blame (skip the first admin, "reddit")
  80. # If list is empty, just blame "an admin"
  81. admins = g.admins[1:] or ["an admin"]
  82. def __before__(self):
  83. try:
  84. c.error_page = True
  85. RedditController.__before__(self)
  86. except (HTTPMovedPermanently, HTTPFound):
  87. # ignore an attempt to redirect from an error page
  88. pass
  89. except:
  90. handle_awful_failure("Error occurred in ErrorController.__before__")
  91. def __after__(self):
  92. try:
  93. RedditController.__after__(self)
  94. except:
  95. handle_awful_failure("Error occurred in ErrorController.__after__")
  96. def __call__(self, environ, start_response):
  97. try:
  98. return RedditController.__call__(self, environ, start_response)
  99. except:
  100. return handle_awful_failure("something really awful just happened.")
  101. def send403(self):
  102. c.response.status_code = 403
  103. c.site = DefaultSR()
  104. if 'usable_error_content' in request.environ:
  105. return request.environ['usable_error_content']
  106. else:
  107. res = pages.RedditError(
  108. title=_("forbidden (%(domain)s)") % dict(domain=g.domain),
  109. message=_("you are not allowed to do that"),
  110. explanation=request.GET.get('explanation'))
  111. return res.render()
  112. def send404(self):
  113. c.response.status_code = 404
  114. if 'usable_error_content' in request.environ:
  115. return request.environ['usable_error_content']
  116. return pages.RedditError(_("page not found"),
  117. _("the page you requested does not exist")).render()
  118. def send429(self):
  119. c.response.status_code = 429
  120. if 'retry_after' in request.environ:
  121. c.response.headers['Retry-After'] = str(request.environ['retry_after'])
  122. template_name = '/ratelimit_toofast.html'
  123. else:
  124. template_name = '/ratelimit_throttled.html'
  125. loader = pylons.buffet.engines['mako']['engine']
  126. template = loader.load_template(template_name)
  127. return template.render(logo_url=static(g.default_header_url))
  128. def send503(self):
  129. c.response.status_code = 503
  130. c.response.headers['Retry-After'] = request.environ['retry_after']
  131. return request.environ['usable_error_content']
  132. def GET_document(self):
  133. try:
  134. c.errors = c.errors or ErrorSet()
  135. # clear cookies the old fashioned way
  136. c.cookies = Cookies()
  137. code = request.GET.get('code', '')
  138. try:
  139. code = int(code)
  140. except ValueError:
  141. code = 404
  142. srname = request.GET.get('srname', '')
  143. takedown = request.GET.get('takedown', "")
  144. if srname:
  145. c.site = Subreddit._by_name(srname)
  146. if c.render_style not in self.allowed_render_styles:
  147. if code not in (204, 304):
  148. c.response.content = str(code)
  149. c.response.status_code = code
  150. return c.response
  151. elif c.render_style == "api":
  152. data = request.environ.get('extra_error_data', {'error': code})
  153. c.response.content = json.dumps(data)
  154. return c.response
  155. elif takedown and code == 404:
  156. link = Link._by_fullname(takedown)
  157. return pages.TakedownPage(link).render()
  158. elif code == 403:
  159. return self.send403()
  160. elif code == 429:
  161. return self.send429()
  162. elif code == 500:
  163. randmin = {'admin': rand.choice(self.admins)}
  164. failien_name = 'youbrokeit%d.png' % rand.randint(1, NUM_FAILIENS)
  165. failien_url = static(failien_name)
  166. return redditbroke % (failien_url, rand_strings.sadmessages % randmin)
  167. elif code == 503:
  168. return self.send503()
  169. elif code == 304:
  170. if request.GET.has_key('x-sup-id'):
  171. x_sup_id = request.GET.get('x-sup-id')
  172. if '\r\n' not in x_sup_id:
  173. c.response.headers['x-sup-id'] = x_sup_id
  174. return c.response
  175. elif c.site:
  176. return self.send404()
  177. else:
  178. return "page not found"
  179. except:
  180. return handle_awful_failure("something really bad just happened.")
  181. POST_document = GET_document
  182. def handle_awful_failure(fail_text):
  183. """
  184. Makes sure that no errors generated in the error handler percolate
  185. up to the user unless debug is enabled.
  186. """
  187. if g.debug:
  188. import sys
  189. s = sys.exc_info()
  190. # reraise the original error with the original stack trace
  191. raise s[1], None, s[2]
  192. try:
  193. # log the traceback, and flag the "path" as the error location
  194. import traceback
  195. g.log.error("FULLPATH: %s" % fail_text)
  196. g.log.error(traceback.format_exc())
  197. return redditbroke % (rand.randint(1,NUM_FAILIENS), fail_text)
  198. except:
  199. # we are doomed. Admit defeat
  200. return "This is an error that should never occur. You win."