Repository: falconry/falcon

Issue:
Support obsolete HTTP date formats
In looking at [RFC 7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.1), Falcon date parsing is actually restricted to IMF-fixdate and does not support the other two obsolete formats. Should we support those formats? Doing so may have minor performance implications for little benefit.

Otherwise, we should update the docstrings and such to reference IMF-fixdate instead of HTTP-Date.

Discussion:
mcmasterathl: I will rebase this when #495 is merged, as I am pretty sure they conflict.
jmvrbanac: @mcmasterathl when you have time, could you rebase your change? Thanks!
mcmasterathl: Yup, was working on that right now actually.
mcmasterathl: @jmvrbanac All set
jmvrbanac: :+1: LGTM

Patch:
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):


Test patch:
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({}),