1 """
2 Default error displays for the data center and error helper code.
3
4 Everything in here must render synchronuosly.
5
6 You probably should not construct anything in this module directly
7 but rather just raise the appropriate exceptions from svcs.
8 """
9
10
11
12
13
14
15
16 import urlparse
17
18 from nevow import inevow
19 from nevow import rend
20 from nevow import tags as T
21 from twisted.internet import defer
22 from twisted.python import failure
23 from twisted.python import log
24 from zope.interface import implements
25
26 from gavo import base
27 from gavo import svcs
28 from gavo import utils
29 from gavo.base import config
30 from gavo.web import common
31
32
39
40
41 -class ErrorPage(rend.Page, common.CommonRenderers):
42 """A base for error handling pages.
43
44 The idea is that you set the "handles" class attribute to
45 the exception you handle. The exception has to match exactly, i.e.,
46 no isinstancing is done.
47
48 You also must set status to the HTTP status code the error should
49 return.
50
51 All error pages have a failure attribute that's a twisted failure
52 with all the related mess (e.g., tracebacks).
53
54 You have the status and message data methods.
55 """
56 handles = None
57 status = 500
58 titleMessage = "Unspecified Error"
59 beforeMessage = "We're sorry, but something didn't work out:"
60 afterMessage = T.p["This generic text shouldn't be here. The"
61 " child class should override afterMessage."]
62 _footer = "delete this when done"
63
64 - def __init__(self, error):
66
67 - def data_status(self, ctx, data):
68 return str(self.status)
69
70 - def data_message(self, ctx, data):
71 return self.failure.getErrorMessage()
72
73 - def render_beforeMessage(self, ctx, data):
74 return ctx.tag[self.beforeMessage]
75
76 - def render_afterMessage(self, ctx, data):
77 return ctx.tag[self.afterMessage]
78
79 - def render_message(self, ctx, data):
80 return ctx.tag(class_="errmsg")[self.failure.getErrorMessage()]
81
82 - def render_hint(self, ctx, data):
83 if (hasattr(self.failure.value, "hint") and self.failure.value.hint):
84 return ctx.tag[T.strong["Hint: "],
85 self.failure.value.hint]
86 return ""
87
88 - def render_rdlink(self, ctx, data):
89 if hasattr(self.failure.value, "rd") and self.failure.value.rd:
90 rdURL = base.makeAbsoluteURL("/browse/%s"%
91 self.failure.value.rd.sourceId)
92 return T.p(class_="rdbacklink")["Also see the ",
93 T.a(href=rdURL)["resources provided by this RD"],
94 "."]
95 return ""
96
97 - def render_titlemessage(self, ctx, data):
98 return ctx.tag["%s -- %s"%(
99 base.getConfig("web", "sitename"), self.titleMessage)]
100
105
106 - def renderHTTP(self, ctx):
107 request = inevow.IRequest(ctx)
108 request.setResponseCode(self.status)
109 return rend.Page.renderHTTP(self, ctx)
110
111 docFactory = common.doctypedStan(T.html[
112 T.head(render=T.directive("commonhead"))[
113 T.title(render=T.directive("titlemessage"))],
114 T.body[
115 T.img(src="/static/img/logo_medium.png", class_="headlinelogo",
116 style="position:absolute;right:5pt"),
117 T.h1[
118 T.invisible(render=T.directive("titlemessage")),
119 " (",
120 T.invisible(data=T.directive("status"), render=T.directive("string")),
121 ")"],
122 T.p(render=T.directive("beforeMessage")),
123 T.div(class_="errors", render=T.directive("message")),
124 T.div(render=T.directive("afterMessage")),
125 T.invisible(render=T.directive("footer"))]])
126
127
128
129 -class NotFoundPage(ErrorPage):
130 handles = svcs.UnknownURI
131 status = 404
132 titleMessage = "Not Found"
133 beforeMessage = ("We're sorry, but the resource you"
134 " requested could not be located.")
135 afterMessage = [
136 T.p["If this message resulted from following a link from ",
137 T.strong["within the data center"],
138 ", you have discovered a bug, and we would be"
139 " extremely grateful if you could notify us."],
140 T.p["If you got here following an ",
141 T.strong["external link"],
142 ", we would be"
143 " grateful for a notification as well. We will ask the"
144 " external operators to fix their links or provide"
145 " redirects as appropriate."],
146 T.p["In either case, you may find whatever you were looking"
147 " for by inspecting our ",
148 T.a(href="/")["list of published services"], "."],
149 T.p(render=T.directive("rdlink"))]
150
151 - def renderHTTP_notFound(self, ctx):
152 return self.renderHTTP(ctx)
153
154
156 """A NotFoundPage with a message that's taken from a piece of stan.
157 """
158 - def __init__(self, message):
160
161 - def render_message(self, ctx, data):
162 return ctx.tag[self.message]
163
164 - def render_rdlink(self, ctx, data):
166
167
168 -class OtherNotFoundPage(NotFoundPage):
170
171
172 -class RDNotFoundPage(NotFoundPage):
174
175
176 -class ForbiddenPage(ErrorPage):
177 handles = svcs.ForbiddenURI
178 status = 403
179 titleMessage = "Forbidden"
180 beforeMessage = "We're sorry, but the resource you requested is forbidden."
181 afterMessage = T.div[
182 T.p["This usually means you tried to use a renderer on a service"
183 " that does not support it. If you did not come up with the"
184 " URL in question yourself, complain fiercely to the staff of ",
185 T.invisible(render=T.directive("getconfig"))["[web]sitename"],
186 "."],
187 T.p(render=T.directive("rdlink"))]
188
189
192 request = inevow.IRequest(ctx)
193
194
195 self.destURL = self.failure.value.dest
196 if '?' not in self.destURL:
197 args = urlparse.urlparse(request.uri).query
198 if args:
199 self.destURL = self.failure.value.dest+"?"+args
200 request.setHeader("location", str(self.destURL))
201 return ErrorPage.renderHTTP(self, ctx)
202
204 return ctx.tag(href=self.destURL)
205
206 afterMessage = T.p["You should not see this page -- either your browser or"
207 " our site is broken. Complain."]
208
209
210 -class RedirectPage(RedirectBase):
211 handles = svcs.WebRedirect
212 status = 301
213 titleMessage = "Moved Permanently"
214 beforeMessage = ["The resource you requested is available from a ",
215 T.a(render=T.directive("destLink"))[
216 "different URL"],
217 "."]
218
219
220 -class FoundPage(RedirectBase):
221 handles = svcs.Found
222 status = 302
223 titleMessage = "Found"
224 beforeMessage = ["The resource you requested can be found at ",
225 T.a(render=T.directive("destLink"))[
226 T.invisible(render=T.directive("destLink"))],
227 "."]
228
229
230 -class SeeOtherPage(RedirectBase):
231 handles = svcs.SeeOther
232 status = 303
233 titleMessage = "See Other"
234 beforeMessage = ["Please turn to a ",
235 T.a(render=T.directive("destLink"))[
236 "different URL"],
237 " to go on."]
238
239
240 -class AuthenticatePage(ErrorPage):
241 handles = svcs.Authenticate
242 status = 401
243 titleMessage = "Authentication Required"
244
245 - def renderHTTP(self, ctx):
246 request = inevow.IRequest(ctx)
247 request.setHeader('WWW-Authenticate',
248 'Basic realm="%s"'%str(self.failure.value.realm))
249 return ErrorPage.renderHTTP(self, ctx)
250
251 docFactory = svcs.loadSystemTemplate("unauth.html")
252
253
254 -class BadMethodPage(ErrorPage):
255 handles = svcs.BadMethod
256 status = 405
257 titleMessage = "Bad Method"
258 beforeMessage = (
259 "You just tried to use some HTTP method to access this resource"
260 " that this resource does not support. This probably means that"
261 " this resource is for exclusive use for specialized clients.")
262 afterMessage = T.p["You may find whatever you were really looking"
263 " for by inspecting our ",
264 T.a(href="/")["list of published services"],
265 "."]
266
267
275
276
278 handles = base.ReportableError
279 status = 500
280 titleMessage = "Error"
281 beforeMessage = ("A piece of code failed:")
282 afterMessage = [T.p["Problems of this sort usually mean we considered"
283 " the possibility of something like this happening; if the above"
284 " doesn't give you sufficient hints to fix the problem, please"
285 " complain to the address given below."],
286 T.p(render=T.directive("hint"))]
287
288
289 errorTemplate = (
290 '<body><div style="position:fixed;left:4px;top:4px;'
291 'visibility:visible;overflow:visible !important;'
292 'max-width:600px !important;z-index:500">'
293 '<div style="border:2px solid red;'
294 'width:400px !important;background:white">'
295 '%s'
296 '</div></div></body></html>')
297
310
311
313 """A catch-all page served when no other error page seemed responsible.
314 """
315 handles = base.Error
316 status = 500
317 titleMessage = "Uncaught Exception"
318 beforeMessage = T.p["Your action has caused a(n) ",
319 T.span(render=str, data=T.directive("excname")),
320 " exception to occur. As additional info, the failing code"
321 " gave:"],
322 afterMessage = T.p["This is always a bug in our software, and we would really"
323 " be grateful for a report to the contact address below,"
324 " preferably with a description of what you were trying to do,"
325 " including any data pieces if applicable. Thanks."]
326
327 - def data_excname(self, ctx, data):
328 return self.failure.value.__class__.__name__
329
330 - def renderInnerException(self, ctx):
331 """called when rendering already has started.
332
333 We don't know where we're sitting, so we try to break out as well
334 as we can.
335 """
336 request = inevow.IRequest(ctx)
337 request.setResponseCode(500)
338 data = _formatFailure(self.failure)
339 if isinstance(data, unicode):
340 data = data.encode("utf-8", "ignore")
341 request.write(data)
342 request.finishRequest(False)
343 return ""
344
345 - def renderHTTP(self, ctx):
346 request = inevow.IRequest(ctx)
347 base.ui.notifyFailure(self.failure)
348 base.ui.notifyInfo("Arguments of failed request: %s"%
349 repr(request.args)[:2000])
350 if getattr(self.failure.value, "hint", None):
351 base.ui.notifyDebug("Exception hint: %s"%self.failure.value.hint)
352 if getattr(request, "startedWriting", False):
353
354 return self.renderInnerException(ctx)
355 else:
356 return ErrorPage.renderHTTP(self, ctx)
357
358
360 """write some panic-type stuff for failure and finishes the request.
361 """
362 request = inevow.IRequest(ctx)
363 request.setResponseCode(500)
364 base.ui.notifyFailure(failure)
365 base.ui.notifyInfo("Arguments were %s"%request.args)
366
367
368 request.write(
369 "<html><head><title>Severe Error</title></head><body>")
370 try:
371 request.write(_formatFailure(failure))
372 except:
373 request.write("<h1>Ouch</h1><p>There has been an error that in"
374 " addition breaks the toplevel error catching code. Complain.</p>")
375 base.ui.notifyError("Error while processing failure: %s"%secErr)
376 request.write("</body></html>")
377 request.finishRequest(False)
378
379
380 getErrorPage = utils.buildClassResolver(
381 baseClass=ErrorPage,
382 objects=globals().values(),
383 instances=False,
384 key=lambda obj: obj.handles,
385 default=InternalServerErrorPage)
386
387
388 -def getDCErrorPage(error):
389 """returns stuff for root.ErrorCatchingNevowSite.
390 """
391
392
393 if error is None:
394 error = failure.Failure()
395 return getErrorPage(error.value.__class__)(error)
396
397
399 """finishes ctx's request.
400 """
401
402
403 request = inevow.IRequest(ctx)
404 request.finishRequest(False)
405 return ""
406
407
409 """The toplevel exception handler.
410 """
411
412 implements(inevow.ICanHandleException, inevow.ICanHandleNotFound)
413
415 try:
416 handler = getDCErrorPage(error)
417 return defer.maybeDeferred(handler.renderHTTP, ctx
418 ).addCallback(lambda ignored: _finishErrorProcessing(ctx, error)
419 ).addErrback(lambda secErr: _writePanicInfo(ctx, error, secErr))
420 except:
421 base.ui.notifyError("Error while handling %s error:"%error)
422 _writePanicInfo(ctx, error)
423
429
431
432 log.err(error, _why="Inline exception")
433 return ('<div style="border: 1px dashed red; color: red; clear: both">'
434 '[[ERROR]]</div>')
435