Merge pull request #960 from spg/feat-s3-if-modified-since

feat(s3) HeadObject: honor If-Modified-Since header
This commit is contained in:
Jack Danger 2017-06-01 09:10:20 -07:00 committed by GitHub
commit 6163363c15
3 changed files with 46 additions and 3 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'
def rfc_1123_datetime(datetime):
RFC1123 = '%a, %d %b %Y %H:%M:%S GMT' RFC1123 = '%a, %d %b %Y %H:%M:%S GMT'
def rfc_1123_datetime(datetime):
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
@ -483,7 +484,7 @@ class ResponseObject(_TemplateEnvironmentMixin):
elif method == 'PUT': elif method == 'PUT':
return self._key_response_put(request, body, bucket_name, query, key_name, headers) return self._key_response_put(request, body, bucket_name, query, key_name, headers)
elif method == 'HEAD': elif method == 'HEAD':
return self._key_response_head(bucket_name, query, key_name, headers) return self._key_response_head(bucket_name, query, key_name, headers=request.headers)
elif method == 'DELETE': elif method == 'DELETE':
return self._key_response_delete(bucket_name, query, key_name, headers) return self._key_response_delete(bucket_name, query, key_name, headers)
elif method == 'POST': elif method == 'POST':
@ -597,11 +598,20 @@ 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)
if if_modified_since and key.last_modified < if_modified_since:
return 304, response_headers, 'Not Modified'
else:
return 200, response_headers, "" 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
@ -1267,6 +1270,30 @@ 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'
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.utcnow() + datetime.timedelta(hours=1)
)
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():