bpo-29654 : Support If-Modified-Since HTTP header (browser cache) (#298)
Return 304 response if file was not modified.
diff --git a/Lib/http/server.py b/Lib/http/server.py
index 61ddecc..429490b 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -87,6 +87,9 @@
"SimpleHTTPRequestHandler", "CGIHTTPRequestHandler",
]
+import argparse
+import copy
+import datetime
import email.utils
import html
import http.client
@@ -101,8 +104,6 @@
import sys
import time
import urllib.parse
-import copy
-import argparse
from http import HTTPStatus
@@ -686,12 +687,42 @@
except OSError:
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return None
+
try:
+ fs = os.fstat(f.fileno())
+ # Use browser cache if possible
+ if ("If-Modified-Since" in self.headers
+ and "If-None-Match" not in self.headers):
+ # compare If-Modified-Since and time of last file modification
+ try:
+ ims = email.utils.parsedate_to_datetime(
+ self.headers["If-Modified-Since"])
+ except (TypeError, IndexError, OverflowError, ValueError):
+ # ignore ill-formed values
+ pass
+ else:
+ if ims.tzinfo is None:
+ # obsolete format with no timezone, cf.
+ # https://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ ims = ims.replace(tzinfo=datetime.timezone.utc)
+ if ims.tzinfo is datetime.timezone.utc:
+ # compare to UTC datetime of last modification
+ last_modif = datetime.datetime.fromtimestamp(
+ fs.st_mtime, datetime.timezone.utc)
+ # remove microseconds, like in If-Modified-Since
+ last_modif = last_modif.replace(microsecond=0)
+
+ if last_modif <= ims:
+ self.send_response(HTTPStatus.NOT_MODIFIED)
+ self.end_headers()
+ f.close()
+ return None
+
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", ctype)
- fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
- self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
+ self.send_header("Last-Modified",
+ self.date_time_string(fs.st_mtime))
self.end_headers()
return f
except: