~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web2/iweb.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.web2.test -*-
 
2
 
 
3
"""
 
4
    I contain the interfaces for several web related objects including IRequest
 
5
    and IResource.  I am based heavily on ideas from nevow.inevow
 
6
"""
 
7
 
 
8
from zope.interface import Attribute, Interface, interface
 
9
 
 
10
# server.py interfaces
 
11
class IResource(Interface):
 
12
    """
 
13
    An HTTP resource.
 
14
 
 
15
    I serve 2 main purposes: one is to provide a standard representation for
 
16
    what HTTP specification calls an 'entity', and the other is to provide an
 
17
    mechanism for mapping URLs to content.
 
18
    """
 
19
 
 
20
    def locateChild(req, segments):
 
21
        """Locate another object which can be adapted to IResource.
 
22
 
 
23
        @return: A 2-tuple of (resource, remaining-path-segments),
 
24
                 or a deferred which will fire the above.
 
25
                 
 
26
                 Causes the object publishing machinery to continue on
 
27
                 with specified resource and segments, calling the
 
28
                 appropriate method on the specified resource.
 
29
                 
 
30
                 If you return (self, L{server.StopTraversal}), this
 
31
                 instructs web2 to immediately stop the lookup stage,
 
32
                 and switch to the rendering stage, leaving the
 
33
                 remaining path alone for your render function to
 
34
                 handle.
 
35
        """
 
36
 
 
37
    def renderHTTP(req):
 
38
        """Return an IResponse or a deferred which will fire an
 
39
        IResponse. This response will be written to the web browser
 
40
        which initiated the request.
 
41
        """
 
42
 
 
43
# Is there a better way to do this than this funky extra class?
 
44
_default = object()
 
45
class SpecialAdaptInterfaceClass(interface.InterfaceClass):
 
46
    # A special adapter for IResource to handle the extra step of adapting
 
47
    # from IOldNevowResource-providing resources.
 
48
    def __call__(self, other, alternate=_default):
 
49
        result = super(SpecialAdaptInterfaceClass, self).__call__(other, alternate)
 
50
        if result is not alternate:
 
51
            return result
 
52
        
 
53
        result = IOldNevowResource(other, alternate)
 
54
        if result is not alternate:
 
55
            result = IResource(result)
 
56
            return result
 
57
        if alternate is not _default:
 
58
            return alternate
 
59
        raise TypeError('Could not adapt', other, self)
 
60
IResource.__class__ = SpecialAdaptInterfaceClass
 
61
 
 
62
class IOldNevowResource(Interface):
 
63
    # Shared interface with inevow.IResource
 
64
    """
 
65
        I am a web resource.
 
66
    """
 
67
 
 
68
    def locateChild(ctx, segments):
 
69
        """Locate another object which can be adapted to IResource
 
70
        Return a tuple of resource, path segments
 
71
        """
 
72
 
 
73
    def renderHTTP(ctx):
 
74
        """Return a string or a deferred which will fire a string. This string
 
75
        will be written to the web browser which initiated this request.
 
76
 
 
77
        Unlike iweb.IResource, this expects the incoming data to have already been read
 
78
        and parsed into request.args and request.content, and expects to return a
 
79
        string instead of a response object.
 
80
        """
 
81
 
 
82
class ICanHandleException(Interface):
 
83
    # Shared interface with inevow.ICanHandleException
 
84
    def renderHTTP_exception(request, failure):
 
85
        """Render an exception to the given request object.
 
86
        """
 
87
 
 
88
    def renderInlineException(request, reason):
 
89
        """Return stan representing the exception, to be printed in the page,
 
90
        not replacing the page."""
 
91
 
 
92
 
 
93
# http.py interfaces
 
94
class IResponse(Interface):
 
95
    """I'm a response."""
 
96
    code = Attribute("The HTTP response code")
 
97
    headers = Attribute("A http_headers.Headers instance of headers to send")
 
98
    stream = Attribute("A stream.IByteStream of outgoing data, or else None.")
 
99
 
 
100
class IRequest(Interface):
 
101
    """I'm a request for a web resource
 
102
    """
 
103
 
 
104
    method = Attribute("The HTTP method from the request line, e.g. GET")
 
105
    uri = Attribute("The raw URI from the request line. May or may not include host.")
 
106
    clientproto = Attribute("Protocol from the request line, e.g. HTTP/1.1")
 
107
    
 
108
    headers = Attribute("A http_headers.Headers instance of incoming headers.")
 
109
    stream = Attribute("A stream.IByteStream of incoming data.")
 
110
    
 
111
    def writeResponse(response):
 
112
        """Write an IResponse object to the client"""
 
113
        
 
114
    chanRequest = Attribute("The ChannelRequest. I wonder if this is public really?")
 
115
 
 
116
class IOldRequest(Interface):
 
117
    # Shared interface with inevow.ICurrentSegments
 
118
    """An old HTTP request.
 
119
 
 
120
    Subclasses should override the process() method to determine how
 
121
    the request will be processed.
 
122
    
 
123
    @ivar method: The HTTP method that was used.
 
124
    @ivar uri: The full URI that was requested (includes arguments).
 
125
    @ivar path: The path only (arguments not included).
 
126
    @ivar args: All of the arguments, including URL and POST arguments.
 
127
    @type args: A mapping of strings (the argument names) to lists of values.
 
128
                i.e., ?foo=bar&foo=baz&quux=spam results in
 
129
                {'foo': ['bar', 'baz'], 'quux': ['spam']}.
 
130
    @ivar received_headers: All received headers
 
131
    """
 
132
    # Methods for received request
 
133
    def getHeader(key):
 
134
        """Get a header that was sent from the network.
 
135
        """
 
136
        
 
137
    def getCookie(key):
 
138
        """Get a cookie that was sent from the network.
 
139
        """    
 
140
 
 
141
 
 
142
    def getAllHeaders():
 
143
        """Return dictionary of all headers the request received."""
 
144
 
 
145
    def getRequestHostname():
 
146
        """Get the hostname that the user passed in to the request.
 
147
 
 
148
        This will either use the Host: header (if it is available) or the
 
149
        host we are listening on if the header is unavailable.
 
150
        """
 
151
 
 
152
    def getHost():
 
153
        """Get my originally requesting transport's host.
 
154
 
 
155
        Don't rely on the 'transport' attribute, since Request objects may be
 
156
        copied remotely.  For information on this method's return value, see
 
157
        twisted.internet.tcp.Port.
 
158
        """
 
159
        
 
160
    def getClientIP():
 
161
        pass
 
162
    def getClient():
 
163
        pass
 
164
    def getUser():
 
165
        pass
 
166
    def getPassword():
 
167
        pass
 
168
    def isSecure():
 
169
        pass
 
170
 
 
171
    def getSession(sessionInterface = None):
 
172
        pass
 
173
    
 
174
    def URLPath():
 
175
        pass
 
176
 
 
177
    def prePathURL():
 
178
        pass
 
179
 
 
180
    def rememberRootURL():
 
181
        """
 
182
        Remember the currently-processed part of the URL for later
 
183
        recalling.
 
184
        """
 
185
        
 
186
    def getRootURL():
 
187
        """
 
188
        Get a previously-remembered URL.
 
189
        """
 
190
        
 
191
    # Methods for outgoing request
 
192
    def finish():
 
193
        """We are finished writing data."""
 
194
 
 
195
    def write(data):
 
196
        """
 
197
        Write some data as a result of an HTTP request.  The first
 
198
        time this is called, it writes out response data.
 
199
        """
 
200
 
 
201
    def addCookie(k, v, expires=None, domain=None, path=None, max_age=None, comment=None, secure=None):
 
202
        """Set an outgoing HTTP cookie.
 
203
 
 
204
        In general, you should consider using sessions instead of cookies, see
 
205
        twisted.web.server.Request.getSession and the
 
206
        twisted.web.server.Session class for details.
 
207
        """
 
208
 
 
209
    def setResponseCode(code, message=None):
 
210
        """Set the HTTP response code.
 
211
        """
 
212
 
 
213
    def setHeader(k, v):
 
214
        """Set an outgoing HTTP header.
 
215
        """
 
216
 
 
217
    def redirect(url):
 
218
        """Utility function that does a redirect.
 
219
 
 
220
        The request should have finish() called after this.
 
221
        """
 
222
 
 
223
    def setLastModified(when):
 
224
        """Set the X{Last-Modified} time for the response to this request.
 
225
 
 
226
        If I am called more than once, I ignore attempts to set
 
227
        Last-Modified earlier, only replacing the Last-Modified time
 
228
        if it is to a later value.
 
229
 
 
230
        If I am a conditional request, I may modify my response code
 
231
        to L{NOT_MODIFIED} if appropriate for the time given.
 
232
 
 
233
        @param when: The last time the resource being returned was
 
234
            modified, in seconds since the epoch.
 
235
        @type when: number
 
236
        @return: If I am a X{If-Modified-Since} conditional request and
 
237
            the time given is not newer than the condition, I return
 
238
            L{http.CACHED<CACHED>} to indicate that you should write no
 
239
            body.  Otherwise, I return a false value.
 
240
        """
 
241
 
 
242
    def setETag(etag):
 
243
        """Set an X{entity tag} for the outgoing response.
 
244
 
 
245
        That's \"entity tag\" as in the HTTP/1.1 X{ETag} header, \"used
 
246
        for comparing two or more entities from the same requested
 
247
        resource.\"
 
248
 
 
249
        If I am a conditional request, I may modify my response code
 
250
        to L{NOT_MODIFIED} or L{PRECONDITION_FAILED}, if appropriate
 
251
        for the tag given.
 
252
 
 
253
        @param etag: The entity tag for the resource being returned.
 
254
        @type etag: string
 
255
        @return: If I am a X{If-None-Match} conditional request and
 
256
            the tag matches one in the request, I return
 
257
            L{http.CACHED<CACHED>} to indicate that you should write
 
258
            no body.  Otherwise, I return a false value.
 
259
        """
 
260
 
 
261
    def setHost(host, port, ssl=0):
 
262
        """Change the host and port the request thinks it's using.
 
263
 
 
264
        This method is useful for working with reverse HTTP proxies (e.g.
 
265
        both Squid and Apache's mod_proxy can do this), when the address
 
266
        the HTTP client is using is different than the one we're listening on.
 
267
 
 
268
        For example, Apache may be listening on https://www.example.com, and then
 
269
        forwarding requests to http://localhost:8080, but we don't want HTML produced
 
270
        by Twisted to say 'http://localhost:8080', they should say 'https://www.example.com',
 
271
        so we do::
 
272
 
 
273
           request.setHost('www.example.com', 443, ssl=1)
 
274
 
 
275
        This method is experimental.
 
276
        """
 
277
 
 
278
class IChanRequestCallbacks(Interface):
 
279
    """The bits that are required of a Request for interfacing with a
 
280
    IChanRequest object"""
 
281
 
 
282
    def __init__(chanRequest, command, path, version, contentLength, inHeaders):
 
283
        """Create a new Request object.
 
284
        @param chanRequest: the IChanRequest object creating this request
 
285
        @param command: the HTTP command e.g. GET
 
286
        @param path: the HTTP path e.g. /foo/bar.html
 
287
        @param version: the parsed HTTP version e.g. (1,1)
 
288
        @param contentLength: how much data to expect, or None if unknown
 
289
        @param inHeaders: the request headers"""
 
290
 
 
291
    def process():
 
292
        """Process the request. Called as soon as it's possibly reasonable to
 
293
        return a response. handleContentComplete may or may not have been called already."""
 
294
        
 
295
    def handleContentChunk(data):
 
296
        """Called when a piece of incoming data has been received."""
 
297
        
 
298
    def handleContentComplete():
 
299
        """Called when the incoming data stream is finished."""
 
300
        
 
301
    def connectionLost(reason):
 
302
        """Called if the connection was lost."""
 
303
        
 
304
    
 
305
class IChanRequest(Interface):
 
306
    def writeIntermediateResponse(code, headers=None):
 
307
        """Write a non-terminating response.
 
308
        
 
309
        Intermediate responses cannot contain data.
 
310
        If the channel does not support intermediate responses, do nothing.
 
311
        
 
312
        @ivar code: The response code. Should be in the 1xx range.
 
313
        @type code: int
 
314
        @ivar headers: the headers to send in the response
 
315
        @type headers: C{twisted.web.http_headers.Headers}
 
316
        """
 
317
        pass
 
318
    
 
319
    def writeHeaders(code, headers):
 
320
        """Write a final response.
 
321
 
 
322
        @param code: The response code. Should not be in the 1xx range.
 
323
        @type code: int
 
324
        @param headers: the headers to send in the response. They will be augmented
 
325
            with any connection-oriented headers as necessary for the protocol.
 
326
        @type headers: C{twisted.web.http_headers.Headers}
 
327
        """
 
328
        pass
 
329
        
 
330
    def write(data):
 
331
        """Write some data.
 
332
 
 
333
        @param data: the data bytes
 
334
        @type data: str
 
335
        """
 
336
        pass
 
337
    
 
338
    def finish():
 
339
        """Finish the request, and clean up the connection if necessary.
 
340
        """
 
341
        pass
 
342
    
 
343
    def abortConnection():
 
344
        """Forcibly abort the connection without cleanly closing.
 
345
        Use if, for example, you can't write all the data you promised.
 
346
        """
 
347
        pass
 
348
 
 
349
    def registerProducer(producer, streaming):
 
350
        """Register a producer with the standard API."""
 
351
        pass
 
352
    
 
353
    def unregisterProducer():
 
354
        """Unregister a producer."""
 
355
        pass
 
356
 
 
357
    def getHostInfo():
 
358
        """Returns a tuple of (address, socket user connected to,
 
359
        boolean, was it secure).  Note that this should not necsessarily
 
360
        always return the actual local socket information from
 
361
        twisted. E.g. in a CGI, it should use the variables coming
 
362
        from the invoking script.
 
363
        """
 
364
 
 
365
    def getRemoteHost():
 
366
        """Returns an address of the remote host.
 
367
 
 
368
        Like getHostInfo, this information may come from the real
 
369
        socket, or may come from additional information, depending on
 
370
        the transport.
 
371
        """
 
372
 
 
373
    persistent = Attribute("""Whether this request supports HTTP connection persistence. May be set to False. Should not be set to other values.""")
 
374
 
 
375
 
 
376
class ISite(Interface):
 
377
    pass
 
378
 
 
379
__all__ = ['ICanHandleException', 'IChanRequest', 'IChanRequestCallbacks', 'IOldNevowResource', 'IOldRequest', 'IRequest', 'IResource', 'IResponse', 'ISite']