diff --git a/swift/proxy/server.py b/swift/proxy/server.py index e1d5824b4e..9c2ecc12ee 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -814,11 +814,17 @@ class ObjectController(Controller): else: # For objects with a reasonable number of segments, we'll serve # them with a set content-length and computed etag. - content_length = sum(o['bytes'] for o in listing) - last_modified = max(o['last_modified'] for o in listing) - last_modified = \ - datetime(*map(int, re.split('[^\d]', last_modified)[:-1])) - etag = md5('"'.join(o['hash'] for o in listing)).hexdigest() + if listing: + content_length = sum(o['bytes'] for o in listing) + last_modified = max(o['last_modified'] for o in listing) + last_modified = datetime(*map(int, re.split('[^\d]', + last_modified)[:-1])) + etag = md5( + '"'.join(o['hash'] for o in listing)).hexdigest() + else: + content_length = 0 + last_modified = resp.last_modified + etag = md5().hexdigest() headers = { 'X-Object-Manifest': resp.headers['x-object-manifest'], 'Content-Type': resp.content_type, diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 4562652b87..e5a4e40652 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -2042,6 +2042,31 @@ class TestObjectController(unittest.TestCase): self.assert_('Content-Length: 25\r' in headers) body = fd.read() self.assertEquals(body, '1234 1234 1234 1234 1234 ') + # Create an object manifest file pointing to nothing + sock = connect_tcp(('localhost', prolis.getsockname()[1])) + fd = sock.makefile() + fd.write('PUT /v1/a/segmented/empty HTTP/1.1\r\nHost: ' + 'localhost\r\nConnection: close\r\nX-Storage-Token: ' + 't\r\nContent-Length: 0\r\nX-Object-Manifest: ' + 'segmented/empty/\r\nContent-Type: text/jibberish\r\n\r\n') + fd.flush() + headers = readuntil2crlfs(fd) + exp = 'HTTP/1.1 201' + self.assertEquals(headers[:len(exp)], exp) + # Ensure retrieving the manifest file gives a zero-byte file + sock = connect_tcp(('localhost', prolis.getsockname()[1])) + fd = sock.makefile() + fd.write('GET /v1/a/segmented/empty HTTP/1.1\r\nHost: ' + 'localhost\r\nConnection: close\r\nX-Auth-Token: ' + 't\r\n\r\n') + fd.flush() + headers = readuntil2crlfs(fd) + exp = 'HTTP/1.1 200' + self.assertEquals(headers[:len(exp)], exp) + self.assert_('X-Object-Manifest: segmented/empty/' in headers) + self.assert_('Content-Type: text/jibberish' in headers) + body = fd.read() + self.assertEquals(body, '') # Check copy content type sock = connect_tcp(('localhost', prolis.getsockname()[1])) fd = sock.makefile()