mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
gh-113471: Add custom default Content-Type to http.server (#113475)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> Co-authored-by: donBarbos <donbarbos@proton.me> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
This commit is contained in:
@@ -390,6 +390,14 @@ instantiation, of which this module provides three different variants:
|
||||
This will be ``"SimpleHTTP/" + __version__``, where ``__version__`` is
|
||||
defined at the module level.
|
||||
|
||||
.. attribute:: default_content_type
|
||||
|
||||
Specifies the Content-Type header value sent when the MIME type
|
||||
cannot be guessed from the file extension of the requested URL.
|
||||
By default, it is set to ``'application/octet-stream'``.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. attribute:: extensions_map
|
||||
|
||||
A dictionary mapping suffixes into MIME types, contains custom overrides
|
||||
@@ -528,6 +536,18 @@ The following options are accepted:
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. option:: --content-type <content_type>
|
||||
|
||||
Specifies the default Content-Type HTTP header used when the MIME type
|
||||
cannot be guessed from the URL's file extension. By default, the server
|
||||
uses ``'application/octet-stream'``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m http.server --content-type text/html
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. option:: --tls-cert
|
||||
|
||||
Specifies a TLS certificate chain for HTTPS connections:
|
||||
|
||||
@@ -964,6 +964,12 @@ http.server
|
||||
<using-on-controlling-color>`.
|
||||
(Contributed by Hugo van Kemenade in :gh:`146292`.)
|
||||
|
||||
* Added :attr:`~http.server.SimpleHTTPRequestHandler.default_content_type`
|
||||
and the :option:`--content-type <http.server --content-type>` command-line
|
||||
option to allow customizing the default ``Content-Type`` header
|
||||
for files with unknown extensions.
|
||||
(Contributed by John Comeau and Hugo van Kemenade in :gh:`113471`.)
|
||||
|
||||
|
||||
inspect
|
||||
-------
|
||||
|
||||
+10
-2
@@ -727,6 +727,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
"""
|
||||
|
||||
server_version = "SimpleHTTP"
|
||||
default_content_type = "application/octet-stream"
|
||||
index_pages = ("index.html", "index.htm")
|
||||
extensions_map = _encodings_map_default = {
|
||||
'.gz': 'application/gzip',
|
||||
@@ -974,7 +975,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
guess, _ = mimetypes.guess_file_type(path)
|
||||
if guess:
|
||||
return guess
|
||||
return 'application/octet-stream'
|
||||
return self.default_content_type
|
||||
|
||||
|
||||
nobody = None
|
||||
@@ -1010,9 +1011,10 @@ def _get_best_family(*address):
|
||||
return family, sockaddr
|
||||
|
||||
|
||||
def test(HandlerClass=BaseHTTPRequestHandler,
|
||||
def test(HandlerClass=SimpleHTTPRequestHandler,
|
||||
ServerClass=ThreadingHTTPServer,
|
||||
protocol="HTTP/1.0", port=8000, bind=None,
|
||||
content_type=SimpleHTTPRequestHandler.default_content_type,
|
||||
tls_cert=None, tls_key=None, tls_password=None):
|
||||
"""Test the HTTP request handler class.
|
||||
|
||||
@@ -1021,6 +1023,7 @@ def test(HandlerClass=BaseHTTPRequestHandler,
|
||||
"""
|
||||
ServerClass.address_family, addr = _get_best_family(bind, port)
|
||||
HandlerClass.protocol_version = protocol
|
||||
HandlerClass.default_content_type = content_type
|
||||
|
||||
if tls_cert:
|
||||
server = ServerClass(addr, HandlerClass, certfile=tls_cert,
|
||||
@@ -1060,6 +1063,10 @@ def _main(args=None):
|
||||
default='HTTP/1.0',
|
||||
help='conform to this HTTP version '
|
||||
'(default: %(default)s)')
|
||||
parser.add_argument('--content-type',
|
||||
default=SimpleHTTPRequestHandler.default_content_type,
|
||||
help='default content type for unknown extensions '
|
||||
'(default: %(default)s)')
|
||||
parser.add_argument('--tls-cert', metavar='PATH',
|
||||
help='path to the TLS certificate chain file')
|
||||
parser.add_argument('--tls-key', metavar='PATH',
|
||||
@@ -1112,6 +1119,7 @@ def _main(args=None):
|
||||
port=args.port,
|
||||
bind=args.bind,
|
||||
protocol=args.protocol,
|
||||
content_type=args.content_type,
|
||||
tls_cert=args.tls_cert,
|
||||
tls_key=args.tls_key,
|
||||
tls_password=tls_key_password,
|
||||
|
||||
@@ -1379,6 +1379,7 @@ class CommandLineTestCase(unittest.TestCase):
|
||||
'protocol': default_protocol,
|
||||
'port': default_port,
|
||||
'bind': default_bind,
|
||||
'content_type': 'application/octet-stream',
|
||||
'tls_cert': None,
|
||||
'tls_key': None,
|
||||
'tls_password': None,
|
||||
@@ -1447,6 +1448,16 @@ class CommandLineTestCase(unittest.TestCase):
|
||||
mock_func.assert_called_once_with(**call_args)
|
||||
mock_func.reset_mock()
|
||||
|
||||
@mock.patch('http.server.test')
|
||||
def test_content_type_flag(self, mock_func):
|
||||
content_types = ['text/html', 'text/plain', 'application/json']
|
||||
for content_type in content_types:
|
||||
with self.subTest(content_type=content_type):
|
||||
self.invoke_httpd('--content-type', content_type)
|
||||
call_args = self.args | dict(content_type=content_type)
|
||||
mock_func.assert_called_once_with(**call_args)
|
||||
mock_func.reset_mock()
|
||||
|
||||
@unittest.skipIf(ssl is None, "requires ssl")
|
||||
@mock.patch('http.server.test')
|
||||
def test_tls_cert_and_key_flags(self, mock_func):
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Allow :mod:`http.server` to set a default content-type when serving
|
||||
files with an unknown or missing extension.
|
||||
Reference in New Issue
Block a user