Source code for wheezy.web.handlers.file
"""
"""
import mimetypes
import os.path
import stat
from datetime import datetime
from datetime import timedelta
from wheezy.core.datetime import parse_http_datetime
from wheezy.http import HTTPResponse
from wheezy.http import HTTPCachePolicy
from wheezy.http import forbidden
from wheezy.http import not_found
from wheezy.web.handlers.method import MethodHandler
HTTP_HEADER_ACCEPT_RANGE_NONE = ('Accept-Ranges', 'none')
[docs]def file_handler(root, age=None):
""" Serves static files out of some directory.
"""
abspath = os.path.abspath(root)
assert os.path.exists(abspath)
assert os.path.isdir(abspath)
assert age is None or isinstance(age, timedelta)
return lambda request: FileHandler(
request,
root=abspath,
age=age)
[docs]class FileHandler(MethodHandler):
""" Serves static files out of some directory.
"""
def __init__(self, request, root, age=None):
self.root = root
self.age = age
super(FileHandler, self).__init__(request)
def head(self):
return self.get(skip_body=True)
def get(self, skip_body=False):
route_args = self.route_args
path = route_args['path']
assert path
abspath = os.path.abspath(os.path.join(self.root, path))
if not abspath.startswith(self.root):
return forbidden()
if not os.path.exists(abspath):
return not_found()
if not os.path.isfile(abspath):
return forbidden()
mime_type, encoding = mimetypes.guess_type(abspath)
response = HTTPResponse(mime_type or 'plain/text', encoding)
last_modified_stamp = os.stat(abspath)[stat.ST_MTIME]
environ = self.request.environ
etag = '\"' + hex(last_modified_stamp)[2:] + '\"'
none_match = environ.get('HTTP_IF_NONE_MATCH', None)
if none_match and etag in none_match:
response.status_code = 304
return response
last_modified = datetime.utcfromtimestamp(last_modified_stamp)
modified_since = environ.get('HTTP_IF_MODIFIED_SINCE', None)
if modified_since:
modified_since = parse_http_datetime(modified_since)
if modified_since >= last_modified:
response.status_code = 304
return response
response.cache_policy = cache_policy = HTTPCachePolicy('public')
cache_policy.etag(etag)
cache_policy.last_modified(last_modified)
age = self.age
if age:
cache_policy.max_age(age)
cache_policy.expires(datetime.utcnow() + age)
if not skip_body:
response.headers.append(HTTP_HEADER_ACCEPT_RANGE_NONE)
file = open(abspath, 'rb')
try:
response.write_bytes(file.read())
finally:
file.close()
return response