perf(request): Optimize HTTP date parsing

Don't check for obsolete date formats by default. When obsolete date
formats are not enabled, avoid unnecessary processing time and data
structure instantiation.
This commit is contained in:
Kurt Griffiths
2015-08-20 22:57:01 -05:00
parent ccb2078733
commit e6a9899e85
3 changed files with 30 additions and 15 deletions

View File

@@ -601,7 +601,7 @@ class Request(object):
raise HTTPMissingHeader(name)
def get_header_as_datetime(self, header, required=False):
def get_header_as_datetime(self, header, required=False, obs_date=False):
"""Return an HTTP header with HTTP-Date values as a datetime.
Args:
@@ -609,6 +609,8 @@ class Request(object):
required (bool, optional): Set to ``True`` to raise
``HTTPBadRequest`` instead of returning gracefully when the
header is not found (default ``False``).
obs_date (bool, optional): Support obs-date formats according to
RFC 7231, e.g.: "Sunday, 06-Nov-94 08:49:37 GMT" (default ``False``).
Returns:
datetime: The value of the specified header if it exists,
@@ -622,7 +624,7 @@ class Request(object):
try:
http_date = self.get_header(header, required=required)
return util.http_date_to_dt(http_date, obs_date=True)
return util.http_date_to_dt(http_date, obs_date=obs_date)
except TypeError:
# When the header does not exist and isn't required
return None

View File

@@ -107,9 +107,8 @@ def http_date_to_dt(http_date, obs_date=False):
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".
obs_date (bool, optional): Support obs-date formats according to
RFC 7231, e.g.: "Sunday, 06-Nov-94 08:49:37 GMT" (default ``False``).
Returns:
datetime: A UTC datetime instance corresponding to the given
@@ -119,13 +118,19 @@ def http_date_to_dt(http_date, obs_date=False):
ValueError: http_date doesn't match any of the available time formats
"""
time_formats = ['%a, %d %b %Y %H:%M:%S %Z']
if not obs_date:
# PERF(kgriffs): This violates DRY, but we do it anyway
# to avoid the overhead of setting up a tuple, looping
# over it, and setting up exception handling blocks each
# time around the loop, in the case that we don't actually
# need to check for multiple formats.
return strptime(http_date, '%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',
])
time_formats = (
'%a, %d %b %Y %H:%M:%S %Z',
'%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:

View File

@@ -79,15 +79,23 @@ 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.assertRaises(
ValueError,
falcon.http_date_to_dt, 'Sun Nov 6 08:49:37 1994')
self.assertRaises(
ValueError,
falcon.http_date_to_dt, 'Nov 6 08:49:37 1994', obs_date=True)
self.assertEqual(
falcon.http_date_to_dt('Sun Nov 6 08:49:37 1994', obs_date=True),
datetime(1994, 11, 6, 8, 49, 37))
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))
def test_pack_query_params_none(self):
self.assertEqual(
falcon.to_query_str({}),