Spaces:
Running
Running
File size: 5,128 Bytes
9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c 9cc8bb0 39eab2c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# main.py
import asyncio
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import uvicorn
# 创建 FastAPI 应用实例
app = FastAPI()
@app.websocket("/")
async def tunnel(websocket: WebSocket):
await websocket.accept()
tcp_writer = None
tcp_reader = None
closed = False
async def safe_close():
nonlocal closed
if not closed:
closed = True
try:
await websocket.close()
except Exception:
pass
if tcp_writer:
try:
tcp_writer.close()
await tcp_writer.wait_closed()
except Exception:
pass
try:
# ---------------------------
# 1. 等待客户端发来的 CONNECT 请求
# ---------------------------
# CONNECT 请求格式示例:
# CONNECT destHost:destPort HTTP/1.1\r\nHost: destHost:destPort\r\n\r\n
request_text = await websocket.receive_text()
lines = request_text.splitlines()
if not lines:
await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
await safe_close()
return
# 解析第一行
first_line = lines[0].strip()
parts = first_line.split()
if len(parts) < 3 or parts[0].upper() != "CONNECT":
await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
await safe_close()
return
# 从第二个字段中获取目标主机及端口,如 destHost:destPort
dest = parts[1]
if ":" not in dest:
await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
await safe_close()
return
dest_parts = dest.split(":", 1)
dest_host = dest_parts[0]
try:
dest_port = int(dest_parts[1])
except Exception:
await websocket.send_text("HTTP/1.1 400 Bad Request\r\n\r\n")
await safe_close()
return
# ---------------------------
# 2. 建立到目标主机的 TCP 连接
# ---------------------------
try:
tcp_reader, tcp_writer = await asyncio.open_connection(dest_host, dest_port)
except Exception as e:
err_msg = f"HTTP/1.1 502 Bad Gateway\r\n\r\n无法连接 {dest_host}:{dest_port},错误:{e}"
await websocket.send_text(err_msg)
await safe_close()
return
# ---------------------------
# 3. 向客户端返回 200 成功响应
# ---------------------------
await websocket.send_text("HTTP/1.1 200 Connection Established\r\n\r\n")
# ---------------------------
# 4. 双向数据转发
# ---------------------------
async def tcp_to_ws():
"""
从 TCP 连接中读取数据,通过 WebSocket 以二进制方式发送给客户端
"""
try:
while not closed:
data = await tcp_reader.read(1024)
if not data:
break
await websocket.send_bytes(data)
except Exception as e:
# 读取异常或对方关闭连接时退出
print("tcp_to_ws 异常:", e)
finally:
await safe_close()
async def ws_to_tcp():
"""
从客户端通过 WebSocket 发送的数据写入 TCP 连接
"""
try:
while not closed:
message = await websocket.receive()
# 接收到的数据可能是文本或二进制,这里尽量以二进制方式处理
if "bytes" in message:
tcp_writer.write(message["bytes"])
await tcp_writer.drain()
elif "text" in message:
# 若收到文本数据,则转换为 bytes(可能只在握手阶段出现)
tcp_writer.write(message["text"].encode("utf-8"))
await tcp_writer.drain()
elif message.get("type") == "websocket.disconnect":
break
except Exception as e:
print("ws_to_tcp 异常:", e)
finally:
await safe_close()
# 并发执行数据转发任务,任一方向关闭则结束隧道
await asyncio.gather(tcp_to_ws(), ws_to_tcp())
except WebSocketDisconnect:
print("WebSocketDisconnect")
await safe_close()
except Exception as e:
print("WebSocket 隧道处理异常:", e)
await safe_close()
finally:
# 关闭连接
await safe_close()
# ---------------------------
# 启动服务器:监听 0.0.0.0:7860
# ---------------------------
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)
|