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 += '