summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Merritt <sam@swiftstack.com>2014-03-03 11:25:43 -0800
committerSamuel Merritt <sam@swiftstack.com>2014-03-14 17:55:42 -0700
commita69789fa0650f2975508b6f8f572f8bc8ffdb078 (patch)
treec1c2dd56677befa9453e4c23f3bb1dd55e98d6e5
parent6c5b0b2b70a98fc1061d514c84c902d237efd35a (diff)
downloadswift-a69789fa0650f2975508b6f8f572f8bc8ffdb078.tar.gz
Allow pre-1970 dates in If-[Un]Modified-Since
If I want to fetch an object only if it is newer than the first moon landing, I send a GET request with header: If-Modified-Since: Sun, 20 Jul 1969 20:18:00 UTC Since that date is older than Swift, I expect a 2xx response. However, I get a 412, which isn't even a valid thing to do for If-Modified-Since; it should either be 2xx or 304. This is because of two problems: (a) Swift treats pre-1970 dates as invalid, and (b) Swift returns 412 when a date is invalid instead of ignoring it. This commit makes it so any time between datetime.datetime.min and datetime.datetime.max is an acceptable value for If-Modified-Since and If-Unmodified-Since. Dates outside that date range are treated as invalid headers and thus are ignored, as RFC 2616 section 14.28 requires ("If the specified date is invalid, the header is ignored"). This only works for dates that the Python standard library can parse, which on my machine is 01 Jan 1 to 31 Dec 9999. Eliminating those restrictions would require implementing our own date parsing and comparison, and that's almost certainly not worth it. Change-Id: I4cb4903c4e5e3b6b3c9506c2cabbfbda62e82f35
-rw-r--r--swift/common/swob.py5
-rw-r--r--swift/obj/server.py15
-rw-r--r--test/unit/common/test_swob.py12
-rwxr-xr-xtest/unit/obj/test_server.py17
4 files changed, 24 insertions, 25 deletions
diff --git a/swift/common/swob.py b/swift/common/swob.py
index ba3b54bb7..638086ea0 100644
--- a/swift/common/swob.py
+++ b/swift/common/swob.py
@@ -138,12 +138,9 @@ def _datetime_property(header):
if value is not None:
try:
parts = parsedate(self.headers[header])[:7]
- date = datetime(*(parts + (UTC,)))
+ return datetime(*(parts + (UTC,)))
except Exception:
return None
- if date.year < 1970:
- raise ValueError('Somehow an invalid year')
- return date
def setter(self, value):
if isinstance(value, (float, int, long)):
diff --git a/swift/obj/server.py b/swift/obj/server.py
index 3436b632c..a4d8de7c9 100644
--- a/swift/obj/server.py
+++ b/swift/obj/server.py
@@ -481,21 +481,16 @@ class ObjectController(object):
obj_size = int(metadata['Content-Length'])
file_x_ts = metadata['X-Timestamp']
file_x_ts_flt = float(file_x_ts)
- try:
- if_unmodified_since = request.if_unmodified_since
- except (OverflowError, ValueError):
- # catches timestamps before the epoch
- return HTTPPreconditionFailed(request=request)
file_x_ts_utc = datetime.fromtimestamp(file_x_ts_flt, UTC)
+
+ if_unmodified_since = request.if_unmodified_since
if if_unmodified_since and file_x_ts_utc > if_unmodified_since:
return HTTPPreconditionFailed(request=request)
- try:
- if_modified_since = request.if_modified_since
- except (OverflowError, ValueError):
- # catches timestamps before the epoch
- return HTTPPreconditionFailed(request=request)
+
+ if_modified_since = request.if_modified_since
if if_modified_since and file_x_ts_utc <= if_modified_since:
return HTTPNotModified(request=request)
+
keep_cache = (self.keep_cache_private or
('X-Auth-Token' not in request.headers and
'X-Storage-Token' not in request.headers))
diff --git a/test/unit/common/test_swob.py b/test/unit/common/test_swob.py
index a2e538704..7cc5439e9 100644
--- a/test/unit/common/test_swob.py
+++ b/test/unit/common/test_swob.py
@@ -18,6 +18,7 @@
import unittest
import datetime
import re
+import time
from StringIO import StringIO
from urllib import quote
@@ -644,13 +645,18 @@ class TestRequest(unittest.TestCase):
self.assertEquals(req.headers['If-Unmodified-Since'], 'something')
self.assertEquals(req.if_unmodified_since, None)
- req.if_unmodified_since = -1
- self.assertRaises(ValueError, lambda: req.if_unmodified_since)
-
self.assert_('If-Unmodified-Since' in req.headers)
req.if_unmodified_since = None
self.assert_('If-Unmodified-Since' not in req.headers)
+ too_big_date_list = list(datetime.datetime.max.timetuple())
+ too_big_date_list[0] += 1 # bump up the year
+ too_big_date = time.strftime(
+ "%a, %d %b %Y %H:%M:%S UTC", time.struct_time(too_big_date_list))
+
+ req.if_unmodified_since = too_big_date
+ self.assertEqual(req.if_unmodified_since, None)
+
def test_bad_range(self):
req = swift.common.swob.Request.blank('/hi/there', body='hi')
req.range = 'bad range'
diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py
index 59555073a..749c9705e 100755
--- a/test/unit/obj/test_server.py
+++ b/test/unit/obj/test_server.py
@@ -17,6 +17,7 @@
"""Tests for swift.obj.server"""
import cPickle as pickle
+import datetime
import operator
import os
import mock
@@ -24,7 +25,7 @@ import unittest
import math
from shutil import rmtree
from StringIO import StringIO
-from time import gmtime, strftime, time
+from time import gmtime, strftime, time, struct_time
from tempfile import mkdtemp
from hashlib import md5
@@ -1911,16 +1912,16 @@ class TestObjectController(unittest.TestCase):
headers={'If-Modified-Since': 'Not a valid date'})
resp = req.get_response(self.object_controller)
self.assertEquals(resp.status_int, 200)
+
+ too_big_date_list = list(datetime.datetime.max.timetuple())
+ too_big_date_list[0] += 1 # bump up the year
+ too_big_date = strftime(
+ "%a, %d %b %Y %H:%M:%S UTC", struct_time(too_big_date_list))
req = Request.blank(
'/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'},
- headers={'If-Unmodified-Since': 'Sat, 29 Oct 1000 19:43:31 GMT'})
- resp = req.get_response(self.object_controller)
- self.assertEquals(resp.status_int, 412)
- req = Request.blank(
- '/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'GET'},
- headers={'If-Modified-Since': 'Sat, 29 Oct 1000 19:43:31 GMT'})
+ headers={'If-Unmodified-Since': too_big_date})
resp = req.get_response(self.object_controller)
- self.assertEquals(resp.status_int, 412)
+ self.assertEquals(resp.status_int, 200)
def test_content_encoding(self):
req = Request.blank(