#!/usr/bin/env python3 """ HTTPS 代理服务器 用于测试环境,将 HTTPS 请求转发到本地 HTTP 服务 使用方法: python3 https_proxy.py --port 8989 --target http://localhost:8000 \ --cert ssl_certs/cert.pem --key ssl_certs/key.pem """ import argparse import ssl import http.server import urllib.request import urllib.error import socketserver import json from urllib.parse import urlparse class ProxyHandler(http.server.BaseHTTPRequestHandler): """HTTPS 到 HTTP 代理处理器""" target_url = "http://localhost:8000" def do_request(self, method: str): """处理所有 HTTP 方法""" # 构建目标 URL target = f"{self.target_url}{self.path}" # 读取请求体 content_length = int(self.headers.get('Content-Length', 0)) body = self.rfile.read(content_length) if content_length > 0 else None # 构建请求头(转发原始头) headers = {} for key, value in self.headers.items(): # 跳过 hop-by-hop 头 if key.lower() not in ('host', 'connection', 'keep-alive', 'transfer-encoding', 'te', 'trailer', 'proxy-authorization', 'proxy-authenticate', 'upgrade'): headers[key] = value # 添加 X-Forwarded 头 headers['X-Forwarded-For'] = self.client_address[0] headers['X-Forwarded-Proto'] = 'https' try: # 创建请求 req = urllib.request.Request( target, data=body, headers=headers, method=method ) # 发送请求 with urllib.request.urlopen(req, timeout=30) as response: # 发送响应状态 self.send_response(response.status) # 转发响应头 for key, value in response.headers.items(): if key.lower() not in ('transfer-encoding', 'connection'): self.send_header(key, value) self.end_headers() # 转发响应体 self.wfile.write(response.read()) except urllib.error.HTTPError as e: self.send_response(e.code) for key, value in e.headers.items(): if key.lower() not in ('transfer-encoding', 'connection'): self.send_header(key, value) self.end_headers() self.wfile.write(e.read()) except urllib.error.URLError as e: self.send_error(502, f"Bad Gateway: {e.reason}") except Exception as e: self.send_error(500, f"Internal Server Error: {str(e)}") def do_GET(self): self.do_request('GET') def do_POST(self): self.do_request('POST') def do_PUT(self): self.do_request('PUT') def do_DELETE(self): self.do_request('DELETE') def do_PATCH(self): self.do_request('PATCH') def do_OPTIONS(self): self.do_request('OPTIONS') def log_message(self, format, *args): """自定义日志格式""" print(f"[HTTPS Proxy] {self.client_address[0]} - {format % args}") class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): """支持多线程的 HTTP 服务器""" daemon_threads = True def main(): parser = argparse.ArgumentParser(description='HTTPS to HTTP Proxy Server') parser.add_argument('--port', type=int, default=8989, help='HTTPS port to listen on') parser.add_argument('--target', default='http://localhost:8000', help='Target HTTP URL') parser.add_argument('--cert', required=True, help='SSL certificate file') parser.add_argument('--key', required=True, help='SSL private key file') args = parser.parse_args() # 设置目标 URL ProxyHandler.target_url = args.target.rstrip('/') # 创建 SSL 上下文 ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_context.load_cert_chain(args.cert, args.key) # 创建服务器 server = ThreadedHTTPServer(('0.0.0.0', args.port), ProxyHandler) server.socket = ssl_context.wrap_socket(server.socket, server_side=True) print(f"HTTPS Proxy Server started on port {args.port}") print(f"Forwarding to: {args.target}") print(f"Press Ctrl+C to stop") print() try: server.serve_forever() except KeyboardInterrupt: print("\nShutting down...") server.shutdown() if __name__ == '__main__': main()