PageRenderTime 53ms CodeModel.GetById 10ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/r2/r2/controllers/error.py

https://github.com/dcarvill/reddit
Python | 215 lines | 180 code | 4 blank | 31 comment | 7 complexity | 4c6725581f412d1a15604a54f325f6a0 MD5 | raw file
  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 the
 17# Original Code is CondeNet, Inc.
 18#
 19# All portions of the code written by CondeNet are Copyright (c) 2006-2010
 20# CondeNet, Inc. All Rights Reserved.
 21################################################################################
 22import os.path
 23
 24import pylons
 25import paste.fileapp
 26from paste.httpexceptions import HTTPFound
 27from pylons.middleware import error_document_template, media_path
 28from pylons import c, request, g
 29from pylons.i18n import _
 30import random as rand
 31from r2.lib.filters import safemarkdown, unsafe
 32
 33try:
 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
 42except 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
 53NUM_FAILIENS = 3
 54
 55redditbroke =  \
 56'''<html>
 57  <head>
 58    <title>reddit broke!</title>
 59  </head>
 60  <body>
 61    <div style="margin: auto; text-align: center">
 62      <p>
 63        <a href="/">
 64          <img border="0" src="%s" alt="you broke reddit" />
 65        </a>
 66      </p>
 67      <p>
 68        %s
 69      </p>
 70  </body>
 71</html>
 72'''
 73
 74class ErrorController(RedditController):
 75    """Generates error documents as and when they are required.
 76
 77    The ErrorDocuments middleware forwards to ErrorController when error
 78    related status codes are returned from the application.
 79
 80    This behaviour can be altered by changing the parameters to the
 81    ErrorDocuments middleware in your config/middleware.py file.
 82    """
 83    allowed_render_styles = ('html', 'xml', 'js', 'embed', '', "compact", 'api')
 84    # List of admins to blame (skip the first admin, "reddit")
 85    # If list is empty, just blame "an admin"
 86    admins = g.admins[1:] or ["an admin"]
 87    def __before__(self):
 88        try:
 89            c.error_page = True
 90            RedditController.__before__(self)
 91        except HTTPFound:
 92            # ignore an attempt to redirect from an error page
 93            pass
 94        except:
 95            handle_awful_failure("Error occurred in ErrorController.__before__")
 96
 97    def __after__(self): 
 98        try:
 99            RedditController.__after__(self)
100        except:
101            handle_awful_failure("Error occurred in ErrorController.__after__")
102
103    def __call__(self, environ, start_response):
104        try:
105            return RedditController.__call__(self, environ, start_response)
106        except:
107            return handle_awful_failure("something really awful just happened.")
108
109
110    def send403(self):
111        c.response.status_code = 403
112        c.site = DefaultSR()
113        if 'usable_error_content' in request.environ:
114            return request.environ['usable_error_content']
115        else:
116            res = pages.RedditError(
117                title=_("forbidden (%(domain)s)") % dict(domain=g.domain),
118                message=_("you are not allowed to do that"))
119            return res.render()
120
121    def send404(self):
122        c.response.status_code = 404
123        if 'usable_error_content' in request.environ:
124            return request.environ['usable_error_content']
125        return pages.RedditError(_("page not found"),
126                                 _("the page you requested does not exist")).render()
127
128    def send429(self):
129        c.response.status_code = 429
130
131        if 'retry_after' in request.environ:
132            c.response.headers['Retry-After'] = str(request.environ['retry_after'])
133            template_name = '/ratelimit_toofast.html'
134        else:
135            template_name = '/ratelimit_throttled.html'
136
137        loader = pylons.buffet.engines['mako']['engine']
138        template = loader.load_template(template_name)
139        return template.render(logo_url=static(g.default_header_url))
140
141    def send503(self):
142        c.response.status_code = 503
143        c.response.headers['Retry-After'] = request.environ['retry_after']
144        return request.environ['usable_error_content']
145
146    def GET_document(self):
147        try:
148            # clear cookies the old fashioned way 
149            c.cookies = Cookies()
150
151            code =  request.GET.get('code', '')
152            try:
153                code = int(code)
154            except ValueError:
155                code = 404
156            srname = request.GET.get('srname', '')
157            takedown = request.GET.get('takedown', "")
158            
159            if srname:
160                c.site = Subreddit._by_name(srname)
161            if c.render_style not in self.allowed_render_styles:
162                if code not in (204, 304):
163                     c.response.content = str(code)
164                return c.response
165            elif c.render_style == "api":
166                c.response.content = "{\"error\": %s}" % code
167                return c.response
168            elif takedown and code == 404:
169                link = Link._by_fullname(takedown)
170                return pages.TakedownPage(link).render()
171            elif code == 403:
172                return self.send403()
173            elif code == 429:
174                return self.send429()
175            elif code == 500:
176                randmin = {'admin': rand.choice(self.admins)}
177                failien_name = 'youbrokeit%d.png' % rand.randint(1, NUM_FAILIENS)
178                failien_url = static(failien_name)
179                return redditbroke % (failien_url, rand_strings.sadmessages % randmin)
180            elif code == 503:
181                return self.send503()
182            elif code == 304:
183                if request.GET.has_key('x-sup-id'):
184                    x_sup_id = request.GET.get('x-sup-id')
185                    if '\r\n' not in x_sup_id:
186                        c.response.headers['x-sup-id'] = x_sup_id
187                return c.response
188            elif c.site:
189                return self.send404()
190            else:
191                return "page not found"
192        except:
193            return handle_awful_failure("something really bad just happened.")
194
195    POST_document = GET_document
196
197def handle_awful_failure(fail_text):
198    """
199    Makes sure that no errors generated in the error handler percolate
200    up to the user unless debug is enabled.
201    """
202    if g.debug:
203        import sys
204        s = sys.exc_info()
205        # reraise the original error with the original stack trace
206        raise s[1], None, s[2]
207    try:
208        # log the traceback, and flag the "path" as the error location
209        import traceback
210        g.log.error("FULLPATH: %s" % fail_text)
211        g.log.error(traceback.format_exc())
212        return redditbroke % (rand.randint(1,NUM_FAILIENS), fail_text)
213    except:
214        # we are doomed.  Admit defeat
215        return "This is an error that should never occur.  You win."