Permalink
Browse files

Parse if-modified-since timestamps without going through time_t or lo…

…cal time.

This fixes a bug on windows in which mktime cannot work with times before
the epoch.

Closes #713.
  • Loading branch information...
1 parent 4059df2 commit d2457ec1eef95b4c4749a11f3957ce799b041b45 @bdarnell bdarnell committed Apr 13, 2013
Showing with 26 additions and 2 deletions.
  1. +24 −0 tornado/test/web_test.py
  2. +2 −2 tornado/web.py
View
24 tornado/test/web_test.py
@@ -1,6 +1,7 @@
from __future__ import absolute_import, division, print_function, with_statement
from tornado import gen
from tornado.escape import json_decode, utf8, to_unicode, recursive_unicode, native_str, to_basestring
+from tornado.httputil import format_timestamp
from tornado.iostream import IOStream
from tornado.log import app_log, gen_log
from tornado.simple_httpclient import SimpleAsyncHTTPClient
@@ -813,6 +814,29 @@ def test_static_304_if_none_match(self):
'If-None-Match': response1.headers['Etag']})
self.assertEqual(response2.code, 304)
+ def test_static_if_modified_since_pre_epoch(self):
+ # On windows, the functions that work with time_t do not accept
+ # negative values, and at least one client (processing.js) seems
+ # to use if-modified-since 1/1/1960 as a cache-busting technique.
+ response = self.fetch("/static/robots.txt", headers={
+ 'If-Modified-Since': 'Fri, 01 Jan 1960 00:00:00 GMT'})
+ self.assertEqual(response.code, 200)
+
+ def test_static_if_modified_since_time_zone(self):
+ # Instead of the value from Last-Modified, make requests with times
+ # chosen just before and after the known modification time
+ # of the file to ensure that the right time zone is being used
+ # when parsing If-Modified-Since.
+ stat = os.stat(os.path.join(os.path.dirname(__file__),
+ 'static/robots.txt'))
+
+ response = self.fetch('/static/robots.txt', headers={
+ 'If-Modified-Since': format_timestamp(stat.st_mtime - 1)})
+ self.assertEqual(response.code, 200)
+ response = self.fetch('/static/robots.txt', headers={
+ 'If-Modified-Since': format_timestamp(stat.st_mtime + 1)})
+ self.assertEqual(response.code, 304)
+
@wsgi_safe
class CustomStaticFileTest(WebTestCase):
View
4 tornado/web.py
@@ -1638,7 +1638,7 @@ def get(self, path, include_body=True):
raise HTTPError(403, "%s is not a file", path)
stat_result = os.stat(abspath)
- modified = datetime.datetime.fromtimestamp(stat_result[stat.ST_MTIME])
+ modified = datetime.datetime.utcfromtimestamp(stat_result[stat.ST_MTIME])
self.set_header("Last-Modified", modified)
@@ -1660,7 +1660,7 @@ def get(self, path, include_body=True):
ims_value = self.request.headers.get("If-Modified-Since")
if ims_value is not None:
date_tuple = email.utils.parsedate(ims_value)
- if_since = datetime.datetime.fromtimestamp(time.mktime(date_tuple))
+ if_since = datetime.datetime(*date_tuple[:6])
if if_since >= modified:
self.set_status(304)
return

0 comments on commit d2457ec

Please sign in to comment.