diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 4435f12fb4..9129e1e0ba 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -156,3 +156,11 @@ use = egg:swift#cname_lookup use = egg:swift#staticweb # Seconds to cache container x-container-meta-web-* header values. # cache_timeout = 300 +# You can override the default log routing for this filter here: +# set log_name = staticweb +# set log_facility = LOG_LOCAL0 +# set log_level = INFO +# set access_log_name = staticweb +# set access_log_facility = LOG_LOCAL0 +# set access_log_level = INFO +# set log_headers = False diff --git a/swift/common/middleware/staticweb.py b/swift/common/middleware/staticweb.py index c0028fda45..fd3c1a4d12 100644 --- a/swift/common/middleware/staticweb.py +++ b/swift/common/middleware/staticweb.py @@ -106,7 +106,8 @@ except ImportError: import json import cgi -import urllib +import time +from urllib import unquote, quote from webob import Response, Request from webob.exc import HTTPMovedPermanently, HTTPNotFound @@ -133,6 +134,15 @@ class StaticWeb(object): self.cache_timeout = int(conf.get('cache_timeout', 300)) #: Logger for this filter. self.logger = get_logger(conf, log_route='staticweb') + access_log_conf = {} + for key in ('log_facility', 'log_name', 'log_level'): + value = conf.get('access_' + key, conf.get(key, None)) + if value: + access_log_conf[key] = value + self.access_logger = get_logger(access_log_conf, + log_route='staticweb-access') + #: Indicates whether full HTTP headers should be logged or not. + self.log_headers = conf.get('log_headers') == 'True' # Results from the last call to self._start_response. self._response_status = None self._response_headers = None @@ -158,6 +168,7 @@ class StaticWeb(object): :param env: The original request WSGI environment. :param start_response: The WSGI start_response hook. """ + self._log_response(env, self._get_status_int()) if not self._error: start_response(self._response_status, self._response_headers, self._response_exc_info) @@ -165,11 +176,10 @@ class StaticWeb(object): save_response_status = self._response_status save_response_headers = self._response_headers save_response_exc_info = self._response_exc_info - tmp_env = dict(env) - self._strip_ifs(tmp_env) + tmp_env = self._get_escalated_env(env) + tmp_env['REQUEST_METHOD'] = 'GET' tmp_env['PATH_INFO'] = '/%s/%s/%s/%s%s' % (self.version, self.account, self.container, self._get_status_int(), self._error) - tmp_env['REQUEST_METHOD'] = 'GET' resp = self.app(tmp_env, self._start_response) if self._get_status_int() // 100 == 2: start_response(save_response_status, self._response_headers, @@ -186,10 +196,20 @@ class StaticWeb(object): """ return int(self._response_status.split(' ', 1)[0]) - def _strip_ifs(self, env): - """ Strips any HTTP_IF_* keys from the env dict. """ - for key in [k for k in env.keys() if k.startswith('HTTP_IF_')]: - del env[key] + def _get_escalated_env(self, env): + """ + Returns a new fresh WSGI environment with escalated privileges to do + backend checks, listings, etc. that the remote user wouldn't be able to + accomplish directly. + """ + new_env = {'REQUEST_METHOD': 'GET', + 'HTTP_USER_AGENT': '%s StaticWeb' % env.get('HTTP_USER_AGENT')} + for name in ('eventlet.posthooks', 'HTTP_X_CF_TRANS_ID', 'REMOTE_USER', + 'SCRIPT_NAME', 'SERVER_NAME', 'SERVER_PORT', + 'SERVER_PROTOCOL', 'swift.cache'): + if name in env: + new_env[name] = env[name] + return new_env def _get_container_info(self, env, start_response): """ @@ -211,10 +231,8 @@ class StaticWeb(object): (self._index, self._error, self._listings, self._listings_css) = cached_data return - tmp_env = {'REQUEST_METHOD': 'HEAD', 'HTTP_USER_AGENT': 'StaticWeb'} - for name in ('swift.cache', 'HTTP_X_CF_TRANS_ID'): - if name in env: - tmp_env[name] = env[name] + tmp_env = self._get_escalated_env(env) + tmp_env['REQUEST_METHOD'] = 'HEAD' req = Request.blank('/%s/%s/%s' % (self.version, self.account, self.container), environ=tmp_env) resp = req.get_response(self.app) @@ -245,14 +263,13 @@ class StaticWeb(object): if self._listings not in TRUE_VALUES: resp = HTTPNotFound()(env, self._start_response) return self._error_response(resp, env, start_response) - tmp_env = dict(env) - self._strip_ifs(tmp_env) + tmp_env = self._get_escalated_env(env) tmp_env['REQUEST_METHOD'] = 'GET' tmp_env['PATH_INFO'] = \ '/%s/%s/%s' % (self.version, self.account, self.container) tmp_env['QUERY_STRING'] = 'delimiter=/&format=json' if prefix: - tmp_env['QUERY_STRING'] += '&prefix=%s' % urllib.quote(prefix) + tmp_env['QUERY_STRING'] += '&prefix=%s' % quote(prefix) resp = self.app(tmp_env, self._start_response) if self._get_status_int() // 100 != 2: return self._error_response(resp, env, start_response) @@ -271,7 +288,7 @@ class StaticWeb(object): body += ' \n' % \ (self.version, self.account, self.container, - urllib.quote(self._listings_css)) + quote(self._listings_css)) else: body += '