From 79db2f2216f347c2ad19ef2c2c188a9155906b5c Mon Sep 17 00:00:00 2001 From: ggqlq Date: Tue, 15 Apr 2025 02:35:59 +0800 Subject: [PATCH 01/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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/81] 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() From 811d86dcd845549b51849791aeb50635142699b7 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Fri, 16 May 2025 16:11:02 +0800 Subject: [PATCH 40/81] remove test_cli_flag function --- Lib/test/test_httpservers.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 1f1a571f097ba9..e2c9bd5a94b54d 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1626,19 +1626,6 @@ 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, - 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') def test_tls_flag(self, mock_func): From e5df251016b8efda147c14eb89da5b86711b5604 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:47:54 +0800 Subject: [PATCH 41/81] 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 e2c9bd5a94b54d..06379e5cfe9292 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -36,7 +36,7 @@ from test.support import ( is_apple, import_helper, os_helper, requires_subprocess, threading_helper ) -from test.support.script_helper import spawn_python, kill_python +from test.support.script_helper import kill_python, spawn_python from test.support.socket_helper import find_unused_port try: From 1843c804d98aee39b083c0cb25b09b479ced6efb Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:48:07 +0800 Subject: [PATCH 42/81] 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 06379e5cfe9292..931f1d734dc19b 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1745,7 +1745,7 @@ 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]: + def parse_cli_output(self, output): try: matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) return matches.group(1), matches.group(2), int(matches.group(3)) From 8e8b755a59421bade7132cc7784e688e7053ff75 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:48:18 +0800 Subject: [PATCH 43/81] 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 931f1d734dc19b..81db8623c3db07 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1736,7 +1736,7 @@ def setUp(self): 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: + def fetch_file(self, path, allow_self_signed_cert=True): context = ssl.create_default_context() if allow_self_signed_cert: context.check_hostname = False From 2f742d9c487017e091b135c3b6caee8122e73886 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:48:57 +0800 Subject: [PATCH 44/81] 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 81db8623c3db07..822221e5859a81 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1716,7 +1716,7 @@ def test_help_flag(self, _): def test_unknown_flag(self, _): with self.assertRaises(SystemExit): output = self.invoke_httpd('--unknown-flag') - self.assertStartsWith(output, 'usage: ') + self.assertStartsWith(output, 'usage: ') class CommandLineRunTimeTestCase(unittest.TestCase): From b5c5ab0d27201ca420f7016531a95a20364a9869 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:49:39 +0800 Subject: [PATCH 45/81] 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 | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 822221e5859a81..6b95d0c898becc 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1694,14 +1694,7 @@ def test_tls_flag(self, mock_func): @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.assert_called_once_with(**self.args) mock_func.reset_mock() @mock.patch('http.server.test') From 05aea0616e7c3467ecab5aa26f37edc32cef186f Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Fri, 16 May 2025 16:50:08 +0800 Subject: [PATCH 46/81] 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 6b95d0c898becc..ebc42e150cf3b0 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1703,7 +1703,7 @@ def test_help_flag(self, _): for option in options: with self.assertRaises(SystemExit): output = self.invoke_httpd(option) - self.assertStartsWith(output, 'usage: ') + self.assertStartsWith(output, 'usage: ') @mock.patch('http.server.test') def test_unknown_flag(self, _): From 333a761a4e8bac96a865c99f6dcd6682a04de26f Mon Sep 17 00:00:00 2001 From: ggqlq Date: Fri, 16 May 2025 17:24:12 +0800 Subject: [PATCH 47/81] split stdout and stderr, remove output check after self.assertRaises(SystemExit) --- Lib/test/test_httpservers.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index ebc42e150cf3b0..83fee58ff5b852 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1569,11 +1569,12 @@ def setUp(self): self.addCleanup(os_helper.unlink, self.tls_password_file) def invoke_httpd(self, *args): - output = StringIO() - with contextlib.redirect_stdout(output), \ - contextlib.redirect_stderr(output): + stdout = StringIO() + stderr = StringIO() + with contextlib.redirect_stdout(stdout), \ + contextlib.redirect_stderr(stderr): server._main(args) - return output.getvalue() + return {'stdout': stdout.getvalue(), 'stderr': stderr.getvalue()} @mock.patch('http.server.test') def test_port_flag(self, mock_func): @@ -1703,14 +1704,11 @@ def test_help_flag(self, _): for option in options: with self.assertRaises(SystemExit): 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: ') - class CommandLineRunTimeTestCase(unittest.TestCase): random_data = os.urandom(1024) From 4304354b4db2baeb007b59d6ac267f2db402fa48 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Fri, 16 May 2025 17:45:46 +0800 Subject: [PATCH 48/81] split tls tests --- Lib/test/test_httpservers.py | 75 +++++++++++++++++------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 83fee58ff5b852..05ddc5bedb1dac 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1550,6 +1550,9 @@ class CommandLineTestCase(unittest.TestCase): tls_cert = certdata_file('ssl_cert.pem') tls_key = certdata_file('ssl_key.pem') tls_password = 'somepass' + tls_cert_options = ['--tls-cert'] + tls_key_options = ['--tls-key'] + tls_password_options = ['--tls-password-file'] args = { 'HandlerClass': default_handler, 'ServerClass': default_server, @@ -1629,63 +1632,55 @@ def test_protocol_flag(self, mock_func): @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', ] - 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: + def test_tls_cert_and_key_flags(self, mock_func): + for tls_cert_option in self.tls_cert_options: + for tls_key_option in self.tls_key_options: 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 + call_args = { + 'tls_cert': self.tls_cert, + 'tls_key': self.tls_key, + } + call_args = self.args | call_args + mock_func.assert_called_once_with(**call_args) 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: + @unittest.skipIf(ssl is None, "requires ssl") + @mock.patch('http.server.test') + def test_tls_cert_and_key_and_password_flags(self, mock_func): + for tls_cert_option in self.tls_cert_options: + for tls_key_option in self.tls_key_options: + for tls_password_option in self.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) - 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 + call_args = { + 'tls_cert': self.tls_cert, + 'tls_key': self.tls_key, + 'tls_password': self.tls_password, + } + call_args = self.args | call_args + mock_func.assert_called_once_with(**call_args) 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: + @unittest.skipIf(ssl is None, "requires ssl") + @mock.patch('http.server.test') + def test_missing_tls_cert_flag(self, mock_func): + for tls_key_option in self.tls_key_options: + for tls_cert_option in self.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 - + @unittest.skipIf(ssl is None, "requires ssl") + @mock.patch('http.server.test') + def test_invalid_password_file(self, mock_func): non_existent_file = 'non_existent_file' - for tls_password_option in tls_password_options: - for tls_cert_option in tls_cert_options: + for tls_password_option in self.tls_password_options: + for tls_cert_option in self.tls_cert_options: with self.assertRaises(SystemExit): self.invoke_httpd(tls_cert_option, self.tls_cert, From 6c4c13402e136ccbabd752892683a9183a8de1eb Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Sat, 17 May 2025 02:47:26 +0800 Subject: [PATCH 49/81] 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 05ddc5bedb1dac..31ca49ffbac5f4 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1673,7 +1673,7 @@ def test_missing_tls_cert_flag(self, mock_func): for tls_cert_option in self.tls_cert_options: with self.assertRaises(SystemExit): self.invoke_httpd(tls_key_option, self.tls_key) - mock_func.reset_mock() + mock_func.reset_mock() @unittest.skipIf(ssl is None, "requires ssl") @mock.patch('http.server.test') From 7b3bb1d7eb88ab78890619d0bd40c332a5056388 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Sat, 17 May 2025 02:47:43 +0800 Subject: [PATCH 50/81] 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 31ca49ffbac5f4..9b6daf9510a3e5 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1698,7 +1698,7 @@ def test_help_flag(self, _): options = ['-h', '--help'] for option in options: with self.assertRaises(SystemExit): - output = self.invoke_httpd(option) + _ = self.invoke_httpd(option) @mock.patch('http.server.test') def test_unknown_flag(self, _): From eed42289b13a6e072f18e4798f9a6893bafe3814 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Sat, 17 May 2025 02:47:56 +0800 Subject: [PATCH 51/81] 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 9b6daf9510a3e5..fa292be0482240 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1703,7 +1703,7 @@ def test_help_flag(self, _): @mock.patch('http.server.test') def test_unknown_flag(self, _): with self.assertRaises(SystemExit): - output = self.invoke_httpd('--unknown-flag') + _ = self.invoke_httpd('--unknown-flag') class CommandLineRunTimeTestCase(unittest.TestCase): random_data = os.urandom(1024) From 7c1713e8f31bfa2318acd2271db712f41dced3d5 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Sat, 17 May 2025 02:48:07 +0800 Subject: [PATCH 52/81] 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 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index fa292be0482240..04a3a209180457 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1732,11 +1732,10 @@ def fetch_file(self, path, allow_self_signed_cert=True): return res.read() def parse_cli_output(self, output): - try: - matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) - return matches.group(1), matches.group(2), int(matches.group(3)) - except: + matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) + if matches is None: return None, None, None + return matches.group(1), matches.group(2), int(matches.group(3)) def wait_for_server(self, proc, protocol, port, bind, timeout=50) -> bool: while timeout > 0: From a34fa51ab8ef86fcfb7bc75219e417e1a7f20238 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Sat, 17 May 2025 18:45:35 +0800 Subject: [PATCH 53/81] 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 04a3a209180457..ce9a112458777d 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1706,7 +1706,7 @@ def test_unknown_flag(self, _): _ = self.invoke_httpd('--unknown-flag') class CommandLineRunTimeTestCase(unittest.TestCase): - random_data = os.urandom(1024) + random_data = os.urandom(32) random_file_name = 'random.bin' tls_cert = certdata_file('ssl_cert.pem') tls_key = certdata_file('ssl_key.pem') From 8f73c224d00fd8d00c3bb444283877b7bdbd00a8 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 03:11:37 +0800 Subject: [PATCH 54/81] make invoke_httpd function return a pair --- 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 ce9a112458777d..7895b07e6b9e9f 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(stdout), \ contextlib.redirect_stderr(stderr): server._main(args) - return {'stdout': stdout.getvalue(), 'stderr': stderr.getvalue()} + return stdout.getvalue(), stderr.getvalue() @mock.patch('http.server.test') def test_port_flag(self, mock_func): From 2daf3f8351466bbc9fd870059cb0abec184a2d7b Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 17:34:17 +0800 Subject: [PATCH 55/81] rename vars in wait_for_server function --- Lib/test/test_httpservers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7895b07e6b9e9f..a9ad05a340d60e 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1744,12 +1744,12 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50) -> bool: time.sleep(0.1) timeout -= 1 continue - _protocol, _host, _port = self.parse_cli_output(line) - if not _protocol or not _host or not _port: + 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: + if protocol_ == protocol and host_ == bind and port_ == port: return True else: break From 1c67654e7218c39bda57821b4bcd9d7e0cd696bb Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 17:51:30 +0800 Subject: [PATCH 56/81] use call_args = self.args | dict(...) --- Lib/test/test_httpservers.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index a9ad05a340d60e..d190b1b65f7386 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1585,9 +1585,8 @@ def test_port_flag(self, mock_func): for port in ports: with self.subTest(port=port): self.invoke_httpd(str(port)) - self.args['port'] = port - mock_func.assert_called_once_with(**self.args) - self.args['port'] = self.default_port + call_args = self.args | dict(port=port) + mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1612,9 +1611,8 @@ def test_bind_flag(self, mock_func): for bind_address in bind_addresses: with self.subTest(flag=flag, bind_address=bind_address): self.invoke_httpd(flag, bind_address) - self.args['bind'] = bind_address - mock_func.assert_called_once_with(**self.args) - self.args['bind'] = self.default_bind + call_args = self.args | dict(bind=bind_address) + mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() @mock.patch('http.server.test') @@ -1625,9 +1623,8 @@ def test_protocol_flag(self, mock_func): for protocol in protocols: with self.subTest(flag=flag, protocol=protocol): self.invoke_httpd(flag, protocol) - self.args['protocol'] = protocol - mock_func.assert_called_once_with(**self.args) - self.args['protocol'] = self.default_protocol + call_args = self.args | dict(protocol=protocol) + mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() @unittest.skipIf(ssl is None, "requires ssl") @@ -1728,8 +1725,8 @@ def fetch_file(self, path, allow_self_signed_cert=True): 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() + with urllib.request.urlopen(req, context=context) as res: + return res.read() def parse_cli_output(self, output): matches = re.search(r'\((https?)://([^/:]+):(\d+)/?\)', output) From 9639219d468c7d949394d7664fe79c005f1d075d Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 17:53:24 +0800 Subject: [PATCH 57/81] rename 'random.bin' as 'served_filename' --- 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 d190b1b65f7386..4bd1f7cf01ac15 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1704,7 +1704,7 @@ def test_unknown_flag(self, _): class CommandLineRunTimeTestCase(unittest.TestCase): random_data = os.urandom(32) - random_file_name = 'random.bin' + random_file_name = 'served_filename' tls_cert = certdata_file('ssl_cert.pem') tls_key = certdata_file('ssl_key.pem') tls_password = 'somepass' From 4156e78c72049a33cae7d01f55164bb2a30b6c19 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 18:26:53 +0800 Subject: [PATCH 58/81] fix indentation in test_missing_tls_cert_flag --- Lib/test/test_httpservers.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 4bd1f7cf01ac15..377e5f72206ab5 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1667,10 +1667,14 @@ def test_tls_cert_and_key_and_password_flags(self, mock_func): @mock.patch('http.server.test') def test_missing_tls_cert_flag(self, mock_func): for tls_key_option in self.tls_key_options: - for tls_cert_option in self.tls_cert_options: - with self.assertRaises(SystemExit): - self.invoke_httpd(tls_key_option, self.tls_key) - mock_func.reset_mock() + with self.assertRaises(SystemExit): + self.invoke_httpd(tls_key_option, self.tls_key) + mock_func.reset_mock() + + for tls_password_option in self.tls_password_options: + with self.assertRaises(SystemExit): + self.invoke_httpd(tls_password_option, self.tls_password) + mock_func.reset_mock() @unittest.skipIf(ssl is None, "requires ssl") @mock.patch('http.server.test') From 0522a17ec1afe7f4c638cee54d67299243f661ac Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 18:50:32 +0800 Subject: [PATCH 59/81] add docstring for wait_for_server --- Lib/test/test_httpservers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 377e5f72206ab5..73ba72a2e5c069 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1738,7 +1738,9 @@ def parse_cli_output(self, output): return None, None, None return matches.group(1), matches.group(2), int(matches.group(3)) - def wait_for_server(self, proc, protocol, port, bind, timeout=50) -> bool: + def wait_for_server(self, proc, protocol, port, bind, timeout=50): + """Parses the output of the server process by lines and returns True if + the server is listening on the given port and bind address.""" while timeout > 0: line = proc.stdout.readline() if not line: From 1b5b3f80c1a5b46c706c077a28a3091a78c595a3 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 19:19:32 +0800 Subject: [PATCH 60/81] capture the output outside invoke_httpd --- Lib/test/test_httpservers.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 62a914a4e6de42..7edb685e2c78c5 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1315,9 +1315,9 @@ def setUp(self): f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) - def invoke_httpd(self, *args): - stdout = StringIO() - stderr = StringIO() + def invoke_httpd(self, *args, stdout=None, stderr=None): + stdout = StringIO() if stdout is None else stdout + stderr = StringIO() if stderr is None else stderr with contextlib.redirect_stdout(stdout), \ contextlib.redirect_stderr(stderr): server._main(args) @@ -1441,14 +1441,20 @@ def test_no_arguments(self, mock_func): @mock.patch('http.server.test') def test_help_flag(self, _): options = ['-h', '--help'] + stdout, stderr = StringIO(), StringIO() for option in options: with self.assertRaises(SystemExit): - _ = self.invoke_httpd(option) + self.invoke_httpd(option, stdout=stdout, stderr=stderr) + self.assertIn('usage', stdout.getvalue()) + self.assertEqual('', stderr.getvalue()) @mock.patch('http.server.test') def test_unknown_flag(self, _): + stdout, stderr = StringIO(), StringIO() with self.assertRaises(SystemExit): - _ = self.invoke_httpd('--unknown-flag') + self.invoke_httpd('--unknown-flag', stdout=stdout, stderr=stderr) + self.assertEqual('', stdout.getvalue()) + self.assertIn('error', stderr.getvalue()) class CommandLineRunTimeTestCase(unittest.TestCase): random_data = os.urandom(32) From 5637928b61a11414329b9f8f5980977bfcf75863 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Sat, 17 May 2025 19:35:35 +0800 Subject: [PATCH 61/81] update terminate processes in test_http_client and test_https_client --- Lib/test/test_httpservers.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7edb685e2c78c5..7437b1a4e2c205 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1513,11 +1513,11 @@ def test_http_client(self): bind = '127.0.0.1' proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, bufsize=1, text=True) + self.addCleanup(kill_python, proc) + self.addCleanup(proc.terminate) 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.terminate() - kill_python(proc) def test_https_client(self): port = find_unused_port() @@ -1527,12 +1527,11 @@ def test_https_client(self): '--tls-key', self.tls_key, '--tls-password-file', self.tls_password_file, bufsize=1, text=True) + self.addCleanup(kill_python, proc) + self.addCleanup(proc.terminate) 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.terminate() - kill_python(proc) - def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 6beb7f1b52179b24aa5df695cb97b97e1198b480 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:32:29 +0800 Subject: [PATCH 62/81] 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 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7437b1a4e2c205..444eb33b43e60f 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1398,12 +1398,11 @@ def test_tls_cert_and_key_and_password_flags(self, mock_func): self.tls_key, tls_password_option, self.tls_password_file) - call_args = { + call_args = self.args | { 'tls_cert': self.tls_cert, 'tls_key': self.tls_key, 'tls_password': self.tls_password, } - call_args = self.args | call_args mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() From 4c25dcdb51f1b17b0d5348cefe144616082bada4 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:32:42 +0800 Subject: [PATCH 63/81] 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 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 444eb33b43e60f..05624efbb460c8 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1378,11 +1378,10 @@ def test_tls_cert_and_key_flags(self, mock_func): for tls_key_option in self.tls_key_options: self.invoke_httpd(tls_cert_option, self.tls_cert, tls_key_option, self.tls_key) - call_args = { + call_args = self.args | { 'tls_cert': self.tls_cert, 'tls_key': self.tls_key, } - call_args = self.args | call_args mock_func.assert_called_once_with(**call_args) mock_func.reset_mock() From d67ee16b0f4bd53ded3ffb9791788188c69a468f Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:32:51 +0800 Subject: [PATCH 64/81] 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 05624efbb460c8..a3a92227390d55 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1444,7 +1444,7 @@ def test_help_flag(self, _): with self.assertRaises(SystemExit): self.invoke_httpd(option, stdout=stdout, stderr=stderr) self.assertIn('usage', stdout.getvalue()) - self.assertEqual('', stderr.getvalue()) + self.assertEqual(stderr.getvalue(), '') @mock.patch('http.server.test') def test_unknown_flag(self, _): From d5914dc6a192d90cba28fdb220000974736c3ef1 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:33:02 +0800 Subject: [PATCH 65/81] 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 a3a92227390d55..0d2d179837c9f7 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1451,7 +1451,7 @@ def test_unknown_flag(self, _): stdout, stderr = StringIO(), StringIO() with self.assertRaises(SystemExit): self.invoke_httpd('--unknown-flag', stdout=stdout, stderr=stderr) - self.assertEqual('', stdout.getvalue()) + self.assertEqual(stdout.getvalue(), '') self.assertIn('error', stderr.getvalue()) class CommandLineRunTimeTestCase(unittest.TestCase): From 4d0154e71f7a84f752fc9ef7ce10b0cffe7c67ac Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:33:12 +0800 Subject: [PATCH 66/81] 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 0d2d179837c9f7..ec20bfaedeec52 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1439,8 +1439,8 @@ def test_no_arguments(self, mock_func): @mock.patch('http.server.test') def test_help_flag(self, _): options = ['-h', '--help'] - stdout, stderr = StringIO(), StringIO() for option in options: + stdout, stderr = StringIO(), StringIO() with self.assertRaises(SystemExit): self.invoke_httpd(option, stdout=stdout, stderr=stderr) self.assertIn('usage', stdout.getvalue()) From 2ba700125cff037fb43343b01316caebb0221b67 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:33:28 +0800 Subject: [PATCH 67/81] 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 ec20bfaedeec52..46ff0712f0eced 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1350,7 +1350,7 @@ def test_directory_flag(self, mock_func): 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',] + '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): From 0c3721679c7e3b21049f2aa010407baf6d373efb Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:33:43 +0800 Subject: [PATCH 68/81] 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 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 46ff0712f0eced..13bd309ee6a3ef 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1487,8 +1487,11 @@ def parse_cli_output(self, output): return matches.group(1), matches.group(2), int(matches.group(3)) def wait_for_server(self, proc, protocol, port, bind, timeout=50): - """Parses the output of the server process by lines and returns True if - the server is listening on the given port and bind address.""" + """Check the server process output. + + Return True if the server was successfully started + and is listening on the given port and bind address. + """ while timeout > 0: line = proc.stdout.readline() if not line: From 6938dd96132a9977e9b9b3dc5a72c6bcaadc58c4 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:34:00 +0800 Subject: [PATCH 69/81] 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 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 13bd309ee6a3ef..ebc0acd45bba58 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1505,8 +1505,7 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50): continue if protocol_ == protocol and host_ == bind and port_ == port: return True - else: - break + break return False def test_http_client(self): From 59be989ee2424f2f4a0f73eb4a55e4f0a079334b Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:34:16 +0800 Subject: [PATCH 70/81] 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 insertion(+) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index ebc0acd45bba58..f9946ff8261b1b 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1533,6 +1533,7 @@ def test_https_client(self): res = self.fetch_file(f'https://{bind}:{port}/{self.random_file_name}') self.assertEqual(res, self.random_data) + def setUpModule(): unittest.addModuleCleanup(os.chdir, os.getcwd()) From 7a8d6f10e3e3fc21bb7f4a129a92469cc59afc4a Mon Sep 17 00:00:00 2001 From: ggqlq Date: Mon, 19 May 2025 19:39:17 +0800 Subject: [PATCH 71/81] rename random_... as served_... in the CommandLineRunTimeTestCase class --- Lib/test/test_httpservers.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index f9946ff8261b1b..3378f93fd8f7c9 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1455,17 +1455,17 @@ def test_unknown_flag(self, _): self.assertIn('error', stderr.getvalue()) class CommandLineRunTimeTestCase(unittest.TestCase): - random_data = os.urandom(32) - random_file_name = 'served_filename' + served_data = os.urandom(32) + served_file_name = 'served_filename' 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) + with open(self.served_file_name, 'wb') as f: + f.write(self.served_data) + self.addCleanup(os_helper.unlink, self.served_file_name) self.tls_password_file = tempfile.mktemp() with open(self.tls_password_file, 'wb') as f: f.write(self.tls_password.encode()) @@ -1489,7 +1489,7 @@ def parse_cli_output(self, output): def wait_for_server(self, proc, protocol, port, bind, timeout=50): """Check the server process output. - Return True if the server was successfully started + Return True if the server was successfully started and is listening on the given port and bind address. """ while timeout > 0: @@ -1516,8 +1516,8 @@ def test_http_client(self): self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) 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) + res = self.fetch_file(f'http://{bind}:{port}/{self.served_file_name}') + self.assertEqual(res, self.served_data) def test_https_client(self): port = find_unused_port() @@ -1530,8 +1530,8 @@ def test_https_client(self): self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) 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) + res = self.fetch_file(f'https://{bind}:{port}/{self.served_file_name}') + self.assertEqual(res, self.served_data) def setUpModule(): From 921739c056b2a5bc73fa751a9159db36dabe5d41 Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:43:37 +0800 Subject: [PATCH 72/81] 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 insertion(+) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 3378f93fd8f7c9..4ac1080cbce822 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1454,6 +1454,7 @@ def test_unknown_flag(self, _): self.assertEqual(stdout.getvalue(), '') self.assertIn('error', stderr.getvalue()) + class CommandLineRunTimeTestCase(unittest.TestCase): served_data = os.urandom(32) served_file_name = 'served_filename' From 6d9981f18386788cc8e339e6bbc8140beeaa181e Mon Sep 17 00:00:00 2001 From: ggqlq <124190229+ggqlq@users.noreply.github.com> Date: Mon, 19 May 2025 19:44:53 +0800 Subject: [PATCH 73/81] 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 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 4ac1080cbce822..c890527958d975 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1472,11 +1472,11 @@ def setUp(self): 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): + def fetch_file(self, path): context = ssl.create_default_context() - if allow_self_signed_cert: - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE + # allow self-signed certificates + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE req = urllib.request.Request(path, method='GET') with urllib.request.urlopen(req, context=context) as res: return res.read() From 1d42f2cd3784994c2d6cba5ce537c2439b08da33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 13:47:35 +0200 Subject: [PATCH 74/81] Update Lib/test/test_httpservers.py --- 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 c890527958d975..60f86881f54b00 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -11,7 +11,6 @@ import contextlib import os import socket -import subprocess import sys import re import ntpath From d0fe4a6c6ee0a460020462c25fb8e6a1997504c2 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Mon, 19 May 2025 21:54:58 +0800 Subject: [PATCH 75/81] fix buildbot failure --- Lib/test/test_httpservers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 60f86881f54b00..4af3be5c7b6f28 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1472,10 +1472,12 @@ def setUp(self): self.addCleanup(os_helper.unlink, self.tls_password_file) def fetch_file(self, path): - context = ssl.create_default_context() - # allow self-signed certificates - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE + context = None + if ssl is not None: + context = ssl.create_default_context() + # allow self-signed certificates + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE req = urllib.request.Request(path, method='GET') with urllib.request.urlopen(req, context=context) as res: return res.read() @@ -1519,6 +1521,7 @@ def test_http_client(self): res = self.fetch_file(f'http://{bind}:{port}/{self.served_file_name}') self.assertEqual(res, self.served_data) + @unittest.skipIf(ssl is None, "requires ssl") def test_https_client(self): port = find_unused_port() bind = '127.0.0.1' From fc741e9c86bf06990b241df6ae0d9963222458c6 Mon Sep 17 00:00:00 2001 From: ggqlq Date: Mon, 19 May 2025 22:41:34 +0800 Subject: [PATCH 76/81] pass ssl context by argument --- Lib/test/test_httpservers.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index c6d78a606d5191..36fa494e7d00af 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1472,13 +1472,7 @@ def setUp(self): f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) - def fetch_file(self, path): - context = None - if ssl is not None: - context = ssl.create_default_context() - # allow self-signed certificates - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE + def fetch_file(self, path, context=None): req = urllib.request.Request(path, method='GET') with urllib.request.urlopen(req, context=context) as res: return res.read() @@ -1524,6 +1518,11 @@ def test_http_client(self): @unittest.skipIf(ssl is None, "requires ssl") def test_https_client(self): + context = ssl.create_default_context() + # allow self-signed certificates + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + port = find_unused_port() bind = '127.0.0.1' proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, @@ -1534,7 +1533,8 @@ def test_https_client(self): self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) self.assertTrue(self.wait_for_server(proc, 'https', port, bind)) - res = self.fetch_file(f'https://{bind}:{port}/{self.served_file_name}') + res = self.fetch_file(f'https://{bind}:{port}/{self.served_file_name}', + context=context) self.assertEqual(res, self.served_data) From 919228150b53dd4e00a1305424b0df70ceb1a8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 19:49:45 +0200 Subject: [PATCH 77/81] Update test_httpservers.py --- Lib/test/test_httpservers.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 36fa494e7d00af..c47b1fd668d6cb 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1472,6 +1472,14 @@ def setUp(self): f.write(self.tls_password.encode()) self.addCleanup(os_helper.unlink, self.tls_password_file) + @unittest.skipIf(ssl is None, "requires ssl") + def get_ssl_context(self): + context = ssl.create_default_context() + # allow self-signed certificates + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + return context + def fetch_file(self, path, context=None): req = urllib.request.Request(path, method='GET') with urllib.request.urlopen(req, context=context) as res: @@ -1506,8 +1514,7 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50): return False def test_http_client(self): - port = find_unused_port() - bind = '127.0.0.1' + _, (bind, port) = server._get_best_family(None, find_unused_port()) proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, bufsize=1, text=True) self.addCleanup(kill_python, proc) @@ -1516,15 +1523,8 @@ def test_http_client(self): res = self.fetch_file(f'http://{bind}:{port}/{self.served_file_name}') self.assertEqual(res, self.served_data) - @unittest.skipIf(ssl is None, "requires ssl") def test_https_client(self): - context = ssl.create_default_context() - # allow self-signed certificates - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - - port = find_unused_port() - bind = '127.0.0.1' + _, (bind, port) = server._get_best_family(None, find_unused_port()) proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, '--tls-cert', self.tls_cert, '--tls-key', self.tls_key, @@ -1533,8 +1533,8 @@ def test_https_client(self): self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) self.assertTrue(self.wait_for_server(proc, 'https', port, bind)) - res = self.fetch_file(f'https://{bind}:{port}/{self.served_file_name}', - context=context) + url = f'https://{bind}:{port}/{self.served_file_name}' + res = self.fetch_file(url, context=self.get_ssl_context()) self.assertEqual(res, self.served_data) From bd4282702e86c234259f76539ad9cac429e6a5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 20:42:11 +0200 Subject: [PATCH 78/81] fix tests --- Lib/test/test_httpservers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index c47b1fd668d6cb..7ce362a7e704cd 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1514,7 +1514,8 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50): return False def test_http_client(self): - _, (bind, port) = server._get_best_family(None, find_unused_port()) + _, addr = server._get_best_family(None, find_unused_port()) + bind, port = addr[:2] proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, bufsize=1, text=True) self.addCleanup(kill_python, proc) @@ -1524,7 +1525,8 @@ def test_http_client(self): self.assertEqual(res, self.served_data) def test_https_client(self): - _, (bind, port) = server._get_best_family(None, find_unused_port()) + _, addr = server._get_best_family(None, find_unused_port()) + bind, port = addr[:2] proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, '--tls-cert', self.tls_cert, '--tls-key', self.tls_key, From 5c6f0228c17eee20437fef8cad454661eb66a782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 21:23:21 +0200 Subject: [PATCH 79/81] Update Lib/test/test_httpservers.py --- Lib/test/test_httpservers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7ce362a7e704cd..cfd2a3c0f23191 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1514,8 +1514,7 @@ def wait_for_server(self, proc, protocol, port, bind, timeout=50): return False def test_http_client(self): - _, addr = server._get_best_family(None, find_unused_port()) - bind, port = addr[:2] + bind, port = 'localhost', find_unused_port() proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, bufsize=1, text=True) self.addCleanup(kill_python, proc) From 4dfcfe341a6f5572257a43e3d8365ce6a8173afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 21:23:31 +0200 Subject: [PATCH 80/81] Update Lib/test/test_httpservers.py --- Lib/test/test_httpservers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index cfd2a3c0f23191..2be822e7452a43 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -1524,8 +1524,7 @@ def test_http_client(self): self.assertEqual(res, self.served_data) def test_https_client(self): - _, addr = server._get_best_family(None, find_unused_port()) - bind, port = addr[:2] + bind, port = 'localhost', find_unused_port() proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, '--tls-cert', self.tls_cert, '--tls-key', self.tls_key, From 7123976f2f4e7cfcfa2e82834c147e6afe825325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Mon, 19 May 2025 22:19:14 +0200 Subject: [PATCH 81/81] now it should work --- Lib/test/test_httpservers.py | 47 ++++++++++++++---------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 2be822e7452a43..552fc4068d975d 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -35,7 +35,6 @@ is_apple, import_helper, os_helper, threading_helper ) from test.support.script_helper import kill_python, spawn_python -from test.support.socket_helper import find_unused_port try: import ssl @@ -1491,48 +1490,38 @@ def parse_cli_output(self, output): return None, None, None return matches.group(1), matches.group(2), int(matches.group(3)) - def wait_for_server(self, proc, protocol, port, bind, timeout=50): - """Check the server process output. - - Return True if the server was successfully started - and is listening on the given port and bind address. - """ - 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 - break - return False + def wait_for_server(self, proc, protocol): + """Check the server process output.""" + line = proc.stdout.readline() + if line: + if support.verbose > 1: + print(line) + parsed_protocol, host, port = self.parse_cli_output(line) + if protocol == parsed_protocol: + return host, port + return None, None def test_http_client(self): - bind, port = 'localhost', find_unused_port() - proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, - bufsize=1, text=True) + proc = spawn_python('-u', '-m', 'http.server', bufsize=1, text=True) self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) - self.assertTrue(self.wait_for_server(proc, 'http', port, bind)) + bind, port = self.wait_for_server(proc, 'http') + self.assertIsNotNone(bind) + self.assertIsNotNone(port) res = self.fetch_file(f'http://{bind}:{port}/{self.served_file_name}') self.assertEqual(res, self.served_data) def test_https_client(self): - bind, port = 'localhost', find_unused_port() - proc = spawn_python('-u', '-m', 'http.server', str(port), '-b', bind, + proc = spawn_python('-u', '-m', 'http.server', '--tls-cert', self.tls_cert, '--tls-key', self.tls_key, '--tls-password-file', self.tls_password_file, bufsize=1, text=True) self.addCleanup(kill_python, proc) self.addCleanup(proc.terminate) - self.assertTrue(self.wait_for_server(proc, 'https', port, bind)) + bind, port = self.wait_for_server(proc, 'https') + self.assertIsNotNone(bind) + self.assertIsNotNone(port) url = f'https://{bind}:{port}/{self.served_file_name}' res = self.fetch_file(url, context=self.get_ssl_context()) self.assertEqual(res, self.served_data) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy