8000 bpo-29654 : Support If-Modified-Since HTTP header (browser cache) by PierreQuentel · Pull Request #298 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

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

Merged
merged 32 commits into from
Apr 2, 2017
Merged
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
20cc898
Support If-Modified-Since HTTP headers, return 304 response if file w…
Feb 25, 2017
f1d8a48
Fix bug in datetime comparisons. Ignore If-Modified-Since if If-None-…
Feb 26, 2017
c4403ec
Update Misc/NEWS and What's New
Feb 26, 2017
3225080
Fix typo
Feb 26, 2017
415e4af
Use parsedate_to_datetime to extract datetime from If-Modified-Since,…
Feb 26, 2017
8595735
Change computing of dates used to test browser cache
Feb 26, 2017
2be2f8c
Remove microseconds from time of last modification with .replace(micr…
Feb 26, 2017
c3337f4
Put os.fstat() call inside the try block ; handle obsolete HTTP date …
Feb 26, 2017
143e1e9
Restore alphabetical order in section Improved Modules
Feb 26, 2017
42edfe3
Store last modification date in setUp ; split browser cache tests in …
Feb 26, 2017
1e68be9
Specify the exceptions to catch in parsedate_to_datetime
PierreQuentel Feb 27, 2017
fc20596
Use except/else for parsedate_to_datetime exception handling
PierreQuentel Feb 27, 2017
9436e45
Restore blank line
Feb 28, 2017
708ff03
Put imports in alphabetical order. Change version to 0.7.
Mar 1, 2017
6a58894
Presentation changes.
Mar 1, 2017
db46671
Change order of imports.
Mar 1, 2017
41f74ef
Update Misc/NEWS (conflict)
Mar 2, 2017
204c503
Merge branch 'master' into master
PierreQuentel Mar 2, 2017
e2fee49
Merge branch 'master' into master
PierreQuentel Mar 4, 2017
6dadf6e
Restore version number 0.6
Mar 7, 2017
c389bf6
Replace "datetime" by "time"
Mar 7, 2017
3cc4f6d
Merge branch 'master' of https://github.com/PierreQuentel/cpython
Mar 7, 2017
294e164
Proposal for an update of the http.server module documentation
Mar 7, 2017
6c5ba39
Merge branch 'master' into master
PierreQuentel Mar 8, 2017
1e0f3e3
Merge branch 'master' into master
PierreQuentel Mar 15, 2017
2503add
Merge branch 'master' into master
serhiy-storchaka Mar 20, 2017
36a8e2a
Merge branch 'master' into master
PierreQuentel Mar 23, 2017
c8b108f
Add a "Changed in version 3.7" comment
Mar 24, 2017
bd91346
Merge branch 'master' of https://github.com/PierreQuentel/cpython
Mar 24, 2017
d3238d2
Merge branch 'master' into master
PierreQuentel Apr 1, 2017
16f9e11
Remove trailing whitespaces
Apr 1, 2017
c669909
Merge branch 'master' of https://github.com/PierreQuentel/cpython
Apr 1, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Store last modification date in setUp ; split browser cache tests in …
…several methods ; add a test for Last-Modified header.
  • Loading branch information
Pierre Quentel committed Feb 26, 2017
commit 42edfe3856e0ae683dbffe3f7a75b9daec0898e8
64 changes: 41 additions & 23 deletions Lib/test/test_httpservers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
import urllib.parse
import html
import http.client
import email.message
Copy link
Member

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.

Copy link
Contributor Author

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 :

from http.server import BaseHTTPRequestHandler, HTTPServer, \
     SimpleHTTPRequestHandler, CGIHTTPRequestHandler
from http import server, HTTPStatus

import os
import sys
import re
import base64
import ntpath
import shutil
import urllib.parse
import html
import http.client
import tempfile
import time
from io import BytesIO

import unittest
from test import support

There doesn't seem to be any logical order either - http.client is not grouped with http.server for instance.

Would you mind explaining more what you want and why ? It will be helpful for next Pull Requests.

Copy link
Member

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 and import datetime after it. You can also move import base64 before them.

Copy link
Member

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.

Copy link
Contributor Author

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 and datetime together.

... and thanks for the expression "style nit", I didn't know it :-)

import email.utils
import tempfile
import time
import datetime
from io import BytesIO

import unittest
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -445,43 +455,42 @@ def test_head(self):
'application/octet-stream')

def test_browser_cache(self):
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 42edfe3.
The datetime of last modification is stored in method setUp and used in the browser cache tests. I also added a test for the Last-Modified header.

#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)
Copy link
Member

Choose a reason for hiding this comment

The 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')
Expand All @@ -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)
Expand Down
0