|
|
|
@ -14,11 +14,14 @@ import re |
|
|
|
import base64 |
|
|
|
import ntpath |
|
|
|
import shutil |
|
|
|
import urllib.parse |
|
|
|
import email.message |
|
|
|
import email.utils |
|
|
|
import html |
|
|
|
import http.client |
|
|
|
import urllib.parse |
|
|
|
import tempfile |
|
|
|
import time |
|
|
|
import datetime |
|
|
|
from io import BytesIO |
|
|
|
|
|
|
|
import unittest |
|
|
|
@ -333,6 +336,13 @@ class SimpleHTTPServerTestCase(BaseTestCase): |
|
|
|
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: |
|
|
|
@ -444,6 +454,44 @@ class SimpleHTTPServerTestCase(BaseTestCase): |
|
|
|
self.assertEqual(response.getheader('content-type'), |
|
|
|
'application/octet-stream') |
|
|
|
|
|
|
|
def test_browser_cache(self): |
|
|
|
"""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) |
|
|
|
|
|
|
|
# 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.NOT_MODIFIED) |
|
|
|
|
|
|
|
def test_browser_cache_file_changed(self): |
|
|
|
# with If-Modified-Since earlier than Last-Modified, must return 200 |
|
|
|
dt = self.last_modif_datetime |
|
|
|
# build datetime object : 365 days before last modification |
|
|
|
old_dt = dt - datetime.timedelta(days=365) |
|
|
|
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) |
|
|
|
|
|
|
|
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.OK) |
|
|
|
|
|
|
|
def test_invalid_requests(self): |
|
|
|
response = self.request('/', method='FOO') |
|
|
|
self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) |
|
|
|
@ -453,6 +501,15 @@ class SimpleHTTPServerTestCase(BaseTestCase): |
|
|
|
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) |
|
|
|
|