Merge branch '22-configurable-static-headers' into 'staging'
Resolve "Configurable static headers" See merge request s3lph/matemat!29
This commit is contained in:
commit
36176610c0
6 changed files with 68 additions and 13 deletions
2
doc
2
doc
|
@ -1 +1 @@
|
|||
Subproject commit e52eec0831ef72edab816d549d7ee2a85575e292
|
||||
Subproject commit 1a181d3c72cf20fe21e93b063dc3c395e1fa8f93
|
|
@ -67,7 +67,9 @@ def parse_config_file(paths: Union[str, Iterable[str]]) -> Dict[str, Any]:
|
|||
# Log target: An IO stream (stderr, stdout, ...) or a filename
|
||||
'log_handler': logging.StreamHandler(),
|
||||
# Variables passed to pagelets
|
||||
'pagelet_variables': dict()
|
||||
'pagelet_variables': dict(),
|
||||
# Statically configured headers
|
||||
'headers': dict()
|
||||
}
|
||||
|
||||
# Initialize the config parser
|
||||
|
@ -101,4 +103,9 @@ def parse_config_file(paths: Union[str, Iterable[str]]) -> Dict[str, Any]:
|
|||
for k, v in parser['Pagelets'].items():
|
||||
config['pagelet_variables'][k] = v
|
||||
|
||||
# Read all values from the [HttpHeaders] section, if present. These values are set as HTTP response headers
|
||||
if 'HttpHeaders' in parser.sections():
|
||||
for k, v in parser['HttpHeaders'].items():
|
||||
config['headers'][k] = v
|
||||
|
||||
return config
|
||||
|
|
|
@ -105,6 +105,7 @@ class MatematHTTPServer(HTTPServer):
|
|||
staticroot: str,
|
||||
templateroot: str,
|
||||
pagelet_variables: Dict[str, str],
|
||||
static_headers: Dict[str, str],
|
||||
log_level: int,
|
||||
log_handler: logging.Handler,
|
||||
bind_and_activate: bool = True) -> None:
|
||||
|
@ -115,6 +116,8 @@ class MatematHTTPServer(HTTPServer):
|
|||
self.session_vars: Dict[str, Tuple[datetime, Dict[str, Any]]] = dict()
|
||||
# Set up pagelet arguments dict
|
||||
self.pagelet_variables = pagelet_variables
|
||||
# Set up static HTTP Response headers
|
||||
self.static_headers = static_headers
|
||||
# Set up the Jinja2 environment
|
||||
self.jinja_env: jinja2.Environment = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(os.path.abspath(templateroot)),
|
||||
|
@ -150,6 +153,7 @@ class MatematWebserver(object):
|
|||
staticroot: str,
|
||||
templateroot: str,
|
||||
pagelet_variables: Dict[str, str],
|
||||
headers: Dict[str, str],
|
||||
log_level: int,
|
||||
log_handler: logging.Handler) -> None:
|
||||
"""
|
||||
|
@ -160,6 +164,7 @@ class MatematWebserver(object):
|
|||
:param staticroot: Path to the static webroot directory.
|
||||
:param templateroot: Path to the Jinja2 templates root directory.
|
||||
:param pagelet_variables: Dictionary of variables to pass to pagelet functions.
|
||||
:param headers: Dictionary of statically configured headers.
|
||||
:param log_level: The log level, as defined in the builtin logging module.
|
||||
:param log_handler: The logging handler.
|
||||
"""
|
||||
|
@ -173,6 +178,7 @@ class MatematWebserver(object):
|
|||
staticroot,
|
||||
templateroot,
|
||||
pagelet_variables,
|
||||
headers,
|
||||
log_level,
|
||||
log_handler)
|
||||
|
||||
|
@ -308,12 +314,13 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
return
|
||||
self.session_id: str = session_id
|
||||
|
||||
# Set all static headers. These can still be overwritten by a pagelet
|
||||
headers: Dict[str, str] = dict()
|
||||
for k, v in self.server.static_headers.items():
|
||||
headers[k] = v
|
||||
|
||||
# Call a pagelet function, if one is registered for the requested path
|
||||
if path in _PAGELET_PATHS:
|
||||
# Prepare some headers. Those can still be overwritten by the pagelet
|
||||
headers: Dict[str, str] = {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
# Call the pagelet function
|
||||
pagelet_res = _PAGELET_PATHS[path](method,
|
||||
path,
|
||||
|
@ -327,8 +334,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
self.send_response(hsc)
|
||||
# Format the session cookie timeout string and send the session cookie header
|
||||
expires = timeout.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
self.send_header('Set-Cookie',
|
||||
f'matemat_session_id={session_id}; expires={expires}')
|
||||
headers['Set-Cookie'] = f'matemat_session_id={session_id}; expires={expires}'
|
||||
# Disable caching
|
||||
headers['Cache-Control'] = 'no-cache'
|
||||
# Compute the body length and add the appropriate header
|
||||
headers['Content-Length'] = str(len(data))
|
||||
# If the pagelet did not set its own Content-Type header, use libmagic to guess an appropriate one
|
||||
|
@ -394,12 +402,15 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
charset = 'binary'
|
||||
# Send content type and length header. Only set the charset if it's not "binary"
|
||||
if charset == 'binary':
|
||||
self.send_header('Content-Type', mimetype)
|
||||
headers['Content-Type'] = mimetype
|
||||
else:
|
||||
self.send_header('Content-Type', f'{mimetype}; charset={charset}')
|
||||
self.send_header('Content-Length', str(len(data)))
|
||||
self.send_header('Last-Modified', fileage.strftime('%a, %d %b %Y %H:%M:%S GMT'))
|
||||
self.send_header('Cache-Control', 'max-age=1')
|
||||
headers['Content-Type'] = f'{mimetype}; charset={charset}'
|
||||
headers['Content-Length'] = str(len(data))
|
||||
headers['Last-Modified'] = fileage.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
headers['Cache-Control'] = 'max-age=1'
|
||||
# Send all headers
|
||||
for name, value in headers.items():
|
||||
self.send_header(name, value)
|
||||
self.end_headers()
|
||||
# Send the requested resource as response body
|
||||
self.wfile.write(data)
|
||||
|
|
|
@ -112,6 +112,10 @@ class MockServer:
|
|||
self.webroot: str = webroot
|
||||
# Variables to pass to pagelets
|
||||
self.pagelet_variables: Dict[str, str] = dict()
|
||||
# Static response headers
|
||||
self.static_headers: Dict[str, str] = {
|
||||
'X-Static-Testing-Header': 'helloworld!'
|
||||
}
|
||||
# Jinja environment with a single, static template
|
||||
self.jinja_env = jinja2.Environment(
|
||||
loader=jinja2.DictLoader({'test.txt': 'Hello, {{ what }}!'})
|
||||
|
|
|
@ -29,6 +29,10 @@ Name=Matemat
|
|||
(Unit Test)
|
||||
UploadDir= /var/test/static/upload
|
||||
DatabaseFile=/var/test/db/test.db
|
||||
|
||||
[HttpHeaders]
|
||||
Content-Security-Policy = default-src: 'self';
|
||||
X-I-Am-A-Header = andthisismyvalue
|
||||
'''
|
||||
|
||||
_PARTIAL_CONFIG = '''
|
||||
|
@ -37,6 +41,9 @@ Port=443
|
|||
|
||||
[Pagelets]
|
||||
Name=Matemat (Unit Test 2)
|
||||
|
||||
[HttpHeaders]
|
||||
X-I-Am-A-Header = andthisismyothervalue
|
||||
'''
|
||||
|
||||
_LOG_NONE_CONFIG = '''
|
||||
|
@ -113,6 +120,8 @@ class TestConfig(TestCase):
|
|||
self.assertEqual(sys.stderr, config['log_handler'].stream)
|
||||
self.assertIsInstance(config['pagelet_variables'], dict)
|
||||
self.assertEqual(0, len(config['pagelet_variables']))
|
||||
self.assertIsInstance(config['headers'], dict)
|
||||
self.assertEqual(0, len(config['headers']))
|
||||
|
||||
def test_parse_config_full(self):
|
||||
"""
|
||||
|
@ -144,6 +153,10 @@ class TestConfig(TestCase):
|
|||
self.assertEqual('Matemat\n(Unit Test)', config['pagelet_variables']['Name'])
|
||||
self.assertEqual('/var/test/static/upload', config['pagelet_variables']['UploadDir'])
|
||||
self.assertEqual('/var/test/db/test.db', config['pagelet_variables']['DatabaseFile'])
|
||||
self.assertIsInstance(config['headers'], dict)
|
||||
self.assertEqual(2, len(config['headers']))
|
||||
self.assertEqual('default-src: \'self\';', config['headers']['Content-Security-Policy'])
|
||||
self.assertEqual('andthisismyvalue', config['headers']['X-I-Am-A-Header'])
|
||||
|
||||
def test_parse_config_precedence(self):
|
||||
"""
|
||||
|
@ -172,6 +185,10 @@ class TestConfig(TestCase):
|
|||
self.assertEqual('Matemat (Unit Test 2)', config['pagelet_variables']['Name'])
|
||||
self.assertEqual('/var/test/static/upload', config['pagelet_variables']['UploadDir'])
|
||||
self.assertEqual('/var/test/db/test.db', config['pagelet_variables']['DatabaseFile'])
|
||||
self.assertIsInstance(config['headers'], dict)
|
||||
self.assertEqual(2, len(config['headers']))
|
||||
self.assertEqual('default-src: \'self\';', config['headers']['Content-Security-Policy'])
|
||||
self.assertEqual('andthisismyothervalue', config['headers']['X-I-Am-A-Header'])
|
||||
|
||||
def test_parse_config_logging_none(self):
|
||||
"""
|
||||
|
|
|
@ -272,3 +272,19 @@ class TestServe(AbstractHttpdTest):
|
|||
packet = self.client_sock.get_response()
|
||||
# No charset should be in the header. Yes, this is a stupid example
|
||||
self.assertEqual('text/plain', packet.headers['Content-Type'])
|
||||
|
||||
def test_serve_static_pagelet_header(self):
|
||||
# Send a request
|
||||
self.client_sock.set_request(b'GET /just/testing/serve_pagelet_str HTTP/1.1\r\n\r\n')
|
||||
HttpHandler(self.client_sock, ('::1', 45678), self.server)
|
||||
packet = self.client_sock.get_response()
|
||||
# The statically set header must be present
|
||||
self.assertEqual('helloworld!', packet.headers['X-Static-Testing-Header'])
|
||||
|
||||
def test_serve_static_static_header(self):
|
||||
# Send a request
|
||||
self.client_sock.set_request(b'GET /testbin.txt HTTP/1.1\r\n\r\n')
|
||||
HttpHandler(self.client_sock, ('::1', 45678), self.server)
|
||||
packet = self.client_sock.get_response()
|
||||
# The statically set header must be present
|
||||
self.assertEqual('helloworld!', packet.headers['X-Static-Testing-Header'])
|
||||
|
|
Loading…
Reference in a new issue