(args=None)
| 1006 | |
| 1007 | |
| 1008 | def _main(args=None): |
| 1009 | import argparse |
| 1010 | import contextlib |
| 1011 | |
| 1012 | parser = argparse.ArgumentParser(color=True) |
| 1013 | parser.add_argument('-b', '--bind', metavar='ADDRESS', |
| 1014 | help='bind to this address ' |
| 1015 | '(default: all interfaces)') |
| 1016 | parser.add_argument('-d', '--directory', default=os.getcwd(), |
| 1017 | help='serve this directory ' |
| 1018 | '(default: current directory)') |
| 1019 | parser.add_argument('-p', '--protocol', metavar='VERSION', |
| 1020 | default='HTTP/1.0', |
| 1021 | help='conform to this HTTP version ' |
| 1022 | '(default: %(default)s)') |
| 1023 | parser.add_argument('--tls-cert', metavar='PATH', |
| 1024 | help='path to the TLS certificate chain file') |
| 1025 | parser.add_argument('--tls-key', metavar='PATH', |
| 1026 | help='path to the TLS key file') |
| 1027 | parser.add_argument('--tls-password-file', metavar='PATH', |
| 1028 | help='path to the password file for the TLS key') |
| 1029 | parser.add_argument('port', default=8000, type=int, nargs='?', |
| 1030 | help='bind to this port ' |
| 1031 | '(default: %(default)s)') |
| 1032 | args = parser.parse_args(args) |
| 1033 | |
| 1034 | if not args.tls_cert and args.tls_key: |
| 1035 | parser.error("--tls-key requires --tls-cert to be set") |
| 1036 | |
| 1037 | tls_key_password = None |
| 1038 | if args.tls_password_file: |
| 1039 | if not args.tls_cert: |
| 1040 | parser.error("--tls-password-file requires --tls-cert to be set") |
| 1041 | |
| 1042 | try: |
| 1043 | with open(args.tls_password_file, "r", encoding="utf-8") as f: |
| 1044 | tls_key_password = f.read().strip() |
| 1045 | except OSError as e: |
| 1046 | parser.error(f"Failed to read TLS password file: {e}") |
| 1047 | |
| 1048 | # ensure dual-stack is not disabled; ref #38907 |
| 1049 | class DualStackServerMixin: |
| 1050 | |
| 1051 | def server_bind(self): |
| 1052 | # suppress exception when protocol is IPv4 |
| 1053 | with contextlib.suppress(Exception): |
| 1054 | self.socket.setsockopt( |
| 1055 | socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) |
| 1056 | return super().server_bind() |
| 1057 | |
| 1058 | def finish_request(self, request, client_address): |
| 1059 | self.RequestHandlerClass(request, client_address, self, |
| 1060 | directory=args.directory) |
| 1061 | |
| 1062 | class HTTPDualStackServer(DualStackServerMixin, ThreadingHTTPServer): |
| 1063 | pass |
| 1064 | class HTTPSDualStackServer(DualStackServerMixin, ThreadingHTTPSServer): |
| 1065 | pass |
no test coverage detected
searching dependent graphs…