diff --git a/addons/web/common/http.py b/addons/web/common/http.py index cc76a554693..a1d28536cd4 100644 --- a/addons/web/common/http.py +++ b/addons/web/common/http.py @@ -452,7 +452,7 @@ class Root(object): static_dirs = self._load_addons(openerp_addons_namespace) if options.serve_static: - self.dispatch = werkzeug.wsgi.SharedDataMiddleware( + self.dispatch = SuperSharedDataMiddleware( self.dispatch, static_dirs, cache=False) if options.session_storage: @@ -555,6 +555,66 @@ class Root(object): return m ps, _slash, meth = ps.rpartition('/') return None + +class SuperSharedDataMiddleware(werkzeug.wsgi.SharedDataMiddleware): + def __call__(self, environ, start_response): + import os + import mimetypes + import werkzeug.http + # sanitize the path for non unix systems + cleaned_path = environ.get('PATH_INFO', '').strip('/') + for sep in os.sep, os.altsep: + if sep and sep != '/': + cleaned_path = cleaned_path.replace(sep, '/') + path = '/'.join([''] + [x for x in cleaned_path.split('/') + if x and x != '..']) + file_loader = None + for search_path, loader in self.exports.iteritems(): + if search_path == path: + real_filename, file_loader = loader(None) + if file_loader is not None: + break + if not search_path.endswith('/'): + search_path += '/' + if path.startswith(search_path): + real_filename, file_loader = loader(path[len(search_path):]) + if file_loader is not None: + break + if file_loader is None or not self.is_allowed(real_filename): + return self.app(environ, start_response) + + guessed_type = mimetypes.guess_type(real_filename) + mime_type = guessed_type[0] or self.fallback_mimetype + f, mtime, file_size = file_loader() + + etag = self.generate_etag(mtime, file_size, real_filename) + modified = werkzeug.http.is_resource_modified(environ, etag, last_modified=mtime) + + headers = [('Date', werkzeug.http.http_date())] + if self.cache: + timeout = self.cache_timeout + headers += [ + ('Etag', '"%s"' % etag), + ('Cache-Control', 'max-age=%d, public' % timeout) + ] + if modified: + headers.append(('Expires', werkzeug.http.http_date(time() + timeout))) + else: + headers.append(('Cache-Control', 'no-cache')) + + if not modified: + f.close() + start_response('304 Not Modified', headers) + return [] + + headers.extend(( + ('Content-Type', mime_type), + ('Content-Length', str(file_size)), + ('Last-Modified', werkzeug.http.http_date(mtime)) + )) + start_response('200 OK', headers) + return werkzeug.wsgi.wrap_file(environ, f) + class LibException(Exception): """ Base of all client lib exceptions """