-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
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 1 commit
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
…several methods ; add a test for Last-Modified header.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,8 +17,11 @@ | |
import urllib.parse | ||
import html | ||
import http.client | ||
import email.message | ||
import email.utils | ||
import tempfile | ||
import time | ||
import datetime | ||
from io import BytesIO | ||
|
||
import unittest | ||
|
@@ -333,6 +336,13 @@ def setUp(self): | |
self.base_url = '/' + self.tempdir_name | ||
with open(os.path.join(self.tempdir, 'test'), 'wb') as temp: | ||
temp.write(self.data) | ||
mtime = os.fstat(temp.fileno()).st_mtime | ||
# compute last modification datetime for browser cache tests | ||
last_modif = datetime.datetime.fromtimestamp(mtime, | ||
datetime.timezone.utc) | ||
self.last_modif_datetime = last_modif.replace(microsecond=0) | ||
self.last_modif_header = email.utils.formatdate( | ||
last_modif.timestamp(), usegmt=True) | ||
|
||
def tearDown(self): | ||
try: | ||
|
@@ -445,43 +455,42 @@ def test_head(self): | |
'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 | ||
"""Check that when a request to /test is sent with the request header | ||
If-Modified-Since set to date of last modification, the server returns | ||
status code 304, not 200 | ||
""" | ||
headers = email.message.Message() | ||
headers['If-Modified-Since'] = self.last_modif_header | ||
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'] = "*" | ||
# one hour after last modification : must return 304 | ||
new_dt = self.last_modif_datetime + datetime.timedelta(hours=1) | ||
headers = email.message.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.OK) | ||
self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) | ||
|
||
def test_browser_cache_file_changed(self): | ||
# with If-Modified-Since earlier than Last-Modified, must return 200 | ||
import datetime | ||
import email.utils | ||
dt = email.utils.parsedate_to_datetime(last_modif) | ||
dt = self.last_modif_datetime | ||
# build datetime object : 365 days before last modification | ||
old_dt = dt - datetime.timedelta(days=365) | ||
headers = Message() | ||
headers = email.message.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) | ||
def test_browser_cache_with_If_None_Match_header(self): | ||
# if If-None-Match header is present, ignore If-Modified-Since | ||
|
||
headers = email.message.Message() | ||
headers['If-Modified-Since'] = self.last_modif_header | ||
headers['If-None-Match'] = "*" | ||
response = self.request(self.base_url + '/test', headers=headers) | ||
self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) | ||
self.check_status_and_reason(response, HTTPStatus.OK) | ||
|
||
def test_invalid_requests(self): | ||
response = self.request('/', method='FOO') | ||
|
@@ -492,6 +501,15 @@ def test_invalid_requests(self): | |
response = self.request('/', method='GETs') | ||
self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) | ||
|
||
def test_last_modified(self): | ||
"""Checks that the datetime returned in Last-Modified response header | ||
is the actual datetime of last modification, rounded to the second | ||
""" | ||
response = self.request(self.base_url + '/test') | ||
self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) | ||
last_modif_header = response.headers['Last-modified'] | ||
self.assertEqual(last_modif_header, self.last_modif_header) | ||
|
||
def test_path_without_leading_slash(self): | ||
response = self.request(self.tempdir_name + '/test') | ||
self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) | ||
|
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.
Try to support alphabetical order of imports.
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.
I don't understand. In the current version, the imports are not in alphabetic order :
There doesn't seem to be any logical order either -
http.client
is not grouped withhttp.server
for instance.Would you mind explaining more what you want and why ? It will be helpful for next Pull Requests.
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.
Just place
import email.utils
the first andimport datetime
after it. You can also moveimport base64
before them.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.
This is just a style nit, you shouldn't follow it if you have other reasons.
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.
I changed the order in commit db46671. I tried several orders but couldn't find something obvious. I prefer to leave Internet-related modules together, and
time
anddatetime
together.... and thanks for the expression "style nit", I didn't know it :-)