Add File server CORS support

This commit is contained in:
allegroai
2019-07-17 18:16:43 +03:00
parent 02671910b2
commit bed714890d
6 changed files with 24 additions and 4 deletions

View File

@@ -0,0 +1,8 @@
import logging.config
from pathlib import Path
from .basic import BasicConfig
config = BasicConfig(Path(__file__).with_name("default"))
logging.config.dictConfig(config.get("logging"))

120
fileserver/config/basic.py Normal file
View File

@@ -0,0 +1,120 @@
import logging
from functools import reduce
from os import getenv
from os.path import expandvars
from pathlib import Path
from pyhocon import ConfigTree, ConfigFactory
from pyparsing import (
ParseFatalException,
ParseException,
RecursiveGrammarException,
ParseSyntaxException,
)
DEFAULT_EXTRA_CONFIG_PATH = "/opt/trains/config"
EXTRA_CONFIG_PATH_ENV_KEY = "TRAINS_CONFIG_DIR"
EXTRA_CONFIG_PATH_SEP = ":"
class BasicConfig:
NotSet = object()
def __init__(self, folder):
self.folder = Path(folder)
if not self.folder.is_dir():
raise ValueError("Invalid configuration folder")
self.prefix = "trains"
self._load()
def __getitem__(self, key):
return self._config[key]
def get(self, key, default=NotSet):
value = self._config.get(key, default)
if value is self.NotSet and not default:
raise KeyError(
f"Unable to find value for key '{key}' and default value was not provided."
)
return value
def logger(self, name):
if Path(name).is_file():
name = Path(name).stem
path = ".".join((self.prefix, Path(name).stem))
return logging.getLogger(path)
def _read_env_paths(self, key):
value = getenv(EXTRA_CONFIG_PATH_ENV_KEY, DEFAULT_EXTRA_CONFIG_PATH)
if value is None:
return
paths = [
Path(expandvars(v)).expanduser() for v in value.split(EXTRA_CONFIG_PATH_SEP)
]
invalid = [
path
for path in paths
if not path.is_dir() and str(path) != DEFAULT_EXTRA_CONFIG_PATH
]
if invalid:
print(f"WARNING: Invalid paths in {key} env var: {' '.join(invalid)}")
return [path for path in paths if path.is_dir()]
def _load(self, verbose=True):
extra_config_paths = self._read_env_paths(EXTRA_CONFIG_PATH_ENV_KEY) or []
self._config = reduce(
lambda config, path: ConfigTree.merge_configs(
config, self._read_recursive(path, verbose=verbose), copy_trees=True
),
[self.folder] + extra_config_paths,
ConfigTree(),
)
def _read_recursive(self, conf_root, verbose=True):
conf = ConfigTree()
if not conf_root:
return conf
if not conf_root.is_dir():
if verbose:
if not conf_root.exists():
print(f"No config in {conf_root}")
else:
print(f"Not a directory: {conf_root}")
return conf
if verbose:
print(f"Loading config from {conf_root}")
for file in conf_root.rglob("*.conf"):
key = ".".join(file.relative_to(conf_root).with_suffix("").parts)
conf.put(key, self._read_single_file(file, verbose=verbose))
return conf
@staticmethod
def _read_single_file(file_path, verbose=True):
if verbose:
print(f"Loading config from file {file_path}")
try:
return ConfigFactory.parse_file(file_path)
except ParseSyntaxException as ex:
msg = f"Failed parsing {file_path} ({ex.__class__.__name__}): (at char {ex.loc}, line:{ex.lineno}, col:{ex.column})"
raise ConfigurationError(msg, file_path=file_path) from ex
except (ParseException, ParseFatalException, RecursiveGrammarException) as ex:
msg = f"Failed parsing {file_path} ({ex.__class__.__name__}): {ex}"
raise ConfigurationError(msg) from ex
except Exception as ex:
print(f"Failed loading {file_path}: {ex}")
raise
class ConfigurationError(Exception):
def __init__(self, msg, file_path=None, *args):
super(ConfigurationError, self).__init__(msg, *args)
self.file_path = file_path

View File

@@ -0,0 +1,8 @@
download {
# Add response headers requesting no caching for served files
disable_browser_caching: false
}
cors {
origins: "*"
}

View File

@@ -1,16 +1,18 @@
""" A Simple file server for uploading and downloading files """
import json
import logging.config
import os
from argparse import ArgumentParser
from pathlib import Path
from flask import Flask, request, send_from_directory, safe_join
from pyhocon import ConfigFactory
from flask_compress import Compress
from flask_cors import CORS
logging.config.dictConfig(ConfigFactory.parse_file("logging.conf"))
from config import config
app = Flask(__name__)
CORS(app, **config.get("fileserver.cors"))
Compress(app)
@app.route("/", methods=["POST"])
@@ -29,7 +31,15 @@ def upload():
@app.route("/<path:path>", methods=["GET"])
def download(path):
return send_from_directory(app.config["UPLOAD_FOLDER"], path)
response = send_from_directory(app.config["UPLOAD_FOLDER"], path)
if config.get("fileserver.download.disable_browser_caching", False):
headers = response.headers
headers["Pragma-directive"] = "no-cache"
headers["Cache-directive"] = "no-cache"
headers["Cache-control"] = "no-cache"
headers["Pragma"] = "no-cache"
headers["Expires"] = "0"
return response
def main():

View File

@@ -1,2 +1,4 @@
Flask
Flask-Cors>=3.0.5
Flask-Compress>=1.4.0
pyhocon>=0.3.35