/usr/share/pyshared/twisted/web2/resource.py is in python-twisted-web2 8.1.0-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | # -*- test-case-name: twisted.web2.test.test_server,twisted.web2.test.test_resource -*-
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
# See LICENSE for details.
"""
I hold the lowest-level L{Resource} class and related mix-in classes.
"""
# System Imports
from zope.interface import implements
from twisted.web2 import iweb, http, server, responsecode
class RenderMixin(object):
"""
Mix-in class for L{iweb.IResource} which provides a dispatch mechanism for
handling HTTP methods.
"""
def allowedMethods(self):
"""
@return: A tuple of HTTP methods that are allowed to be invoked on this resource.
"""
if not hasattr(self, "_allowed_methods"):
self._allowed_methods = tuple([name[5:] for name in dir(self) if name.startswith('http_')])
return self._allowed_methods
def checkPreconditions(self, request):
"""
Checks all preconditions imposed by this resource upon a request made
against it.
@param request: the request to process.
@raise http.HTTPError: if any precondition fails.
@return: C{None} or a deferred whose callback value is C{request}.
"""
#
# http.checkPreconditions() gets called by the server after every
# GET or HEAD request.
#
# For other methods, we need to know to bail out before request
# processing, especially for methods that modify server state (eg. PUT).
# We also would like to do so even for methods that don't, if those
# methods might be expensive to process. We're assuming that GET and
# HEAD are not expensive.
#
if request.method not in ("GET", "HEAD"):
http.checkPreconditions(request)
# Check per-method preconditions
method = getattr(self, "preconditions_" + request.method, None)
if method:
return method(request)
def renderHTTP(self, request):
"""
See L{iweb.IResource.renderHTTP}.
This implementation will dispatch the given C{request} to another method
of C{self} named C{http_}METHOD, where METHOD is the HTTP method used by
C{request} (eg. C{http_GET}, C{http_POST}, etc.).
Generally, a subclass should implement those methods instead of
overriding this one.
C{http_*} methods are expected provide the same interface and return the
same results as L{iweb.IResource}C{.renderHTTP} (and therefore this method).
C{etag} and C{last-modified} are added to the response returned by the
C{http_*} header, if known.
If an appropriate C{http_*} method is not found, a
L{responsecode.NOT_ALLOWED}-status response is returned, with an
appropriate C{allow} header.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
method = getattr(self, "http_" + request.method, None)
if not method:
response = http.Response(responsecode.NOT_ALLOWED)
response.headers.setHeader("allow", self.allowedMethods())
return response
d = self.checkPreconditions(request)
if d is None:
return method(request)
else:
return d.addCallback(lambda _: method(request))
def http_OPTIONS(self, request):
"""
Respond to a OPTIONS request.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
response = http.Response(responsecode.OK)
response.headers.setHeader("allow", self.allowedMethods())
return response
def http_TRACE(self, request):
"""
Respond to a TRACE request.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
return server.doTrace(request)
def http_HEAD(self, request):
"""
Respond to a HEAD request.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
return self.http_GET(request)
def http_GET(self, request):
"""
Respond to a GET request.
This implementation validates that the request body is empty and then
dispatches the given C{request} to L{render} and returns its result.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
if request.stream.length != 0:
return responsecode.REQUEST_ENTITY_TOO_LARGE
return self.render(request)
def render(self, request):
"""
Subclasses should implement this method to do page rendering.
See L{http_GET}.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
raise NotImplementedError("Subclass must implement render method.")
class Resource(RenderMixin):
"""
An L{iweb.IResource} implementation with some convenient mechanisms for
locating children.
"""
implements(iweb.IResource)
addSlash = False
def locateChild(self, request, segments):
"""
Locates a child resource of this resource.
@param request: the request to process.
@param segments: a sequence of URL path segments.
@return: a tuple of C{(child, segments)} containing the child
of this resource which matches one or more of the given C{segments} in
sequence, and a list of remaining segments.
"""
w = getattr(self, 'child_%s' % (segments[0], ), None)
if w:
r = iweb.IResource(w, None)
if r:
return r, segments[1:]
return w(request), segments[1:]
factory = getattr(self, 'childFactory', None)
if factory is not None:
r = factory(request, segments[0])
if r:
return r, segments[1:]
return None, []
def child_(self, request):
"""
This method locates a child with a trailing C{"/"} in the URL.
@param request: the request to process.
"""
if self.addSlash and len(request.postpath) == 1:
return self
return None
def putChild(self, path, child):
"""
Register a static child.
This implementation registers children by assigning them to attributes
with a C{child_} prefix. C{resource.putChild("foo", child)} is
therefore same as C{o.child_foo = child}.
@param path: the name of the child to register. You almost certainly
don't want C{"/"} in C{path}. If you want to add a "directory"
resource (e.g. C{/foo/}) specify C{path} as C{""}.
@param child: an object adaptable to L{iweb.IResource}.
"""
setattr(self, 'child_%s' % (path, ), child)
def http_GET(self, request):
if self.addSlash and request.prepath[-1] != '':
# If this is a directory-ish resource...
return http.RedirectResponse(request.unparseURL(path=request.path+'/'))
return super(Resource, self).http_GET(request)
class PostableResource(Resource):
"""
A L{Resource} capable of handling the POST request method.
@cvar maxMem: maximum memory used during the parsing of the data.
@type maxMem: C{int}
@cvar maxFields: maximum number of form fields allowed.
@type maxFields: C{int}
@cvar maxSize: maximum size of the whole post allowed.
@type maxSize: C{int}
"""
maxMem = 100 * 1024
maxFields = 1024
maxSize = 10 * 1024 * 1024
def http_POST(self, request):
"""
Respond to a POST request.
Reads and parses the incoming body data then calls L{render}.
@param request: the request to process.
@return: an object adaptable to L{iweb.IResponse}.
"""
return server.parsePOSTData(request,
self.maxMem, self.maxFields, self.maxSize
).addCallback(lambda res: self.render(request))
class LeafResource(RenderMixin):
"""
A L{Resource} with no children.
"""
implements(iweb.IResource)
def locateChild(self, request, segments):
return self, server.StopTraversal
class RedirectResource(LeafResource):
"""
A L{LeafResource} which always performs a redirect.
"""
implements(iweb.IResource)
def __init__(self, *args, **kwargs):
"""
Parameters are URL components and are the same as those for
L{urlparse.urlunparse}. URL components which are not specified will
default to the corresponding component of the URL of the request being
redirected.
"""
self._args = args
self._kwargs = kwargs
def renderHTTP(self, request):
return http.RedirectResponse(request.unparseURL(*self._args, **self._kwargs))
class WrapperResource(object):
"""
An L{iweb.IResource} implementation which wraps a L{RenderMixin} instance
and provides a hook in which a subclass can implement logic that is called
before request processing on the contained L{Resource}.
"""
implements(iweb.IResource)
def __init__(self, resource):
self.resource=resource
def hook(self, request):
"""
Override this method in order to do something before passing control on
to the wrapped resource's C{renderHTTP} and C{locateChild} methods.
@return: None or a L{Deferred}. If a deferred object is
returned, it's value is ignored, but C{renderHTTP} and
C{locateChild} are chained onto the deferred as callbacks.
"""
raise NotImplementedError()
def locateChild(self, request, segments):
x = self.hook(request)
if x is not None:
return x.addCallback(lambda data: (self.resource, segments))
return self.resource, segments
def renderHTTP(self, request):
x = self.hook(request)
if x is not None:
return x.addCallback(lambda data: self.resource)
return self.resource
__all__ = ['RenderMixin', 'Resource', 'PostableResource', 'LeafResource', 'WrapperResource']
|