## TODO

检查连接被提前关闭的问题

访问失败的也返回访问记录

解决节点断开立即重连会导致 status 覆盖的问题

实现一个机制使更新数据的后项能够覆盖前项

节点每次发送心跳后提供或计算 loss 和 rtt 信息，并对比 host 和 status，将需要更新的数据提交到更新队列

### 长期

实现一个接口以手动全量同步数据库中的节点信息，防止网关节点数据与数据库出现偏差

将协议内容抽离出公共包，gateway 和 edge 节点共同调用

## 开发相关

### 目录结构

server/fwd: 网关核心代码

- core: 核心代码，目前主要是连接管理
- dispatcher: 请求处理器，负责解析传入协议，并将请求分发到对应的处理器
- http: http 处理器，负责处理 http 请求
- socks: socks5 处理器，负责处理 socks5 请求
- repo: 状态仓库，所有有状态数据都通过 repo 中的接口与外部服务交互

### 转发服务结束时资源清理

1. 关闭接听端口，防止新连接接入（user, data, ctrl）
2. 通知并等待所有正在运行的 conn 处理协程全部关闭（user, data, ctrl）
3. 结束所有保存且未使用的 conn 连接（user, ctrl）

### proxy.lock 文件格式

| mag_num(1)  | name(16)  |
|-------------|-----------|
| 魔法数，固定 0x72 | 服务名称，uuid |

## 底层协议设计

### 步骤说明（待更新）

1. 启动转发服务，尝试注册自身到后端服务，随后持续报告心跳
2. 启动边缘节点后，尝试注册自身到后端服务，随后持续报告心跳
3. 后端服务根据在线转发服务的状态，返回分配给边缘节点的转发服务地址
4. 边缘节点根据配置尝试连接到转发服务（建立控制通道）
5. 连接成功后，控制通道将长期保留，边缘节点定时发送保活心跳，代理服务丢弃所有心跳包
6. 当用户请求代理时，转发服务通过控制通道向边缘节点提供代理目标信息
7. 边缘节点尝试连接到目标地址，同时尝试建立数据通道
8. 当成功建立数据通道后，边缘节点将数据通道标识以及对目标地址的连接结果提供给转发服务
9. 如果连接成功建立，则开始代理流量，如果连接失败，则关闭数据通道

### 协议报文说明

协议中所有数值都以大端形式传输

控制通道命令列表：

| 编码 | 命令    | 发起方   | 备注                 |
|----|-------|-------|--------------------|
| 1  | pong  | proxy | 网关响应，和 status 相同   |
| 2  | ping  | edge  | 边缘节点心跳             |
| 3  | open  | edge  | 边缘节点连接到网关          |
| 4  | close | edge  | 边缘节点断开连接           |
| 5  | proxy | proxy | 网关通知节点打开数据通道用来转发数据 |

### 建立控制通道

1.节点发起

| cmd(1)  | id(4) |
|---------|-------|
| 命令固定为 3 | 节点 ID |

2.网关响应

| status(1) |
|-----------|
| 状态，固定为 1  |

### 控制通道心跳

1.节点发起

| cmd(1)  |
|---------|
| 命令固定为 2 |

2.网关响应：

| status(1) |
|-----------|
| 状态，固定为 1  |

### 关闭控制通道

1.节点发起

| cmd(1)  |
|---------|
| 命令固定为 4 |

2.网关响应：

| status(1) |
|-----------|
| 状态，固定为 1  |

### 建立数据通道

1.网关发送：

| cmd(1)  | tag(16) | dst_len(2) | dst_buf(n) |
|---------|---------|------------|------------|
| 命令固定为 5 | 通道标识    | 目标地址长度     | 目标地址       |

2.节点发送：

| tag(16) | status(1)             |
|---------|-----------------------|
| 通道标识    | 目标地址连接结果，成功为 1，不成功为 0 |

## 接口安全性

网关服务不强制使用 tls，因此非公开端口的请求（来自平台端的请求）都需要通过额外设计的加密方案进行请求内容验证

安全接口的请求体格式为：

```json5
{
  "content": "string",
  // base64 编码的请求体
  "nonce": "string",
  // 随机数值
  "timestamp": "number"
  // 时间戳，精确到毫秒
}
```

其中，请求体的内容为经过 aes-gcm 加密后的数据，密钥为网关服务报告上线时，平台端返回的 secret
