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

« back to all changes in this revision

Viewing changes to MoinMoin/support/werkzeug/contrib/fixers.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.contrib.fixers
4
 
    ~~~~~~~~~~~~~~~~~~~~~~~
5
 
 
6
 
    .. versionadded:: 0.5
7
 
 
8
 
    This module includes various helpers that fix bugs in web servers.  They may
9
 
    be necessary for some versions of a buggy web server but not others.  We try
10
 
    to stay updated with the status of the bugs as good as possible but you have
11
 
    to make sure whether they fix the problem you encounter.
12
 
 
13
 
    If you notice bugs in webservers not fixed in this module consider
14
 
    contributing a patch.
15
 
 
16
 
    :copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details.
17
 
    :license: BSD, see LICENSE for more details.
18
 
"""
19
 
from urllib import unquote
20
 
from werkzeug.http import parse_options_header, parse_cache_control_header, \
21
 
     parse_set_header, dump_header
22
 
from werkzeug.useragents import UserAgent
23
 
from werkzeug.datastructures import Headers, ResponseCacheControl
24
 
 
25
 
 
26
 
class LighttpdCGIRootFix(object):
27
 
    """Wrap the application in this middleware if you are using lighttpd
28
 
    with FastCGI or CGI and the application is mounted on the URL root.
29
 
 
30
 
    :param app: the WSGI application
31
 
    """
32
 
 
33
 
    def __init__(self, app):
34
 
        self.app = app
35
 
 
36
 
    def __call__(self, environ, start_response):
37
 
        environ['PATH_INFO'] = environ.get('SCRIPT_NAME', '') + \
38
 
                               environ.get('PATH_INFO', '')
39
 
        environ['SCRIPT_NAME'] = ''
40
 
        return self.app(environ, start_response)
41
 
 
42
 
 
43
 
class PathInfoFromRequestUriFix(object):
44
 
    """On windows environment variables are limited to the system charset
45
 
    which makes it impossible to store the `PATH_INFO` variable in the
46
 
    environment without loss of information on some systems.
47
 
 
48
 
    This is for example a problem for CGI scripts on a Windows Apache.
49
 
 
50
 
    This fixer works by recreating the `PATH_INFO` from `REQUEST_URI`,
51
 
    `REQUEST_URL`, or `UNENCODED_URL` (whatever is available).  Thus the
52
 
    fix can only be applied if the webserver supports either of these
53
 
    variables.
54
 
 
55
 
    :param app: the WSGI application
56
 
    """
57
 
 
58
 
    def __init__(self, app):
59
 
        self.app = app
60
 
 
61
 
    def __call__(self, environ, start_response):
62
 
        for key in 'REQUEST_URL', 'REQUEST_URI', 'UNENCODED_URL':
63
 
            if key not in environ:
64
 
                continue
65
 
            request_uri = unquote(environ[key])
66
 
            script_name = unquote(environ.get('SCRIPT_NAME', ''))
67
 
            if request_uri.startswith(script_name):
68
 
                environ['PATH_INFO'] = request_uri[len(script_name):] \
69
 
                    .split('?', 1)[0]
70
 
                break
71
 
        return self.app(environ, start_response)
72
 
 
73
 
 
74
 
class ProxyFix(object):
75
 
    """This middleware can be applied to add HTTP proxy support to an
76
 
    application that was not designed with HTTP proxies in mind.  It
77
 
    sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers.
78
 
 
79
 
    Werkzeug wrappers have builtin support for this by setting the
80
 
    :attr:`~werkzeug.BaseRequest.is_behind_proxy` attribute to `True`.
81
 
 
82
 
    Do not use this middleware in non-proxy setups for security reasons.
83
 
 
84
 
    The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in
85
 
    the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and
86
 
    `werkzeug.proxy_fix.orig_http_host`.
87
 
 
88
 
    :param app: the WSGI application
89
 
    """
90
 
 
91
 
    def __init__(self, app):
92
 
        self.app = app
93
 
 
94
 
    def __call__(self, environ, start_response):
95
 
        getter = environ.get
96
 
        forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',')
97
 
        forwarded_host = getter('HTTP_X_FORWARDED_HOST', '')
98
 
        environ.update({
99
 
            'werkzeug.proxy_fix.orig_remote_addr':  getter('REMOTE_ADDR'),
100
 
            'werkzeug.proxy_fix.orig_http_host':    getter('HTTP_HOST')
101
 
        })
102
 
        if forwarded_for:
103
 
            environ['REMOTE_ADDR'] = forwarded_for[0].strip()
104
 
        if forwarded_host:
105
 
            environ['HTTP_HOST'] = forwarded_host
106
 
        return self.app(environ, start_response)
107
 
 
108
 
 
109
 
class HeaderRewriterFix(object):
110
 
    """This middleware can remove response headers and add others.  This
111
 
    is for example useful to remove the `Date` header from responses if you
112
 
    are using a server that adds that header, no matter if it's present or
113
 
    not or to add `X-Powered-By` headers::
114
 
 
115
 
        app = HeaderRewriterFix(app, remove_headers=['Date'],
116
 
                                add_headers=[('X-Powered-By', 'WSGI')])
117
 
 
118
 
    :param app: the WSGI application
119
 
    :param remove_headers: a sequence of header keys that should be
120
 
                           removed.
121
 
    :param add_headers: a sequence of ``(key, value)`` tuples that should
122
 
                        be added.
123
 
    """
124
 
 
125
 
    def __init__(self, app, remove_headers=None, add_headers=None):
126
 
        self.app = app
127
 
        self.remove_headers = set(x.lower() for x in (remove_headers or ()))
128
 
        self.add_headers = list(add_headers or ())
129
 
 
130
 
    def __call__(self, environ, start_response):
131
 
        def rewriting_start_response(status, headers, exc_info=None):
132
 
            new_headers = []
133
 
            for key, value in headers:
134
 
                if key.lower() not in self.remove_headers:
135
 
                    new_headers.append((key, value))
136
 
            new_headers += self.add_headers
137
 
            return start_response(status, new_headers, exc_info)
138
 
        return self.app(environ, rewriting_start_response)
139
 
 
140
 
 
141
 
class InternetExplorerFix(object):
142
 
    """This middleware fixes a couple of bugs with Microsoft Internet
143
 
    Explorer.  Currently the following fixes are applied:
144
 
 
145
 
    -   removing of `Vary` headers for unsupported mimetypes which
146
 
        causes troubles with caching.  Can be disabled by passing
147
 
        ``fix_vary=False`` to the constructor.
148
 
        see: http://support.microsoft.com/kb/824847/en-us
149
 
 
150
 
    -   removes offending headers to work around caching bugs in
151
 
        Internet Explorer if `Content-Disposition` is set.  Can be
152
 
        disabled by passing ``fix_attach=False`` to the constructor.
153
 
 
154
 
    If it does not detect affected Internet Explorer versions it won't touch
155
 
    the request / response.
156
 
    """
157
 
 
158
 
    # This code was inspired by Django fixers for the same bugs.  The
159
 
    # fix_vary and fix_attach fixers were originally implemented in Django
160
 
    # by Michael Axiak and is available as part of the Django project:
161
 
    #     http://code.djangoproject.com/ticket/4148
162
 
 
163
 
    def __init__(self, app, fix_vary=True, fix_attach=True):
164
 
        self.app = app
165
 
        self.fix_vary = fix_vary
166
 
        self.fix_attach = fix_attach
167
 
 
168
 
    def fix_headers(self, environ, headers, status=None):
169
 
        if self.fix_vary:
170
 
            header = headers.get('content-type', '')
171
 
            mimetype, options = parse_options_header(header)
172
 
            if mimetype not in ('text/html', 'text/plain', 'text/sgml'):
173
 
                headers.pop('vary', None)
174
 
 
175
 
        if self.fix_attach and 'content-disposition' in headers:
176
 
            pragma = parse_set_header(headers.get('pragma', ''))
177
 
            pragma.discard('no-cache')
178
 
            header = pragma.to_header()
179
 
            if not header:
180
 
                headers.pop('pragma', '')
181
 
            else:
182
 
                headers['Pragma'] = header
183
 
            header = headers.get('cache-control', '')
184
 
            if header:
185
 
                cc = parse_cache_control_header(header,
186
 
                                                cls=ResponseCacheControl)
187
 
                cc.no_cache = None
188
 
                cc.no_store = False
189
 
                header = cc.to_header()
190
 
                if not header:
191
 
                    headers.pop('cache-control', '')
192
 
                else:
193
 
                    headers['Cache-Control'] = header
194
 
 
195
 
    def run_fixed(self, environ, start_response):
196
 
        def fixing_start_response(status, headers, exc_info=None):
197
 
            self.fix_headers(environ, Headers.linked(headers), status)
198
 
            return start_response(status, headers, exc_info)
199
 
        return self.app(environ, fixing_start_response)
200
 
 
201
 
    def __call__(self, environ, start_response):
202
 
        ua = UserAgent(environ)
203
 
        if ua.browser != 'msie':
204
 
            return self.app(environ, start_response)
205
 
        return self.run_fixed(environ, start_response)