Merge "Remove double GET on range requests."

This commit is contained in:
Jenkins 2012-10-29 05:11:43 +00:00 committed by Gerrit Code Review
commit 1f232e19cf
5 changed files with 29 additions and 26 deletions

View File

@ -70,7 +70,7 @@ RESPONSE_REASONS = {
'server.'), 'server.'),
415: ('Unsupported Media Type', 'The request media type is not ' 415: ('Unsupported Media Type', 'The request media type is not '
'supported by this server.'), 'supported by this server.'),
416: ('Request Range Not Satisfiable', 'The Range requested is not ' 416: ('Requested Range Not Satisfiable', 'The Range requested is not '
'available.'), 'available.'),
417: ('Expectation Failed', 'Expectation failed.'), 417: ('Expectation Failed', 'Expectation failed.'),
422: ('Unprocessable Entity', 'Unable to process the contained ' 422: ('Unprocessable Entity', 'Unable to process the contained '
@ -271,8 +271,6 @@ def _resp_status_property():
When set to a str, it splits status_int and title apart. When set to a str, it splits status_int and title apart.
When set to an integer, retrieves the correct title for that When set to an integer, retrieves the correct title for that
response code from the RESPONSE_REASONS dict. response code from the RESPONSE_REASONS dict.
:param header: name of the header, e.g. "Content-Length"
""" """
def getter(self): def getter(self):
return '%s %s' % (self.status_int, self.title) return '%s %s' % (self.status_int, self.title)
@ -383,8 +381,8 @@ def _resp_charset_property():
def _resp_app_iter_property(): def _resp_app_iter_property():
""" """
Set and retrieve Response.app_iter Set and retrieve Response.app_iter
Mostly a pass-through to Response._app_iter, it's a property so it can zero Mostly a pass-through to Response._app_iter; it's a property so it can zero
out an exsisting content-length on assignment. out an existing content-length on assignment.
""" """
def getter(self): def getter(self):
return self._app_iter return self._app_iter

View File

@ -580,6 +580,13 @@ class Controller(object):
except Exception: except Exception:
pass pass
def is_good_source(self, src):
"""
Indicates whether or not the request made to the backend found
what it was looking for.
"""
return is_success(src.status) or is_redirection(src.status)
def GETorHEAD_base(self, req, server_type, partition, nodes, path, def GETorHEAD_base(self, req, server_type, partition, nodes, path,
attempts): attempts):
""" """
@ -623,8 +630,7 @@ class Controller(object):
node, server_type, _('Trying to %(method)s %(path)s') % node, server_type, _('Trying to %(method)s %(path)s') %
{'method': req.method, 'path': req.path}) {'method': req.method, 'path': req.path})
continue continue
if is_success(possible_source.status) or \ if self.is_good_source(possible_source):
is_redirection(possible_source.status):
# 404 if we know we don't have a synced copy # 404 if we know we don't have a synced copy
if not float(possible_source.getheader('X-PUT-Timestamp', 1)): if not float(possible_source.getheader('X-PUT-Timestamp', 1)):
statuses.append(HTTP_NOT_FOUND) statuses.append(HTTP_NOT_FOUND)

View File

@ -276,6 +276,17 @@ class ObjectController(Controller):
for obj in sublisting: for obj in sublisting:
yield obj yield obj
def is_good_source(self, src):
"""
Indicates whether or not the request made to the backend found
what it was looking for.
In the case of an object, a 416 indicates that we found a
backend with the object.
"""
return src.status == 416 or \
super(ObjectController, self).is_good_source(src)
def GETorHEAD(self, req): def GETorHEAD(self, req):
"""Handle HTTP GET or HEAD requests.""" """Handle HTTP GET or HEAD requests."""
container_info = self.container_info(self.account_name, container_info = self.container_info(self.account_name,
@ -285,28 +296,13 @@ class ObjectController(Controller):
aresp = req.environ['swift.authorize'](req) aresp = req.environ['swift.authorize'](req)
if aresp: if aresp:
return aresp return aresp
partition, nodes = self.app.object_ring.get_nodes( partition, nodes = self.app.object_ring.get_nodes(
self.account_name, self.container_name, self.object_name) self.account_name, self.container_name, self.object_name)
shuffle(nodes) shuffle(nodes)
resp = self.GETorHEAD_base(req, _('Object'), partition, resp = self.GETorHEAD_base(req, _('Object'), partition,
self.iter_nodes(partition, nodes, self.app.object_ring), self.iter_nodes(partition, nodes, self.app.object_ring),
req.path_info, len(nodes)) req.path_info, len(nodes))
# Whether we get a 416 Requested Range Not Satisfiable or not,
# we should request a manifest because size of manifest file
# can be not 0. After checking a manifest, redo the range request
# on the whole object.
if req.range:
req_range = req.range
req.range = None
resp2 = self.GETorHEAD_base(req, _('Object'), partition,
self.iter_nodes(partition,
nodes,
self.app.object_ring),
req.path_info, len(nodes))
if 'x-object-manifest' not in resp2.headers:
return resp
resp = resp2
req.range = str(req_range)
if 'x-object-manifest' in resp.headers: if 'x-object-manifest' in resp.headers:
lcontainer, lprefix = \ lcontainer, lprefix = \
@ -367,6 +363,10 @@ class ObjectController(Controller):
resp.last_modified = last_modified resp.last_modified = last_modified
resp.etag = etag resp.etag = etag
resp.headers['accept-ranges'] = 'bytes' resp.headers['accept-ranges'] = 'bytes'
# In case of a manifest file of nonzero length, the
# backend may have sent back a Content-Range header for
# the manifest. It's wrong for the client, though.
resp.content_range = None
return resp return resp

View File

@ -419,14 +419,14 @@ class TestResponse(unittest.TestCase):
body = ''.join(resp([], start_response)) body = ''.join(resp([], start_response))
self.assertEquals(body, '') self.assertEquals(body, '')
self.assertEquals(resp.content_length, 0) self.assertEquals(resp.content_length, 0)
self.assertEquals(resp.status, '416 Request Range Not Satisfiable') self.assertEquals(resp.status, '416 Requested Range Not Satisfiable')
resp = swift.common.swob.Response( resp = swift.common.swob.Response(
body='1234567890', request=req, body='1234567890', request=req,
conditional_response=True) conditional_response=True)
body = ''.join(resp([], start_response)) body = ''.join(resp([], start_response))
self.assertEquals(body, '') self.assertEquals(body, '')
self.assertEquals(resp.status, '416 Request Range Not Satisfiable') self.assertEquals(resp.status, '416 Requested Range Not Satisfiable')
# Syntactically-invalid Range headers "MUST" be ignored # Syntactically-invalid Range headers "MUST" be ignored
req = swift.common.swob.Request.blank( req = swift.common.swob.Request.blank(

View File

@ -1495,7 +1495,6 @@ class TestObjectController(unittest.TestCase):
'container', 'object') 'container', 'object')
self.assert_status_map(controller.HEAD, (200, 200, 503, 200, 200), self.assert_status_map(controller.HEAD, (200, 200, 503, 200, 200),
200) 200)
print controller.app.object_ring.devs
self.assertEquals(controller.app.object_ring.devs[0]['errors'], 2) self.assertEquals(controller.app.object_ring.devs[0]['errors'], 2)
self.assert_('last_error' in controller.app.object_ring.devs[0]) self.assert_('last_error' in controller.app.object_ring.devs[0])
for _junk in xrange(self.app.error_suppression_limit): for _junk in xrange(self.app.error_suppression_limit):