Source code for wheezy.http.middleware
""" ``middleware`` module.
"""
from wheezy.core.datetime import parse_http_datetime
from wheezy.http.cache import CacheableResponse
from wheezy.http.cache import NotModifiedResponse
from wheezy.http.cache import SurfaceResponse
from wheezy.http.cacheprofile import RequestVary
from wheezy.http.response import HTTPResponse
class HTTPCacheMiddleware(object):
[docs] """ HTTP cache middleware.
"""
def __init__(self, cache, middleware_vary):
"""
``cache`` - cache to be used.
``middleware_vary`` - a way to determine cache profile
key for the request.
"""
assert cache
assert hasattr(cache, 'get')
assert hasattr(cache, 'incr')
assert hasattr(cache, 'set')
assert hasattr(cache, 'set_multi')
assert middleware_vary
self.cache = cache
self.key = middleware_vary.key
self.profiles = {}
def __call__(self, request, following):
middleware_key = self.key(request)
if middleware_key in self.profiles:
cache_profile = self.profiles[middleware_key]
request_key = cache_profile.request_vary.key(request)
response = self.cache.get(request_key, cache_profile.namespace)
if response: # cache hit
environ = request.environ
if response.etag and 'HTTP_IF_NONE_MATCH' in environ:
if response.etag in environ['HTTP_IF_NONE_MATCH']:
return NotModifiedResponse(response)
elif (response.last_modified and
'HTTP_IF_MODIFIED_SINCE' in environ and
parse_http_datetime(
environ['HTTP_IF_MODIFIED_SINCE']) >=
response.last_modified):
return NotModifiedResponse(response)
return response
response = following(request)
if response and response.status_code == 200:
cache_profile = response.cache_profile
if cache_profile:
if middleware_key not in self.profiles or \
cache_profile != self.profiles[middleware_key]:
self.profiles[middleware_key] = cache_profile
request_key = cache_profile.request_vary.key(request)
cache_dependency = response.cache_dependency
# cachable response filters out set-cookie headers
cacheable = CacheableResponse(response)
if cache_dependency:
# determine next key for dependency
mapping = dict.fromkeys([
key + str(self.cache.incr(
key, 1, cache_profile.namespace, 0))
for key in cache_dependency], request_key)
mapping[request_key] = cacheable
self.cache.set_multi(
mapping,
cache_profile.duration,
cache_profile.namespace)
else:
self.cache.set(
request_key,
cacheable,
cache_profile.duration,
cache_profile.namespace)
environ = request.environ
if cacheable.etag and 'HTTP_IF_NONE_MATCH' in environ:
if cacheable.etag in environ['HTTP_IF_NONE_MATCH']:
return NotModifiedResponse(response)
elif (cacheable.last_modified and
'HTTP_IF_MODIFIED_SINCE' in environ and
parse_http_datetime(
environ['HTTP_IF_MODIFIED_SINCE']) >=
cacheable.last_modified):
return NotModifiedResponse(response)
# the response already has all necessary headers
return SurfaceResponse(response)
return response
def http_cache_middleware_factory(options):
[docs] """ HTTP cache middleware factory.
Requires ``http_cache`` in options.
Supports ``http_cache_middleware_vary`` - a way to determine
cache key for the request.
"""
cache = options['http_cache']
middleware_vary = options.get('http_cache_middleware_vary', None)
if middleware_vary is None:
middleware_vary = RequestVary()
options['http_cache_middleware_vary'] = middleware_vary
return HTTPCacheMiddleware(
cache=cache,
middleware_vary=middleware_vary)
class WSGIAdapterMiddleware(object):
[docs] """ WSGI adapter middleware.
"""
def __init__(self, wsgi_app):
""" `` wsgi_app`` - a WSGI application used to adapt calls.
"""
self.wsgi_app = wsgi_app
def __call__(self, request, following):
assert not following
response = HTTPResponse()
def start_response(status, headers):
response.status_code = int(status.split(' ', 1)[0])
response.headers = [(name, value) for name, value in headers
if name != 'Content-Length']
return response.write_bytes
result = self.wsgi_app(request.environ, start_response)
try:
response.buffer.extend(result)
finally:
if hasattr(result, 'close'): # pragma: nocover
result.close()
return response
def wsgi_adapter_middleware_factory(options):
[docs] """ WSGI adapter middleware factory.
Requires ``wsgi_app`` in options.
"""
wsgi_app = options['wsgi_app']
return WSGIAdapterMiddleware(wsgi_app)
class EnvironCacheAdapterMiddleware(object):
[docs] """ WSGI environ cache adapter middleware.
"""
def __call__(self, request, following):
assert following
response = following(request)
environ = request.environ
policy = None
if 'wheezy.http.cache_policy' in environ:
policy = environ['wheezy.http.cache_policy']
response.cache_policy = policy
if 'wheezy.http.cache_profile' in environ:
profile = environ['wheezy.http.cache_profile']
response.cache_profile = profile
if policy is None:
response.cache_policy = profile.cache_policy()
if profile.etag_func is not None:
response.cache_policy.etag(
profile.etag_func(response.buffer))
if 'wheezy.http.cache_dependency' in environ:
response.cache_dependency = environ[
'wheezy.http.cache_dependency']
return response
def environ_cache_adapter_middleware_factory(options):
[docs] """ WSGI environ cache adapter middleware factory.
"""
return EnvironCacheAdapterMiddleware()