From 4e9806a2b1d5220ecb4504f21cfa253e74e499dd Mon Sep 17 00:00:00 2001 From: Steve McMaster Date: Tue, 14 Apr 2015 09:55:01 -0400 Subject: [PATCH] fix(util) Add support for obsolete HTTP date formats Update http_date_to_dt to support obsolete date formats per RFC 7231. Fixes #255 --- falcon/request.py | 5 +++-- falcon/util/misc.py | 26 ++++++++++++++++++++++++-- tests/test_req_vars.py | 2 +- tests/test_utils.py | 9 +++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/falcon/request.py b/falcon/request.py index 6acb1e6..116af67 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -617,12 +617,13 @@ class Request(object): try: http_date = self.get_header(header, required=required) - return util.http_date_to_dt(http_date) + return util.http_date_to_dt(http_date, obs_date=True) except TypeError: # When the header does not exist and isn't required return None except ValueError: - msg = ('It must be formatted according to RFC 1123.') + msg = ('It must be formatted according to RFC 7231, ' + 'Section 7.1.1.1') raise HTTPInvalidHeader(msg, header) def get_param(self, name, required=False, store=None, default=None): diff --git a/falcon/util/misc.py b/falcon/util/misc.py index ebfefcf..8922e44 100644 --- a/falcon/util/misc.py +++ b/falcon/util/misc.py @@ -88,19 +88,41 @@ def dt_to_http(dt): return dt.strftime('%a, %d %b %Y %H:%M:%S GMT') -def http_date_to_dt(http_date): +def http_date_to_dt(http_date, obs_date=False): """Converts an HTTP date string to a datetime instance. Args: http_date (str): An RFC 1123 date string, e.g.: "Tue, 15 Nov 1994 12:45:26 GMT". + obs_date (bool): Support obs-date formats according to RFC 7231, e.g.: + "Sunday, 06-Nov-94 08:49:37 GMT" + "Sun Nov 6 08:49:37 1994". Returns: datetime: A UTC datetime instance corresponding to the given HTTP date. + + Raises: + ValueError: http_date doesn't match any of the available time formats """ - return strptime(http_date, '%a, %d %b %Y %H:%M:%S %Z') + time_formats = ['%a, %d %b %Y %H:%M:%S %Z'] + + if obs_date: + time_formats.extend([ + '%A, %d-%b-%y %H:%M:%S %Z', + '%a %b %d %H:%M:%S %Y', + ]) + + # Loop through the formats and return the first that matches + for time_format in time_formats: + try: + return strptime(http_date, time_format) + except ValueError: + continue + + # Did not match any formats + raise ValueError('time data %r does not match known formats' % http_date) def to_query_str(params): diff --git a/tests/test_req_vars.py b/tests/test_req_vars.py index 56a786d..48dfe06 100644 --- a/tests/test_req_vars.py +++ b/tests/test_req_vars.py @@ -527,7 +527,7 @@ class TestReqVars(testing.TestBase): headers = {header: 'Thu, 04 Apr 2013'} expected_desc = ('The value provided for the {0} ' 'header is invalid. It must be formatted ' - 'according to RFC 1123.') + 'according to RFC 7231, Section 7.1.1.1') self._test_error_details(headers, attr, falcon.HTTPInvalidHeader, diff --git a/tests/test_utils.py b/tests/test_utils.py index b5a0d35..29576a8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -70,6 +70,15 @@ class TestFalconUtils(testtools.TestCase): falcon.http_date_to_dt('Thu, 04 Apr 2013 10:28:54 GMT'), datetime(2013, 4, 4, 10, 28, 54)) + self.assertEqual( + falcon.http_date_to_dt('Sunday, 06-Nov-94 08:49:37 GMT', + obs_date=True), + datetime(1994, 11, 6, 8, 49, 37)) + + self.assertEqual( + falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994', obs_date=True), + datetime(1994, 11, 6, 8, 49, 37)) + def test_pack_query_params_none(self): self.assertEqual( falcon.to_query_str({}),