Alternate meta header removal method.

Prior to this patch, you removed header metadata (such as
x-account-meta-name or x-container-meta-name) by sending the header
with no value. However, many tools such as curl will not send headers
with empty values.

This patch adds an alternate method for header metadata removal: Send
an x-remove-account-meta-name (x-remove-container-meta-name) header
with any value and the proxy will translate it to the original header
name with no value, indicating removal.

For safety, if you specify both x-remove-account-meta-name and
x-account-meta-name headers in the same request, the -remove header
will be ignored.

Change-Id: Ic220bec05a0e266db85fc8fa50011146ee886d9c
This commit is contained in:
gholt 2012-03-10 20:02:49 +00:00
parent 7443f08f69
commit abc6313e35
2 changed files with 40 additions and 18 deletions

View File

@ -300,11 +300,25 @@ class Controller(object):
"""Base WSGI controller class for the proxy"""
server_type = _('Base')
# Ensure these are all lowercase
pass_through_headers = []
def __init__(self, app):
self.account_name = None
self.app = app
self.trans_id = '-'
def transfer_headers(self, src_headers, dst_headers):
x_remove = 'x-remove-%s-meta-' % self.server_type.lower()
x_meta = 'x-%s-meta-' % self.server_type.lower()
dst_headers.update((k.lower().replace('-remove', '', 1), '')
for k in src_headers
if k.lower().startswith(x_remove))
dst_headers.update((k.lower(), v)
for k, v in src_headers.iteritems()
if k.lower() in self.pass_through_headers or
k.lower().startswith(x_meta))
def error_increment(self, node):
"""
Handles incrementing error counts when talking to nodes.
@ -1503,9 +1517,7 @@ class ContainerController(Controller):
'X-Account-Partition': account_partition,
'X-Account-Device': account['device'],
'Connection': 'close'}
nheaders.update(value for value in req.headers.iteritems()
if value[0].lower() in self.pass_through_headers or
value[0].lower().startswith('x-container-meta-'))
self.transfer_headers(req.headers, nheaders)
headers.append(nheaders)
if self.app.memcache:
cache_key = get_container_memcache_key(self.account_name,
@ -1530,9 +1542,7 @@ class ContainerController(Controller):
headers = {'X-Timestamp': normalize_timestamp(time.time()),
'x-trans-id': self.trans_id,
'Connection': 'close'}
headers.update(value for value in req.headers.iteritems()
if value[0].lower() in self.pass_through_headers or
value[0].lower().startswith('x-container-meta-'))
self.transfer_headers(req.headers, headers)
if self.app.memcache:
cache_key = get_container_memcache_key(self.account_name,
self.container_name)
@ -1620,8 +1630,7 @@ class AccountController(Controller):
headers = {'X-Timestamp': normalize_timestamp(time.time()),
'x-trans-id': self.trans_id,
'Connection': 'close'}
headers.update(value for value in req.headers.iteritems()
if value[0].lower().startswith('x-account-meta-'))
self.transfer_headers(req.headers, headers)
if self.app.memcache:
self.app.memcache.delete('account%s' % req.path_info.rstrip('/'))
return self.make_requests(req, self.app.account_ring,
@ -1638,8 +1647,7 @@ class AccountController(Controller):
headers = {'X-Timestamp': normalize_timestamp(time.time()),
'X-Trans-Id': self.trans_id,
'Connection': 'close'}
headers.update(value for value in req.headers.iteritems()
if value[0].lower().startswith('x-account-meta-'))
self.transfer_headers(req.headers, headers)
if self.app.memcache:
self.app.memcache.delete('account%s' % req.path_info.rstrip('/'))
resp = self.make_requests(req, self.app.account_ring,

View File

@ -3372,19 +3372,26 @@ class TestContainerController(unittest.TestCase):
def metadata_helper(self, method):
for test_header, test_value in (
('X-Container-Meta-TestHeader', 'TestValue'),
('X-Container-Meta-TestHeader', '')):
('X-Container-Meta-TestHeader', ''),
('X-Remove-Container-Meta-TestHeader', 'anything')):
test_errors = []
def test_connect(ipaddr, port, device, partition, method, path,
headers=None, query_string=None):
if path == '/a/c':
find_header = test_header
find_value = test_value
if find_header.lower().startswith('x-remove-'):
find_header = \
find_header.lower().replace('-remove', '', 1)
find_value = ''
for k, v in headers.iteritems():
if k.lower() == test_header.lower() and \
v == test_value:
if k.lower() == find_header.lower() and \
v == find_value:
break
else:
test_errors.append('%s: %s not in %s' %
(test_header, test_value, headers))
(find_header, find_value, headers))
with save_globals():
controller = \
proxy_server.ContainerController(self.app, 'a', 'c')
@ -3788,19 +3795,26 @@ class TestAccountController(unittest.TestCase):
def metadata_helper(self, method):
for test_header, test_value in (
('X-Account-Meta-TestHeader', 'TestValue'),
('X-Account-Meta-TestHeader', '')):
('X-Account-Meta-TestHeader', ''),
('X-Remove-Account-Meta-TestHeader', 'anything')):
test_errors = []
def test_connect(ipaddr, port, device, partition, method, path,
headers=None, query_string=None):
if path == '/a':
find_header = test_header
find_value = test_value
if find_header.lower().startswith('x-remove-'):
find_header = \
find_header.lower().replace('-remove', '', 1)
find_value = ''
for k, v in headers.iteritems():
if k.lower() == test_header.lower() and \
v == test_value:
if k.lower() == find_header.lower() and \
v == find_value:
break
else:
test_errors.append('%s: %s not in %s' %
(test_header, test_value, headers))
(find_header, find_value, headers))
with save_globals():
self.app.allow_account_management = True
controller = \