Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-85162: Add HTTPSServer to http.server #129607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
5bfc939b3829854cc80c575fff2b64c3070e4652a7abd949c8fc23114f587bddb796cd96d4a68947f581b8ba15197e2032bd97fd615b25811951e22b4e1eba4838ff83a7821f85ee1b5196e71defd44a44b33ecc5fcc9474df61de08a572043ae6b86cff3500b2d50a8a7f316e7d92501b64e3dc004b71b6ba37fbf86a0db89f4c4c6879de1ee542f0c40dd76e51ec34b8525309d32b34b8786f96ba50d5d87f8005f5f65e7a42f74c68c273ca55d13daf4848b84be250e0ed54f36fbfFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -599,6 +599,17 @@ http | ||
| module allow the browser to apply its default dark mode. | ||
| (Contributed by Yorik Hansen in :gh:`123430`.) | ||
| * The :mod:`http.server` module now supports serving over HTTPS using the | ||
| :class:`http.server.HTTPSServer` class. This functionality is exposed by | ||
| the command-line interface (``python -m http.server``) through the following | ||
| options: | ||
| * ``--tls-cert <path>``: Path to the TLS certificate file. | ||
donbarbos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| * ``--tls-key <path>``: Optional path to the private key file. | ||
| * ``--tls-password-file <path>``: Optional path to the password file for the private key. | ||
| (Contributed by Semyon Moroz in :gh:`85162`.) | ||
| imaplib | ||
| ------- | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -83,8 +83,10 @@ | ||
| __version__ = "0.6" | ||
| __all__ = [ | ||
| "HTTPServer", "ThreadingHTTPServer", "BaseHTTPRequestHandler", | ||
| "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler", | ||
| "HTTPServer", "ThreadingHTTPServer", | ||
| "HTTPSServer", "ThreadingHTTPSServer", | ||
| "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", | ||
| "CGIHTTPRequestHandler", | ||
| ] | ||
| import copy | ||
| @@ -149,6 +151,47 @@ class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer): | ||
| daemon_threads = True | ||
| class HTTPSServer(HTTPServer): | ||
| def __init__(self, server_address, RequestHandlerClass, | ||
donbarbos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| bind_and_activate=True, *, certfile, keyfile=None, | ||
| password=None, alpn_protocols=None): | ||
| try: | ||
| import ssl | ||
| except ImportError: | ||
| raise RuntimeError("SSL module is missing; " | ||
| "HTTPS support is unavailable") | ||
| self.ssl = ssl | ||
| self.certfile = certfile | ||
| self.keyfile = keyfile | ||
| self.password = password | ||
| # Support by default HTTP/1.1 | ||
| self.alpn_protocols = ( | ||
| ["http/1.1"] if alpn_protocols is None else alpn_protocols | ||
| ) | ||
| super().__init__(server_address, | ||
| RequestHandlerClass, | ||
| bind_and_activate) | ||
| def server_activate(self): | ||
| """Wrap the socket in SSLSocket.""" | ||
| super().server_activate() | ||
| context = self._create_context() | ||
| self.socket = context.wrap_socket(self.socket, server_side=True) | ||
| def _create_context(self): | ||
| """Create a secure SSL context.""" | ||
| context = self.ssl.create_default_context(self.ssl.Purpose.CLIENT_AUTH) | ||
| context.load_cert_chain(self.certfile, self.keyfile, self.password) | ||
| context.set_alpn_protocols(self.alpn_protocols) | ||
donbarbos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| return context | ||
| class ThreadingHTTPSServer(socketserver.ThreadingMixIn, HTTPSServer): | ||
| daemon_threads = True | ||
| class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): | ||
| """HTTP request handler base class. | ||
| @@ -1263,20 +1306,29 @@ 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, | ||
donbarbos marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading. Please reload this page. | ||
| tls_cert=None, tls_key=None, tls_password=None): | ||
| """Test the HTTP request handler class. | ||
| This runs an HTTP server on port 8000 (or the port argument). | ||
| """ | ||
| ServerClass.address_family, addr = _get_best_family(bind, port) | ||
| HandlerClass.protocol_version = protocol | ||
| with ServerClass(addr, HandlerClass) as httpd: | ||
| if tls_cert: | ||
| server = ThreadingHTTPSServer(addr, HandlerClass, certfile=tls_cert, | ||
| keyfile=tls_key, password=tls_password) | ||
| else: | ||
| server = ServerClass(addr, HandlerClass) | ||
| with server as httpd: | ||
| host, port = httpd.socket.getsockname()[:2] | ||
| url_host = f'[{host}]' if ':' in host else host | ||
| protocol = 'HTTPS' if tls_cert else 'HTTP' | ||
| print( | ||
| f"Serving HTTP on{host} port{port} " | ||
| f"(http://{url_host}:{port}/) ..." | ||
| f"Serving {protocol} on{host} port{port} " | ||
| f"({protocol.lower()}://{url_host}:{port}/) ..." | ||
| ) | ||
| try: | ||
| httpd.serve_forever() | ||
| @@ -1301,10 +1353,31 @@ def test(HandlerClass=BaseHTTPRequestHandler, | ||
| default='HTTP/1.0', | ||
| help='conform to this HTTP version ' | ||
| '(default: %(default)s)') | ||
| parser.add_argument('--tls-cert', metavar='PATH', | ||
| help='path to the TLS certificate chain file') | ||
| parser.add_argument('--tls-key', metavar='PATH', | ||
| help='path to the TLS key file') | ||
| parser.add_argument('--tls-password-file', metavar='PATH', | ||
| help='path to the password file for the TLS key') | ||
| parser.add_argument('port', default=8000, type=int, nargs='?', | ||
| help='bind to this port ' | ||
| '(default: %(default)s)') | ||
| args = parser.parse_args() | ||
| if not args.tls_cert and args.tls_key: | ||
| parser.error("--tls-key requires --tls-cert to be set") | ||
| tls_key_password = None | ||
| if args.tls_password_file: | ||
| if not args.tls_cert: | ||
| parser.error("--tls-password-file requires --tls-cert to be set") | ||
| try: | ||
| with open(args.tls_password_file, "r", encoding="utf-8") as f: | ||
| tls_key_password = f.read().strip() | ||
| except OSError as e: | ||
| parser.error(f"Failed to read TLS password file:{e}") | ||
| if args.cgi: | ||
| handler_class = CGIHTTPRequestHandler | ||
| else: | ||
| @@ -1330,4 +1403,7 @@ def finish_request(self, request, client_address): | ||
| port=args.port, | ||
| bind=args.bind, | ||
| protocol=args.protocol, | ||
| tls_cert=args.tls_cert, | ||
| tls_key=args.tls_key, | ||
| tls_password=tls_key_password, | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.