Merge branch 'fix/wrongEtagForDownloads' into devel

This commit is contained in:
Gina Häußge 2015-10-07 12:25:52 +02:00
commit b458759159
2 changed files with 31 additions and 8 deletions

View file

@ -332,13 +332,29 @@ class Server(object):
upload_suffixes = dict(name=s.get(["server", "uploads", "nameSuffix"]), path=s.get(["server", "uploads", "pathSuffix"]))
download_handler_kwargs = dict(
as_attachment=True,
allow_client_caching=False
)
admin_validator = dict(access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))
no_hidden_files_validator = dict(path_validation=util.tornado.path_validation_factory(lambda path: not os.path.basename(path).startswith("."), status_code=404))
def joined_dict(*dicts):
if not len(dicts):
return dict()
joined = dict()
for d in dicts:
joined.update(d)
return joined
server_routes = self._router.urls + [
# various downloads
(r"/downloads/timelapse/([^/]*\.mpg)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("timelapse"), as_attachment=True)),
(r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("uploads"), as_attachment=True, path_validation=util.tornado.path_validation_factory(lambda path: not os.path.basename(path).startswith("."), status_code=404))),
(r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, dict(path=s.getBaseFolder("logs"), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.admin_validator))),
(r"/downloads/timelapse/([^/]*\.mpg)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("timelapse")), download_handler_kwargs, no_hidden_files_validator)),
(r"/downloads/files/local/(.*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("uploads")), download_handler_kwargs, no_hidden_files_validator)),
(r"/downloads/logs/([^/]*)", util.tornado.LargeResponseHandler, joined_dict(dict(path=s.getBaseFolder("logs")), download_handler_kwargs, admin_validator)),
# camera snapshot
(r"/downloads/camera/current", util.tornado.UrlForwardHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))),
(r"/downloads/camera/current", util.tornado.UrlProxyHandler, dict(url=s.get(["webcam", "snapshot"]), as_attachment=True, access_validation=util.tornado.access_validation_factory(app, loginManager, util.flask.user_validator))),
# generated webassets
(r"/static/webassets/(.*)", util.tornado.LargeResponseHandler, dict(path=os.path.join(s.getBaseFolder("generated"), "webassets")))
]

View file

@ -768,6 +768,8 @@ class LargeResponseHandler(tornado.web.StaticFileHandler):
:class:``~tornado.web.StaticFileHandler`` as the ``default_filename`` keyword parameter). Defaults to ``None``.
as_attachment (bool): Whether to serve requested files with ``Content-Disposition: attachment`` header (``True``)
or not. Defaults to ``False``.
allow_client_caching (bool): Whether to allow the client to cache (by not setting any ``Cache-Control`` or
``Expires`` headers on the response) or not.
access_validation (function): Callback to call in the ``get`` method to validate access to the resource. Will
be called with ``self.request`` as parameter which contains the full tornado request object. Should raise
a ``tornado.web.HTTPError`` if access is not allowed in which case the request will not be further processed.
@ -782,10 +784,11 @@ class LargeResponseHandler(tornado.web.StaticFileHandler):
by ``get_content_version``.
"""
def initialize(self, path, default_filename=None, as_attachment=False,
def initialize(self, path, default_filename=None, as_attachment=False, allow_client_caching=True,
access_validation=None, path_validation=None, etag_generator=None):
tornado.web.StaticFileHandler.initialize(self, os.path.abspath(path), default_filename)
self._as_attachment = as_attachment
self._allow_client_caching = allow_client_caching
self._access_validation = access_validation
self._path_validation = path_validation
self._etag_generator = etag_generator
@ -802,6 +805,10 @@ class LargeResponseHandler(tornado.web.StaticFileHandler):
if self._as_attachment:
self.set_header("Content-Disposition", "attachment")
if not self._allow_client_caching:
self.set_header("Cache-Control", "max-age=0, must-revalidate, private")
self.set_header("Expires", "-1")
def compute_etag(self):
if self._etag_generator is not None:
return self._etag_generator(self)
@ -817,7 +824,7 @@ class LargeResponseHandler(tornado.web.StaticFileHandler):
##~~ URL Forward Handler for forwarding requests to a preconfigured static URL
class UrlForwardHandler(tornado.web.RequestHandler):
class UrlProxyHandler(tornado.web.RequestHandler):
"""
`tornado.web.RequestHandler <http://tornado.readthedocs.org/en/branch4.0/web.html#request-handlers>`_ that proxies
requests to a preconfigured url and returns the response. Allows delivery of the requested content as attachment
@ -827,7 +834,7 @@ class UrlForwardHandler(tornado.web.RequestHandler):
for making the request to the configured endpoint and return the body of the client response with the status code
from the client response and the following headers:
* ``Date``, ``Cache-Control``, ``Server``, ``Content-Type`` and ``Location`` will be copied over.
* ``Date``, ``Cache-Control``, ``Expires``, ``ETag``, ``Server``, ``Content-Type`` and ``Location`` will be copied over.
* If ``as_attachment`` is set to True, ``Content-Disposition`` will be set to ``attachment``. If ``basename`` is
set including the attachement's ``filename`` attribute will be set to the base name followed by the extension
guessed based on the MIME type from the ``Content-Type`` header of the response. If no extension can be guessed
@ -877,7 +884,7 @@ class UrlForwardHandler(tornado.web.RequestHandler):
filename = None
self.set_status(response.code)
for name in ("Date", "Cache-Control", "Server", "Content-Type", "Location"):
for name in ("Date", "Cache-Control", "Server", "Content-Type", "Location", "Expires", "ETag"):
value = response.headers.get(name)
if value:
self.set_header(name, value)