Merge "Last-Modified header support on HEAD/GET container"
This commit is contained in:
@@ -17,6 +17,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
import math
|
||||||
from swift import gettext_ as _
|
from swift import gettext_ as _
|
||||||
from xml.etree.cElementTree import Element, SubElement, tostring
|
from xml.etree.cElementTree import Element, SubElement, tostring
|
||||||
|
|
||||||
@@ -433,7 +434,9 @@ class ContainerController(BaseStorageServer):
|
|||||||
if value != '' and (key.lower() in self.save_headers or
|
if value != '' and (key.lower() in self.save_headers or
|
||||||
is_sys_or_user_meta('container', key)))
|
is_sys_or_user_meta('container', key)))
|
||||||
headers['Content-Type'] = out_content_type
|
headers['Content-Type'] = out_content_type
|
||||||
return HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
resp = HTTPNoContent(request=req, headers=headers, charset='utf-8')
|
||||||
|
resp.last_modified = math.ceil(float(headers['X-PUT-Timestamp']))
|
||||||
|
return resp
|
||||||
|
|
||||||
def update_data_record(self, record):
|
def update_data_record(self, record):
|
||||||
"""
|
"""
|
||||||
@@ -530,6 +533,7 @@ class ContainerController(BaseStorageServer):
|
|||||||
if not container_list:
|
if not container_list:
|
||||||
return HTTPNoContent(request=req, headers=resp_headers)
|
return HTTPNoContent(request=req, headers=resp_headers)
|
||||||
ret.body = '\n'.join(rec[0] for rec in container_list) + '\n'
|
ret.body = '\n'.join(rec[0] for rec in container_list) + '\n'
|
||||||
|
ret.last_modified = math.ceil(float(resp_headers['X-PUT-Timestamp']))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@public
|
@public
|
||||||
|
@@ -625,7 +625,8 @@ class Container(Base):
|
|||||||
|
|
||||||
if self.conn.response.status == 204:
|
if self.conn.response.status == 204:
|
||||||
required_fields = [['bytes_used', 'x-container-bytes-used'],
|
required_fields = [['bytes_used', 'x-container-bytes-used'],
|
||||||
['object_count', 'x-container-object-count']]
|
['object_count', 'x-container-object-count'],
|
||||||
|
['last_modified', 'last-modified']]
|
||||||
optional_fields = [
|
optional_fields = [
|
||||||
['versions', 'x-versions-location'],
|
['versions', 'x-versions-location'],
|
||||||
['tempurl_key', 'x-container-meta-temp-url-key'],
|
['tempurl_key', 'x-container-meta-temp-url-key'],
|
||||||
|
@@ -851,6 +851,45 @@ class TestContainer(Base):
|
|||||||
file_item = cont.file(Utils.create_name())
|
file_item = cont.file(Utils.create_name())
|
||||||
file_item.write_random()
|
file_item.write_random()
|
||||||
|
|
||||||
|
def testContainerLastModified(self):
|
||||||
|
container = self.env.account.container(Utils.create_name())
|
||||||
|
self.assertTrue(container.create())
|
||||||
|
info = container.info()
|
||||||
|
t0 = info['last_modified']
|
||||||
|
# last modified header is in date format which supports in second
|
||||||
|
# so we need to wait to increment a sec in the header.
|
||||||
|
eventlet.sleep(1)
|
||||||
|
|
||||||
|
# POST container change last modified timestamp
|
||||||
|
self.assertTrue(
|
||||||
|
container.update_metadata({'x-container-meta-japan': 'mitaka'}))
|
||||||
|
info = container.info()
|
||||||
|
t1 = info['last_modified']
|
||||||
|
self.assertNotEqual(t0, t1)
|
||||||
|
eventlet.sleep(1)
|
||||||
|
|
||||||
|
# PUT container (overwrite) also change last modified
|
||||||
|
self.assertTrue(container.create())
|
||||||
|
info = container.info()
|
||||||
|
t2 = info['last_modified']
|
||||||
|
self.assertNotEqual(t1, t2)
|
||||||
|
eventlet.sleep(1)
|
||||||
|
|
||||||
|
# PUT object doesn't change container last modified timestamp
|
||||||
|
obj = container.file(Utils.create_name())
|
||||||
|
self.assertTrue(
|
||||||
|
obj.write("aaaaa", hdrs={'Content-Type': 'text/plain'}))
|
||||||
|
info = container.info()
|
||||||
|
t3 = info['last_modified']
|
||||||
|
self.assertEqual(t2, t3)
|
||||||
|
|
||||||
|
# POST object also doesn't change container last modified timestamp
|
||||||
|
self.assertTrue(
|
||||||
|
obj.sync_metadata({'us': 'austin'}))
|
||||||
|
info = container.info()
|
||||||
|
t4 = info['last_modified']
|
||||||
|
self.assertEqual(t2, t4)
|
||||||
|
|
||||||
|
|
||||||
class TestContainerUTF8(Base2, TestContainer):
|
class TestContainerUTF8(Base2, TestContainer):
|
||||||
set_up = False
|
set_up = False
|
||||||
|
@@ -180,12 +180,7 @@ class TestContainerController(unittest.TestCase):
|
|||||||
self.assertEqual(response.headers.get('x-container-write'),
|
self.assertEqual(response.headers.get('x-container-write'),
|
||||||
'account:user')
|
'account:user')
|
||||||
|
|
||||||
def test_HEAD(self):
|
def _test_head(self, start, ts):
|
||||||
start = int(time.time())
|
|
||||||
ts = (Timestamp(t).internal for t in itertools.count(start))
|
|
||||||
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
|
|
||||||
'x-timestamp': next(ts)})
|
|
||||||
req.get_response(self.controller)
|
|
||||||
req = Request.blank('/sda1/p/a/c', method='HEAD')
|
req = Request.blank('/sda1/p/a/c', method='HEAD')
|
||||||
response = req.get_response(self.controller)
|
response = req.get_response(self.controller)
|
||||||
self.assertEqual(response.status_int, 204)
|
self.assertEqual(response.status_int, 204)
|
||||||
@@ -213,6 +208,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
self.assertTrue(created_at_header >= start)
|
self.assertTrue(created_at_header >= start)
|
||||||
self.assertEqual(response.headers['x-put-timestamp'],
|
self.assertEqual(response.headers['x-put-timestamp'],
|
||||||
Timestamp(start).normal)
|
Timestamp(start).normal)
|
||||||
|
self.assertEqual(
|
||||||
|
response.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
||||||
|
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(start)))
|
||||||
|
|
||||||
# backend headers
|
# backend headers
|
||||||
self.assertEqual(int(response.headers
|
self.assertEqual(int(response.headers
|
||||||
@@ -227,6 +225,22 @@ class TestContainerController(unittest.TestCase):
|
|||||||
self.assertEqual(response.headers['x-backend-status-changed-at'],
|
self.assertEqual(response.headers['x-backend-status-changed-at'],
|
||||||
Timestamp(start).internal)
|
Timestamp(start).internal)
|
||||||
|
|
||||||
|
def test_HEAD(self):
|
||||||
|
start = int(time.time())
|
||||||
|
ts = (Timestamp(t).internal for t in itertools.count(start))
|
||||||
|
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
|
||||||
|
'x-timestamp': next(ts)})
|
||||||
|
req.get_response(self.controller)
|
||||||
|
self._test_head(Timestamp(start), ts)
|
||||||
|
|
||||||
|
def test_HEAD_timestamp_with_offset(self):
|
||||||
|
start = int(time.time())
|
||||||
|
ts = (Timestamp(t, offset=1).internal for t in itertools.count(start))
|
||||||
|
req = Request.blank('/sda1/p/a/c', method='PUT', headers={
|
||||||
|
'x-timestamp': next(ts)})
|
||||||
|
req.get_response(self.controller)
|
||||||
|
self._test_head(Timestamp(start, offset=1), ts)
|
||||||
|
|
||||||
def test_HEAD_not_found(self):
|
def test_HEAD_not_found(self):
|
||||||
req = Request.blank('/sda1/p/a/c', method='HEAD')
|
req = Request.blank('/sda1/p/a/c', method='HEAD')
|
||||||
resp = req.get_response(self.controller)
|
resp = req.get_response(self.controller)
|
||||||
@@ -241,6 +255,8 @@ class TestContainerController(unittest.TestCase):
|
|||||||
Timestamp(0).internal)
|
Timestamp(0).internal)
|
||||||
self.assertEqual(resp.headers['x-backend-delete-timestamp'],
|
self.assertEqual(resp.headers['x-backend-delete-timestamp'],
|
||||||
Timestamp(0).internal)
|
Timestamp(0).internal)
|
||||||
|
self.assertIsNone(resp.last_modified)
|
||||||
|
|
||||||
for header in ('x-container-object-count', 'x-container-bytes-used',
|
for header in ('x-container-object-count', 'x-container-bytes-used',
|
||||||
'x-timestamp', 'x-put-timestamp'):
|
'x-timestamp', 'x-put-timestamp'):
|
||||||
self.assertEqual(resp.headers[header], None)
|
self.assertEqual(resp.headers[header], None)
|
||||||
@@ -264,6 +280,7 @@ class TestContainerController(unittest.TestCase):
|
|||||||
req = Request.blank('/sda1/p/a/c', method=method)
|
req = Request.blank('/sda1/p/a/c', method=method)
|
||||||
resp = req.get_response(self.controller)
|
resp = req.get_response(self.controller)
|
||||||
self.assertEqual(resp.status_int, 404)
|
self.assertEqual(resp.status_int, 404)
|
||||||
|
self.assertIsNone(resp.last_modified)
|
||||||
# backend headers
|
# backend headers
|
||||||
self.assertEqual(int(resp.headers[
|
self.assertEqual(int(resp.headers[
|
||||||
'X-Backend-Storage-Policy-Index']),
|
'X-Backend-Storage-Policy-Index']),
|
||||||
@@ -2021,6 +2038,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
environ={'REQUEST_METHOD': 'GET'})
|
environ={'REQUEST_METHOD': 'GET'})
|
||||||
resp = req.get_response(self.controller)
|
resp = req.get_response(self.controller)
|
||||||
self.assertEqual(resp.content_type, 'application/json')
|
self.assertEqual(resp.content_type, 'application/json')
|
||||||
|
self.assertEqual(
|
||||||
|
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
||||||
|
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
|
||||||
self.assertEqual(json.loads(resp.body), json_body)
|
self.assertEqual(json.loads(resp.body), json_body)
|
||||||
self.assertEqual(resp.charset, 'utf-8')
|
self.assertEqual(resp.charset, 'utf-8')
|
||||||
|
|
||||||
@@ -2082,6 +2102,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
environ={'REQUEST_METHOD': 'GET'})
|
environ={'REQUEST_METHOD': 'GET'})
|
||||||
resp = req.get_response(self.controller)
|
resp = req.get_response(self.controller)
|
||||||
self.assertEqual(resp.content_type, 'text/plain')
|
self.assertEqual(resp.content_type, 'text/plain')
|
||||||
|
self.assertEqual(
|
||||||
|
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
||||||
|
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
|
||||||
self.assertEqual(resp.body, plain_body)
|
self.assertEqual(resp.body, plain_body)
|
||||||
self.assertEqual(resp.charset, 'utf-8')
|
self.assertEqual(resp.charset, 'utf-8')
|
||||||
|
|
||||||
@@ -2212,6 +2235,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
environ={'REQUEST_METHOD': 'GET'})
|
environ={'REQUEST_METHOD': 'GET'})
|
||||||
resp = req.get_response(self.controller)
|
resp = req.get_response(self.controller)
|
||||||
self.assertEqual(resp.content_type, 'application/xml')
|
self.assertEqual(resp.content_type, 'application/xml')
|
||||||
|
self.assertEqual(
|
||||||
|
resp.last_modified.strftime("%a, %d %b %Y %H:%M:%S GMT"),
|
||||||
|
time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(0)))
|
||||||
self.assertEqual(resp.body, xml_body)
|
self.assertEqual(resp.body, xml_body)
|
||||||
self.assertEqual(resp.charset, 'utf-8')
|
self.assertEqual(resp.charset, 'utf-8')
|
||||||
|
|
||||||
|
@@ -6229,6 +6229,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
if expected < 400:
|
if expected < 400:
|
||||||
self.assertIn('x-works', res.headers)
|
self.assertIn('x-works', res.headers)
|
||||||
self.assertEqual(res.headers['x-works'], 'yes')
|
self.assertEqual(res.headers['x-works'], 'yes')
|
||||||
|
if expected < 300:
|
||||||
|
self.assertIn('last-modified', res.headers)
|
||||||
|
self.assertEqual(res.headers['last-modified'], '1')
|
||||||
if c_expected:
|
if c_expected:
|
||||||
self.assertIn('container/a/c', infocache)
|
self.assertIn('container/a/c', infocache)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -6254,6 +6257,9 @@ class TestContainerController(unittest.TestCase):
|
|||||||
if expected < 400:
|
if expected < 400:
|
||||||
self.assertTrue('x-works' in res.headers)
|
self.assertTrue('x-works' in res.headers)
|
||||||
self.assertEqual(res.headers['x-works'], 'yes')
|
self.assertEqual(res.headers['x-works'], 'yes')
|
||||||
|
if expected < 300:
|
||||||
|
self.assertIn('last-modified', res.headers)
|
||||||
|
self.assertEqual(res.headers['last-modified'], '1')
|
||||||
if c_expected:
|
if c_expected:
|
||||||
self.assertIn('container/a/c', infocache)
|
self.assertIn('container/a/c', infocache)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
Reference in New Issue
Block a user