FakeFooters middleware
This middleware is not intended to ever be merged on master branch. This middleware mimics behaviour that we expect to eventually integrate into the proxy app/object controller once feature/ec has merged to master. Specifically, we need to be able to add trailing headers (aka footers) to PUT requests, after the request body has been read by a downstream app, and have these footers sent on to the object server. Change-Id: Ibf2959c4c2715f477e6df94ccf0ab0681702e881
This commit is contained in:
parent
942c9bb45c
commit
25440665fa
|
@ -95,6 +95,8 @@ paste.filter_factory =
|
||||||
gatekeeper = swift.common.middleware.gatekeeper:filter_factory
|
gatekeeper = swift.common.middleware.gatekeeper:filter_factory
|
||||||
container_sync = swift.common.middleware.container_sync:filter_factory
|
container_sync = swift.common.middleware.container_sync:filter_factory
|
||||||
xprofile = swift.common.middleware.xprofile:filter_factory
|
xprofile = swift.common.middleware.xprofile:filter_factory
|
||||||
|
fake_footers = swift.common.middleware.fake_footers:filter_factory
|
||||||
|
test_fake_footers = swift.common.middleware.test_fake_footers:filter_factory
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Copyright (c) 2010-2012 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
"""
|
||||||
|
This middleware is not intended to ever be merged on master branch.
|
||||||
|
|
||||||
|
This middleware mimics behaviour that we expect to eventually integrate into
|
||||||
|
the proxy app/object controller once feature/ec has merged to master.
|
||||||
|
Specifically, we need to be able to add trailing headers (aka footers) to PUT
|
||||||
|
requests, after the request body has been read by a downstream app, and have
|
||||||
|
these footers sent on to the object server.
|
||||||
|
|
||||||
|
As a workaround, while we wait for feature/ec to merge to master,this
|
||||||
|
middleware provides a callback mechanism for other (upstream) middlewares to
|
||||||
|
provide footers. FakeFooters will store these footers in memcache (since we
|
||||||
|
have no way currently to forward them on to the object server) using a key
|
||||||
|
based on the request path.
|
||||||
|
|
||||||
|
Then, when handling any response, FakeFooters will look in memcache for any
|
||||||
|
footers stored under the current request path, and append those footers to the
|
||||||
|
response headers.
|
||||||
|
|
||||||
|
Middleware wishing to send footers to FakeFooters should add a reference to a
|
||||||
|
callback function in the request environ under key 'swift.update.footers'.
|
||||||
|
Fake Footers will call this function with a single argument - a dict - after it
|
||||||
|
completes reading the request body. The middleware callback should add any
|
||||||
|
footers to this dict before returning.
|
||||||
|
|
||||||
|
To use FakeFooters you will need to add it to the proxy pipeline in
|
||||||
|
proxy-server.conf and also add a filter section::
|
||||||
|
|
||||||
|
pipeline = catch_errors proxy-logging cache tempauth fake_footers \
|
||||||
|
proxy-logging proxy-server
|
||||||
|
|
||||||
|
[filter:fake_footers]
|
||||||
|
use = egg:swift#fake_footers
|
||||||
|
|
||||||
|
An example of a middleware taking advantage of FakeFooters is given below::
|
||||||
|
|
||||||
|
from swift.common.utils import get_logger
|
||||||
|
from swift.common.wsgi import WSGIContext
|
||||||
|
|
||||||
|
|
||||||
|
FAKE_KEY = 'X-Object-Meta-FakeFooter'
|
||||||
|
|
||||||
|
|
||||||
|
class TestFakeFootersContext(WSGIContext):
|
||||||
|
def __init__(self, app, logger):
|
||||||
|
super(TestFakeFootersContext, self).__init__(app)
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
|
def footers_callback(self, footers):
|
||||||
|
footers.update(
|
||||||
|
{FAKE_KEY: self.env.get('swift.trans_id', 'bogus_tran_id')})
|
||||||
|
|
||||||
|
def handle_request(self, env, start_response):
|
||||||
|
self.env = env
|
||||||
|
env['swift.update.footers'] = self.footers_callback
|
||||||
|
resp = self._app_call(env)
|
||||||
|
for item in self._response_headers:
|
||||||
|
if item[0].lower() == FAKE_KEY.lower():
|
||||||
|
self.logger.info('TestFakeFooters resp found %s=%s' % item)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.logger.info('TestFakeFooters resp MISSING test footer')
|
||||||
|
start_response(self._response_status, self._response_headers,
|
||||||
|
self._response_exc_info)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
class TestFakeFootersMiddleware(object):
|
||||||
|
def __init__(self, app, conf):
|
||||||
|
self.app = app
|
||||||
|
self.logger = get_logger(conf, log_route='fake-footers')
|
||||||
|
|
||||||
|
def __call__(self, env, start_response):
|
||||||
|
context = TestFakeFootersContext(self.app, self.logger)
|
||||||
|
return context.handle_request(env, start_response)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_factory(global_conf, **local_conf):
|
||||||
|
conf = global_conf.copy()
|
||||||
|
conf.update(local_conf)
|
||||||
|
|
||||||
|
def except_filter(app):
|
||||||
|
return TestFakeFootersMiddleware(app, conf)
|
||||||
|
return except_filter
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from swift.common.utils import get_logger, cache_from_env
|
||||||
|
from swift.common.wsgi import WSGIContext
|
||||||
|
|
||||||
|
|
||||||
|
MEMCACHE_TIMEOUT = 3600
|
||||||
|
|
||||||
|
|
||||||
|
class FakeFootersContext(WSGIContext):
|
||||||
|
|
||||||
|
def __init__(self, app, logger):
|
||||||
|
super(FakeFootersContext, self).__init__(app)
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
|
def retrieve_footers(self, env):
|
||||||
|
key = 'swift.footer.%s' % env['PATH_INFO']
|
||||||
|
footers = self.memcache_client.get(key) or {}
|
||||||
|
self.logger.info('retrieve_footers %s %s' % (key, footers))
|
||||||
|
return footers
|
||||||
|
|
||||||
|
class ReaderWrapper(object):
|
||||||
|
def __init__(self, env, callback, context):
|
||||||
|
self.env = env
|
||||||
|
self.footer_callback = callback
|
||||||
|
self.original_read = env['wsgi.input'].read
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def store_footers(self, footers):
|
||||||
|
key = 'swift.footer.%s' % self.env['PATH_INFO']
|
||||||
|
self.context.memcache_client.set(key, footers, MEMCACHE_TIMEOUT)
|
||||||
|
self.context.logger.info('store_footers %s %s' % (key, footers))
|
||||||
|
|
||||||
|
def read(self, size):
|
||||||
|
chunk = self.original_read(size)
|
||||||
|
if chunk == '':
|
||||||
|
# end of stream, call back for footers
|
||||||
|
footers = {}
|
||||||
|
self.footer_callback(footers)
|
||||||
|
self.store_footers(footers)
|
||||||
|
return chunk
|
||||||
|
|
||||||
|
def handle_request(self, env, start_response):
|
||||||
|
self.memcache_client = cache_from_env(env)
|
||||||
|
if not self.memcache_client:
|
||||||
|
raise Exception('Memcache required for FakeFooters')
|
||||||
|
callback = env.get('swift.update.footers')
|
||||||
|
if callback:
|
||||||
|
env['wsgi.input'] = self.ReaderWrapper(env, callback, self)
|
||||||
|
|
||||||
|
resp = self._app_call(env)
|
||||||
|
footers = self.retrieve_footers(env)
|
||||||
|
for item in footers.items():
|
||||||
|
self._response_headers.append(item)
|
||||||
|
start_response(self._response_status, self._response_headers,
|
||||||
|
self._response_exc_info)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
class FakeFootersMiddleware(object):
|
||||||
|
"""
|
||||||
|
Middleware that fakes footer handling by storing footer values in memcache.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app, conf):
|
||||||
|
self.app = app
|
||||||
|
self.logger = get_logger(conf, log_route='fake-footers')
|
||||||
|
|
||||||
|
def __call__(self, env, start_response):
|
||||||
|
context = FakeFootersContext(self.app,
|
||||||
|
self.logger)
|
||||||
|
return context.handle_request(env, start_response)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_factory(global_conf, **local_conf):
|
||||||
|
conf = global_conf.copy()
|
||||||
|
conf.update(local_conf)
|
||||||
|
|
||||||
|
def except_filter(app):
|
||||||
|
return FakeFootersMiddleware(app, conf)
|
||||||
|
return except_filter
|
Loading…
Reference in New Issue