From c2a24ae0c60f06328664273b15f58671ae373fed Mon Sep 17 00:00:00 2001 From: Bruce Martin Date: Wed, 1 Apr 2020 16:43:43 -0600 Subject: [PATCH] add cache control headers (#1333) * add cache control headers * add config param check --- server/app/app.py | 42 +++++++++++++++++++++++++++++++++ server/common/app_config.py | 2 ++ server/common/default_config.py | 1 + 3 files changed, 45 insertions(+) diff --git a/server/app/app.py b/server/app/app.py index fe7f1e1e..39d27a67 100644 --- a/server/app/app.py +++ b/server/app/app.py @@ -20,7 +20,39 @@ from functools import wraps webbp = Blueprint("webapp", "server.common.web", template_folder="templates") +def _cache_control(always, **cache_kwargs): + """ + Used to easily manage cache control headers on responses. + See Werkzeug for attributes that can be set, eg, no_cache, private, max_age, etc. + https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.ResponseCacheControl + """ + def inner_cache_control(f): + @wraps(f) + def wrapper(*args, **kwargs): + response = make_response(f(*args, **kwargs)) + if not always and not current_app.app_config.server__generate_cache_control_headers: + return response + if response.status_code >= 400: + return response + for k, v in cache_kwargs.items(): + setattr(response.cache_control, k, v) + return response + return wrapper + return inner_cache_control + + +def cache_control(**cache_kwargs): + """ configu driven """ + return _cache_control(False, **cache_kwargs) + + +def cache_control_always(**cache_kwargs): + """ always generate headers, regardless of the config """ + return _cache_control(True, **cache_kwargs) + + @webbp.route("/", methods=["GET"]) +@cache_control(public=True, max_age=3600) def dataset_index(dataset=None): config = current_app.app_config if dataset is None: @@ -52,6 +84,7 @@ def favicon(): @webbp.route("/health", methods=["GET"]) +@cache_control_always(no_cache=True) def health(): config = current_app.app_config return health_check(config) @@ -132,50 +165,59 @@ def dataroot_index(): class SchemaAPI(Resource): + @cache_control(public=True, max_age=3600) @rest_get_data_adaptor def get(self, data_adaptor): return common_rest.schema_get(data_adaptor, current_app.annotations) class ConfigAPI(Resource): + @cache_control(public=True, max_age=3600) @rest_get_data_adaptor def get(self, data_adaptor): return common_rest.config_get(current_app.app_config, data_adaptor, current_app.annotations) class AnnotationsObsAPI(Resource): + @cache_control(public=True, max_age=3600) @rest_get_data_adaptor def get(self, data_adaptor): return common_rest.annotations_obs_get(request, data_adaptor, current_app.annotations) + @cache_control(no_cache=True) @rest_get_data_adaptor def put(self, data_adaptor): return common_rest.annotations_obs_put(request, data_adaptor, current_app.annotations) class AnnotationsVarAPI(Resource): + @cache_control(public=True, max_age=3600) @rest_get_data_adaptor def get(self, data_adaptor): return common_rest.annotations_var_get(request, data_adaptor, current_app.annotations) class DataVarAPI(Resource): + @cache_control(no_cache=True) @rest_get_data_adaptor def put(self, data_adaptor): return common_rest.data_var_put(request, data_adaptor) class DiffExpObsAPI(Resource): + @cache_control(no_cache=True) @rest_get_data_adaptor def post(self, data_adaptor): return common_rest.diffexp_obs_post(request, data_adaptor) class LayoutObsAPI(Resource): + @cache_control(public=True, max_age=3600) @rest_get_data_adaptor def get(self, data_adaptor): return common_rest.layout_obs_get(request, data_adaptor) + @cache_control(no_cache=True) @rest_get_data_adaptor def put(self, data_adaptor): return common_rest.layout_obs_put(request, data_adaptor) diff --git a/server/common/app_config.py b/server/common/app_config.py index cdd0d459..572932cc 100644 --- a/server/common/app_config.py +++ b/server/common/app_config.py @@ -66,6 +66,7 @@ class AppConfig(object): self.server__about_legal_privacy = dc["server"]["about_legal_privacy"] self.server__force_https = dc["server"]["force_https"] self.server__flask_secret_key = dc["server"]["flask_secret_key"] + self.server__generate_cache_control_headers = dc["server"]["generate_cache_control_headers"] self.multi_dataset__dataroot = dc["multi_dataset"]["dataroot"] self.multi_dataset__index = dc["multi_dataset"]["index"] @@ -197,6 +198,7 @@ class AppConfig(object): self.__check_attr("server__open_browser", bool) self.__check_attr("server__force_https", bool) self.__check_attr("server__flask_secret_key", (type(None), str)) + self.__check_attr("server__generate_cache_control_headers", bool) if self.server__port: if not is_port_available(self.server__host, self.server__port): diff --git a/server/common/default_config.py b/server/common/default_config.py index 0d4a08bb..32ecca33 100644 --- a/server/common/default_config.py +++ b/server/common/default_config.py @@ -14,6 +14,7 @@ server: about_legal_privacy: null force_https: false flask_secret_key: null + generate_cache_control_headers: false presentation: max_categories: 1000