1
# -*- test-case-name: twisted.web.test.test_web -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Implementation of the lowest-level Resource class.
11
from zope.interface import Attribute, implements, Interface
13
from twisted.web import http
16
class IResource(Interface):
23
Signal if this IResource implementor is a "leaf node" or not. If True,
24
getChildWithDefault will not be called on this Resource.
27
def getChildWithDefault(name, request):
29
Return a child with the given name for the given request.
30
This is the external interface used by the Resource publishing
31
machinery. If implementing IResource without subclassing
32
Resource, it must be provided. However, if subclassing Resource,
33
getChild overridden instead.
36
def putChild(path, child):
38
Put a child IResource implementor at the given path.
43
Render a request. This is called on the leaf resource for
44
a request. Render must return either a string, which will
45
be sent to the browser as the HTML for the request, or
46
server.NOT_DONE_YET. If NOT_DONE_YET is returned,
47
at some point later (in a Deferred callback, usually)
48
call request.write("<html>") to write data to the request,
49
and request.finish() to send the data to the browser.
54
def getChildForRequest(resource, request):
56
Traverse resource tree to find who will handle the request.
58
while request.postpath and not resource.isLeaf:
59
pathElement = request.postpath.pop(0)
60
request.prepath.append(pathElement)
61
resource = resource.getChildWithDefault(pathElement, request)
68
I define a web-accessible resource.
70
I serve 2 main purposes; one is to provide a standard representation for
71
what HTTP specification calls an 'entity', and the other is to provide an
72
abstract directory structure for URL retrieval.
77
entityType = IResource
88
### Abstract Collection Interface
90
def listStaticNames(self):
91
return self.children.keys()
93
def listStaticEntities(self):
94
return self.children.items()
97
return self.listStaticNames() + self.listDynamicNames()
99
def listEntities(self):
100
return self.listStaticEntities() + self.listDynamicEntities()
102
def listDynamicNames(self):
105
def listDynamicEntities(self, request=None):
108
def getStaticEntity(self, name):
109
return self.children.get(name)
111
def getDynamicEntity(self, name, request):
112
if not self.children.has_key(name):
113
return self.getChild(name, request)
117
def delEntity(self, name):
118
del self.children[name]
120
def reallyPutEntity(self, name, entity):
121
self.children[name] = entity
123
# Concrete HTTP interface
125
def getChild(self, path, request):
127
Retrieve a 'child' resource from me.
129
Implement this to create dynamic resource generation -- resources which
130
are always available may be registered with self.putChild().
132
This will not be called if the class-level variable 'isLeaf' is set in
133
your subclass; instead, the 'postpath' attribute of the request will be
134
left as a list of the remaining path elements.
136
For example, the URL /foo/bar/baz will normally be::
138
| site.resource.getChild('foo').getChild('bar').getChild('baz').
140
However, if the resource returned by 'bar' has isLeaf set to true, then
141
the getChild call will never be made on it.
143
@param path: a string, describing the child
145
@param request: a twisted.web.server.Request specifying meta-information
146
about the request that is being made for this child.
148
return NoResource("No such child resource.")
151
def getChildWithDefault(self, path, request):
153
Retrieve a static or dynamically generated child resource from me.
155
First checks if a resource was added manually by putChild, and then
156
call getChild to check for dynamic resources. Only override if you want
157
to affect behaviour of all child lookups, rather than just dynamic
160
This will check to see if I have a pre-registered child resource of the
161
given name, and call getChild if I do not.
163
if path in self.children:
164
return self.children[path]
165
return self.getChild(path, request)
168
def getChildForRequest(self, request):
169
warnings.warn("Please use module level getChildForRequest.", DeprecationWarning, 2)
170
return getChildForRequest(self, request)
173
def putChild(self, path, child):
175
Register a static child.
177
You almost certainly don't want '/' in your path. If you
178
intended to have the root of a folder, e.g. /foo/, you want
181
self.children[path] = child
182
child.server = self.server
185
def render(self, request):
187
Render a given resource. See L{IResource}'s render method.
189
I delegate to methods of self with the form 'render_METHOD'
190
where METHOD is the HTTP that was used to make the
191
request. Examples: render_GET, render_HEAD, render_POST, and
192
so on. Generally you should implement those methods instead of
195
render_METHOD methods are expected to return a string which
196
will be the rendered page, unless the return value is
197
twisted.web.server.NOT_DONE_YET, in which case it is this
198
class's responsibility to write the results to
199
request.write(data), then call request.finish().
201
Old code that overrides render() directly is likewise expected
202
to return a string or NOT_DONE_YET.
204
m = getattr(self, 'render_' + request.method, None)
206
# This needs to be here until the deprecated subclasses of the
207
# below three error resources in twisted.web.error are removed.
208
from twisted.web.error import UnsupportedMethod
209
raise UnsupportedMethod(getattr(self, 'allowedMethods', ()))
213
def render_HEAD(self, request):
215
Default handling of HEAD method.
217
I just return self.render_GET(request). When method is HEAD,
218
the framework will handle this correctly.
220
return self.render_GET(request)
224
class ErrorPage(Resource):
226
L{ErrorPage} is a resource which responds with a particular
227
(parameterized) status and a body consisting of HTML containing some
228
descriptive text. This is useful for rendering simple error pages.
230
@ivar template: A C{str} which will have a dictionary interpolated into
231
it to generate the response body. The dictionary has the following
234
- C{"code"}: The status code passed to L{ErrorPage.__init__}.
235
- C{"brief"}: The brief description passed to L{ErrorPage.__init__}.
236
- C{"detail"}: The detailed description passed to
237
L{ErrorPage.__init__}.
239
@ivar code: An integer status code which will be used for the response.
240
@ivar brief: A short string which will be included in the response body.
241
@ivar detail: A longer string which will be included in the response body.
246
<head><title>%(code)s - %(brief)s</title></head>
254
def __init__(self, status, brief, detail):
255
Resource.__init__(self)
261
def render(self, request):
262
request.setResponseCode(self.code)
263
request.setHeader("content-type", "text/html")
264
return self.template % dict(
270
def getChild(self, chnam, request):
275
class NoResource(ErrorPage):
277
L{NoResource} is a specialization of L{ErrorPage} which returns the HTTP
278
response code I{NOT FOUND}.
280
def __init__(self, message="Sorry. No luck finding that resource."):
281
ErrorPage.__init__(self, http.NOT_FOUND,
287
class ForbiddenResource(ErrorPage):
289
L{ForbiddenResource} is a specialization of L{ErrorPage} which returns the
290
I{FORBIDDEN} HTTP response code.
292
def __init__(self, message="Sorry, resource is forbidden."):
293
ErrorPage.__init__(self, http.FORBIDDEN,
294
"Forbidden Resource",
299
'IResource', 'getChildForRequest',
300
'Resource', 'ErrorPage', 'NoResource', 'ForbiddenResource']