From 79db2f2216f347c2ad19ef2c2c188a9155906b5c Mon Sep 17 00:00:00 2001 From: ggqlq Date: Tue, 15 Apr 2025 02:35:59 +0800 Subject: [PATCH 01/39] Add tests for http.server command-line interface --- Lib/http/server.py | 29 +++++-- Lib/test/test_httpservers.py | 157 ++++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 7 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index a2aad4c9be3c51..d0a7777f67c76a 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -106,6 +106,8 @@ import sys import time import urllib.parse +import argparse +import contextlib from http import HTTPStatus @@ -150,6 +152,20 @@ def server_bind(self): class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): daemon_threads = True +class CommandLineServerClass(ThreadingHTTPServer): + def __init__(self, server_address, RequestHandlerClass, directory=None): + super().__init__(server_address, RequestHandlerClass) + self.directory = directory + def server_bind(self): + # suppress exception when protocol is IPv4 + with contextlib.suppress(Exception): + self.socket.setsockopt( + socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + return super().server_bind() + + def finish_request(self, request, client_address): + self.RequestHandlerClass(request, client_address, self, + directory=self.directory) class HTTPSServer(HTTPServer): def __init__(self, server_address, RequestHandlerClass, @@ -1336,10 +1352,7 @@ def test(HandlerClass=BaseHTTPRequestHandler, print("\nKeyboard interrupt received, exiting.") sys.exit(0) -if __name__ == '__main__': - import argparse - import contextlib - +def _main(args=None): parser = argparse.ArgumentParser() parser.add_argument('--cgi', action='store_true', help='run as CGI server') @@ -1362,7 +1375,7 @@ def test(HandlerClass=BaseHTTPRequestHandler, parser.add_argument('port', default=8000, type=int, nargs='?', help='bind to this port ' '(default: %(default)s)') - args = parser.parse_args() + args = parser.parse_args(args=args) if not args.tls_cert and args.tls_key: parser.error("--tls-key requires --tls-cert to be set") @@ -1399,7 +1412,7 @@ def finish_request(self, request, client_address): test( HandlerClass=handler_class, - ServerClass=DualStackServer, + ServerClass=CommandLineServerClass, port=args.port, bind=args.bind, protocol=args.protocol, @@ -1407,3 +1420,7 @@ def finish_request(self, request, client_address): tls_key=args.tls_key, tls_password=tls_key_password, ) + + +if __name__ == '__main__': + _main() \ No newline at end of file diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2cafa4e45a1313..51a63715a3a4a8 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -8,6 +8,7 @@ SimpleHTTPRequestHandler, CGIHTTPRequestHandler from http import server, HTTPStatus +import http.server import os import socket import sys @@ -27,6 +28,8 @@ import threading from unittest import mock from io import BytesIO, StringIO +import textwrap +import contextlib import unittest from test import support @@ -1466,7 +1469,7 @@ def test_windows_colon(self): class MiscTestCase(unittest.TestCase): def test_all(self): expected = [] - denylist = {'executable', 'nobody_uid', 'test'} + denylist = {'executable', 'nobody_uid', 'test', 'CommandLineServerClass', 'CommandLineServerClass'} for name in dir(server): if name.startswith('_') or name in denylist: continue @@ -1535,6 +1538,158 @@ def test_server_test_ipv4(self, _): server.test(ServerClass=mock_server, bind=bind) self.assertEqual(mock_server.address_family, socket.AF_INET) +class CommandLineTestCase(unittest.TestCase): + def setUp(self): + self.default_port = 8000 + self.default_bind = None + self.default_protocol = 'HTTP/1.0' + self.default_handler = SimpleHTTPRequestHandler + self.default_server = http.server.CommandLineServerClass + self.tls_cert = '-----BEGIN CERTIFICATE-----\n' + '-----END CERTIFICATE-----\n' + self.tls_key = '-----BEGIN RSA PRIVATE KEY-----\n' + '-----END RSA PRIVATE KEY-----\n' + + self.tls_password = '' + tls_password_file_object = tempfile.NamedTemporaryFile(mode='w+', delete=False) + tls_password_file_object.write(self.tls_password) + self.tls_password_file = tls_password_file_object.name + tls_password_file_object.close() + return super().setUp() + + def tearDown(self): + if os.path.exists(self.tls_password_file): + os.remove(self.tls_password_file) + return super().tearDown() + + def text_normalizer(self, string): + return textwrap.dedent(string).strip() + + def invoke_httpd(self, args=[]): + output = StringIO() + with contextlib.redirect_stdout(output): + server._main(args) + return self.text_normalizer(output.getvalue()) + + @mock.patch('http.server.test') + def test_port_flag(self, mock_func): + ports = [8000, 65535,] + for port in ports: + with self.subTest(port=port): + self.invoke_httpd([str(port)]) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=self.default_protocol, port=port, bind=self.default_bind, tls_cert=None, + tls_key=None, tls_password=None) + mock_func.reset_mock() + + @mock.patch('http.server.test') + def test_directory_flag(self, mock_func): + options = ['-d', '--directory'] + directories = ['.', '/foo', '\\bar', '/', 'C:\\', 'C:\\foo', 'C:\\bar',] + for flag in options: + for directory in directories: + with self.subTest(flag=flag, directory=directory): + self.invoke_httpd([flag, directory]) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + tls_cert=None, tls_key=None, tls_password=None) + mock_func.reset_mock() + + @mock.patch('http.server.test') + def test_bind_flag(self, mock_func): + options = ['-b', '--bind'] + bind_addresses = ['localhost', '127.0.0.1', '::1', '0.0.0.0', '8.8.8.8',] + for flag in options: + for bind_address in bind_addresses: + with self.subTest(flag=flag, bind_address=bind_address): + self.invoke_httpd([flag, bind_address]) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=bind_address, + tls_cert=None, tls_key=None, tls_password=None) + mock_func.reset_mock() + + @mock.patch('http.server.test') + def test_protocol_flag(self, mock_func): + options = ['-p', '--protocol'] + protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0',] + for flag in options: + for protocol in protocols: + with self.subTest(flag=flag, protocol=protocol): + self.invoke_httpd([flag, protocol]) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=protocol, port=self.default_port, bind=self.default_bind, + tls_cert=None, tls_key=None, tls_password=None) + mock_func.reset_mock() + + @mock.patch('http.server.test') + def test_cgi_flag(self, mock_func): + self.invoke_httpd(['--cgi']) + mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + tls_cert=None, tls_key=None, tls_password=None) + + @mock.patch('http.server.test') + def test_tls_flag(self, mock_func): + tls_cert_options = ['--tls-cert', ] + tls_key_options = ['--tls-key', ] + tls_password_options = ['--tls-password-file', ] + # Normal: --tls-cert and --tls-key + + for tls_cert_option in tls_cert_options: + for tls_key_option in tls_key_options: + self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key]) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=None) + mock_func.reset_mock() + + # Normal: --tls-cert, --tls-key and --tls-password-file + + for tls_cert_option in tls_cert_options: + for tls_key_option in tls_key_options: + for tls_password_option in tls_password_options: + self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file]) + + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=self.tls_password) + mock_func.reset_mock() + + # Abnormal: --tls-key without --tls-cert + + for tls_key_option in tls_key_options: + for tls_cert_option in tls_cert_options: + with self.assertRaises(SystemExit): + self.invoke_httpd([tls_key_option, self.tls_key]) + mock_func.reset_mock() + + # Abnormal: --tls-password-file without --tls-cert + + for tls_password_option in tls_password_options: + with self.assertRaises(SystemExit): + self.invoke_httpd([tls_password_option, self.tls_password_file]) + mock_func.reset_mock() + + # Abnormal: --tls-password-file cannot be opened + + non_existent_file = os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + retry_count = 0 + while os.path.exists(non_existent_file) and retry_count < 10: + non_existent_file = os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + if not os.path.exists(non_existent_file): + for tls_password_option in tls_password_options: + for tls_cert_option in tls_cert_options: + with self.assertRaises(SystemExit): + self.invoke_httpd([tls_cert_option, self.tls_cert, tls_password_option, non_existent_file]) + + def test_help_flag(self): + options = ['-h', '--help'] + for option in options: + with self.assertRaises(SystemExit): + output = self.invoke_httpd([option]) + self.assertIn('usage:', output) + + def test_unknown_flag(self): + with self.assertRaises(SystemExit): + self.invoke_httpd(['--unknown-flag']) def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 2b589bf54be4054385e15e9de9ee04a5f706efad Mon Sep 17 00:00:00 2001 From: ggqlq Date: Tue, 15 Apr 2025 12:50:01 +0800 Subject: [PATCH 02/39] add news --- ...-04-15-12-47-09.gh-issue-131178.Td8j5x.rst | 1 + confdefs.h | 139 ++++++++++++ conftest.c | 200 ++++++++++++++++++ 3 files changed, 340 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst create mode 100644 confdefs.h create mode 100644 conftest.c diff --git a/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst b/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst new file mode 100644 index 00000000000000..9742c7791995c7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst @@ -0,0 +1 @@ +Add tests for the command line interface of the ``http.server`` module. \ No newline at end of file diff --git a/confdefs.h b/confdefs.h new file mode 100644 index 00000000000000..8a4fb56e9fa822 --- /dev/null +++ b/confdefs.h @@ -0,0 +1,139 @@ +/* confdefs.h */ +#define _NETBSD_SOURCE 1 +#define __BSD_VISIBLE 1 +#define _DARWIN_C_SOURCE 1 +#define _PYTHONFRAMEWORK "" +#define _XOPEN_SOURCE 700 +#define _XOPEN_SOURCE_EXTENDED 1 +#define _POSIX_C_SOURCE 200809L +#define HAVE_STDIO_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_WCHAR_H 1 +#define STDC_HEADERS 1 +#define _ALL_SOURCE 1 +#define _DARWIN_C_SOURCE 1 +#define _GNU_SOURCE 1 +#define _HPUX_ALT_XOPEN_SOCKET_API 1 +#define _NETBSD_SOURCE 1 +#define _OPENBSD_SOURCE 1 +#define _POSIX_PTHREAD_SEMANTICS 1 +#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 +#define __STDC_WANT_IEC_60559_BFP_EXT__ 1 +#define __STDC_WANT_IEC_60559_DFP_EXT__ 1 +#define __STDC_WANT_IEC_60559_EXT__ 1 +#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 +#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 +#define __STDC_WANT_LIB_EXT2__ 1 +#define __STDC_WANT_MATH_SPEC_FUNCS__ 1 +#define _TANDEM_SOURCE 1 +#define __EXTENSIONS__ 1 +#define PY_SUPPORT_TIER 1 +#define STDC_HEADERS 1 +#define HAVE_ALLOCA_H 1 +#define HAVE_ASM_TYPES_H 1 +#define HAVE_DLFCN_H 1 +#define HAVE_ENDIAN_H 1 +#define HAVE_ERRNO_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_GRP_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_LIBINTL_H 1 +#define HAVE_LINUX_AUXVEC_H 1 +#define HAVE_SYS_AUXV_H 1 +#define HAVE_LINUX_FS_H 1 +#define HAVE_LINUX_LIMITS_H 1 +#define HAVE_LINUX_MEMFD_H 1 +#define HAVE_LINUX_NETFILTER_IPV4_H 1 +#define HAVE_LINUX_RANDOM_H 1 +#define HAVE_LINUX_SOUNDCARD_H 1 +#define HAVE_LINUX_SCHED_H 1 +#define HAVE_LINUX_TIPC_H 1 +#define HAVE_LINUX_WAIT_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_NET_ETHERNET_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETPACKET_PACKET_H 1 +#define HAVE_POLL_H 1 +#define HAVE_PTHREAD_H 1 +#define HAVE_PTY_H 1 +#define HAVE_SCHED_H 1 +#define HAVE_SETJMP_H 1 +#define HAVE_SHADOW_H 1 +#define HAVE_SIGNAL_H 1 +#define HAVE_SPAWN_H 1 +#define HAVE_SYS_EPOLL_H 1 +#define HAVE_SYS_EVENTFD_H 1 +#define HAVE_SYS_FILE_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_MMAN_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_PIDFD_H 1 +#define HAVE_SYS_POLL_H 1 +#define HAVE_SYS_RANDOM_H 1 +#define HAVE_SYS_RESOURCE_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_SENDFILE_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_SOUNDCARD_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_STATVFS_H 1 +#define HAVE_SYS_SYSCALL_H 1 +#define HAVE_SYS_SYSMACROS_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMES_H 1 +#define HAVE_SYS_TIMERFD_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_UIO_H 1 +#define HAVE_SYS_UN_H 1 +#define HAVE_SYS_UTSNAME_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_SYS_XATTR_H 1 +#define HAVE_SYSEXITS_H 1 +#define HAVE_SYSLOG_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_UTIME_H 1 +#define HAVE_UTMP_H 1 +#define HAVE_DIRENT_H 1 +#define MAJOR_IN_SYSMACROS 1 +#define HAVE_NET_IF_H 1 +#define HAVE_LINUX_NETLINK_H 1 +#define HAVE_LINUX_QRTR_H 1 +#define HAVE_LINUX_VM_SOCKETS_H 1 +#define HAVE_LINUX_CAN_H 1 +#define HAVE_LINUX_CAN_BCM_H 1 +#define HAVE_LINUX_CAN_J1939_H 1 +#define HAVE_LINUX_CAN_RAW_H 1 +#define HAVE_CLOCK_T 1 +#define HAVE_MAKEDEV 1 +#define HAVE_HTOLE64 1 +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#if defined(SCO_DS) +#undef _OFF_T +#endif +#define RETSIGTYPE void +#define HAVE_SSIZE_T 1 +#define HAVE___UINT128_T 1 +#define HAVE_GCC_UINT128_T 1 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 8 +#define ALIGNOF_LONG 8 +#define SIZEOF_LONG_LONG 8 +#define SIZEOF_VOID_P 8 +#define SIZEOF_SHORT 2 +#define SIZEOF_FLOAT 4 +#define SIZEOF_DOUBLE 8 +#define SIZEOF_FPOS_T 16 +#define SIZEOF_SIZE_T 8 +#define ALIGNOF_SIZE_T 8 +#define SIZEOF_PID_T 4 +#define SIZEOF_UINTPTR_T 8 +#define ALIGNOF_MAX_ALIGN_T 16 +#define HAVE_LONG_DOUBLE 1 diff --git a/conftest.c b/conftest.c new file mode 100644 index 00000000000000..eccb334eb63ef1 --- /dev/null +++ b/conftest.c @@ -0,0 +1,200 @@ +/* confdefs.h */ +#define _NETBSD_SOURCE 1 +#define __BSD_VISIBLE 1 +#define _DARWIN_C_SOURCE 1 +#define _PYTHONFRAMEWORK "" +#define _XOPEN_SOURCE 700 +#define _XOPEN_SOURCE_EXTENDED 1 +#define _POSIX_C_SOURCE 200809L +#define HAVE_STDIO_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_WCHAR_H 1 +#define STDC_HEADERS 1 +#define _ALL_SOURCE 1 +#define _DARWIN_C_SOURCE 1 +#define _GNU_SOURCE 1 +#define _HPUX_ALT_XOPEN_SOCKET_API 1 +#define _NETBSD_SOURCE 1 +#define _OPENBSD_SOURCE 1 +#define _POSIX_PTHREAD_SEMANTICS 1 +#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 +#define __STDC_WANT_IEC_60559_BFP_EXT__ 1 +#define __STDC_WANT_IEC_60559_DFP_EXT__ 1 +#define __STDC_WANT_IEC_60559_EXT__ 1 +#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 +#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 +#define __STDC_WANT_LIB_EXT2__ 1 +#define __STDC_WANT_MATH_SPEC_FUNCS__ 1 +#define _TANDEM_SOURCE 1 +#define __EXTENSIONS__ 1 +#define PY_SUPPORT_TIER 1 +#define STDC_HEADERS 1 +#define HAVE_ALLOCA_H 1 +#define HAVE_ASM_TYPES_H 1 +#define HAVE_DLFCN_H 1 +#define HAVE_ENDIAN_H 1 +#define HAVE_ERRNO_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_GRP_H 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_LIBINTL_H 1 +#define HAVE_LINUX_AUXVEC_H 1 +#define HAVE_SYS_AUXV_H 1 +#define HAVE_LINUX_FS_H 1 +#define HAVE_LINUX_LIMITS_H 1 +#define HAVE_LINUX_MEMFD_H 1 +#define HAVE_LINUX_NETFILTER_IPV4_H 1 +#define HAVE_LINUX_RANDOM_H 1 +#define HAVE_LINUX_SOUNDCARD_H 1 +#define HAVE_LINUX_SCHED_H 1 +#define HAVE_LINUX_TIPC_H 1 +#define HAVE_LINUX_WAIT_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_NET_ETHERNET_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETPACKET_PACKET_H 1 +#define HAVE_POLL_H 1 +#define HAVE_PTHREAD_H 1 +#define HAVE_PTY_H 1 +#define HAVE_SCHED_H 1 +#define HAVE_SETJMP_H 1 +#define HAVE_SHADOW_H 1 +#define HAVE_SIGNAL_H 1 +#define HAVE_SPAWN_H 1 +#define HAVE_SYS_EPOLL_H 1 +#define HAVE_SYS_EVENTFD_H 1 +#define HAVE_SYS_FILE_H 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_MMAN_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_PIDFD_H 1 +#define HAVE_SYS_POLL_H 1 +#define HAVE_SYS_RANDOM_H 1 +#define HAVE_SYS_RESOURCE_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_SENDFILE_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_SOUNDCARD_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_STATVFS_H 1 +#define HAVE_SYS_SYSCALL_H 1 +#define HAVE_SYS_SYSMACROS_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMES_H 1 +#define HAVE_SYS_TIMERFD_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_UIO_H 1 +#define HAVE_SYS_UN_H 1 +#define HAVE_SYS_UTSNAME_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_SYS_XATTR_H 1 +#define HAVE_SYSEXITS_H 1 +#define HAVE_SYSLOG_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_UTIME_H 1 +#define HAVE_UTMP_H 1 +#define HAVE_DIRENT_H 1 +#define MAJOR_IN_SYSMACROS 1 +#define HAVE_NET_IF_H 1 +#define HAVE_LINUX_NETLINK_H 1 +#define HAVE_LINUX_QRTR_H 1 +#define HAVE_LINUX_VM_SOCKETS_H 1 +#define HAVE_LINUX_CAN_H 1 +#define HAVE_LINUX_CAN_BCM_H 1 +#define HAVE_LINUX_CAN_J1939_H 1 +#define HAVE_LINUX_CAN_RAW_H 1 +#define HAVE_CLOCK_T 1 +#define HAVE_MAKEDEV 1 +#define HAVE_HTOLE64 1 +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#if defined(SCO_DS) +#undef _OFF_T +#endif +#define RETSIGTYPE void +#define HAVE_SSIZE_T 1 +#define HAVE___UINT128_T 1 +#define HAVE_GCC_UINT128_T 1 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 8 +#define ALIGNOF_LONG 8 +#define SIZEOF_LONG_LONG 8 +#define SIZEOF_VOID_P 8 +#define SIZEOF_SHORT 2 +#define SIZEOF_FLOAT 4 +#define SIZEOF_DOUBLE 8 +#define SIZEOF_FPOS_T 16 +#define SIZEOF_SIZE_T 8 +#define ALIGNOF_SIZE_T 8 +#define SIZEOF_PID_T 4 +#define SIZEOF_UINTPTR_T 8 +#define ALIGNOF_MAX_ALIGN_T 16 +#define HAVE_LONG_DOUBLE 1 +/* end confdefs.h. */ +#include +#ifdef HAVE_STDIO_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +static long int longval (void) { return (long int) (sizeof (long double)); } +static unsigned long int ulongval (void) { return (long int) (sizeof (long double)); } +#include +#include +int +main (void) +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (((long int) (sizeof (long double))) < 0) + { + long int i = longval (); + if (i != ((long int) (sizeof (long double)))) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ((long int) (sizeof (long double)))) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} From e11f8fe53fcc932b6d24667d95310568b2192ce3 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Tue, 15 Apr 2025 12:50:29 +0800 Subject: [PATCH 03/39] add news --- confdefs.h | 139 ------------------------------------- conftest.c | 200 ----------------------------------------------------- 2 files changed, 339 deletions(-) delete mode 100644 confdefs.h delete mode 100644 conftest.c diff --git a/confdefs.h b/confdefs.h deleted file mode 100644 index 8a4fb56e9fa822..00000000000000 --- a/confdefs.h +++ /dev/null @@ -1,139 +0,0 @@ -/* confdefs.h */ -#define _NETBSD_SOURCE 1 -#define __BSD_VISIBLE 1 -#define _DARWIN_C_SOURCE 1 -#define _PYTHONFRAMEWORK "" -#define _XOPEN_SOURCE 700 -#define _XOPEN_SOURCE_EXTENDED 1 -#define _POSIX_C_SOURCE 200809L -#define HAVE_STDIO_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRING_H 1 -#define HAVE_INTTYPES_H 1 -#define HAVE_STDINT_H 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_UNISTD_H 1 -#define HAVE_WCHAR_H 1 -#define STDC_HEADERS 1 -#define _ALL_SOURCE 1 -#define _DARWIN_C_SOURCE 1 -#define _GNU_SOURCE 1 -#define _HPUX_ALT_XOPEN_SOCKET_API 1 -#define _NETBSD_SOURCE 1 -#define _OPENBSD_SOURCE 1 -#define _POSIX_PTHREAD_SEMANTICS 1 -#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 -#define __STDC_WANT_IEC_60559_BFP_EXT__ 1 -#define __STDC_WANT_IEC_60559_DFP_EXT__ 1 -#define __STDC_WANT_IEC_60559_EXT__ 1 -#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 -#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 -#define __STDC_WANT_LIB_EXT2__ 1 -#define __STDC_WANT_MATH_SPEC_FUNCS__ 1 -#define _TANDEM_SOURCE 1 -#define __EXTENSIONS__ 1 -#define PY_SUPPORT_TIER 1 -#define STDC_HEADERS 1 -#define HAVE_ALLOCA_H 1 -#define HAVE_ASM_TYPES_H 1 -#define HAVE_DLFCN_H 1 -#define HAVE_ENDIAN_H 1 -#define HAVE_ERRNO_H 1 -#define HAVE_FCNTL_H 1 -#define HAVE_GRP_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_LIBINTL_H 1 -#define HAVE_LINUX_AUXVEC_H 1 -#define HAVE_SYS_AUXV_H 1 -#define HAVE_LINUX_FS_H 1 -#define HAVE_LINUX_LIMITS_H 1 -#define HAVE_LINUX_MEMFD_H 1 -#define HAVE_LINUX_NETFILTER_IPV4_H 1 -#define HAVE_LINUX_RANDOM_H 1 -#define HAVE_LINUX_SOUNDCARD_H 1 -#define HAVE_LINUX_SCHED_H 1 -#define HAVE_LINUX_TIPC_H 1 -#define HAVE_LINUX_WAIT_H 1 -#define HAVE_NETDB_H 1 -#define HAVE_NET_ETHERNET_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_NETPACKET_PACKET_H 1 -#define HAVE_POLL_H 1 -#define HAVE_PTHREAD_H 1 -#define HAVE_PTY_H 1 -#define HAVE_SCHED_H 1 -#define HAVE_SETJMP_H 1 -#define HAVE_SHADOW_H 1 -#define HAVE_SIGNAL_H 1 -#define HAVE_SPAWN_H 1 -#define HAVE_SYS_EPOLL_H 1 -#define HAVE_SYS_EVENTFD_H 1 -#define HAVE_SYS_FILE_H 1 -#define HAVE_SYS_IOCTL_H 1 -#define HAVE_SYS_MMAN_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_PIDFD_H 1 -#define HAVE_SYS_POLL_H 1 -#define HAVE_SYS_RANDOM_H 1 -#define HAVE_SYS_RESOURCE_H 1 -#define HAVE_SYS_SELECT_H 1 -#define HAVE_SYS_SENDFILE_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_SYS_SOUNDCARD_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_STATVFS_H 1 -#define HAVE_SYS_SYSCALL_H 1 -#define HAVE_SYS_SYSMACROS_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TIMES_H 1 -#define HAVE_SYS_TIMERFD_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_SYS_UIO_H 1 -#define HAVE_SYS_UN_H 1 -#define HAVE_SYS_UTSNAME_H 1 -#define HAVE_SYS_WAIT_H 1 -#define HAVE_SYS_XATTR_H 1 -#define HAVE_SYSEXITS_H 1 -#define HAVE_SYSLOG_H 1 -#define HAVE_TERMIOS_H 1 -#define HAVE_UTIME_H 1 -#define HAVE_UTMP_H 1 -#define HAVE_DIRENT_H 1 -#define MAJOR_IN_SYSMACROS 1 -#define HAVE_NET_IF_H 1 -#define HAVE_LINUX_NETLINK_H 1 -#define HAVE_LINUX_QRTR_H 1 -#define HAVE_LINUX_VM_SOCKETS_H 1 -#define HAVE_LINUX_CAN_H 1 -#define HAVE_LINUX_CAN_BCM_H 1 -#define HAVE_LINUX_CAN_J1939_H 1 -#define HAVE_LINUX_CAN_RAW_H 1 -#define HAVE_CLOCK_T 1 -#define HAVE_MAKEDEV 1 -#define HAVE_HTOLE64 1 -#define _LARGEFILE_SOURCE 1 -#define _FILE_OFFSET_BITS 64 -#if defined(SCO_DS) -#undef _OFF_T -#endif -#define RETSIGTYPE void -#define HAVE_SSIZE_T 1 -#define HAVE___UINT128_T 1 -#define HAVE_GCC_UINT128_T 1 -#define SIZEOF_INT 4 -#define SIZEOF_LONG 8 -#define ALIGNOF_LONG 8 -#define SIZEOF_LONG_LONG 8 -#define SIZEOF_VOID_P 8 -#define SIZEOF_SHORT 2 -#define SIZEOF_FLOAT 4 -#define SIZEOF_DOUBLE 8 -#define SIZEOF_FPOS_T 16 -#define SIZEOF_SIZE_T 8 -#define ALIGNOF_SIZE_T 8 -#define SIZEOF_PID_T 4 -#define SIZEOF_UINTPTR_T 8 -#define ALIGNOF_MAX_ALIGN_T 16 -#define HAVE_LONG_DOUBLE 1 diff --git a/conftest.c b/conftest.c deleted file mode 100644 index eccb334eb63ef1..00000000000000 --- a/conftest.c +++ /dev/null @@ -1,200 +0,0 @@ -/* confdefs.h */ -#define _NETBSD_SOURCE 1 -#define __BSD_VISIBLE 1 -#define _DARWIN_C_SOURCE 1 -#define _PYTHONFRAMEWORK "" -#define _XOPEN_SOURCE 700 -#define _XOPEN_SOURCE_EXTENDED 1 -#define _POSIX_C_SOURCE 200809L -#define HAVE_STDIO_H 1 -#define HAVE_STDLIB_H 1 -#define HAVE_STRING_H 1 -#define HAVE_INTTYPES_H 1 -#define HAVE_STDINT_H 1 -#define HAVE_STRINGS_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_UNISTD_H 1 -#define HAVE_WCHAR_H 1 -#define STDC_HEADERS 1 -#define _ALL_SOURCE 1 -#define _DARWIN_C_SOURCE 1 -#define _GNU_SOURCE 1 -#define _HPUX_ALT_XOPEN_SOCKET_API 1 -#define _NETBSD_SOURCE 1 -#define _OPENBSD_SOURCE 1 -#define _POSIX_PTHREAD_SEMANTICS 1 -#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1 -#define __STDC_WANT_IEC_60559_BFP_EXT__ 1 -#define __STDC_WANT_IEC_60559_DFP_EXT__ 1 -#define __STDC_WANT_IEC_60559_EXT__ 1 -#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1 -#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1 -#define __STDC_WANT_LIB_EXT2__ 1 -#define __STDC_WANT_MATH_SPEC_FUNCS__ 1 -#define _TANDEM_SOURCE 1 -#define __EXTENSIONS__ 1 -#define PY_SUPPORT_TIER 1 -#define STDC_HEADERS 1 -#define HAVE_ALLOCA_H 1 -#define HAVE_ASM_TYPES_H 1 -#define HAVE_DLFCN_H 1 -#define HAVE_ENDIAN_H 1 -#define HAVE_ERRNO_H 1 -#define HAVE_FCNTL_H 1 -#define HAVE_GRP_H 1 -#define HAVE_LANGINFO_H 1 -#define HAVE_LIBINTL_H 1 -#define HAVE_LINUX_AUXVEC_H 1 -#define HAVE_SYS_AUXV_H 1 -#define HAVE_LINUX_FS_H 1 -#define HAVE_LINUX_LIMITS_H 1 -#define HAVE_LINUX_MEMFD_H 1 -#define HAVE_LINUX_NETFILTER_IPV4_H 1 -#define HAVE_LINUX_RANDOM_H 1 -#define HAVE_LINUX_SOUNDCARD_H 1 -#define HAVE_LINUX_SCHED_H 1 -#define HAVE_LINUX_TIPC_H 1 -#define HAVE_LINUX_WAIT_H 1 -#define HAVE_NETDB_H 1 -#define HAVE_NET_ETHERNET_H 1 -#define HAVE_NETINET_IN_H 1 -#define HAVE_NETPACKET_PACKET_H 1 -#define HAVE_POLL_H 1 -#define HAVE_PTHREAD_H 1 -#define HAVE_PTY_H 1 -#define HAVE_SCHED_H 1 -#define HAVE_SETJMP_H 1 -#define HAVE_SHADOW_H 1 -#define HAVE_SIGNAL_H 1 -#define HAVE_SPAWN_H 1 -#define HAVE_SYS_EPOLL_H 1 -#define HAVE_SYS_EVENTFD_H 1 -#define HAVE_SYS_FILE_H 1 -#define HAVE_SYS_IOCTL_H 1 -#define HAVE_SYS_MMAN_H 1 -#define HAVE_SYS_PARAM_H 1 -#define HAVE_SYS_PIDFD_H 1 -#define HAVE_SYS_POLL_H 1 -#define HAVE_SYS_RANDOM_H 1 -#define HAVE_SYS_RESOURCE_H 1 -#define HAVE_SYS_SELECT_H 1 -#define HAVE_SYS_SENDFILE_H 1 -#define HAVE_SYS_SOCKET_H 1 -#define HAVE_SYS_SOUNDCARD_H 1 -#define HAVE_SYS_STAT_H 1 -#define HAVE_SYS_STATVFS_H 1 -#define HAVE_SYS_SYSCALL_H 1 -#define HAVE_SYS_SYSMACROS_H 1 -#define HAVE_SYS_TIME_H 1 -#define HAVE_SYS_TIMES_H 1 -#define HAVE_SYS_TIMERFD_H 1 -#define HAVE_SYS_TYPES_H 1 -#define HAVE_SYS_UIO_H 1 -#define HAVE_SYS_UN_H 1 -#define HAVE_SYS_UTSNAME_H 1 -#define HAVE_SYS_WAIT_H 1 -#define HAVE_SYS_XATTR_H 1 -#define HAVE_SYSEXITS_H 1 -#define HAVE_SYSLOG_H 1 -#define HAVE_TERMIOS_H 1 -#define HAVE_UTIME_H 1 -#define HAVE_UTMP_H 1 -#define HAVE_DIRENT_H 1 -#define MAJOR_IN_SYSMACROS 1 -#define HAVE_NET_IF_H 1 -#define HAVE_LINUX_NETLINK_H 1 -#define HAVE_LINUX_QRTR_H 1 -#define HAVE_LINUX_VM_SOCKETS_H 1 -#define HAVE_LINUX_CAN_H 1 -#define HAVE_LINUX_CAN_BCM_H 1 -#define HAVE_LINUX_CAN_J1939_H 1 -#define HAVE_LINUX_CAN_RAW_H 1 -#define HAVE_CLOCK_T 1 -#define HAVE_MAKEDEV 1 -#define HAVE_HTOLE64 1 -#define _LARGEFILE_SOURCE 1 -#define _FILE_OFFSET_BITS 64 -#if defined(SCO_DS) -#undef _OFF_T -#endif -#define RETSIGTYPE void -#define HAVE_SSIZE_T 1 -#define HAVE___UINT128_T 1 -#define HAVE_GCC_UINT128_T 1 -#define SIZEOF_INT 4 -#define SIZEOF_LONG 8 -#define ALIGNOF_LONG 8 -#define SIZEOF_LONG_LONG 8 -#define SIZEOF_VOID_P 8 -#define SIZEOF_SHORT 2 -#define SIZEOF_FLOAT 4 -#define SIZEOF_DOUBLE 8 -#define SIZEOF_FPOS_T 16 -#define SIZEOF_SIZE_T 8 -#define ALIGNOF_SIZE_T 8 -#define SIZEOF_PID_T 4 -#define SIZEOF_UINTPTR_T 8 -#define ALIGNOF_MAX_ALIGN_T 16 -#define HAVE_LONG_DOUBLE 1 -/* end confdefs.h. */ -#include -#ifdef HAVE_STDIO_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -static long int longval (void) { return (long int) (sizeof (long double)); } -static unsigned long int ulongval (void) { return (long int) (sizeof (long double)); } -#include -#include -int -main (void) -{ - - FILE *f = fopen ("conftest.val", "w"); - if (! f) - return 1; - if (((long int) (sizeof (long double))) < 0) - { - long int i = longval (); - if (i != ((long int) (sizeof (long double)))) - return 1; - fprintf (f, "%ld", i); - } - else - { - unsigned long int i = ulongval (); - if (i != ((long int) (sizeof (long double)))) - return 1; - fprintf (f, "%lu", i); - } - /* Do not output a trailing newline, as this causes \r\n confusion - on some platforms. */ - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} From 4e008fd710406d74f0008391757bed6c7923afa1 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Tue, 15 Apr 2025 13:31:59 +0800 Subject: [PATCH 04/39] lint --- Lib/http/server.py | 2 +- Lib/test/test_httpservers.py | 24 +++++++++---------- ...-04-15-12-47-09.gh-issue-131178.Td8j5x.rst | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index d0a7777f67c76a..28f431a64c211a 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1423,4 +1423,4 @@ def finish_request(self, request, client_address): if __name__ == '__main__': - _main() \ No newline at end of file + _main() diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 51a63715a3a4a8..fa4523b34f329a 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1547,7 +1547,7 @@ def setUp(self): self.default_server = http.server.CommandLineServerClass self.tls_cert = '-----BEGIN CERTIFICATE-----\n' + '-----END CERTIFICATE-----\n' self.tls_key = '-----BEGIN RSA PRIVATE KEY-----\n' + '-----END RSA PRIVATE KEY-----\n' - + self.tls_password = '' tls_password_file_object = tempfile.NamedTemporaryFile(mode='w+', delete=False) tls_password_file_object.write(self.tls_password) @@ -1624,7 +1624,7 @@ def test_cgi_flag(self, mock_func): self.invoke_httpd(['--cgi']) mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, ServerClass=self.default_server, protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + tls_cert=None, tls_key=None, tls_password=None) @mock.patch('http.server.test') def test_tls_flag(self, mock_func): @@ -1632,7 +1632,7 @@ def test_tls_flag(self, mock_func): tls_key_options = ['--tls-key', ] tls_password_options = ['--tls-password-file', ] # Normal: --tls-cert and --tls-key - + for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key]) @@ -1640,36 +1640,36 @@ def test_tls_flag(self, mock_func): protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=None) mock_func.reset_mock() - + # Normal: --tls-cert, --tls-key and --tls-password-file - + for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: for tls_password_option in tls_password_options: self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file]) - + mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=self.tls_password) mock_func.reset_mock() - + # Abnormal: --tls-key without --tls-cert - + for tls_key_option in tls_key_options: for tls_cert_option in tls_cert_options: with self.assertRaises(SystemExit): self.invoke_httpd([tls_key_option, self.tls_key]) mock_func.reset_mock() - + # Abnormal: --tls-password-file without --tls-cert - + for tls_password_option in tls_password_options: with self.assertRaises(SystemExit): self.invoke_httpd([tls_password_option, self.tls_password_file]) mock_func.reset_mock() - + # Abnormal: --tls-password-file cannot be opened - + non_existent_file = os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) retry_count = 0 while os.path.exists(non_existent_file) and retry_count < 10: diff --git a/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst b/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst index 9742c7791995c7..1fdd23447a5086 100644 --- a/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst +++ b/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst @@ -1 +1 @@ -Add tests for the command line interface of the ``http.server`` module. \ No newline at end of file +Add tests for the command line interface of the ``http.server`` module. From ad76ab1906b8067882eded2d872c559f40bcd4e1 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Wed, 16 Apr 2025 16:02:25 +0800 Subject: [PATCH 05/39] move a new class into test --- Lib/http/server.py | 52 +++++++++++++++--------------------- Lib/test/test_httpservers.py | 28 +++++++++---------- 2 files changed, 35 insertions(+), 45 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index 28f431a64c211a..c7a92f6d4f8b2d 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -152,21 +152,6 @@ def server_bind(self): class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): daemon_threads = True -class CommandLineServerClass(ThreadingHTTPServer): - def __init__(self, server_address, RequestHandlerClass, directory=None): - super().__init__(server_address, RequestHandlerClass) - self.directory = directory - def server_bind(self): - # suppress exception when protocol is IPv4 - with contextlib.suppress(Exception): - self.socket.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) - return super().server_bind() - - def finish_request(self, request, client_address): - self.RequestHandlerClass(request, client_address, self, - directory=self.directory) - class HTTPSServer(HTTPServer): def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, *, certfile, keyfile=None, @@ -1322,7 +1307,7 @@ def _get_best_family(*address): def test(HandlerClass=BaseHTTPRequestHandler, ServerClass=ThreadingHTTPServer, - protocol="HTTP/1.0", port=8000, bind=None, + protocol="HTTP/1.0", port=8000, bind=None, directory=None, tls_cert=None, tls_key=None, tls_password=None): """Test the HTTP request handler class. @@ -1335,6 +1320,24 @@ def test(HandlerClass=BaseHTTPRequestHandler, if tls_cert: server = ThreadingHTTPSServer(addr, HandlerClass, certfile=tls_cert, keyfile=tls_key, password=tls_password) + elif ServerClass is ThreadingHTTPServer: + # ensure dual-stack is not disabled; ref #38907 + class DualStackServer(ThreadingHTTPServer): + def __init__(self, server_address, RequestHandlerClass, directory=None): + super().__init__(server_address, RequestHandlerClass) + self.directory = directory + + def server_bind(self): + # suppress exception when protocol is IPv4 + with contextlib.suppress(Exception): + self.socket.setsockopt( + socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + return super().server_bind() + + def finish_request(self, request, client_address): + self.RequestHandlerClass(request, client_address, self, + directory=self.directory) + server = DualStackServer(addr, HandlerClass, directory=directory) else: server = ServerClass(addr, HandlerClass) @@ -1396,26 +1399,13 @@ def _main(args=None): else: handler_class = SimpleHTTPRequestHandler - # ensure dual-stack is not disabled; ref #38907 - class DualStackServer(ThreadingHTTPServer): - - def server_bind(self): - # suppress exception when protocol is IPv4 - with contextlib.suppress(Exception): - self.socket.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) - return super().server_bind() - - def finish_request(self, request, client_address): - self.RequestHandlerClass(request, client_address, self, - directory=args.directory) - test( HandlerClass=handler_class, - ServerClass=CommandLineServerClass, + ServerClass=ThreadingHTTPServer, port=args.port, bind=args.bind, protocol=args.protocol, + directory=args.directory, tls_cert=args.tls_cert, tls_key=args.tls_key, tls_password=tls_key_password, diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index fa4523b34f329a..a07b3ff37141d0 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1469,7 +1469,7 @@ def test_windows_colon(self): class MiscTestCase(unittest.TestCase): def test_all(self): expected = [] - denylist = {'executable', 'nobody_uid', 'test', 'CommandLineServerClass', 'CommandLineServerClass'} + denylist = {'executable', 'nobody_uid', 'test', 'CommandLineServerClass'} for name in dir(server): if name.startswith('_') or name in denylist: continue @@ -1543,12 +1543,12 @@ def setUp(self): self.default_port = 8000 self.default_bind = None self.default_protocol = 'HTTP/1.0' + self.default_directory = os.getcwd() self.default_handler = SimpleHTTPRequestHandler - self.default_server = http.server.CommandLineServerClass - self.tls_cert = '-----BEGIN CERTIFICATE-----\n' + '-----END CERTIFICATE-----\n' - self.tls_key = '-----BEGIN RSA PRIVATE KEY-----\n' + '-----END RSA PRIVATE KEY-----\n' - - self.tls_password = '' + self.default_server = http.server.ThreadingHTTPServer + self.tls_cert = certdata_file('ssl_cert.pem') + self.tls_key = certdata_file('ssl_key.pem') + self.tls_password = 'somepass' tls_password_file_object = tempfile.NamedTemporaryFile(mode='w+', delete=False) tls_password_file_object.write(self.tls_password) self.tls_password_file = tls_password_file_object.name @@ -1576,8 +1576,8 @@ def test_port_flag(self, mock_func): with self.subTest(port=port): self.invoke_httpd([str(port)]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=port, bind=self.default_bind, tls_cert=None, - tls_key=None, tls_password=None) + protocol=self.default_protocol, port=port, bind=self.default_bind, directory=self.default_directory, + tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1589,7 +1589,7 @@ def test_directory_flag(self, mock_func): with self.subTest(flag=flag, directory=directory): self.invoke_httpd([flag, directory]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=directory, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1602,7 +1602,7 @@ def test_bind_flag(self, mock_func): with self.subTest(flag=flag, bind_address=bind_address): self.invoke_httpd([flag, bind_address]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=bind_address, + protocol=self.default_protocol, port=self.default_port, bind=bind_address, directory=self.default_directory, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1615,7 +1615,7 @@ def test_protocol_flag(self, mock_func): with self.subTest(flag=flag, protocol=protocol): self.invoke_httpd([flag, protocol]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=protocol, port=self.default_port, bind=self.default_bind, + protocol=protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1623,7 +1623,7 @@ def test_protocol_flag(self, mock_func): def test_cgi_flag(self, mock_func): self.invoke_httpd(['--cgi']) mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, tls_cert=None, tls_key=None, tls_password=None) @mock.patch('http.server.test') @@ -1637,7 +1637,7 @@ def test_tls_flag(self, mock_func): for tls_key_option in tls_key_options: self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=None) mock_func.reset_mock() @@ -1649,7 +1649,7 @@ def test_tls_flag(self, mock_func): self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=self.tls_password) mock_func.reset_mock() From 5e563d3c5e10c6e82d81dfe6656454fe407968ce Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:31:43 +0800 Subject: [PATCH 06/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index c7a92f6d4f8b2d..53912cb6178cd7 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -106,8 +106,6 @@ import sys import time import urllib.parse -import argparse -import contextlib from http import HTTPStatus From 86b856e00c341271711bb5cce8ec3f83a5a0337e Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:00 +0800 Subject: [PATCH 07/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index 53912cb6178cd7..ef19d25573306e 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1305,7 +1305,7 @@ def _get_best_family(*address): def test(HandlerClass=BaseHTTPRequestHandler, ServerClass=ThreadingHTTPServer, - protocol="HTTP/1.0", port=8000, bind=None, directory=None, + protocol="HTTP/1.0", port=8000, bind=None, tls_cert=None, tls_key=None, tls_password=None): """Test the HTTP request handler class. From 4d5c2b5bb2a3be0ff287ec18fedb5f7571a0a473 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:14 +0800 Subject: [PATCH 08/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index ef19d25573306e..ea23e2b1fc9b13 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1318,24 +1318,6 @@ def test(HandlerClass=BaseHTTPRequestHandler, if tls_cert: server = ThreadingHTTPSServer(addr, HandlerClass, certfile=tls_cert, keyfile=tls_key, password=tls_password) - elif ServerClass is ThreadingHTTPServer: - # ensure dual-stack is not disabled; ref #38907 - class DualStackServer(ThreadingHTTPServer): - def __init__(self, server_address, RequestHandlerClass, directory=None): - super().__init__(server_address, RequestHandlerClass) - self.directory = directory - - def server_bind(self): - # suppress exception when protocol is IPv4 - with contextlib.suppress(Exception): - self.socket.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) - return super().server_bind() - - def finish_request(self, request, client_address): - self.RequestHandlerClass(request, client_address, self, - directory=self.directory) - server = DualStackServer(addr, HandlerClass, directory=directory) else: server = ServerClass(addr, HandlerClass) From 574d6beed6c7025a5fdc6230644218f7883ee482 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:25 +0800 Subject: [PATCH 09/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/http/server.py b/Lib/http/server.py index ea23e2b1fc9b13..37f275c8179fcf 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1336,6 +1336,9 @@ def test(HandlerClass=BaseHTTPRequestHandler, sys.exit(0) def _main(args=None): + import argparse + import contextlib + parser = argparse.ArgumentParser() parser.add_argument('--cgi', action='store_true', help='run as CGI server') From c1f3358da768b5d27003e4ee7739c6a84a2777a4 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:33 +0800 Subject: [PATCH 10/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/http/server.py b/Lib/http/server.py index 37f275c8179fcf..d78e3218333eae 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -150,6 +150,7 @@ def server_bind(self): class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): daemon_threads = True + class HTTPSServer(HTTPServer): def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, *, certfile, keyfile=None, From 01d5fb85de67944fdfedd82ca8f60f30aa332c0c Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:46 +0800 Subject: [PATCH 11/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Lib/http/server.py b/Lib/http/server.py index d78e3218333eae..fe347eb03686d4 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1383,6 +1383,20 @@ def _main(args=None): else: handler_class = SimpleHTTPRequestHandler + # ensure dual-stack is not disabled; ref #38907 + class DualStackServer(ThreadingHTTPServer): + + def server_bind(self): + # suppress exception when protocol is IPv4 + with contextlib.suppress(Exception): + self.socket.setsockopt( + socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + return super().server_bind() + + def finish_request(self, request, client_address): + self.RequestHandlerClass(request, client_address, self, + directory=args.directory) + test( HandlerClass=handler_class, ServerClass=ThreadingHTTPServer, From 540700f4dd73f5ee9de24bff85b7dbc123fe3ce7 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:32:54 +0800 Subject: [PATCH 12/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index fe347eb03686d4..5df23c7ca20c97 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1399,11 +1399,10 @@ def finish_request(self, request, client_address): test( HandlerClass=handler_class, - ServerClass=ThreadingHTTPServer, + ServerClass=DualStackServer, port=args.port, bind=args.bind, protocol=args.protocol, - directory=args.directory, tls_cert=args.tls_cert, tls_key=args.tls_key, tls_password=tls_key_password, From 1bdb0ec603220ef0f8b2c25efab95291c8d36fad Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:33:03 +0800 Subject: [PATCH 13/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index a07b3ff37141d0..7a03fb7b690458 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -8,7 +8,6 @@ SimpleHTTPRequestHandler, CGIHTTPRequestHandler from http import server, HTTPStatus -import http.server import os import socket import sys From 38aea9ef833f8d56d5dd174baff508aa0ad111d5 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:34:18 +0800 Subject: [PATCH 14/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7a03fb7b690458..1bf2897bdb54d9 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -27,8 +27,6 @@ import threading from unittest import mock from io import BytesIO, StringIO -import textwrap -import contextlib import unittest from test import support From 3277327db79d7f912ae0edd6b89576637cc1a583 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Wed, 16 Apr 2025 22:09:33 +0800 Subject: [PATCH 15/39] update --- Lib/test/test_httpservers.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 1bf2897bdb54d9..fef52831fd756a 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -8,6 +8,7 @@ SimpleHTTPRequestHandler, CGIHTTPRequestHandler from http import server, HTTPStatus +import contextlib import os import socket import sys @@ -22,6 +23,7 @@ import http, http.client import urllib.parse import tempfile +import textwrap import time import datetime import threading @@ -1466,7 +1468,7 @@ def test_windows_colon(self): class MiscTestCase(unittest.TestCase): def test_all(self): expected = [] - denylist = {'executable', 'nobody_uid', 'test', 'CommandLineServerClass'} + denylist = {'executable', 'nobody_uid', 'test'} for name in dir(server): if name.startswith('_') or name in denylist: continue @@ -1542,7 +1544,7 @@ def setUp(self): self.default_protocol = 'HTTP/1.0' self.default_directory = os.getcwd() self.default_handler = SimpleHTTPRequestHandler - self.default_server = http.server.ThreadingHTTPServer + self.default_server = unittest.mock.ANY self.tls_cert = certdata_file('ssl_cert.pem') self.tls_key = certdata_file('ssl_key.pem') self.tls_password = 'somepass' @@ -1573,7 +1575,7 @@ def test_port_flag(self, mock_func): with self.subTest(port=port): self.invoke_httpd([str(port)]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=port, bind=self.default_bind, directory=self.default_directory, + protocol=self.default_protocol, port=port, bind=self.default_bind, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1586,7 +1588,7 @@ def test_directory_flag(self, mock_func): with self.subTest(flag=flag, directory=directory): self.invoke_httpd([flag, directory]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=directory, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1599,7 +1601,7 @@ def test_bind_flag(self, mock_func): with self.subTest(flag=flag, bind_address=bind_address): self.invoke_httpd([flag, bind_address]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=bind_address, directory=self.default_directory, + protocol=self.default_protocol, port=self.default_port, bind=bind_address, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1612,7 +1614,7 @@ def test_protocol_flag(self, mock_func): with self.subTest(flag=flag, protocol=protocol): self.invoke_httpd([flag, protocol]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, + protocol=protocol, port=self.default_port, bind=self.default_bind, tls_cert=None, tls_key=None, tls_password=None) mock_func.reset_mock() @@ -1620,9 +1622,10 @@ def test_protocol_flag(self, mock_func): def test_cgi_flag(self, mock_func): self.invoke_httpd(['--cgi']) mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=None, tls_key=None, tls_password=None) + @unittest.skipIf(ssl is None, "requires ssl") @mock.patch('http.server.test') def test_tls_flag(self, mock_func): tls_cert_options = ['--tls-cert', ] @@ -1634,7 +1637,7 @@ def test_tls_flag(self, mock_func): for tls_key_option in tls_key_options: self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=None) mock_func.reset_mock() @@ -1646,7 +1649,7 @@ def test_tls_flag(self, mock_func): self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file]) mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, directory=self.default_directory, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=self.tls_password) mock_func.reset_mock() From 771263d6c6482980c026d3aaa57fdeeddce060d3 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:44:57 +0800 Subject: [PATCH 16/39] Update Lib/http/server.py Co-authored-by: Semyon Moroz --- Lib/http/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/http/server.py b/Lib/http/server.py index 5df23c7ca20c97..7537b7f3df464a 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -1362,7 +1362,7 @@ def _main(args=None): parser.add_argument('port', default=8000, type=int, nargs='?', help='bind to this port ' '(default: %(default)s)') - args = parser.parse_args(args=args) + args = parser.parse_args(args) if not args.tls_cert and args.tls_key: parser.error("--tls-key requires --tls-cert to be set") From 3679a76b076e9f9b2ae0bcd568c96f1460636e23 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Wed, 16 Apr 2025 23:08:21 +0800 Subject: [PATCH 17/39] remove news --- .../next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst diff --git a/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst b/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst deleted file mode 100644 index 1fdd23447a5086..00000000000000 --- a/Misc/NEWS.d/next/Library/2025-04-15-12-47-09.gh-issue-131178.Td8j5x.rst +++ /dev/null @@ -1 +0,0 @@ -Add tests for the command line interface of the ``http.server`` module. From 6c58710d37b73881a96cfbef81400c7e3f4e360f Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:50:20 +0800 Subject: [PATCH 18/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index fef52831fd756a..35132b046d1b22 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1542,7 +1542,6 @@ def setUp(self): self.default_port = 8000 self.default_bind = None self.default_protocol = 'HTTP/1.0' - self.default_directory = os.getcwd() self.default_handler = SimpleHTTPRequestHandler self.default_server = unittest.mock.ANY self.tls_cert = certdata_file('ssl_cert.pem') From 3e4a6aab3f2afea21641dd01fee46b76c5ce8e5f Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:51:12 +0800 Subject: [PATCH 19/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 35132b046d1b22..ad9c21d62698bc 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1558,14 +1558,11 @@ def tearDown(self): os.remove(self.tls_password_file) return super().tearDown() - def text_normalizer(self, string): - return textwrap.dedent(string).strip() - - def invoke_httpd(self, args=[]): + def invoke_httpd(self, *args): output = StringIO() with contextlib.redirect_stdout(output): server._main(args) - return self.text_normalizer(output.getvalue()) + return textwrap.dedent(output.getvalue()).strip() @mock.patch('http.server.test') def test_port_flag(self, mock_func): From 8e93c5d40256bf2e424b4328c4af284e8e9c97cc Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:51:37 +0800 Subject: [PATCH 20/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index ad9c21d62698bc..72200e2ff504b0 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1681,7 +1681,7 @@ def test_help_flag(self): for option in options: with self.assertRaises(SystemExit): output = self.invoke_httpd([option]) - self.assertIn('usage:', output) + self.assertStartsWith(output, 'usage: ') def test_unknown_flag(self): with self.assertRaises(SystemExit): From 439c36df843a8c41a6bc6b0b80281ad6bd7a3b95 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Thu, 17 Apr 2025 15:49:43 +0800 Subject: [PATCH 21/39] add no argument test and redirect stderr --- Lib/test/test_httpservers.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 72200e2ff504b0..680232294d8670 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1561,8 +1561,14 @@ def tearDown(self): def invoke_httpd(self, *args): output = StringIO() with contextlib.redirect_stdout(output): +<<<<<<< HEAD server._main(args) return textwrap.dedent(output.getvalue()).strip() +======= + with contextlib.redirect_stderr(output): + server._main(args) + return self.text_normalizer(output.getvalue()) +>>>>>>> 26e6b963f04 (add no argument test and redirect stderr) @mock.patch('http.server.test') def test_port_flag(self, mock_func): @@ -1676,6 +1682,15 @@ def test_tls_flag(self, mock_func): with self.assertRaises(SystemExit): self.invoke_httpd([tls_cert_option, self.tls_cert, tls_password_option, non_existent_file]) + @mock.patch('http.server.test') + def test_no_arguments(self, mock_func): + self.invoke_httpd() + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, + tls_cert=None, tls_key=None, tls_password=None) + mock_func.reset_mock() + def test_help_flag(self): options = ['-h', '--help'] for option in options: From 7e8aedc18f770a37be23835f6824df16773a04f5 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Thu, 17 Apr 2025 16:16:42 +0800 Subject: [PATCH 22/39] wrap some lines to fit into 79 characters --- Lib/test/test_httpservers.py | 117 ++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 680232294d8670..39137f44bba379 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1560,25 +1560,24 @@ def tearDown(self): def invoke_httpd(self, *args): output = StringIO() - with contextlib.redirect_stdout(output): -<<<<<<< HEAD + with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output): server._main(args) return textwrap.dedent(output.getvalue()).strip() -======= - with contextlib.redirect_stderr(output): - server._main(args) - return self.text_normalizer(output.getvalue()) ->>>>>>> 26e6b963f04 (add no argument test and redirect stderr) @mock.patch('http.server.test') def test_port_flag(self, mock_func): ports = [8000, 65535,] for port in ports: with self.subTest(port=port): - self.invoke_httpd([str(port)]) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + self.invoke_httpd(str(port)) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=port, + bind=self.default_bind, + tls_cert=None, + tls_key=None, + tls_password=None) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1588,10 +1587,15 @@ def test_directory_flag(self, mock_func): for flag in options: for directory in directories: with self.subTest(flag=flag, directory=directory): - self.invoke_httpd([flag, directory]) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + self.invoke_httpd(flag, directory) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=None, + tls_key=None, + tls_password=None) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1601,10 +1605,15 @@ def test_bind_flag(self, mock_func): for flag in options: for bind_address in bind_addresses: with self.subTest(flag=flag, bind_address=bind_address): - self.invoke_httpd([flag, bind_address]) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=bind_address, - tls_cert=None, tls_key=None, tls_password=None) + self.invoke_httpd(flag, bind_address) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=self.default_port, + bind=bind_address, + tls_cert=None, + tls_key=None, + tls_password=None) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1614,18 +1623,28 @@ def test_protocol_flag(self, mock_func): for flag in options: for protocol in protocols: with self.subTest(flag=flag, protocol=protocol): - self.invoke_httpd([flag, protocol]) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=protocol, port=self.default_port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + self.invoke_httpd(flag, protocol) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=None, + tls_key=None, + tls_password=None) mock_func.reset_mock() @mock.patch('http.server.test') def test_cgi_flag(self, mock_func): - self.invoke_httpd(['--cgi']) - mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + self.invoke_httpd('--cgi') + mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=None, + tls_key=None, + tls_password=None) @unittest.skipIf(ssl is None, "requires ssl") @mock.patch('http.server.test') @@ -1637,10 +1656,15 @@ def test_tls_flag(self, mock_func): for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: - self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key]) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=None) + self.invoke_httpd(tls_cert_option, self.tls_cert, tls_key_option, self.tls_key) + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=self.tls_cert, + tls_key=self.tls_key, + tls_password=None) mock_func.reset_mock() # Normal: --tls-cert, --tls-key and --tls-password-file @@ -1648,11 +1672,16 @@ def test_tls_flag(self, mock_func): for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: for tls_password_option in tls_password_options: - self.invoke_httpd([tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file]) - - mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=self.tls_cert, tls_key=self.tls_key, tls_password=self.tls_password) + self.invoke_httpd(tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file) + + mock_func.assert_called_once_with(HandlerClass=self.default_handler, + ServerClass=self.default_server, + protocol=self.default_protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=self.tls_cert, + tls_key=self.tls_key, + tls_password=self.tls_password) mock_func.reset_mock() # Abnormal: --tls-key without --tls-cert @@ -1660,14 +1689,14 @@ def test_tls_flag(self, mock_func): for tls_key_option in tls_key_options: for tls_cert_option in tls_cert_options: with self.assertRaises(SystemExit): - self.invoke_httpd([tls_key_option, self.tls_key]) + self.invoke_httpd(tls_key_option, self.tls_key) mock_func.reset_mock() # Abnormal: --tls-password-file without --tls-cert for tls_password_option in tls_password_options: with self.assertRaises(SystemExit): - self.invoke_httpd([tls_password_option, self.tls_password_file]) + self.invoke_httpd(tls_password_option, self.tls_password_file) mock_func.reset_mock() # Abnormal: --tls-password-file cannot be opened @@ -1687,20 +1716,24 @@ def test_no_arguments(self, mock_func): self.invoke_httpd() mock_func.assert_called_once_with(HandlerClass=self.default_handler, ServerClass=self.default_server, - protocol=self.default_protocol, port=self.default_port, bind=self.default_bind, - tls_cert=None, tls_key=None, tls_password=None) + protocol=self.default_protocol, + port=self.default_port, + bind=self.default_bind, + tls_cert=None, + tls_key=None, + tls_password=None) mock_func.reset_mock() def test_help_flag(self): options = ['-h', '--help'] for option in options: with self.assertRaises(SystemExit): - output = self.invoke_httpd([option]) + output = self.invoke_httpd(option) self.assertStartsWith(output, 'usage: ') def test_unknown_flag(self): with self.assertRaises(SystemExit): - self.invoke_httpd(['--unknown-flag']) + self.invoke_httpd('--unknown-flag') def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 8f3e7adb1c408461d3833098843502e2b561d3ff Mon Sep 17 00:00:00 2001 From: ggqlq Date: Thu, 17 Apr 2025 18:18:11 +0800 Subject: [PATCH 23/39] wrap some lines to fit into 79 characters(2) --- Lib/test/test_httpservers.py | 128 +++++++++++++++++------------------ 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 39137f44bba379..cbe757cfb2940e 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1537,6 +1537,7 @@ def test_server_test_ipv4(self, _): server.test(ServerClass=mock_server, bind=bind) self.assertEqual(mock_server.address_family, socket.AF_INET) +@mock.patch('http.server.test') class CommandLineTestCase(unittest.TestCase): def setUp(self): self.default_port = 8000 @@ -1547,10 +1548,21 @@ def setUp(self): self.tls_cert = certdata_file('ssl_cert.pem') self.tls_key = certdata_file('ssl_key.pem') self.tls_password = 'somepass' - tls_password_file_object = tempfile.NamedTemporaryFile(mode='w+', delete=False) + tls_password_file_object = \ + tempfile.NamedTemporaryFile(mode='w+', delete=False) tls_password_file_object.write(self.tls_password) self.tls_password_file = tls_password_file_object.name tls_password_file_object.close() + self.args = { + 'HandlerClass': self.default_handler, + 'ServerClass': self.default_server, + 'protocol': self.default_protocol, + 'port': self.default_port, + 'bind': self.default_bind, + 'tls_cert': None, + 'tls_key': None, + 'tls_password': None, + } return super().setUp() def tearDown(self): @@ -1558,65 +1570,51 @@ def tearDown(self): os.remove(self.tls_password_file) return super().tearDown() + def get_random_temporary_file_name(self): + return os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + def invoke_httpd(self, *args): output = StringIO() - with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output): + with contextlib.redirect_stdout(output), \ + contextlib.redirect_stderr(output): server._main(args) return textwrap.dedent(output.getvalue()).strip() - @mock.patch('http.server.test') + def test_port_flag(self, mock_func): ports = [8000, 65535,] for port in ports: with self.subTest(port=port): self.invoke_httpd(str(port)) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=self.default_protocol, - port=port, - bind=self.default_bind, - tls_cert=None, - tls_key=None, - tls_password=None) + self.args['port'] = port + mock_func.assert_called_once_with(**self.args) + self.args['port'] = self.default_port mock_func.reset_mock() - @mock.patch('http.server.test') def test_directory_flag(self, mock_func): options = ['-d', '--directory'] - directories = ['.', '/foo', '\\bar', '/', 'C:\\', 'C:\\foo', 'C:\\bar',] + directories = ['.', '/foo', '\\bar', '/', + 'C:\\', 'C:\\foo', 'C:\\bar',] for flag in options: for directory in directories: with self.subTest(flag=flag, directory=directory): self.invoke_httpd(flag, directory) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=self.default_protocol, - port=self.default_port, - bind=self.default_bind, - tls_cert=None, - tls_key=None, - tls_password=None) + mock_func.assert_called_once_with(**self.args) mock_func.reset_mock() - @mock.patch('http.server.test') def test_bind_flag(self, mock_func): options = ['-b', '--bind'] - bind_addresses = ['localhost', '127.0.0.1', '::1', '0.0.0.0', '8.8.8.8',] + bind_addresses = ['localhost', '127.0.0.1', '::1', + '0.0.0.0', '8.8.8.8',] for flag in options: for bind_address in bind_addresses: with self.subTest(flag=flag, bind_address=bind_address): self.invoke_httpd(flag, bind_address) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=self.default_protocol, - port=self.default_port, - bind=bind_address, - tls_cert=None, - tls_key=None, - tls_password=None) + self.args['bind'] = bind_address + mock_func.assert_called_once_with(**self.args) + self.args['bind'] = self.default_bind mock_func.reset_mock() - @mock.patch('http.server.test') def test_protocol_flag(self, mock_func): options = ['-p', '--protocol'] protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0',] @@ -1624,17 +1622,11 @@ def test_protocol_flag(self, mock_func): for protocol in protocols: with self.subTest(flag=flag, protocol=protocol): self.invoke_httpd(flag, protocol) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=protocol, - port=self.default_port, - bind=self.default_bind, - tls_cert=None, - tls_key=None, - tls_password=None) + self.args['protocol'] = protocol + mock_func.assert_called_once_with(**self.args) + self.args['protocol'] = self.default_protocol mock_func.reset_mock() - @mock.patch('http.server.test') def test_cgi_flag(self, mock_func): self.invoke_httpd('--cgi') mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, @@ -1647,7 +1639,6 @@ def test_cgi_flag(self, mock_func): tls_password=None) @unittest.skipIf(ssl is None, "requires ssl") - @mock.patch('http.server.test') def test_tls_flag(self, mock_func): tls_cert_options = ['--tls-cert', ] tls_key_options = ['--tls-key', ] @@ -1656,15 +1647,13 @@ def test_tls_flag(self, mock_func): for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: - self.invoke_httpd(tls_cert_option, self.tls_cert, tls_key_option, self.tls_key) - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=self.default_protocol, - port=self.default_port, - bind=self.default_bind, - tls_cert=self.tls_cert, - tls_key=self.tls_key, - tls_password=None) + self.invoke_httpd(tls_cert_option, self.tls_cert, + tls_key_option, self.tls_key) + self.args['tls_cert'] = self.tls_cert + self.args['tls_key'] = self.tls_key + mock_func.assert_called_once_with(**self.args) + self.args['tls_cert'] = None + self.args['tls_key'] = None mock_func.reset_mock() # Normal: --tls-cert, --tls-key and --tls-password-file @@ -1672,16 +1661,19 @@ def test_tls_flag(self, mock_func): for tls_cert_option in tls_cert_options: for tls_key_option in tls_key_options: for tls_password_option in tls_password_options: - self.invoke_httpd(tls_cert_option, self.tls_cert, tls_key_option, self.tls_key, tls_password_option, self.tls_password_file) - - mock_func.assert_called_once_with(HandlerClass=self.default_handler, - ServerClass=self.default_server, - protocol=self.default_protocol, - port=self.default_port, - bind=self.default_bind, - tls_cert=self.tls_cert, - tls_key=self.tls_key, - tls_password=self.tls_password) + self.invoke_httpd(tls_cert_option, + self.tls_cert, + tls_key_option, + self.tls_key, + tls_password_option, + self.tls_password_file) + self.args['tls_cert'] = self.tls_cert + self.args['tls_key'] = self.tls_key + self.args['tls_password'] = self.tls_password + mock_func.assert_called_once_with(**self.args) + self.args['tls_cert'] = None + self.args['tls_key'] = None + self.args['tls_password'] = None mock_func.reset_mock() # Abnormal: --tls-key without --tls-cert @@ -1701,17 +1693,19 @@ def test_tls_flag(self, mock_func): # Abnormal: --tls-password-file cannot be opened - non_existent_file = os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + non_existent_file = self.get_random_temporary_file_name() retry_count = 0 while os.path.exists(non_existent_file) and retry_count < 10: - non_existent_file = os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + non_existent_file = self.get_random_temporary_file_name() if not os.path.exists(non_existent_file): for tls_password_option in tls_password_options: for tls_cert_option in tls_cert_options: with self.assertRaises(SystemExit): - self.invoke_httpd([tls_cert_option, self.tls_cert, tls_password_option, non_existent_file]) + self.invoke_httpd(tls_cert_option, + self.tls_cert, + tls_password_option, + non_existent_file) - @mock.patch('http.server.test') def test_no_arguments(self, mock_func): self.invoke_httpd() mock_func.assert_called_once_with(HandlerClass=self.default_handler, @@ -1724,14 +1718,14 @@ def test_no_arguments(self, mock_func): tls_password=None) mock_func.reset_mock() - def test_help_flag(self): + def test_help_flag(self, _): options = ['-h', '--help'] for option in options: with self.assertRaises(SystemExit): output = self.invoke_httpd(option) self.assertStartsWith(output, 'usage: ') - def test_unknown_flag(self): + def test_unknown_flag(self, _): with self.assertRaises(SystemExit): self.invoke_httpd('--unknown-flag') From 85cb0994c45a3eb5a2c926cfb94232a97e27cc95 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:43:09 +0800 Subject: [PATCH 24/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index cbe757cfb2940e..4edc56335c41f1 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -23,7 +23,6 @@ import http, http.client import urllib.parse import tempfile -import textwrap import time import datetime import threading From 9417864891efd44fce3698e6b6baa8f12b53c615 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:43:20 +0800 Subject: [PATCH 25/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 4edc56335c41f1..7f4fed5ada062f 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1577,7 +1577,7 @@ def invoke_httpd(self, *args): with contextlib.redirect_stdout(output), \ contextlib.redirect_stderr(output): server._main(args) - return textwrap.dedent(output.getvalue()).strip() + return output.getvalue() def test_port_flag(self, mock_func): From a5a7d8ce25ebd593af134ae83abf46f3fcd1b982 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:43:43 +0800 Subject: [PATCH 26/39] Update Lib/test/test_httpservers.py Co-authored-by: Semyon Moroz --- Lib/test/test_httpservers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7f4fed5ada062f..5d71287be42155 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1726,7 +1726,8 @@ def test_help_flag(self, _): def test_unknown_flag(self, _): with self.assertRaises(SystemExit): - self.invoke_httpd('--unknown-flag') + output = self.invoke_httpd('--unknown-flag') + self.assertStartsWith(output, 'usage: ') def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From a35e0d60b0410accf3c36c8928c15f09602194fe Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:44:14 +0800 Subject: [PATCH 27/39] Update Lib/test/test_httpservers.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 5d71287be42155..5bf3725c3ef08b 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1570,7 +1570,7 @@ def tearDown(self): return super().tearDown() def get_random_temporary_file_name(self): - return os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) + return os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) def invoke_httpd(self, *args): output = StringIO() From e2266c0313226a506ed494b876f14ec4c4604ed5 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:44:27 +0800 Subject: [PATCH 28/39] Update Lib/test/test_httpservers.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_httpservers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 5bf3725c3ef08b..f9dd0170bdc0dd 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1579,7 +1579,6 @@ def invoke_httpd(self, *args): server._main(args) return output.getvalue() - def test_port_flag(self, mock_func): ports = [8000, 65535,] for port in ports: From b4f9e726fe701907e69bae8ca539f07388cfebb6 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:44:55 +0800 Subject: [PATCH 29/39] Update Lib/test/test_httpservers.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index f9dd0170bdc0dd..2fdccff9e48654 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1580,7 +1580,7 @@ def invoke_httpd(self, *args): return output.getvalue() def test_port_flag(self, mock_func): - ports = [8000, 65535,] + ports = [8000, 65535] for port in ports: with self.subTest(port=port): self.invoke_httpd(str(port)) From a61b5b127cbfa38fd16bf32439182283719dd384 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:45:13 +0800 Subject: [PATCH 30/39] Update Lib/test/test_httpservers.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2fdccff9e48654..baae42e2bf13a6 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1615,7 +1615,7 @@ def test_bind_flag(self, mock_func): def test_protocol_flag(self, mock_func): options = ['-p', '--protocol'] - protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0',] + protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0'] for flag in options: for protocol in protocols: with self.subTest(flag=flag, protocol=protocol): From fd7093221c2a4df50e38089a46cf3535fac5e2f4 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Fri, 18 Apr 2025 21:52:03 +0800 Subject: [PATCH 31/39] update --- Lib/test/test_httpservers.py | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index baae42e2bf13a6..09fd5b27295752 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1536,9 +1536,9 @@ def test_server_test_ipv4(self, _): server.test(ServerClass=mock_server, bind=bind) self.assertEqual(mock_server.address_family, socket.AF_INET) -@mock.patch('http.server.test') class CommandLineTestCase(unittest.TestCase): def setUp(self): + super().setUp() self.default_port = 8000 self.default_bind = None self.default_protocol = 'HTTP/1.0' @@ -1562,16 +1562,12 @@ def setUp(self): 'tls_key': None, 'tls_password': None, } - return super().setUp() def tearDown(self): if os.path.exists(self.tls_password_file): os.remove(self.tls_password_file) return super().tearDown() - def get_random_temporary_file_name(self): - return os.path.join(tempfile.gettempdir(), os.urandom(16).hex()) - def invoke_httpd(self, *args): output = StringIO() with contextlib.redirect_stdout(output), \ @@ -1579,6 +1575,7 @@ def invoke_httpd(self, *args): server._main(args) return output.getvalue() + @mock.patch('http.server.test') def test_port_flag(self, mock_func): ports = [8000, 65535] for port in ports: @@ -1589,10 +1586,12 @@ def test_port_flag(self, mock_func): self.args['port'] = self.default_port mock_func.reset_mock() + @mock.patch('http.server.test') def test_directory_flag(self, mock_func): options = ['-d', '--directory'] directories = ['.', '/foo', '\\bar', '/', - 'C:\\', 'C:\\foo', 'C:\\bar',] + 'C:\\', 'C:\\foo', 'C:\\bar', + '/home/user', './foo/foo2', 'D:\\foo\\bar'] for flag in options: for directory in directories: with self.subTest(flag=flag, directory=directory): @@ -1600,6 +1599,7 @@ def test_directory_flag(self, mock_func): mock_func.assert_called_once_with(**self.args) mock_func.reset_mock() + @mock.patch('http.server.test') def test_bind_flag(self, mock_func): options = ['-b', '--bind'] bind_addresses = ['localhost', '127.0.0.1', '::1', @@ -1613,6 +1613,7 @@ def test_bind_flag(self, mock_func): self.args['bind'] = self.default_bind mock_func.reset_mock() + @mock.patch('http.server.test') def test_protocol_flag(self, mock_func): options = ['-p', '--protocol'] protocols = ['HTTP/1.0', 'HTTP/1.1', 'HTTP/2.0', 'HTTP/3.0'] @@ -1625,6 +1626,8 @@ def test_protocol_flag(self, mock_func): self.args['protocol'] = self.default_protocol mock_func.reset_mock() + # TODO: This test should be removed once the CGI component is removed(3.15) + @mock.patch('http.server.test') def test_cgi_flag(self, mock_func): self.invoke_httpd('--cgi') mock_func.assert_called_once_with(HandlerClass=CGIHTTPRequestHandler, @@ -1637,6 +1640,7 @@ def test_cgi_flag(self, mock_func): tls_password=None) @unittest.skipIf(ssl is None, "requires ssl") + @mock.patch('http.server.test') def test_tls_flag(self, mock_func): tls_cert_options = ['--tls-cert', ] tls_key_options = ['--tls-key', ] @@ -1691,19 +1695,16 @@ def test_tls_flag(self, mock_func): # Abnormal: --tls-password-file cannot be opened - non_existent_file = self.get_random_temporary_file_name() - retry_count = 0 - while os.path.exists(non_existent_file) and retry_count < 10: - non_existent_file = self.get_random_temporary_file_name() - if not os.path.exists(non_existent_file): - for tls_password_option in tls_password_options: - for tls_cert_option in tls_cert_options: - with self.assertRaises(SystemExit): - self.invoke_httpd(tls_cert_option, - self.tls_cert, - tls_password_option, - non_existent_file) + non_existent_file = 'non_existent_file' + for tls_password_option in tls_password_options: + for tls_cert_option in tls_cert_options: + with self.assertRaises(SystemExit): + self.invoke_httpd(tls_cert_option, + self.tls_cert, + tls_password_option, + non_existent_file) + @mock.patch('http.server.test') def test_no_arguments(self, mock_func): self.invoke_httpd() mock_func.assert_called_once_with(HandlerClass=self.default_handler, @@ -1716,6 +1717,7 @@ def test_no_arguments(self, mock_func): tls_password=None) mock_func.reset_mock() + @mock.patch('http.server.test') def test_help_flag(self, _): options = ['-h', '--help'] for option in options: @@ -1723,11 +1725,13 @@ def test_help_flag(self, _): output = self.invoke_httpd(option) self.assertStartsWith(output, 'usage: ') + @mock.patch('http.server.test') def test_unknown_flag(self, _): with self.assertRaises(SystemExit): output = self.invoke_httpd('--unknown-flag') self.assertStartsWith(output, 'usage: ') + def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 348e256b6cf7206ca73a97bb330ac08167b31b37 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 19 Apr 2025 18:33:04 +0800 Subject: [PATCH 32/39] update --- Lib/test/test_httpservers.py | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 09fd5b27295752..f1757ca59bd295 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1537,31 +1537,31 @@ def test_server_test_ipv4(self, _): self.assertEqual(mock_server.address_family, socket.AF_INET) class CommandLineTestCase(unittest.TestCase): + default_port = 8000 + default_bind = None + default_protocol = 'HTTP/1.0' + default_handler = SimpleHTTPRequestHandler + default_server = unittest.mock.ANY + tls_cert = certdata_file('ssl_cert.pem') + tls_key = certdata_file('ssl_key.pem') + tls_password = 'somepass' + args = { + 'HandlerClass': default_handler, + 'ServerClass': default_server, + 'protocol': default_protocol, + 'port': default_port, + 'bind': default_bind, + 'tls_cert': None, + 'tls_key': None, + 'tls_password': None, + } + def setUp(self): super().setUp() - self.default_port = 8000 - self.default_bind = None - self.default_protocol = 'HTTP/1.0' - self.default_handler = SimpleHTTPRequestHandler - self.default_server = unittest.mock.ANY - self.tls_cert = certdata_file('ssl_cert.pem') - self.tls_key = certdata_file('ssl_key.pem') - self.tls_password = 'somepass' - tls_password_file_object = \ - tempfile.NamedTemporaryFile(mode='w+', delete=False) - tls_password_file_object.write(self.tls_password) - self.tls_password_file = tls_password_file_object.name - tls_password_file_object.close() - self.args = { - 'HandlerClass': self.default_handler, - 'ServerClass': self.default_server, - 'protocol': self.default_protocol, - 'port': self.default_port, - 'bind': self.default_bind, - 'tls_cert': None, - 'tls_key': None, - 'tls_password': None, - } + self.tls_password_file = tempfile.mktemp() + with open(self.tls_password_file, 'wb') as f: + f.write(self.tls_password.encode()) + self.addCleanup(os_helper.unlink, self.tls_password_file) def tearDown(self): if os.path.exists(self.tls_password_file): From 4c315b081d89bfe279c0749b063e2273bd4ad91b Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 19 Apr 2025 18:41:38 +0800 Subject: [PATCH 33/39] update(2) --- Lib/test/test_httpservers.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index f1757ca59bd295..04f2e4b04d374e 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1563,11 +1563,6 @@ def setUp(self): f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) - def tearDown(self): - if os.path.exists(self.tls_password_file): - os.remove(self.tls_password_file) - return super().tearDown() - def invoke_httpd(self, *args): output = StringIO() with contextlib.redirect_stdout(output), \ From a57b95980c66c026b341a6cb3cb0304e6ab13e79 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Mon, 5 May 2025 11:15:10 +0800 Subject: [PATCH 34/39] add cli test --- Lib/test/test_httpservers.py | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 04f2e4b04d374e..91fc0768101949 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -11,6 +11,7 @@ import contextlib import os import socket +import subprocess import sys import re import base64 @@ -22,6 +23,7 @@ import html import http, http.client import urllib.parse +import urllib.request import tempfile import time import datetime @@ -34,6 +36,7 @@ from test.support import ( is_apple, import_helper, os_helper, requires_subprocess, threading_helper ) +from test.support.socket_helper import find_unused_port try: import ssl @@ -1555,6 +1558,8 @@ class CommandLineTestCase(unittest.TestCase): 'tls_key': None, 'tls_password': None, } + random_data = os.urandom(1024) + random_file_name = 'random.bin' def setUp(self): super().setUp() @@ -1562,6 +1567,9 @@ def setUp(self): with open(self.tls_password_file, 'wb') as f: f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) + with open(self.random_file_name, 'wb') as f: + f.write(self.random_data) + self.addCleanup(os_helper.unlink, self.random_file_name) def invoke_httpd(self, *args): output = StringIO() @@ -1726,6 +1734,44 @@ def test_unknown_flag(self, _): output = self.invoke_httpd('--unknown-flag') self.assertStartsWith(output, 'usage: ') + def fetch_file(self, path, allow_self_signed_cert=True) -> bytes: + context = ssl.create_default_context() + if allow_self_signed_cert: + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + req = urllib.request.Request(path, method='GET') + res = urllib.request.urlopen(req, context=context) + return res.read() + + def test_http_client(self): + port = find_unused_port() + bind = '127.0.0.1' + proc = subprocess.Popen([sys.executable, '-m', 'http.server', + str(port), '-b', bind], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + time.sleep(0.5) # Wait for the server to start. + # TODO: Find a better way to wait for the server to start. + res = self.fetch_file(f'http://{bind}:{port}/{self.random_file_name}') + self.assertEqual(res, self.random_data) + proc.kill() + proc.wait() + + def test_https_client(self): + port = find_unused_port() + bind = '127.0.0.1' + proc = subprocess.Popen([sys.executable, '-m', 'http.server', + str(port), '-b', bind, + '--tls-cert', self.tls_cert, + '--tls-key', self.tls_key, + '--tls-password-file', self.tls_password_file], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + time.sleep(0.5) + res = self.fetch_file(f'https://{bind}:{port}/{self.random_file_name}') + self.assertEqual(res, self.random_data) + proc.kill() + proc.wait() def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From b627e02b25e3d105fc5c848981e6ebb3b51a4ec3 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Mon, 5 May 2025 14:34:20 +0800 Subject: [PATCH 35/39] add cli test(1) --- Lib/test/test_httpservers.py | 66 +++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 91fc0768101949..a467622edcf9e2 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1743,33 +1743,81 @@ def fetch_file(self, path, allow_self_signed_cert=True) -> bytes: res = urllib.request.urlopen(req, context=context) return res.read() + def parse_cli_output(self, output: str) -> tuple[str, str, int]: + matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) + return matches.group(1), matches.group(2), int(matches.group(3)) + def test_http_client(self): port = find_unused_port() bind = '127.0.0.1' - proc = subprocess.Popen([sys.executable, '-m', 'http.server', + proc = subprocess.Popen([sys.executable, '-u', '-m', 'http.server', str(port), '-b', bind], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - time.sleep(0.5) # Wait for the server to start. - # TODO: Find a better way to wait for the server to start. + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=1, + text=True) + max_tries = 50 + while True: + # Wait for the server to start. + if max_tries <= 0: + self.fail('Server did not start') + line = proc.stdout.readline() + if not line: + if proc.poll() is not None: + break + time.sleep(0.1) + max_tries -= 1 + continue + _protocol, _host, _port = self.parse_cli_output(line) + if not _protocol or not _host or not _port: + time.sleep(0.1) + max_tries -= 1 + continue + self.assertEqual(_protocol, 'http') + self.assertEqual(_host, bind) + self.assertEqual(_port, port) + break res = self.fetch_file(f'http://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) + proc.stdout.close() proc.kill() proc.wait() def test_https_client(self): port = find_unused_port() bind = '127.0.0.1' - proc = subprocess.Popen([sys.executable, '-m', 'http.server', + proc = subprocess.Popen([sys.executable, '-u', '-m', 'http.server', str(port), '-b', bind, '--tls-cert', self.tls_cert, '--tls-key', self.tls_key, '--tls-password-file', self.tls_password_file], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - time.sleep(0.5) + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=1, + text=True) + max_tries = 50 + while True: + if max_tries <= 0: + self.fail('Server did not start') + line = proc.stdout.readline() + if not line: + if proc.poll() is not None: + break + time.sleep(0.1) + max_tries -= 1 + continue + _protocol, _host, _port = self.parse_cli_output(line) + if not _protocol or not _host or not _port: + time.sleep(0.1) + max_tries -= 1 + continue + self.assertEqual(_protocol, 'https') + self.assertEqual(_host, bind) + self.assertEqual(_port, port) + break res = self.fetch_file(f'https://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) + proc.stdout.close() proc.kill() proc.wait() From 41065c2103291b4a7ac63dcc98b02515b3c0be0e Mon Sep 17 00:00:00 2001 From: ggqlq Date: Wed, 7 May 2025 15:35:29 +0800 Subject: [PATCH 36/39] add cli test(2) --- Lib/test/test_httpservers.py | 68 +++++++++++++----------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index a467622edcf9e2..67cff531d1fade 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1744,8 +1744,29 @@ def fetch_file(self, path, allow_self_signed_cert=True) -> bytes: return res.read() def parse_cli_output(self, output: str) -> tuple[str, str, int]: - matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) - return matches.group(1), matches.group(2), int(matches.group(3)) + try: + matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) + return matches.group(1), matches.group(2), int(matches.group(3)) + except: + return None, None, None + + def wait_for_server(self, proc, protocol, port, bind, timeout=50) -> bool: + while timeout > 0: + line = proc.stdout.readline() + if not line: + time.sleep(0.1) + timeout -= 1 + continue + _protocol, _host, _port = self.parse_cli_output(line) + if not _protocol or not _host or not _port: + time.sleep(0.1) + timeout -= 1 + continue + if _protocol == protocol and _host == bind and _port == port: + return True + else: + break + return False def test_http_client(self): port = find_unused_port() @@ -1756,27 +1777,7 @@ def test_http_client(self): stderr=subprocess.STDOUT, bufsize=1, text=True) - max_tries = 50 - while True: - # Wait for the server to start. - if max_tries <= 0: - self.fail('Server did not start') - line = proc.stdout.readline() - if not line: - if proc.poll() is not None: - break - time.sleep(0.1) - max_tries -= 1 - continue - _protocol, _host, _port = self.parse_cli_output(line) - if not _protocol or not _host or not _port: - time.sleep(0.1) - max_tries -= 1 - continue - self.assertEqual(_protocol, 'http') - self.assertEqual(_host, bind) - self.assertEqual(_port, port) - break + self.assertTrue(self.wait_for_server(proc, 'http', port, bind)) res = self.fetch_file(f'http://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) proc.stdout.close() @@ -1795,26 +1796,7 @@ def test_https_client(self): stderr=subprocess.STDOUT, bufsize=1, text=True) - max_tries = 50 - while True: - if max_tries <= 0: - self.fail('Server did not start') - line = proc.stdout.readline() - if not line: - if proc.poll() is not None: - break - time.sleep(0.1) - max_tries -= 1 - continue - _protocol, _host, _port = self.parse_cli_output(line) - if not _protocol or not _host or not _port: - time.sleep(0.1) - max_tries -= 1 - continue - self.assertEqual(_protocol, 'https') - self.assertEqual(_host, bind) - self.assertEqual(_port, port) - break + self.assertTrue(self.wait_for_server(proc, 'https', port, bind)) res = self.fetch_file(f'https://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) proc.stdout.close() From cbda832f1f699eb8fc34af3d6874fe804fa4590e Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 10 May 2025 03:39:03 +0800 Subject: [PATCH 37/39] move runtime tests into a new class --- Lib/test/test_httpservers.py | 53 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 67cff531d1fade..c7a73c7c888e99 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -34,8 +34,9 @@ import unittest from test import support from test.support import ( - is_apple, import_helper, os_helper, requires_subprocess, threading_helper + is_apple, import_helper, os_helper, requires_subprocess, threading_helper, ) +from test.support.script_helper import spawn_python, kill_python from test.support.socket_helper import find_unused_port try: @@ -1539,6 +1540,7 @@ def test_server_test_ipv4(self, _): server.test(ServerClass=mock_server, bind=bind) self.assertEqual(mock_server.address_family, socket.AF_INET) + class CommandLineTestCase(unittest.TestCase): default_port = 8000 default_bind = None @@ -1558,8 +1560,6 @@ class CommandLineTestCase(unittest.TestCase): 'tls_key': None, 'tls_password': None, } - random_data = os.urandom(1024) - random_file_name = 'random.bin' def setUp(self): super().setUp() @@ -1567,9 +1567,6 @@ def setUp(self): with open(self.tls_password_file, 'wb') as f: f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) - with open(self.random_file_name, 'wb') as f: - f.write(self.random_data) - self.addCleanup(os_helper.unlink, self.random_file_name) def invoke_httpd(self, *args): output = StringIO() @@ -1734,6 +1731,17 @@ def test_unknown_flag(self, _): output = self.invoke_httpd('--unknown-flag') self.assertStartsWith(output, 'usage: ') + +class CommandLineRunTimeTestCase(CommandLineTestCase): + random_data = os.urandom(1024) + random_file_name = 'random.bin' + + def setUp(self): + super().setUp() + with open(self.random_file_name, 'wb') as f: + f.write(self.random_data) + self.addCleanup(os_helper.unlink, self.random_file_name) + def fetch_file(self, path, allow_self_signed_cert=True) -> bytes: context = ssl.create_default_context() if allow_self_signed_cert: @@ -1771,37 +1779,28 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50) -> bool: def test_http_client(self): port = find_unused_port() bind = '127.0.0.1' - proc = subprocess.Popen([sys.executable, '-u', '-m', 'http.server', - str(port), '-b', bind], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - bufsize=1, - text=True) + proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, + bufsize=1, text=True) self.assertTrue(self.wait_for_server(proc, 'http', port, bind)) res = self.fetch_file(f'http://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) - proc.stdout.close() - proc.kill() - proc.wait() + proc.terminate() + kill_python(proc) def test_https_client(self): port = find_unused_port() bind = '127.0.0.1' - proc = subprocess.Popen([sys.executable, '-u', '-m', 'http.server', - str(port), '-b', bind, - '--tls-cert', self.tls_cert, - '--tls-key', self.tls_key, - '--tls-password-file', self.tls_password_file], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - bufsize=1, - text=True) + proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, + '--tls-cert', self.tls_cert, + '--tls-key', self.tls_key, + '--tls-password-file', self.tls_password_file, + bufsize=1, text=True) self.assertTrue(self.wait_for_server(proc, 'https', port, bind)) res = self.fetch_file(f'https://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) - proc.stdout.close() - proc.kill() - proc.wait() + proc.terminate() + kill_python(proc) + def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 86847e8efba9a780ce1eccea2bb1e37dcc5c17fd Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 10 May 2025 11:40:19 +0800 Subject: [PATCH 38/39] update --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index c7a73c7c888e99..59692698370143 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -34,7 +34,7 @@ import unittest from test import support from test.support import ( - is_apple, import_helper, os_helper, requires_subprocess, threading_helper, + is_apple, import_helper, os_helper, requires_subprocess, threading_helper ) from test.support.script_helper import spawn_python, kill_python from test.support.socket_helper import find_unused_port From d5047608ccea7170859f7cca1fed23b8405316d4 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 10 May 2025 13:34:43 +0800 Subject: [PATCH 39/39] update --- Lib/test/test_httpservers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 59692698370143..1f1a571f097ba9 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1732,15 +1732,22 @@ def test_unknown_flag(self, _): self.assertStartsWith(output, 'usage: ') -class CommandLineRunTimeTestCase(CommandLineTestCase): +class CommandLineRunTimeTestCase(unittest.TestCase): random_data = os.urandom(1024) random_file_name = 'random.bin' + tls_cert = certdata_file('ssl_cert.pem') + tls_key = certdata_file('ssl_key.pem') + tls_password = 'somepass' def setUp(self): super().setUp() with open(self.random_file_name, 'wb') as f: f.write(self.random_data) self.addCleanup(os_helper.unlink, self.random_file_name) + self.tls_password_file = tempfile.mktemp() + with open(self.tls_password_file, 'wb') as f: + f.write(self.tls_password.encode()) + self.addCleanup(os_helper.unlink, self.tls_password_file) def fetch_file(self, path, allow_self_signed_cert=True) -> bytes: context = ssl.create_default_context()