Spaces:
Paused
Paused
File size: 4,645 Bytes
34fe2fb |
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 |
# main.py
import asyncio
from fastapi import FastAPI, WebSocket
import uvicorn
# 创建 FastAPI 应用实例
app = FastAPI()
@app.websocket("/")
async def tunnel(websocket: WebSocket):
"""
WebSocket 隧道入口:
1. 接受客户端 WebSocket 连接;
2. 等待客户端发送 CONNECT 请求,解析出目标主机及端口;
3. 尝试与目标主机建立 TCP 连接;
4. 返回 HTTP 200 成功建立隧道;
5. 启动双向数据转发(TCP <--> WebSocket)。
"""
await websocket.accept()
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 websocket.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 websocket.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 websocket.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 websocket.close()
return
# ---------------------------
# 2. 建立到目标主机的 TCP 连接
# ---------------------------
try:
reader, 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 websocket.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 True:
data = await reader.read(1024)
if not data:
break
await websocket.send_bytes(data)
except Exception as e:
# 读取异常或对方关闭连接时退出
print("tcp_to_ws 异常:", e)
async def ws_to_tcp():
"""
从客户端通过 WebSocket 发送的数据写入 TCP 连接
"""
try:
while True:
message = await websocket.receive()
# 接收到的数据可能是文本或二进制,这里尽量以二进制方式处理
if "bytes" in message:
data = message["bytes"]
elif "text" in message:
# 若收到文本数据,则转换为 bytes(可能只在握手阶段出现)
data = message["text"].encode("utf-8")
else:
break
writer.write(data)
await writer.drain()
except Exception as e:
print("ws_to_tcp 异常:", e)
# 并发执行数据转发任务,任一方向关闭则结束隧道
await asyncio.gather(tcp_to_ws(), ws_to_tcp())
except Exception as e:
print("WebSocket 隧道处理异常:", e)
finally:
# 关闭连接
await websocket.close()
# ---------------------------
# 启动服务器:监听 0.0.0.0:7860
# ---------------------------
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False)
|