問題を少し調べてみました。ソケットを 2 つの異なるサーバーのように動作させるのは簡単です (受信したデータの種類によって異なります)。ここで悪いのは、python の _ssl ライブラリが、ネイティブの python オブジェクトである socket._socket から直接読み取るため、正常にフックできないことです。
1 つの方法は、ネイティブの Python ソケットをフックする C モジュールを作成することです。もう 1 つの解決策は、1 つのフロントエンドと 2 つのバックエンド (https と http) を持つことです。フロントエンドは 4443 をリッスンし、https バックエンドと http バックエンドのどちらと接続するかを決定します。両方のサーバーに同じハンドラーを追加すると、同じように動作します。もう 1 つの問題は、バックエンドではクライアントの IP がわからないことですが、回避策があります (dict {(Frontend to backend source port number): Client IP} のように、フロントエンドが保持し、バックエンドが参照します)。 .
C のソリューションと比較すると、2 番目のソリューションはかなり汚れているように見えますが、ここにあります。
import BaseHTTPServer, SimpleHTTPServer
import ssl
import socket
import select
import threading
FRONTEND_PORT = 4443
BACKEND_PORT_SSL = 44431
BACKEND_PORT_HTTP = 44432
HOST = 'localhost'
httpd_ssl = BaseHTTPServer.HTTPServer((HOST, BACKEND_PORT_SSL), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd_ssl.socket = ssl.wrap_socket (httpd_ssl.socket, certfile='key.pem', server_side=True)
httpd_direct = BaseHTTPServer.HTTPServer((HOST, BACKEND_PORT_HTTP), SimpleHTTPServer.SimpleHTTPRequestHandler)
def serve_forever(http_server):
http_server.serve_forever()
def categorize(sock, addr):
data = sock.recv(1)
if data == '\x16':
port = BACKEND_PORT_SSL
else:
port = BACKEND_PORT_HTTP
other_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
other_sock.connect((HOST, port))
other_sock.send(data)
inp = [sock, other_sock]
select_timeout = 1.0
try:
while 1:
r,w,x = select.select(inp,[],[],select_timeout)
if not r:
continue
for s in r:
o_s = inp[1] if inp[0]==s else inp[0]
buf = s.recv(4096)
if not buf:
raise socket.error
o_s.send(buf)
except socket.error:
pass
finally:
for s in inp:
s.close()
threading.Thread(target=serve_forever, args=(httpd_ssl,)).start()
threading.Thread(target=serve_forever, args=(httpd_direct,)).start()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, FRONTEND_PORT))
sock.listen(10)
while True:
conn, addr = sock.accept()
threading.Thread(target=categorize, args=(conn, addr)).start()