/usr/share/pyshared/pylons/controllers/util.py is in python-pylons 1.0.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | """Utility functions and classes available for use by Controllers
Pylons subclasses the `WebOb <http://pythonpaste.org/webob/>`_
:class:`webob.Request` and :class:`webob.Response` classes to provide
backwards compatible functions for earlier versions of Pylons as well
as add a few helper functions to assist with signed cookies.
For reference use, refer to the :class:`Request` and :class:`Response`
below.
Functions available:
:func:`abort`, :func:`forward`, :func:`etag_cache`,
:func:`mimetype` and :func:`redirect`
"""
import base64
import binascii
import hmac
import logging
import re
try:
import cPickle as pickle
except ImportError:
import pickle
try:
from hashlib import sha1
except ImportError:
import sha as sha1
from webob import BaseRequest as WebObRequest
from webob import Response as WebObResponse
from webob.exc import status_map
import pylons
__all__ = ['abort', 'etag_cache', 'redirect', 'Request', 'Response']
log = logging.getLogger(__name__)
IF_NONE_MATCH = re.compile('(?:W/)?(?:"([^"]*)",?\s*)')
class Request(WebObRequest):
"""WebOb Request subclass
The WebOb :class:`webob.Request` has no charset, or other defaults. This subclass
adds defaults, along with several methods for backwards
compatibility with paste.wsgiwrappers.WSGIRequest.
"""
def determine_browser_charset(self):
"""Legacy method to return the
:attr:`webob.Request.accept_charset`"""
return self.accept_charset
def languages(self):
# And we now have the old best_matches code that webob ditched!
al = self.accept_language
items = [i for i, q in sorted(al._parsed, key=lambda iq: -iq[1])]
for index, item in enumerate(items):
if al._match(item, self.language):
items[index:] = [self.language]
break
else:
items.append(self.language)
return items
languages = property(languages)
def match_accept(self, mimetypes):
return self.accept.first_match(mimetypes)
def signed_cookie(self, name, secret):
"""Extract a signed cookie of ``name`` from the request
The cookie is expected to have been created with
``Response.signed_cookie``, and the ``secret`` should be the
same as the one used to sign it.
Any failure in the signature of the data will result in None
being returned.
"""
cookie = self.str_cookies.get(name)
if not cookie:
return None
try:
input_sig, pickled = cookie[:40], base64.standard_b64decode(cookie[40:])
except binascii.Error:
# Badly formed data can make base64 die
return None
sig = hmac.new(secret, pickled, sha1).hexdigest()
# Avoid timing attacks
invalid_bits = 0
if len(sig) != len(input_sig):
return None
for a, b in zip(sig, input_sig):
invalid_bits += a != b
if invalid_bits:
return None
else:
return pickle.loads(pickled)
class Response(WebObResponse):
"""WebOb Response subclass
The WebOb Response has no default content type, or error defaults.
This subclass adds defaults, along with several methods for
backwards compatibility with paste.wsgiwrappers.WSGIResponse.
"""
content = WebObResponse.body
def determine_charset(self):
return self.charset
def has_header(self, header):
return header in self.headers
def get_content(self):
return self.body
def wsgi_response(self):
return self.status, self.headers, self.body
def signed_cookie(self, name, data, secret=None, **kwargs):
"""Save a signed cookie with ``secret`` signature
Saves a signed cookie of the pickled data. All other keyword
arguments that ``WebOb.set_cookie`` accepts are usable and
passed to the WebOb set_cookie method after creating the signed
cookie value.
"""
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
sig = hmac.new(secret, pickled, sha1).hexdigest()
self.set_cookie(name, sig + base64.standard_b64encode(pickled), **kwargs)
def etag_cache(key=None):
"""Use the HTTP Entity Tag cache for Browser side caching
If a "If-None-Match" header is found, and equivilant to ``key``,
then a ``304`` HTTP message will be returned with the ETag to tell
the browser that it should use its current cache of the page.
Otherwise, the ETag header will be added to the response headers.
Suggested use is within a Controller Action like so:
.. code-block:: python
import pylons
class YourController(BaseController):
def index(self):
etag_cache(key=1)
return render('/splash.mako')
.. note::
This works because etag_cache will raise an HTTPNotModified
exception if the ETag received matches the key provided.
"""
if_none_matches = IF_NONE_MATCH.findall(
pylons.request.environ.get('HTTP_IF_NONE_MATCH', ''))
response = pylons.response._current_obj()
response.headers['ETag'] = '"%s"' % key
if str(key) in if_none_matches:
log.debug("ETag match, returning 304 HTTP Not Modified Response")
response.headers.pop('Content-Type', None)
response.headers.pop('Cache-Control', None)
response.headers.pop('Pragma', None)
raise status_map[304]().exception
else:
log.debug("ETag didn't match, returning response object")
def forward(wsgi_app):
"""Forward the request to a WSGI application. Returns its response.
.. code-block:: python
return forward(FileApp('filename'))
"""
environ = pylons.request.environ
controller = environ.get('pylons.controller')
if not controller or not hasattr(controller, 'start_response'):
raise RuntimeError("Unable to forward: environ['pylons.controller'] "
"is not a valid Pylons controller")
return wsgi_app(environ, controller.start_response)
def abort(status_code=None, detail="", headers=None, comment=None):
"""Aborts the request immediately by returning an HTTP exception
In the event that the status_code is a 300 series error, the detail
attribute will be used as the Location header should one not be
specified in the headers attribute.
"""
exc = status_map[status_code](detail=detail, headers=headers,
comment=comment)
log.debug("Aborting request, status: %s, detail: %r, headers: %r, "
"comment: %r", status_code, detail, headers, comment)
raise exc.exception
def redirect(url, code=302):
"""Raises a redirect exception to the specified URL
Optionally, a code variable may be passed with the status code of
the redirect, ie::
redirect(url(controller='home', action='index'), code=303)
"""
log.debug("Generating %s redirect" % code)
exc = status_map[code]
raise exc(location=url).exception
|