~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/support/werkzeug/wrappers.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
"""
3
 
    werkzeug.wrappers
4
 
    ~~~~~~~~~~~~~~~~~
5
 
 
6
 
    The wrappers are simple request and response objects which you can
7
 
    subclass to do whatever you want them to do.  The request object contains
8
 
    the information transmitted by the client (webbrowser) and the response
9
 
    object contains all the information sent back to the browser.
10
 
 
11
 
    An important detail is that the request object is created with the WSGI
12
 
    environ and will act as high-level proxy whereas the response object is an
13
 
    actual WSGI application.
14
 
 
15
 
    Like everything else in Werkzeug these objects will work correctly with
16
 
    unicode data.  Incoming form data parsed by the response object will be
17
 
    decoded into an unicode object if possible and if it makes sense.
18
 
 
19
 
 
20
 
    :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
21
 
    :license: BSD, see LICENSE for more details.
22
 
"""
23
 
import tempfile
24
 
import urlparse
25
 
from datetime import datetime, timedelta
26
 
from werkzeug.http import HTTP_STATUS_CODES, \
27
 
     parse_accept_header, parse_cache_control_header, parse_etags, \
28
 
     parse_date, generate_etag, is_resource_modified, unquote_etag, \
29
 
     quote_etag, parse_set_header, parse_authorization_header, \
30
 
     parse_www_authenticate_header, remove_entity_headers, \
31
 
     default_stream_factory, parse_options_header, \
32
 
     dump_options_header
33
 
from werkzeug.utils import cached_property, environ_property, \
34
 
     get_current_url, url_encode, run_wsgi_app, get_host, \
35
 
     cookie_date, parse_cookie, dump_cookie, http_date, escape, \
36
 
     header_property, parse_form_data, get_content_type, url_decode
37
 
from werkzeug.datastructures import MultiDict, CombinedMultiDict, Headers, \
38
 
     EnvironHeaders, ImmutableMultiDict, ImmutableTypeConversionDict, \
39
 
     ImmutableList, MIMEAccept, CharsetAccept, LanguageAccept, \
40
 
     ResponseCacheControl, RequestCacheControl, CallbackDict
41
 
from werkzeug._internal import _empty_stream, _decode_unicode, \
42
 
     _patch_wrapper
43
 
 
44
 
 
45
 
class BaseRequest(object):
46
 
    """Very basic request object.  This does not implement advanced stuff like
47
 
    entity tag parsing or cache controls.  The request object is created with
48
 
    the WSGI environment as first argument and will add itself to the WSGI
49
 
    environment as ``'werkzeug.request'`` unless it's created with
50
 
    `populate_request` set to False.
51
 
 
52
 
    There are a couple of mixins available that add additional functionality
53
 
    to the request object, there is also a class called `Request` which
54
 
    subclasses `BaseRequest` and all the important mixins.
55
 
 
56
 
    It's a good idea to create a custom subclass of the :class:`BaseRequest`
57
 
    and add missing functionality either via mixins or direct implementation.
58
 
    Here an example for such subclasses::
59
 
 
60
 
        from werkzeug import BaseRequest, ETagRequestMixin
61
 
 
62
 
        class Request(BaseRequest, ETagRequestMixin):
63
 
            pass
64
 
 
65
 
    Request objects are **read only**.  As of 0.5 modifications are not
66
 
    allowed in any place.  Unlike the lower level parsing functions the
67
 
    request object will use immutable objects everywhere possible.
68
 
 
69
 
    Per default the request object will assume all the text data is `utf-8`
70
 
    encoded.  Please refer to `the unicode chapter <unicode.txt>`_ for more
71
 
    details about customizing the behavior.
72
 
 
73
 
    Per default the request object will be added to the WSGI
74
 
    environment as `werkzeug.request` to support the debugging system.
75
 
    If you don't want that, set `populate_request` to `False`.
76
 
 
77
 
    If `shallow` is `True` the environment is initialized as shallow
78
 
    object around the environ.  Every operation that would modify the
79
 
    environ in any way (such as consuming form data) raises an exception
80
 
    unless the `shallow` attribute is explicitly set to `False`.  This
81
 
    is useful for middlewares where you don't want to consume the form
82
 
    data by accident.  A shallow request is not populated to the WSGI
83
 
    environment.
84
 
 
85
 
    .. versionchanged:: 0.5
86
 
       read-only mode was enforced by using immutables classes for all
87
 
       data.
88
 
    """
89
 
 
90
 
    #: the charset for the request, defaults to utf-8
91
 
    charset = 'utf-8'
92
 
 
93
 
    #: the error handling procedure for errors, defaults to 'ignore'
94
 
    encoding_errors = 'ignore'
95
 
 
96
 
    #: set to True if the application runs behind an HTTP proxy
97
 
    is_behind_proxy = False
98
 
 
99
 
    #: the maximum content length.  This is forwarded to the form data
100
 
    #: parsing function (:func:`parse_form_data`).  When set and the
101
 
    #: :attr:`form` or :attr:`files` attribute is accessed and the
102
 
    #: parsing fails because more than the specified value is transmitted
103
 
    #: a :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
104
 
    #:
105
 
    #: Have a look at :ref:`dealing-with-request-data` for more details.
106
 
    #:
107
 
    #: .. versionadded:: 0.5
108
 
    max_content_length = None
109
 
 
110
 
    #: the maximum form field size.  This is forwarded to the form data
111
 
    #: parsing function (:func:`parse_form_data`).  When set and the
112
 
    #: :attr:`form` or :attr:`files` attribute is accessed and the
113
 
    #: data in memory for post data is longer than the specified value a
114
 
    #: :exc:`~exceptions.RequestEntityTooLarge` exception is raised.
115
 
    #:
116
 
    #: Have a look at :ref:`dealing-with-request-data` for more details.
117
 
    #:
118
 
    #: .. versionadded:: 0.5
119
 
    max_form_memory_size = None
120
 
 
121
 
    def __init__(self, environ, populate_request=True, shallow=False):
122
 
        self.environ = environ
123
 
        if populate_request and not shallow:
124
 
            self.environ['werkzeug.request'] = self
125
 
        self.shallow = shallow
126
 
        self._data_stream = None
127
 
 
128
 
    @classmethod
129
 
    def from_values(cls, *args, **kwargs):
130
 
        """Create a new request object based on the values provided.  If
131
 
        environ is given missing values are filled from there.  This method is
132
 
        useful for small scripts when you need to simulate a request from an URL.
133
 
        Do not use this method for unittesting, there is a full featured client
134
 
        object (:class:`Client`) that allows to create multipart requests,
135
 
        support for cookies etc.
136
 
 
137
 
        This accepts the same options as the :class:`EnvironBuilder`.
138
 
 
139
 
        .. versionchanged:: 0.5
140
 
           This method now accepts the same arguments as
141
 
           :class:`EnvironBuilder`.  Because of this the `environ` parameter
142
 
           is now called `environ_overrides`.
143
 
 
144
 
        :return: request object
145
 
        """
146
 
        from werkzeug.test import EnvironBuilder
147
 
        charset = kwargs.pop('charset', cls.charset)
148
 
        environ = kwargs.pop('environ', None)
149
 
        if environ is not None:
150
 
            from warnings import warn
151
 
            warn(DeprecationWarning('The environ parameter to from_values'
152
 
                                    ' is now called environ_overrides for'
153
 
                                    ' consistency with EnvironBuilder'),
154
 
                 stacklevel=2)
155
 
            kwargs['environ_overrides'] = environ
156
 
        builder = EnvironBuilder(*args, **kwargs)
157
 
        try:
158
 
            return builder.get_request(cls)
159
 
        finally:
160
 
            builder.close()
161
 
 
162
 
    @classmethod
163
 
    def application(cls, f):
164
 
        """Decorate a function as responder that accepts the request as first
165
 
        argument.  This works like the :func:`responder` decorator but the
166
 
        function is passed the request object as first argument::
167
 
 
168
 
            @Request.application
169
 
            def my_wsgi_app(request):
170
 
                return Response('Hello World!')
171
 
 
172
 
        :param f: the WSGI callable to decorate
173
 
        :return: a new WSGI callable
174
 
        """
175
 
        #: return a callable that wraps the -2nd argument with the request
176
 
        #: and calls the function with all the arguments up to that one and
177
 
        #: the request.  The return value is then called with the latest
178
 
        #: two arguments.  This makes it possible to use this decorator for
179
 
        #: both methods and standalone WSGI functions.
180
 
        return _patch_wrapper(f, lambda *a: f(*a[:-2]+(cls(a[-2]),))(*a[-2:]))
181
 
 
182
 
    def _get_file_stream(self, total_content_length, content_type, filename=None,
183
 
                         content_length=None):
184
 
        """Called to get a stream for the file upload.
185
 
 
186
 
        This must provide a file-like class with `read()`, `readline()`
187
 
        and `seek()` methods that is both writeable and readable.
188
 
 
189
 
        The default implementation returns a temporary file if the total
190
 
        content length is higher than 500KB.  Because many browsers do not
191
 
        provide a content length for the files only the total content
192
 
        length matters.
193
 
 
194
 
        .. versionchanged:: 0.5
195
 
           Previously this function was not passed any arguments.  In 0.5 older
196
 
           functions not accepting any arguments are still supported for
197
 
           backwards compatibility.
198
 
 
199
 
        :param total_content_length: the total content length of all the
200
 
                                     data in the request combined.  This value
201
 
                                     is guaranteed to be there.
202
 
        :param content_type: the mimetype of the uploaded file.
203
 
        :param filename: the filename of the uploaded file.  May be `None`.
204
 
        :param content_length: the length of this file.  This value is usually
205
 
                               not provided because webbrowsers do not provide
206
 
                               this value.
207
 
        """
208
 
        return default_stream_factory(total_content_length, content_type,
209
 
                                      filename, content_length)
210
 
 
211
 
    def _load_form_data(self):
212
 
        """Method used internally to retrieve submitted data.  After calling
213
 
        this sets `_form` and `_files` on the request object to multi dicts
214
 
        filled with the incoming form data.  As a matter of fact the input
215
 
        stream will be empty afterwards.
216
 
 
217
 
        :internal:
218
 
        """
219
 
        if self._data_stream is None:
220
 
            if self.shallow:
221
 
                raise RuntimeError('A shallow request tried to consume '
222
 
                                   'form data.  If you really want to do '
223
 
                                   'that, set `shallow` to False.')
224
 
            data = None
225
 
            if self.environ['REQUEST_METHOD'] in ('POST', 'PUT'):
226
 
                try:
227
 
                    data = parse_form_data(self.environ, self._get_file_stream,
228
 
                                           self.charset, self.encoding_errors,
229
 
                                           self.max_form_memory_size,
230
 
                                           self.max_content_length,
231
 
                                           cls=ImmutableMultiDict,
232
 
                                           silent=False)
233
 
                except ValueError, e:
234
 
                    self._form_parsing_failed(e)
235
 
            if data is None:
236
 
                data = (_empty_stream, ImmutableMultiDict(),
237
 
                        ImmutableMultiDict())
238
 
            self._data_stream, self._form, self._files = data
239
 
 
240
 
    def _form_parsing_failed(self, error):
241
 
        """Called if parsing of form data failed.  This is currently only
242
 
        invoked for failed multipart uploads.  By default this method does
243
 
        nothing.
244
 
 
245
 
        :param error: a `ValueError` object with a message why the
246
 
                      parsing failed.
247
 
 
248
 
        .. versionadded:: 0.5.1
249
 
        """
250
 
 
251
 
    @property
252
 
    def stream(self):
253
 
        """The parsed stream if the submitted data was not multipart or
254
 
        urlencoded form data.  This stream is the stream left by the form data
255
 
        parser module after parsing.  This is *not* the WSGI input stream but
256
 
        a wrapper around it that ensures the caller does not accidentally
257
 
        read past `Content-Length`.
258
 
        """
259
 
        self._load_form_data()
260
 
        return self._data_stream
261
 
 
262
 
    input_stream = environ_property('wsgi.input', 'The WSGI input stream.\n'
263
 
        'In general it\'s a bad idea to use this one because you can easily '
264
 
        'read past the boundary.  Use the :attr:`stream` instead.')
265
 
 
266
 
    @cached_property
267
 
    def args(self):
268
 
        """The parsed URL parameters as :class:`ImmutableMultiDict`."""
269
 
        return url_decode(self.environ.get('QUERY_STRING', ''), self.charset,
270
 
                          errors=self.encoding_errors,
271
 
                          cls=ImmutableMultiDict)
272
 
 
273
 
    @cached_property
274
 
    def data(self):
275
 
        """This reads the buffered incoming data from the client into the
276
 
        string.  Usually it's a bad idea to access :attr:`data` because a client
277
 
        could send dozens of megabytes or more to cause memory problems on the
278
 
        server.
279
 
 
280
 
        To circumvent that make sure to check the content length first.
281
 
        """
282
 
        return self.stream.read()
283
 
 
284
 
    @property
285
 
    def form(self):
286
 
        """Form parameters.  Currently it's not guaranteed that the
287
 
        :class:`ImmutableMultiDict` returned by this function is ordered in
288
 
        the same way as the submitted form data.
289
 
        """
290
 
        self._load_form_data()
291
 
        return self._form
292
 
 
293
 
    @cached_property
294
 
    def values(self):
295
 
        """Combined multi dict for :attr:`args` and :attr:`form`."""
296
 
        return CombinedMultiDict([self.args, self.form])
297
 
 
298
 
    @property
299
 
    def files(self):
300
 
        """:class:`MultiDict` object containing all uploaded files.  Each key in
301
 
        :attr:`files` is the name from the ``<input type="file" name="">``.  Each
302
 
        value in :attr:`files` is a Werkzeug :class:`FileStorage` object.
303
 
 
304
 
        Note that :attr:`files` will only contain data if the request method was
305
 
        POST or PUT and the ``<form>`` that posted to the request had
306
 
        ``enctype="multipart/form-data"``.  It will be empty otherwise.
307
 
 
308
 
        See the :class:`MultiDict` / :class:`FileStorage` documentation for more
309
 
        details about the used data structure.
310
 
        """
311
 
        self._load_form_data()
312
 
        return self._files
313
 
 
314
 
    @cached_property
315
 
    def cookies(self):
316
 
        """The retrieved cookie values as regular dictionary."""
317
 
        return parse_cookie(self.environ, self.charset,
318
 
                            cls=ImmutableTypeConversionDict)
319
 
 
320
 
    @cached_property
321
 
    def headers(self):
322
 
        """The headers from the WSGI environ as immutable
323
 
        :class:`EnvironHeaders`.
324
 
        """
325
 
        return EnvironHeaders(self.environ)
326
 
 
327
 
    @cached_property
328
 
    def path(self):
329
 
        """Requested path as unicode.  This works a bit like the regular path
330
 
        info in the WSGI environment but will always include a leading slash,
331
 
        even if the URL root is accessed.
332
 
        """
333
 
        path = '/' + (self.environ.get('PATH_INFO') or '').lstrip('/')
334
 
        return _decode_unicode(path, self.charset, self.encoding_errors)
335
 
 
336
 
    @cached_property
337
 
    def script_root(self):
338
 
        """The root path of the script without the trailing slash."""
339
 
        path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/')
340
 
        return _decode_unicode(path, self.charset, self.encoding_errors)
341
 
 
342
 
    @cached_property
343
 
    def url(self):
344
 
        """The reconstructed current URL"""
345
 
        return get_current_url(self.environ)
346
 
 
347
 
    @cached_property
348
 
    def base_url(self):
349
 
        """Like :attr:`url` but without the querystring"""
350
 
        return get_current_url(self.environ, strip_querystring=True)
351
 
 
352
 
    @cached_property
353
 
    def url_root(self):
354
 
        """The full URL root (with hostname), this is the application root."""
355
 
        return get_current_url(self.environ, True)
356
 
 
357
 
    @cached_property
358
 
    def host_url(self):
359
 
        """Just the host with scheme."""
360
 
        return get_current_url(self.environ, host_only=True)
361
 
 
362
 
    @cached_property
363
 
    def host(self):
364
 
        """Just the host including the port if available."""
365
 
        return get_host(self.environ)
366
 
 
367
 
    query_string = environ_property('QUERY_STRING', '', read_only=True, doc=
368
 
        '''The URL parameters as raw bytestring.''')
369
 
    method = environ_property('REQUEST_METHOD', 'GET', read_only=True, doc=
370
 
        '''The transmission method. (For example ``'GET'`` or ``'POST'``).''')
371
 
 
372
 
    @cached_property
373
 
    def access_route(self):
374
 
        """If a forwarded header exists this is a list of all ip addresses
375
 
        from the client ip to the last proxy server.
376
 
        """
377
 
        if 'HTTP_X_FORWARDED_FOR' in self.environ:
378
 
            addr = self.environ['HTTP_X_FORWARDED_FOR'].split(',')
379
 
            return ImmutableList([x.strip() for x in addr])
380
 
        elif 'REMOTE_ADDR' in self.environ:
381
 
            return ImmutableList([self.environ['REMOTE_ADDR']])
382
 
        return ImmutableList()
383
 
 
384
 
    @property
385
 
    def remote_addr(self):
386
 
        """The remote address of the client."""
387
 
        if self.is_behind_proxy and self.access_route:
388
 
            return self.access_route[0]
389
 
        return self.environ.get('REMOTE_ADDR')
390
 
 
391
 
    remote_user = environ_property('REMOTE_USER', doc='''
392
 
        If the server supports user authentication, and the script is
393
 
        protected, this attribute contains the username the user has
394
 
        authenticated as.''')
395
 
 
396
 
    is_xhr = property(lambda x: x.environ.get('HTTP_X_REQUESTED_WITH', '')
397
 
                      .lower() == 'xmlhttprequest', doc='''
398
 
        True if the request was triggered via a JavaScript XMLHttpRequest.
399
 
        This only works with libraries that support the `X-Requested-With`
400
 
        header and set it to "XMLHttpRequest".  Libraries that do that are
401
 
        prototype, jQuery and Mochikit and probably some more.''')
402
 
    is_secure = property(lambda x: x.environ['wsgi.url_scheme'] == 'https',
403
 
                         doc='`True` if the request is secure.')
404
 
    is_multithread = environ_property('wsgi.multithread', doc='''
405
 
        boolean that is `True` if the application is served by
406
 
        a multithreaded WSGI server.''')
407
 
    is_multiprocess = environ_property('wsgi.multiprocess', doc='''
408
 
        boolean that is `True` if the application is served by
409
 
        a WSGI server that spawns multiple processes.''')
410
 
    is_run_once = environ_property('wsgi.run_once', doc='''
411
 
        boolean that is `True` if the application will be executed only
412
 
        once in a process lifetime.  This is the case for CGI for example,
413
 
        but it's not guaranteed that the exeuction only happens one time.''')
414
 
 
415
 
 
416
 
class BaseResponse(object):
417
 
    """Base response class.  The most important fact about a response object
418
 
    is that it's a regular WSGI application.  It's initialized with a couple
419
 
    of response parameters (headers, body, status code etc.) and will start a
420
 
    valid WSGI response when called with the environ and start response
421
 
    callable.
422
 
 
423
 
    Because it's a WSGI application itself processing usually ends before the
424
 
    actual response is sent to the server.  This helps debugging systems
425
 
    because they can catch all the exceptions before responses are started.
426
 
 
427
 
    Here a small example WSGI application that takes advantage of the
428
 
    response objects::
429
 
 
430
 
        from werkzeug import BaseResponse as Response
431
 
 
432
 
        def index():
433
 
            return Response('Index page')
434
 
 
435
 
        def application(environ, start_response):
436
 
            path = environ.get('PATH_INFO') or '/'
437
 
            if path == '/':
438
 
                response = index()
439
 
            else:
440
 
                response = Response('Not Found', status=404)
441
 
            return response(environ, start_response)
442
 
 
443
 
    Like :class:`BaseRequest` which object is lacking a lot of functionality
444
 
    implemented in mixins.  This gives you a better control about the actual
445
 
    API of your response objects, so you can create subclasses and add custom
446
 
    functionality.  A full featured response object is available as
447
 
    :class:`Response` which implements a couple of useful mixins.
448
 
 
449
 
    To enforce a new type of already existing responses you can use the
450
 
    :meth:`force_type` method.  This is useful if you're working with different
451
 
    subclasses of response objects and you want to post process them with a
452
 
    know interface.
453
 
 
454
 
    Per default the request object will assume all the text data is `utf-8`
455
 
    encoded.  Please refer to `the unicode chapter <unicode.txt>`_ for more
456
 
    details about customizing the behavior.
457
 
 
458
 
    Response can be any kind of iterable or string.  If it's a string
459
 
    it's considered being an iterable with one item which is the string
460
 
    passed.  Headers can be a list of tuples or a :class:`Headers` object.
461
 
 
462
 
    Special note for `mimetype` and `content_type`:  For most mime types
463
 
    `mimetype` and `content_type` work the same, the difference affects
464
 
    only 'text' mimetypes.  If the mimetype passed with `mimetype` is a
465
 
    mimetype starting with `text/` it becomes a charset parameter defined
466
 
    with the charset of the response object.  In contrast the
467
 
    `content_type` parameter is always added as header unmodified.
468
 
 
469
 
    .. versionchanged:: 0.5
470
 
       the `direct_passthrough` parameter was added.
471
 
 
472
 
    :param response: a string or response iterable.
473
 
    :param status: a string with a status or an integer with the status code.
474
 
    :param headers: a list of headers or an :class:`Headers` object.
475
 
    :param mimetype: the mimetype for the request.  See notice above.
476
 
    :param content_type: the content type for the request.  See notice above.
477
 
    :param direct_passthrough: if set to `True` :meth:`iter_encoded` is not
478
 
                               called before iteration which makes it
479
 
                               possible to pass special iterators though
480
 
                               unchanged (see :func:`wrap_file` for more
481
 
                               details.)
482
 
    """
483
 
    charset = 'utf-8'
484
 
    default_status = 200
485
 
    default_mimetype = 'text/plain'
486
 
 
487
 
    def __init__(self, response=None, status=None, headers=None,
488
 
                 mimetype=None, content_type=None, direct_passthrough=False):
489
 
        if response is None:
490
 
            self.response = []
491
 
        elif isinstance(response, basestring):
492
 
            self.response = [response]
493
 
        else:
494
 
            self.response = iter(response)
495
 
        if not headers:
496
 
            self.headers = Headers()
497
 
        elif isinstance(headers, Headers):
498
 
            self.headers = headers
499
 
        else:
500
 
            self.headers = Headers(headers)
501
 
        if content_type is None:
502
 
            if mimetype is None and 'Content-Type' not in self.headers:
503
 
                mimetype = self.default_mimetype
504
 
            if mimetype is not None:
505
 
                mimetype = get_content_type(mimetype, self.charset)
506
 
            content_type = mimetype
507
 
        if content_type is not None:
508
 
            self.headers['Content-Type'] = content_type
509
 
        if status is None:
510
 
            status = self.default_status
511
 
        if isinstance(status, (int, long)):
512
 
            self.status_code = status
513
 
        else:
514
 
            self.status = status
515
 
        self.direct_passthrough = direct_passthrough
516
 
 
517
 
    @classmethod
518
 
    def force_type(cls, response, environ=None):
519
 
        """Enforce that the WSGI response is a response object of the current
520
 
        type.  Werkzeug will use the :class:`BaseResponse` internally in many
521
 
        situations like the exceptions.  If you call :meth:`get_response` on an
522
 
        exception you will get back a regular :class:`BaseResponse` object, even
523
 
        if you are using a custom subclass.
524
 
 
525
 
        This method can enforce a given response type, and it will also
526
 
        convert arbitrary WSGI callables into response objects if an environ
527
 
        is provided::
528
 
 
529
 
            # convert a Werkzeug response object into an instance of the
530
 
            # MyResponseClass subclass.
531
 
            response = MyResponseClass.force_type(response)
532
 
 
533
 
            # convert any WSGI application into a response object
534
 
            response = MyResponseClass.force_type(response, environ)
535
 
 
536
 
        This is especially useful if you want to post-process responses in
537
 
        the main dispatcher and use functionality provided by your subclass.
538
 
 
539
 
        Keep in mind that this will modify response objects in place if
540
 
        possible!
541
 
 
542
 
        :param response: a response object or wsgi application.
543
 
        :param environ: a WSGI environment object.
544
 
        :return: a response object.
545
 
        """
546
 
        if not isinstance(response, BaseResponse):
547
 
            if environ is None:
548
 
                raise TypeError('cannot convert WSGI application into '
549
 
                                'response objects without an environ')
550
 
            response = BaseResponse(*run_wsgi_app(response, environ))
551
 
        response.__class__ = cls
552
 
        return response
553
 
 
554
 
    @classmethod
555
 
    def from_app(cls, app, environ, buffered=False):
556
 
        """Create a new response object from an application output.  This
557
 
        works best if you pass it an application that returns a generator all
558
 
        the time.  Sometimes applications may use the `write()` callable
559
 
        returned by the `start_response` function.  This tries to resolve such
560
 
        edge cases automatically.  But if you don't get the expected output
561
 
        you should set `buffered` to `True` which enforces buffering.
562
 
 
563
 
        :param app: the WSGI application to execute.
564
 
        :param environ: the WSGI environment to execute against.
565
 
        :param buffered: set to `True` to enforce buffering.
566
 
        :return: a response object.
567
 
        """
568
 
        return cls(*run_wsgi_app(app, environ, buffered))
569
 
 
570
 
    def _get_status_code(self):
571
 
        try:
572
 
            return int(self.status.split(None, 1)[0])
573
 
        except ValueError:
574
 
            return 0
575
 
    def _set_status_code(self, code):
576
 
        try:
577
 
            self.status = '%d %s' % (code, HTTP_STATUS_CODES[code].upper())
578
 
        except KeyError:
579
 
            self.status = '%d UNKNOWN' % code
580
 
    status_code = property(_get_status_code, _set_status_code,
581
 
                           'The HTTP Status code as number')
582
 
    del _get_status_code, _set_status_code
583
 
 
584
 
    def _get_data(self):
585
 
        """The string representation of the request body.  Whenever you access
586
 
        this property the request iterable is encoded and flattened.  This
587
 
        can lead to unwanted behavior if you stream big data.
588
 
        """
589
 
        if not isinstance(self.response, list):
590
 
            self.response = list(self.response)
591
 
        return ''.join(self.iter_encoded())
592
 
    def _set_data(self, value):
593
 
        self.response = [value]
594
 
    data = property(_get_data, _set_data, doc=_get_data.__doc__)
595
 
    del _get_data, _set_data
596
 
 
597
 
    def iter_encoded(self, charset=None):
598
 
        """Iter the response encoded with the encoding specified.  If no
599
 
        encoding is given the encoding from the class is used.  Note that
600
 
        this does not encode data that is already a bytestring.  If the
601
 
        response object is invoked as WSGI application the return value
602
 
        of this method is used as application iterator except if
603
 
        :attr:`direct_passthrough` was activated.
604
 
        """
605
 
        charset = charset or self.charset or 'ascii'
606
 
        for item in self.response:
607
 
            if isinstance(item, unicode):
608
 
                yield item.encode(charset)
609
 
            else:
610
 
                yield str(item)
611
 
 
612
 
    def set_cookie(self, key, value='', max_age=None, expires=None,
613
 
                   path='/', domain=None, secure=None, httponly=False):
614
 
        """Sets a cookie. The parameters are the same as in the cookie `Morsel`
615
 
        object in the Python standard library but it accepts unicode data, too.
616
 
 
617
 
        :param key: the key (name) of the cookie to be set.
618
 
        :param value: the value of the cookie.
619
 
        :param max_age: should be a number of seconds, or `None` (default) if
620
 
                        the cookie should last only as long as the client's
621
 
                        browser session.
622
 
        :param expires: should be a `datetime` object or UNIX timestamp.
623
 
        :param domain: if you want to set a cross-domain cookie.  For example,
624
 
                       ``domain=".example.com"`` will set a cookie that is
625
 
                       readable by the domain ``www.example.com``,
626
 
                       ``foo.example.com`` etc.  Otherwise, a cookie will only
627
 
                       be readable by the domain that set it.
628
 
        :param path: limits the cookie to a given path, per default it will
629
 
                     span the whole domain.
630
 
        """
631
 
        self.headers.add('Set-Cookie', dump_cookie(key, value, max_age,
632
 
                         expires, path, domain, secure, httponly,
633
 
                         self.charset))
634
 
 
635
 
    def delete_cookie(self, key, path='/', domain=None):
636
 
        """Delete a cookie.  Fails silently if key doesn't exist.
637
 
 
638
 
        :param key: the key (name) of the cookie to be deleted.
639
 
        :param path: if the cookie that should be deleted was limited to a
640
 
                     path, the path has to be defined here.
641
 
        :param domain: if the cookie that should be deleted was limited to a
642
 
                       domain, that domain has to be defined here.
643
 
        """
644
 
        self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)
645
 
 
646
 
    @property
647
 
    def header_list(self):
648
 
        """This returns the headers in the target charset as list.  It's used
649
 
        in __call__ to get the headers for the response.
650
 
        """
651
 
        return self.headers.to_list(self.charset)
652
 
 
653
 
    @property
654
 
    def is_streamed(self):
655
 
        """If the response is streamed (the response is not a sequence) this
656
 
        property is `True`.  In this case streamed means that there is no
657
 
        information about the number of iterations.  This is usully `True`
658
 
        if a generator is passed to the response object.
659
 
 
660
 
        This is useful for checking before applying some sort of post
661
 
        filtering that should not take place for streamed responses.
662
 
        """
663
 
        try:
664
 
            len(self.response)
665
 
        except TypeError:
666
 
            return False
667
 
        return True
668
 
 
669
 
    def fix_headers(self, environ):
670
 
        """This is automatically called right before the response is started
671
 
        and should fix common mistakes in headers.  For example location
672
 
        headers are joined with the root URL here.
673
 
 
674
 
        :param environ: the WSGI environment of the request to be used for
675
 
                        the applied fixes.
676
 
        """
677
 
        if 'Location' in self.headers:
678
 
            self.headers['Location'] = urlparse.urljoin(
679
 
                get_current_url(environ, root_only=True),
680
 
                self.headers['Location']
681
 
            )
682
 
        if 100 <= self.status_code < 200 or self.status_code == 204:
683
 
            self.headers['Content-Length'] = 0
684
 
        elif self.status_code == 304:
685
 
            remove_entity_headers(self.headers)
686
 
 
687
 
    def close(self):
688
 
        """Close the wrapped response if possible."""
689
 
        if hasattr(self.response, 'close'):
690
 
            self.response.close()
691
 
 
692
 
    def freeze(self):
693
 
        """Call this method if you want to make your response object ready for
694
 
        being pickled.  This buffers the generator if there is one.
695
 
        """
696
 
        BaseResponse.data.__get__(self)
697
 
 
698
 
    def __call__(self, environ, start_response):
699
 
        """Process this response as WSGI application.
700
 
 
701
 
        :param environ: the WSGI environment.
702
 
        :param start_response: the response callable provided by the WSGI
703
 
                               server.
704
 
        """
705
 
        self.fix_headers(environ)
706
 
        if environ['REQUEST_METHOD'] == 'HEAD':
707
 
            resp = ()
708
 
        elif 100 <= self.status_code < 200 or self.status_code in (204, 304):
709
 
            # no response for 204/304.  the headers are adapted accordingly
710
 
            # by fix_headers()
711
 
            resp = ()
712
 
        elif self.direct_passthrough:
713
 
            resp = self.response
714
 
        else:
715
 
            resp = self.iter_encoded()
716
 
        start_response(self.status, self.header_list)
717
 
        return resp
718
 
 
719
 
 
720
 
class AcceptMixin(object):
721
 
    """A mixin for classes with an :attr:`~BaseResponse.environ` attribute to
722
 
    get all the HTTP accept headers as :class:`Accept` objects (or subclasses
723
 
    thereof).
724
 
    """
725
 
 
726
 
    @cached_property
727
 
    def accept_mimetypes(self):
728
 
        """List of mimetypes this client supports as :class:`MIMEAccept`
729
 
        object.
730
 
        """
731
 
        return parse_accept_header(self.environ.get('HTTP_ACCEPT'), MIMEAccept)
732
 
 
733
 
    @cached_property
734
 
    def accept_charsets(self):
735
 
        """List of charsets this client supports as :class:`CharsetAccept`
736
 
        object.
737
 
        """
738
 
        return parse_accept_header(self.environ.get('HTTP_ACCEPT_CHARSET'),
739
 
                                   CharsetAccept)
740
 
 
741
 
    @cached_property
742
 
    def accept_encodings(self):
743
 
        """List of encodings this client accepts.  Encodings in a HTTP term
744
 
        are compression encodings such as gzip.  For charsets have a look at
745
 
        :attr:`accept_charset`.
746
 
        """
747
 
        return parse_accept_header(self.environ.get('HTTP_ACCEPT_ENCODING'))
748
 
 
749
 
    @cached_property
750
 
    def accept_languages(self):
751
 
        """List of languages this client accepts as :class:`LanguageAccept`
752
 
        object.
753
 
 
754
 
        .. versionchanged 0.5
755
 
           In previous versions this was a regualr :class:`Accept` object.
756
 
        """
757
 
        return parse_accept_header(self.environ.get('HTTP_ACCEPT_LANGUAGE'),
758
 
                                   LanguageAccept)
759
 
 
760
 
 
761
 
class ETagRequestMixin(object):
762
 
    """Add entity tag and cache descriptors to a request object or object with
763
 
    a WSGI environment available as :attr:`~BaseRequest.environ`.  This not
764
 
    only provides access to etags but also to the cache control header.
765
 
    """
766
 
 
767
 
    @cached_property
768
 
    def cache_control(self):
769
 
        """A :class:`RequestCacheControl` object for the incoming cache control
770
 
        headers.
771
 
        """
772
 
        cache_control = self.environ.get('HTTP_CACHE_CONTROL')
773
 
        return parse_cache_control_header(cache_control, None,
774
 
                                          RequestCacheControl)
775
 
 
776
 
    @cached_property
777
 
    def if_match(self):
778
 
        """An object containing all the etags in the `If-Match` header."""
779
 
        return parse_etags(self.environ.get('HTTP_IF_MATCH'))
780
 
 
781
 
    @cached_property
782
 
    def if_none_match(self):
783
 
        """An object containing all the etags in the `If-None-Match` header."""
784
 
        return parse_etags(self.environ.get('HTTP_IF_NONE_MATCH'))
785
 
 
786
 
    @cached_property
787
 
    def if_modified_since(self):
788
 
        """The parsed `If-Modified-Since` header as datetime object."""
789
 
        return parse_date(self.environ.get('HTTP_IF_MODIFIED_SINCE'))
790
 
 
791
 
    @cached_property
792
 
    def if_unmodified_since(self):
793
 
        """The parsed `If-Unmodified-Since` header as datetime object."""
794
 
        return parse_date(self.environ.get('HTTP_IF_UNMODIFIED_SINCE'))
795
 
 
796
 
 
797
 
class UserAgentMixin(object):
798
 
    """Adds a `user_agent` attribute to the request object which contains the
799
 
    parsed user agent of the browser that triggered the request as `UserAgent`
800
 
    object.
801
 
    """
802
 
 
803
 
    # this class actually belongs to a different module.  For more details
804
 
    # have a look at `werkzeug.useragents`.  On the bottom of that module is
805
 
    # a small comment that explains it.
806
 
    __module__ = 'werkzeug.useragents'
807
 
 
808
 
    @cached_property
809
 
    def user_agent(self):
810
 
        """The current user agent."""
811
 
        from werkzeug.useragents import UserAgent
812
 
        return UserAgent(self.environ)
813
 
 
814
 
 
815
 
class AuthorizationMixin(object):
816
 
    """Adds an :attr:`authorization` property that represents the parsed value
817
 
    of the `Authorization` header as :class:`Authorization` object.
818
 
    """
819
 
 
820
 
    @cached_property
821
 
    def authorization(self):
822
 
        """The `Authorization` object in parsed form."""
823
 
        header = self.environ.get('HTTP_AUTHORIZATION')
824
 
        return parse_authorization_header(header)
825
 
 
826
 
 
827
 
class ETagResponseMixin(object):
828
 
    """Adds extra functionality to a response object for etag and cache
829
 
    handling.  This mixin requires an object with at least a `headers`
830
 
    object that implements a dict like interface similar to :class:`Headers`.
831
 
    """
832
 
 
833
 
    @property
834
 
    def cache_control(self):
835
 
        """The Cache-Control general-header field is used to specify
836
 
        directives that MUST be obeyed by all caching mechanisms along the
837
 
        request/response chain.
838
 
        """
839
 
        def on_update(cache_control):
840
 
            if not cache_control and 'cache-control' in self.headers:
841
 
                del self.headers['cache-control']
842
 
            elif cache_control:
843
 
                self.headers['Cache-Control'] = cache_control.to_header()
844
 
        return parse_cache_control_header(self.headers.get('cache-control'),
845
 
                                          on_update,
846
 
                                          ResponseCacheControl)
847
 
 
848
 
    def make_conditional(self, request_or_environ):
849
 
        """Make the response conditional to the request.  This method works
850
 
        best if an etag was defined for the response already.  The `add_etag`
851
 
        method can be used to do that.  If called without etag just the date
852
 
        header is set.
853
 
 
854
 
        This does nothing if the request method in the request or environ is
855
 
        anything but GET or HEAD.
856
 
 
857
 
        It does not remove the body of the response because that's something
858
 
        the :meth:`__call__` function does for us automatically.
859
 
 
860
 
        Returns self so that you can do ``return resp.make_conditional(req)``
861
 
        but modifies the object in-place.
862
 
 
863
 
        :param request_or_environ: a request object or WSGI environment to be
864
 
                                   used to make the response conditional
865
 
                                   against.
866
 
        """
867
 
        environ = getattr(request_or_environ, 'environ', request_or_environ)
868
 
        if environ['REQUEST_METHOD'] in ('GET', 'HEAD'):
869
 
            self.headers['Date'] = http_date()
870
 
            if 'content-length' in self.headers:
871
 
                self.headers['Content-Length'] = len(self.data)
872
 
            if not is_resource_modified(environ, self.headers.get('etag'), None,
873
 
                                        self.headers.get('last-modified')):
874
 
                self.status_code = 304
875
 
        return self
876
 
 
877
 
    def add_etag(self, overwrite=False, weak=False):
878
 
        """Add an etag for the current response if there is none yet."""
879
 
        if overwrite or 'etag' not in self.headers:
880
 
            self.set_etag(generate_etag(self.data), weak)
881
 
 
882
 
    def set_etag(self, etag, weak=False):
883
 
        """Set the etag, and override the old one if there was one."""
884
 
        self.headers['ETag'] = quote_etag(etag, weak)
885
 
 
886
 
    def get_etag(self):
887
 
        """Return a tuple in the form ``(etag, is_weak)``.  If there is no
888
 
        ETag the return value is ``(None, None)``.
889
 
        """
890
 
        return unquote_etag(self.headers.get('ETag'))
891
 
 
892
 
    def freeze(self, no_etag=False):
893
 
        """Call this method if you want to make your response object ready for
894
 
        pickeling.  This buffers the generator if there is one.  This also
895
 
        sets the etag unless `no_etag` is set to `True`.
896
 
        """
897
 
        if not no_etag:
898
 
            self.add_etag()
899
 
        super(ETagResponseMixin, self).freeze()
900
 
 
901
 
 
902
 
class ResponseStream(object):
903
 
    """A file descriptor like object used by the :class:`ResponseStreamMixin` to
904
 
    represent the body of the stream.  It directly pushes into the response
905
 
    iterable of the response object.
906
 
    """
907
 
 
908
 
    mode = 'wb+'
909
 
 
910
 
    def __init__(self, response):
911
 
        self.response = response
912
 
        self.closed = False
913
 
 
914
 
    def write(self, value):
915
 
        if self.closed:
916
 
            raise ValueError('I/O operation on closed file')
917
 
        buf = self.response.response
918
 
        if not isinstance(buf, list):
919
 
            self.response.response = buf = list(buf)
920
 
        buf.append(value)
921
 
 
922
 
    def writelines(self, seq):
923
 
        for item in seq:
924
 
            self.write(item)
925
 
 
926
 
    def close(self):
927
 
        self.closed = True
928
 
 
929
 
    def flush(self):
930
 
        if self.closed:
931
 
            raise ValueError('I/O operation on closed file')
932
 
 
933
 
    def isatty(self):
934
 
        if self.closed:
935
 
            raise ValueError('I/O operation on closed file')
936
 
        return False
937
 
 
938
 
    @property
939
 
    def encoding(self):
940
 
        return self.response.charset
941
 
 
942
 
 
943
 
class ResponseStreamMixin(object):
944
 
    """Mixin for :class:`BaseRequest` subclasses.  Classes that inherit from
945
 
    this mixin will automatically get a :attr:`stream` property that provides
946
 
    a write-only interface to the response iterable.
947
 
    """
948
 
 
949
 
    @cached_property
950
 
    def stream(self):
951
 
        """The response iterable as write-only stream."""
952
 
        return ResponseStream(self)
953
 
 
954
 
 
955
 
class CommonRequestDescriptorsMixin(object):
956
 
    """A mixin for :class:`BaseRequest` subclasses.  Request objects that
957
 
    mix this class in will automatically get descriptors for a coupl eof
958
 
    HTTP headers with automatic type conversion.
959
 
 
960
 
    .. versionadded:: 0.5
961
 
    """
962
 
 
963
 
    content_type = environ_property('CONTENT_TYPE', doc='''
964
 
         The Content-Type entity-header field indicates the media type of
965
 
         the entity-body sent to the recipient or, in the case of the HEAD
966
 
         method, the media type that would have been sent had the request
967
 
         been a GET.''')
968
 
    content_length = environ_property('CONTENT_LENGTH', None, int, str, doc='''
969
 
         The Content-Length entity-header field indicates the size of the
970
 
         entity-body in bytes or, in the case of the HEAD method, the size of
971
 
         the entity-body that would have been sent had the request been a
972
 
         GET.''')
973
 
    referrer = environ_property('HTTP_REFERER', doc='''
974
 
        The Referer[sic] request-header field allows the client to specify,
975
 
        for the server's benefit, the address (URI) of the resource from which
976
 
        the Request-URI was obtained (the "referrer", although the header
977
 
        field is misspelled).''')
978
 
    date = environ_property('HTTP_DATE', None, parse_date, doc='''
979
 
        The Date general-header field represents the date and time at which
980
 
        the message was originated, having the same semantics as orig-date
981
 
        in RFC 822.''')
982
 
    max_forwards = environ_property('HTTP_MAX_FORWARDS', None, int, doc='''
983
 
         The Max-Forwards request-header field provides a mechanism with the
984
 
         TRACE and OPTIONS methods to limit the number of proxies or gateways
985
 
         that can forward the request to the next inbound server.''')
986
 
 
987
 
    def _parse_content_type(self):
988
 
        if not hasattr(self, '_parsed_content_type'):
989
 
            self._parsed_content_type = \
990
 
                parse_options_header(self.environ.get('CONTENT_TYPE', ''))
991
 
 
992
 
    @property
993
 
    def mimetype(self):
994
 
        """Like :attr:`content_type` but without parameters (eg, without
995
 
        charset, type etc.).  For example if the content
996
 
        type is ``text/html; charset=utf-8`` the mimetype would be
997
 
        ``'text/html'``.
998
 
        """
999
 
        self._parse_content_type()
1000
 
        return self._parsed_content_type[0]
1001
 
 
1002
 
    @property
1003
 
    def mimetype_params(self):
1004
 
        """The mimetype parameters as dict.  For example if the content
1005
 
        type is ``text/html; charset=utf-8`` the params would be
1006
 
        ``{'charset': 'utf-8'}``.
1007
 
        """
1008
 
        self._parse_content_type()
1009
 
        return self._parsed_content_type[1]
1010
 
 
1011
 
    @cached_property
1012
 
    def pragma(self):
1013
 
        """The Pragma general-header field is used to include
1014
 
        implementation-specific directives that might apply to any recipient
1015
 
        along the request/response chain.  All pragma directives specify
1016
 
        optional behavior from the viewpoint of the protocol; however, some
1017
 
        systems MAY require that behavior be consistent with the directives.
1018
 
        """
1019
 
        return parse_set_header(self.environ.get('HTTP_PRAGMA', ''))
1020
 
 
1021
 
 
1022
 
class CommonResponseDescriptorsMixin(object):
1023
 
    """A mixin for :class:`BaseResponse` subclasses.  Response objects that
1024
 
    mix this class in will automatically get descriptors for a couple of
1025
 
    HTTP headers with automatic type conversion.
1026
 
    """
1027
 
 
1028
 
    def _get_mimetype(self):
1029
 
        ct = self.headers.get('content-type')
1030
 
        if ct:
1031
 
            return ct.split(';')[0].strip()
1032
 
 
1033
 
    def _set_mimetype(self, value):
1034
 
        self.headers['Content-Type'] = get_content_type(value, self.charset)
1035
 
 
1036
 
    def _get_mimetype_params(self):
1037
 
        def on_update(d):
1038
 
            self.headers['Content-Type'] = \
1039
 
                dump_options_header(self.mimetype, d)
1040
 
        d = parse_options_header(self.headers.get('content-type', ''))[1]
1041
 
        return CallbackDict(d, on_update)
1042
 
 
1043
 
    mimetype = property(_get_mimetype, _set_mimetype, doc='''
1044
 
        The mimetype (content type without charset etc.)''')
1045
 
    mimetype_params = property(_get_mimetype_params, doc='''
1046
 
        The mimetype parameters as dict.  For example if the content
1047
 
        type is ``text/html; charset=utf-8`` the params would be
1048
 
        ``{'charset': 'utf-8'}``.
1049
 
 
1050
 
        .. versionadded:: 0.5
1051
 
        ''')
1052
 
    location = header_property('Location', doc='''
1053
 
        The Location response-header field is used to redirect the recipient
1054
 
        to a location other than the Request-URI for completion of the request
1055
 
        or identification of a new resource.''')
1056
 
    age = header_property('Age', None, parse_date, http_date, doc='''
1057
 
        The Age response-header field conveys the sender's estimate of the
1058
 
        amount of time since the response (or its revalidation) was
1059
 
        generated at the origin server.
1060
 
 
1061
 
        Age values are non-negative decimal integers, representing time in
1062
 
        seconds.''')
1063
 
    content_type = header_property('Content-Type', doc='''
1064
 
        The Content-Type entity-header field indicates the media type of the
1065
 
        entity-body sent to the recipient or, in the case of the HEAD method,
1066
 
        the media type that would have been sent had the request been a GET.
1067
 
    ''')
1068
 
    content_length = header_property('Content-Length', None, int, str, doc='''
1069
 
        The Content-Length entity-header field indicates the size of the
1070
 
        entity-body, in decimal number of OCTETs, sent to the recipient or,
1071
 
        in the case of the HEAD method, the size of the entity-body that would
1072
 
        have been sent had the request been a GET.''')
1073
 
    content_location = header_property('Content-Location', doc='''
1074
 
        The Content-Location entity-header field MAY be used to supply the
1075
 
        resource location for the entity enclosed in the message when that
1076
 
        entity is accessible from a location separate from the requested
1077
 
        resource's URI.''')
1078
 
    content_encoding = header_property('Content-Encoding', doc='''
1079
 
        The Content-Encoding entity-header field is used as a modifier to the
1080
 
        media-type.  When present, its value indicates what additional content
1081
 
        codings have been applied to the entity-body, and thus what decoding
1082
 
        mechanisms must be applied in order to obtain the media-type
1083
 
        referenced by the Content-Type header field.''')
1084
 
    content_md5 = header_property('Content-MD5', doc='''
1085
 
         The Content-MD5 entity-header field, as defined in RFC 1864, is an
1086
 
         MD5 digest of the entity-body for the purpose of providing an
1087
 
         end-to-end message integrity check (MIC) of the entity-body.  (Note:
1088
 
         a MIC is good for detecting accidental modification of the
1089
 
         entity-body in transit, but is not proof against malicious attacks.)
1090
 
        ''')
1091
 
    date = header_property('Date', None, parse_date, http_date, doc='''
1092
 
        The Date general-header field represents the date and time at which
1093
 
        the message was originated, having the same semantics as orig-date
1094
 
        in RFC 822.''')
1095
 
    expires = header_property('Expires', None, parse_date, http_date, doc='''
1096
 
        The Expires entity-header field gives the date/time after which the
1097
 
        response is considered stale. A stale cache entry may not normally be
1098
 
        returned by a cache.''')
1099
 
    last_modified = header_property('Last-Modified', None, parse_date,
1100
 
                                    http_date, doc='''
1101
 
        The Last-Modified entity-header field indicates the date and time at
1102
 
        which the origin server believes the variant was last modified.''')
1103
 
 
1104
 
    def _get_retry_after(self):
1105
 
        value = self.headers.get('retry-after')
1106
 
        if value is None:
1107
 
            return
1108
 
        elif value.isdigit():
1109
 
            return datetime.utcnow() + timedelta(seconds=int(value))
1110
 
        return parse_date(value)
1111
 
    def _set_retry_after(self, value):
1112
 
        if value is None:
1113
 
            if 'retry-after' in self.headers:
1114
 
                del self.headers['retry-after']
1115
 
            return
1116
 
        elif isinstance(value, datetime):
1117
 
            value = http_date(value)
1118
 
        else:
1119
 
            value = str(value)
1120
 
        self.headers['Retry-After'] = value
1121
 
 
1122
 
    retry_after = property(_get_retry_after, _set_retry_after, doc='''
1123
 
        The Retry-After response-header field can be used with a 503 (Service
1124
 
        Unavailable) response to indicate how long the service is expected
1125
 
        to be unavailable to the requesting client.
1126
 
 
1127
 
        Time in seconds until expiration or date.''')
1128
 
 
1129
 
    def _set_property(name, doc=None):
1130
 
        def fget(self):
1131
 
            def on_update(header_set):
1132
 
                if not header_set and name in self.headers:
1133
 
                    del self.headers[name]
1134
 
                elif header_set:
1135
 
                    self.headers[name] = header_set.to_header()
1136
 
            return parse_set_header(self.headers.get(name), on_update)
1137
 
        return property(fget, doc=doc)
1138
 
 
1139
 
    vary = _set_property('Vary', doc='''
1140
 
         The Vary field value indicates the set of request-header fields that
1141
 
         fully determines, while the response is fresh, whether a cache is
1142
 
         permitted to use the response to reply to a subsequent request
1143
 
         without revalidation.''')
1144
 
    content_language = _set_property('Content-Language', doc='''
1145
 
         The Content-Language entity-header field describes the natural
1146
 
         language(s) of the intended audience for the enclosed entity.  Note
1147
 
         that this might not be equivalent to all the languages used within
1148
 
         the entity-body.''')
1149
 
    allow = _set_property('Allow', doc='''
1150
 
        The Allow entity-header field lists the set of methods supported
1151
 
        by the resource identified by the Request-URI. The purpose of this
1152
 
        field is strictly to inform the recipient of valid methods
1153
 
        associated with the resource. An Allow header field MUST be
1154
 
        present in a 405 (Method Not Allowed) response.''')
1155
 
 
1156
 
    del _set_property, _get_mimetype, _set_mimetype, _get_retry_after, \
1157
 
        _set_retry_after
1158
 
 
1159
 
 
1160
 
class WWWAuthenticateMixin(object):
1161
 
    """Adds a :attr:`www_authenticate` property to a response object."""
1162
 
 
1163
 
    @property
1164
 
    def www_authenticate(self):
1165
 
        """The `WWW-Authenticate` header in a parsed form."""
1166
 
        def on_update(www_auth):
1167
 
            if not www_auth and 'www-authenticate' in self.headers:
1168
 
                del self.headers['www-authenticate']
1169
 
            elif www_auth:
1170
 
                self.headers['WWW-Authenticate'] = www_auth.to_header()
1171
 
        header = self.headers.get('www-authenticate')
1172
 
        return parse_www_authenticate_header(header, on_update)
1173
 
 
1174
 
 
1175
 
class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
1176
 
              UserAgentMixin, AuthorizationMixin,
1177
 
              CommonRequestDescriptorsMixin):
1178
 
    """Full featured request object implementing the following mixins:
1179
 
 
1180
 
    - :class:`AcceptMixin` for accept header parsing
1181
 
    - :class:`ETagRequestMixin` for etag and cache control handling
1182
 
    - :class:`UserAgentMixin` for user agent introspection
1183
 
    - :class:`AuthorizationMixin` for http auth handling
1184
 
    - :class:`CommonRequestDescriptorsMixin` for common headers
1185
 
    """
1186
 
 
1187
 
 
1188
 
class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
1189
 
               CommonResponseDescriptorsMixin,
1190
 
               WWWAuthenticateMixin):
1191
 
    """Full featured response object implementing the following mixins:
1192
 
 
1193
 
    - :class:`ETagResponseMixin` for etag and cache control handling
1194
 
    - :class:`ResponseStreamMixin` to add support for the `stream` property
1195
 
    - :class:`CommonResponseDescriptorsMixin` for various HTTP descriptors
1196
 
    - :class:`WWWAuthenticateMixin` for HTTP authentication support
1197
 
    """