feat(s3) HeadObject: honor If-Modified-Since header

This commit is contained in:
Simon-Pierre Gingras 2017-05-19 15:59:25 -07:00
parent fe51fadda5
commit 517416c4d9
3 changed files with 46 additions and 2 deletions

View File

@ -174,11 +174,17 @@ def iso_8601_datetime_without_milliseconds(datetime):
return datetime.strftime("%Y-%m-%dT%H:%M:%S") + 'Z' return datetime.strftime("%Y-%m-%dT%H:%M:%S") + 'Z'
RFC1123 = '%a, %d %b %Y %H:%M:%S GMT'
def rfc_1123_datetime(datetime): def rfc_1123_datetime(datetime):
RFC1123 = '%a, %d %b %Y %H:%M:%S GMT'
return datetime.strftime(RFC1123) return datetime.strftime(RFC1123)
def str_to_rfc_1123_datetime(str):
return datetime.datetime.strptime(str, RFC1123)
def unix_time(dt=None): def unix_time(dt=None):
dt = dt or datetime.datetime.utcnow() dt = dt or datetime.datetime.utcnow()
epoch = datetime.datetime.utcfromtimestamp(0) epoch = datetime.datetime.utcfromtimestamp(0)

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
import re import re
import six import six
from moto.core.utils import str_to_rfc_1123_datetime
from six.moves.urllib.parse import parse_qs, urlparse from six.moves.urllib.parse import parse_qs, urlparse
import xmltodict import xmltodict
@ -595,12 +596,21 @@ class ResponseObject(_TemplateEnvironmentMixin):
def _key_response_head(self, bucket_name, query, key_name, headers): def _key_response_head(self, bucket_name, query, key_name, headers):
response_headers = {} response_headers = {}
version_id = query.get('versionId', [None])[0] version_id = query.get('versionId', [None])[0]
if_modified_since = headers.get('if-modified-since', None)
if if_modified_since:
if_modified_since = str_to_rfc_1123_datetime(if_modified_since)
key = self.backend.get_key( key = self.backend.get_key(
bucket_name, key_name, version_id=version_id) bucket_name, key_name, version_id=version_id)
if key: if key:
response_headers.update(key.metadata) response_headers.update(key.metadata)
response_headers.update(key.response_dict) response_headers.update(key.response_dict)
return 200, response_headers, ""
if if_modified_since and key.last_modified < if_modified_since:
return 304, response_headers, 'Not Modified'
else:
return 200, response_headers, ""
else: else:
return 404, response_headers, "" return 404, response_headers, ""

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from six.moves.urllib.request import urlopen from six.moves.urllib.request import urlopen
from six.moves.urllib.error import HTTPError from six.moves.urllib.error import HTTPError
from functools import wraps from functools import wraps
@ -10,6 +12,7 @@ import json
import boto import boto
import boto3 import boto3
from botocore.client import ClientError from botocore.client import ClientError
import botocore.exceptions
from boto.exception import S3CreateError, S3ResponseError from boto.exception import S3CreateError, S3ResponseError
from boto.s3.connection import S3Connection from boto.s3.connection import S3Connection
from boto.s3.key import Key from boto.s3.key import Key
@ -1266,6 +1269,31 @@ def test_boto3_head_object_with_versioning():
old_head_object['ContentLength'].should.equal(len(old_content)) old_head_object['ContentLength'].should.equal(len(old_content))
@mock_s3
def test_boto3_head_object_if_modified_since():
s3 = boto3.client('s3', region_name='us-east-1')
bucket_name = "blah"
s3.create_bucket(Bucket=bucket_name)
key = 'hello.txt'
with freeze_time(datetime.datetime.now() - datetime.timedelta(hours=3)):
s3.put_object(
Bucket=bucket_name,
Key=key,
Body='test'
)
with assert_raises(botocore.exceptions.ClientError) as err:
s3.head_object(
Bucket=bucket_name,
Key=key,
IfModifiedSince=datetime.datetime.now() - datetime.timedelta(hours=2)
)
e = err.exception
e.response['Error'].should.equal({'Code': '304', 'Message': 'Not Modified'})
@mock_s3 @mock_s3
@reduced_min_part_size @reduced_min_part_size
def test_boto3_multipart_etag(): def test_boto3_multipart_etag():