From e6a9899e85001e7610d828c2ac7a6bc6ea62b69e Mon Sep 17 00:00:00 2001 From: Kurt Griffiths Date: Thu, 20 Aug 2015 22:57:01 -0500 Subject: [PATCH] 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. --- falcon/request.py | 6 ++++-- falcon/util/misc.py | 23 ++++++++++++++--------- tests/test_utils.py | 16 ++++++++++++---- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/falcon/request.py b/falcon/request.py index f1d4b7e..54ce059 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -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 diff --git a/falcon/util/misc.py b/falcon/util/misc.py index 84f1068..00db982 100644 --- a/falcon/util/misc.py +++ b/falcon/util/misc.py @@ -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: diff --git a/tests/test_utils.py b/tests/test_utils.py index 5649175..5507361 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -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({}),