Source

Webware / WebKit / HTTPContent.py

Full commit
  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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
"""Content producing HTTP servlet."""

from HTTPServlet import HTTPServlet
from WebUtils import Funcs
from Application import EndResponse


class HTTPContentError(Exception):
    """HTTP content error"""


class HTTPContent(HTTPServlet):
    """Content producing HTTP servlet.

    HTTPContent is a type of HTTPServlet that is more convenient for
    Servlets which represent content generated in response to
    GET and POST requests.  If you are generating HTML content, you
    you probably want your servlet to inherit from Page, which contains
    many HTML-related convenience methods.

    If you are generating non-HTML content, it is appropriate to inherit
    from this class directly.

    Subclasses typically override defaultAction().

    In `awake`, the page sets self attributes: `_transaction`, `_response`
    and `_request` which subclasses should use as appropriate.

    For the purposes of output, the `write` and `writeln`
    convenience methods are provided.

    If you plan to produce HTML content, you should start by looking
    at Page instead of this lower-level class.

    """


    ## Transactions ##

    def awake(self, transaction):
        """Let servlet awake.

        Makes instance variables from the transaction. This is
        where Page becomes unthreadsafe, as the page is tied to
        the transaction. This is also what allows us to
        implement functions like `write`, where you don't
        need to pass in the transaction or response.

        """
        HTTPServlet.awake(self, transaction)
        self._response = transaction.response()
        self._request = transaction.request()
        self._session = None # don't create unless needed
        assert self._transaction is not None
        assert self._response is not None
        assert self._request is not None

    def respondToGet(self, transaction):
        """Respond to GET.

        Invoked in response to a GET request method. All methods
        are passed to `_respond`.

        """
        self._respond(transaction)

    def respondToPost(self, transaction):
        """Respond to POST.

        Invoked in response to a POST request method. All methods
        are passed to `_respond`.

        """
        self._respond(transaction)

    def _respond(self, transaction):
        """Respond to action.

        Handles actions if an ``_action_`` or ``_action_name`` field is
        defined, otherwise invokes `writeHTML`. This implementation makes
        sure that exactly one action per request is handled. ``_action_``
        takes precedence over ``_action_name``; and if there are multiple
        ``action_name`` fields, the precedence is given by the order of
        the names in the actions() method. If no action field matches,
        the default action is run. The value of the ``_action_`` field
        is transformed to a method name using the methodNameForAction(),
        whereas ``name`` in ``_action_name`` is left unchanged.

        Invoked by both `respondToGet` and `respondToPost`.

        """
        req = transaction.request()
        prefix = self._actionPrefix
        if prefix:
            # First check whether there is an _action_ field:
            if req.hasField(prefix):
                action = self.methodNameForAction(req.field(prefix))
                if action in self.actions():
                    self.handleAction(action)
                    return
            # Next, check whether there is an _acion_name field:
            for action in self.actions():
                name = prefix + action
                if req.hasField(name) or (req.hasField(name + '.x')
                        and req.hasField(name + '.y')):
                    self.handleAction(action)
                    return
            # If no action was found, run the default:
        self.defaultAction()

    def defaultAction(self):
        """Default action.

        The core method that gets called as a result of requests.
        Subclasses should override this.

        """
        pass

    def sleep(self, transaction):
        """Let servlet sleep again.

        We unset some variables. Very boring.

        """
        self._session = None
        self._request = None
        self._response = None
        self._transaction = None
        HTTPServlet.sleep(self, transaction)


    ## Access ##

    def application(self):
        """The `Application` instance we're using."""
        return self._transaction.application()

    def transaction(self):
        """The `Transaction` we're currently handling."""
        return self._transaction

    def request(self):
        """The request (`HTTPRequest`) we're handling."""
        return self._request

    def response(self):
        """The response (`HTTPResponse`) we're handling."""
        return self._response

    def session(self):
        """The session object.

        This provides a state for the current user
        (associated with a browser instance, really).
        If no session exists, then a session will be created.

        """
        if not self._session:
            self._session = self._transaction.session()
        return self._session


    ## Writing ##

    def write(self, *args):
        """Write to output.

        Writes the arguments, which are turned to strings (with `str`)
        and concatenated before being written to the response.
        Unicode strings must be encoded before they can be written.

        """
        for arg in args:
            self._response.write(arg)

    def writeln(self, *args):
        """Write to output with newline.

        Writes the arguments (like `write`), adding a newline after.
        Unicode strings must be encoded before they can be written.

        """
        for arg in args:
            self._response.write(arg)
        self._response.write('\n')


    ## Threading ##

    def canBeThreaded(self):
        """Declares whether servlet can be threaded.

        Returns False because of the instance variables we set up in `awake`.

        """
        return False


    ## Actions ##

    _actionPrefix = '_action_'

    def handleAction(self, action):
        """Handle action.

        Invoked by `_respond` when a legitimate action has
        been found in a form. Invokes `preAction`, the actual
        action method and `postAction`.

        Subclasses rarely override this method.

        """
        self.preAction(action)
        getattr(self, action)()
        self.postAction(action)

    def actions(self):
        """The allowed actions.

        Returns a list or a set of method names that are allowable
        actions from HTML forms. The default implementation returns [].
        See `_respond` for more about actions.

        """
        return []

    def preAction(self, actionName):
        """Things to do before action.

        Invoked by self prior to invoking a action method.
        The `actionName` is passed to this method,
        although it seems a generally bad idea to rely on this.
        However, it's still provided just in case you need that hook.

        By default this does nothing.
        """
        pass

    def postAction(self, actionName):
        """Things to do after action.

        Invoked by self after invoking a action method.
        Subclasses may override to customize and may or may not
        invoke super as they see fit.
        The `actionName` is passed to this method,
        although it seems a generally bad idea to rely on this.
        However, it's still provided just in case you need that hook.

        By default this does nothing.

        """
        pass

    def methodNameForAction(self, name):
        """Return method name for an action name.

        Invoked by _respond() to determine the method name for a given action
        name which has been derived as the value of an ``_action_`` field.
        Since this is usually the label of an HTML submit button in a form,
        it is often needed to transform it in order to get a valid method name
        (for instance, blanks could be replaced by underscores and the like).
        This default implementation of the name transformation is the identity,
        it simply returns the name. Subclasses should override this method
        when action names don't match their method names; they could "mangle"
        the action names or look the method names up in a dictionary.

        """
        return name

    @staticmethod
    def urlEncode(s):
        """Quotes special characters using the % substitutions.

        This method does the same as the `urllib.quote_plus()` function.

        """
        return Funcs.urlEncode(s) # we could also use urllib.quote

    @staticmethod
    def urlDecode(s):
        """Turn special % characters into actual characters.

        This method does the same as the `urllib.unquote_plus()` function.

        """
        return Funcs.urlDecode(s)

    def forward(self, url):
        """Forward request.

        Forwards this request to another servlet.
        See `Application.forward` for details.
        The main difference is that here you don't have
        to pass in the transaction as the first argument.

        """
        self.application().forward(self.transaction(), url)

    def includeURL(self, url):
        """Include output from other servlet.

        Includes the response of another servlet
        in the current servlet's response.
        See `Application.includeURL` for details.
        The main difference is that here you don't have
        to pass in the transaction as the first argument.

        """
        self.application().includeURL(self.transaction(), url)

    @staticmethod
    def callMethodOfServlet(url, method, *args, **kwargs):
        """Call a method of another servlet.

        See `Application.callMethodOfServlet` for details.
        The main difference is that here you don't have
        to pass in the transaction as the first argument.

        """
        return self.application().callMethodOfServlet(
            self.transaction(), url, method, *args, **kwargs)

    def endResponse(self):
        """End response.

        When this method is called during `awake` or `respond`,
        servlet processing will end immediately,
        and the accumulated response will be sent.

        Note that `sleep` will still be called, providing a
        chance to clean up or free any resources.

        """
        raise EndResponse

    def sendRedirectAndEnd(self, url, status=None):
        """Send redirect and end.

        Sends a redirect back to the client and ends the response.
        This is a very popular pattern.

        """
        self.response().sendRedirect(url, status)
        self.endResponse()

    def sendRedirectPermanentAndEnd(self, url):
        """Send permanent redirect and end."""
        self.response().sendRedirectPermanent(url)
        self.endResponse()

    def sendRedirectSeeOtherAndEnd(self, url):
        """Send redirect to a URL to be retrieved with GET and end.

        This is the proper method for the Post/Redirect/Get pattern.

        """
        self.response().sendRedirectSeeOther(url)
        self.endResponse()

    def sendRedirectTemporaryAndEnd(self, url):
        """Send temporary redirect and end."""
        self.response().sendRedirectTemporary(url)
        self.endResponse()


    ## Utility ##

    def sessionEncode(self, url=None):
        """Utility function to access `Session.sessionEncode`.

        Takes a url and adds the session ID as a parameter.
        This is for cases where you don't know if the client
        will accepts cookies.

        """
        if url is None:
            url = self.request().uri()
        return self.session().sessionEncode(url)


    ## Exception Reports ##

    def writeExceptionReport(self, handler):
        """Write extra information to the exception report.

        The `handler` argument is the exception handler, and
        information is written there (using `writeTitle`,
        `write`, and `writeln`).  This information is added
        to the exception report.

        See `WebKit.ExceptionHandler` for more information.

        """
        handler.writeln('''
<p>Servlets can provide debugging information here by overriding
<tt>writeExceptionReport()</tt>.</p><p>For example:</p>
<pre>
exceptionReportAttrs = 'foo bar baz'.split()
def writeExceptionReport(self, handler):
handler.writeTitle(self.__class__.__name__)
handler.writeAttrs(self, self.exceptionReportAttrs)
handler.write('any string')
</pre>
<p>See WebKit/ExceptionHandler.py for more information.</p>
''')