-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
bpo-29654 : Support If-Modified-Since HTTP header (browser cache) #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
20cc898
f1d8a48
c4403ec
3225080
415e4af
8595735
2be2f8c
c3337f4
143e1e9
42edfe3
1e68be9
fc20596
9436e45
708ff03
6a58894
db46671
41f74ef
204c503
e2fee49
6dadf6e
c389bf6
3cc4f6d
294e164
6c5ba39
1e0f3e3
2503add
36a8e2a
c8b108f
bd91346
d3238d2
16f9e11
c669909
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -110,6 +110,12 @@ urllib.parse | |
adding `~` to the set of characters that is never quoted by default. | ||
(Contributed by Christian Theune and Ratnadeep Debnath in :issue:`16285`.) | ||
|
||
http.server | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't understand what I should do here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You removed an empty line. Insert it back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, done in commit 9436e45 |
||
----------- | ||
:class:`SimpleHTTPRequestHandler` supports the HTTP If-Modified-Since header. | ||
The server returns the 304 response status if the target file was not | ||
modified after the datetime specified in the header. | ||
(Contributed by Pierre Quentel in :issue:`29654`.) | ||
|
||
Optimizations | ||
============= | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,7 @@ | |
import socketserver | ||
import sys | ||
import time | ||
import datetime | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try to support alphabetical order of imports. I.e. add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in commit 708ff03. The order was not supported for argparse and copy... |
||
import urllib.parse | ||
import copy | ||
import argparse | ||
|
@@ -686,10 +687,30 @@ def send_head(self): | |
except OSError: | ||
self.send_error(HTTPStatus.NOT_FOUND, "File not found") | ||
return None | ||
|
||
fs = os.fstat(f.fileno()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
|
||
try: | ||
# Use browser cache if possible | ||
if ("If-Modified-Since" in self.headers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTTP headers are case-insensitive. Does this work with "if-modified-since"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self.headers should be (similar to) an email.message.Message object, so I think it should work fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I confirm that it's case-insensitive |
||
and "If-None-Match" not in self.headers): | ||
# compare If-Modified-Since and time of last file modification | ||
ims = email.utils.parsedate_to_datetime( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Needed a cache for ill-formed "If-Modified-Since" value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in c3337f4 |
||
self.headers["If-Modified-Since"]) | ||
if (ims is not None and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be written in one line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in c3337f4 |
||
ims.tzinfo is datetime.timezone.utc): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you should also allow tzinfo to be None, to support the obsolete “asctime” format from https://tools.ietf.org/html/rfc7231#section-7.1.1.1 without a timezone (e.g. Sun Nov 6 08:49:37 1994) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not happen often, but it is supported in commit c3337f4 |
||
# compare to UTC datetime of last modification, | ||
# rounded to the second | ||
mtime = int(fs.st_mtime) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. “Int” rounds towards 1970. The actual rounding that “date_time_string” uses seems to be a combination of rounding half to even microseconds (thanks to datetime), and then rounding down (always backwards in time) to seconds. It might be more sensible to call datetime.replace(microseconds=0). |
||
last_modif = datetime.datetime.fromtimestamp(mtime, | ||
ims.tzinfo) | ||
if last_modif <= ims: | ||
self.send_response(HTTPStatus.NOT_MODIFIED) | ||
self.end_headers() | ||
f.close() | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
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.end_headers() | ||
|
@@ -1212,3 +1233,4 @@ def test(HandlerClass=BaseHTTPRequestHandler, | |
else: | ||
handler_class = SimpleHTTPRequestHandler | ||
test(HandlerClass=handler_class, port=args.port, bind=args.bind) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't add this line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in commit fc20596 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -444,6 +444,45 @@ def test_head(self): | |
self.assertEqual(response.getheader('content-type'), | ||
'application/octet-stream') | ||
|
||
def test_browser_cache(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to split this test on few tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in commit 42edfe3. |
||
#constructs the path relative to the root directory of the HTTPServer | ||
response = self.request(self.base_url + '/test') | ||
self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) | ||
last_modif = response.headers['Last-modified'] | ||
|
||
# send new request to the same url with request header | ||
# If-Modified-Since set to Last-Modified : must return 304 | ||
from email.message import Message | ||
headers = Message() | ||
headers['If-Modified-Since'] = last_modif | ||
response = self.request(self.base_url + '/test', headers=headers) | ||
self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please repeat the test with an earlier date, verifying that a response body is returned. |
||
|
||
# if If-None-Match header is present, ignore If-Modified-Since | ||
headers['If-None-Match'] = "*" | ||
response = self.request(self.base_url + '/test', headers=headers) | ||
self.check_status_and_reason(response, HTTPStatus.OK) | ||
|
||
# with If-Modified-Since earlier than Last-Modified, must return 200 | ||
import datetime | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Import at module level. Importing inside functions is used in exceptional cases. There was a problem hiding this comment. C F438 hoose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in commit 42edfe3 |
||
import email.utils | ||
dt = email.utils.parsedate_to_datetime(last_modif) | ||
# build datetime object : 365 days before last modification | ||
old_dt = dt - datetime.timedelta(days=365) | ||
headers = Message() | ||
headers['If-Modified-Since'] = email.utils.format_datetime(old_dt, | ||
usegmt=True) | ||
response = self.request(self.base_url + '/test', headers=headers) | ||
self.check_status_and_reason(response, HTTPStatus.OK) | ||
|
||
# one hour after last modification : must return 304 | ||
new_dt = dt + datetime.timedelta(hours=1) | ||
headers = Message() | ||
headers['If-Modified-Since'] = email.utils.format_datetime(new_dt, | ||
usegmt=True) | ||
response = self.request(self.base_url + '/test', headers=headers) | ||
self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) | ||
|
||
def test_invalid_requests(self): | ||
response = self.request('/', method='FOO') | ||
self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -666,6 +666,9 @@ Library | |
- Issue #24142: Reading a corrupt config file left configparser in an | ||
invalid state. Original patch by Florian Höch. | ||
|
||
- bpo-29654 : Support If-Modified-Since HTTP header (browser cache). Patch | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the space before colon. Use two spaces after sentence ending period. Move the entry to the start of the "Library" section (new entries are added at the start). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 6a58894 |
||
by Pierre Quentel. | ||
|
||
Windows | ||
------- | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move before
unittest.mock
. Modules should be lexicographically ordered.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in commit 143e1e9