Hi,
<rjol...@gmail.com> wrote on 2017-Dec-15 at 06:02 PM:
Any such headers need to be configurable, but we want to avoid configuration
option bloat. What we might be able to do is add an [http-headers]
configuration section to trac.ini. We could specify some common configurations
to the documentation.
Example configuration:
[http-headers]
X-Frame-Options = DENY
X-XSS-Protection = 1; mode=block
The option names as read by ConfigParser are case-insensitive, but I think that
may be okay as it looks like the HTTP headers are also case-insensitive.
I've done a PoC patch against 1.2-stable, but I'll want to hear what Jun has to
say before suggesting this is the right solution, since he has much more
experience with web server internals and configuration.
Good feature.
My suggestions:
1. Whether http header name is valid like [trac] xsendfile_header option.
2. Whether http header value is valid (the value cannot contain control
characters except TAB and SPACE).
3. Ignore some headers, e.g. Content-Type, Content-Length, Location, ETag,
Pragma, Cache-Control, Expires
4. I think we should send configured headers for all send_* methods included
send_error().
See attached patch.
I thought it might be good to allow to overwrite headers like "set" in
mod_headers module but it would not be needed in use-case of Trac.
--
Jun Omae <jun6...@gmail.com> (大前 潤)
--
You received this message because you are subscribed to the Google Groups "Trac
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to trac-dev+unsubscr...@googlegroups.com.
To post to this group, send email to trac-dev@googlegroups.com.
Visit this group at https://groups.google.com/group/trac-dev.
For more options, visit https://groups.google.com/d/optout.
diff --git a/trac/web/api.py b/trac/web/api.py
index b2e76f948..062c46550 100644
--- a/trac/web/api.py
+++ b/trac/web/api.py
@@ -605,6 +605,7 @@ class Request(object):
"""Must be called after all headers have been sent and before the
actual content is written.
"""
+ self._send_configurable_headers()
self._send_cookie_headers()
self._write = self._start_response(self._status, self._outheaders)
@@ -725,6 +726,7 @@ class Request(object):
self.send_header('Expires', 'Fri, 01 Jan 1999 00:00:00 GMT')
self.send_header('Content-Type', content_type + ';charset=utf-8')
self.send_header('Content-Length', len(data))
+ self._send_configurable_headers()
self._send_cookie_headers()
self._write = self._start_response(self._status, self._outheaders,
@@ -947,6 +949,10 @@ class Request(object):
return urlparse.urlunparse((self.scheme, host, self.base_path, None,
None, None))
+ def _send_configurable_headers(self):
+ for name, val in getattr(self, 'configurable_headers', []):
+ self.send_header(name, val)
+
def _send_cookie_headers(self):
for name in self.outcookie.keys():
path = self.outcookie[name].get('path')
diff --git a/trac/web/main.py b/trac/web/main.py
index 56b493d38..4008d7e77 100644
--- a/trac/web/main.py
+++ b/trac/web/main.py
@@ -38,8 +38,9 @@ from genshi.output import DocType
from genshi.template import TemplateLoader
from trac import __version__ as TRAC_VERSION
-from trac.config import BoolOption, ChoiceOption, ConfigurationError, \
- ExtensionOption, Option, OrderedExtensionsOption
+from trac.config import (
+ BoolOption, ChoiceOption, ConfigSection, ConfigurationError,
+ ExtensionOption, Option, OrderedExtensionsOption)
from trac.core import *
from trac.env import open_environment
from trac.loader import get_plugin_info, match_plugins_to_frames
@@ -164,6 +165,10 @@ class RequestDispatcher(Component):
"""The header to use if `use_xsendfile` is enabled. If Nginx is used,
set `X-Accel-Redirect`. (''since 1.0.6'')""")
+ configurable_headers = ConfigSection('http-headers', """
+ Headers to be added to the HTTP request. (''since 1.2.3'')
+ """)
+
# Public API
def authenticate(self, req):
@@ -317,6 +322,7 @@ class RequestDispatcher(Component):
'tz': self._get_timezone,
'use_xsendfile': self._get_use_xsendfile,
'xsendfile_header': self._get_xsendfile_header,
+ 'configurable_headers': self._get_configurable_headers,
})
@lazy
@@ -412,12 +418,12 @@ class RequestDispatcher(Component):
return self.use_xsendfile
# RFC7230 3.2 Header Fields
- _xsendfile_header_re = re.compile(r"[-0-9A-Za-z!#$%&'*+.^_`|~]+\Z")
+ _valid_header_re = re.compile(r"[-0-9A-Za-z!#$%&'*+.^_`|~]+\Z")
_warn_xsendfile_header = False
def _get_xsendfile_header(self, req):
header = self.xsendfile_header.strip()
- if self._xsendfile_header_re.match(header):
+ if self._valid_header_re.match(header):
return to_utf8(header)
else:
if not self._warn_xsendfile_header:
@@ -426,6 +432,29 @@ class RequestDispatcher(Component):
header)
return None
+ _control_codes_re = re.compile(r'[\x00-\x08\x0a-\x1f\x7f]')
+ _reserverd_headers = set(['content-type', 'content-length', 'location',
+ 'etag', 'pragma', 'cache-control', 'expires'])
+
+ @lazy
+ def _configurable_headers(self):
+ headers = []
+ invalids = []
+ for name, val in self.configurable_headers.options():
+ if name and name.lower() not in self._reserverd_headers and \
+ self._valid_header_re.match(name) and \
+ not self._control_codes_re.search(val):
+ headers.append((name, val))
+ else:
+ invalids.append((name, val))
+ if invalids:
+ self.log.warning('[http-headers] invalid headers are ignored: %r',
+ invalids)
+ return tuple(headers)
+
+ def _get_configurable_headers(self, req):
+ return iter(self._configurable_headers)
+
def _pre_process_request(self, req, chosen_handler):
for filter_ in self.filters:
chosen_handler = filter_.pre_process_request(req, chosen_handler)