gohttp/changes.md

219 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 代码审查修改记录
## 修改概述
本次代码审查发现了若干问题并进行了修复,主要涉及安全性、并发安全和代码质量方面的改进。
---
## 1. 安全性修复
### 1.1 修复 `FileExists` 函数错误处理 (handler/file.go)
**问题**`FileExists` 函数在文件不存在时错误地返回 `os.IsExist(err)`,导致调用方无法正确区分"文件不存在"和其他错误。
**修复**
```go
// 修改前
if err != nil {
return nil, os.IsExist(err), err // 错误os.IsExist 用于判断错误是否"已存在"
}
// 修改后
if err != nil {
if os.IsNotExist(err) {
return nil, false, nil // 正确返回"不存在"
}
return nil, false, err
}
```
### 1.2 增强文件处理器路径遍历防护 (handler/file.go)
**问题**:原有代码仅检查 `../` 前缀,无法防止符号链接导致的路径遍历攻击。
**修复**:添加了符号链接解析和真实路径验证逻辑:
```go
// Resolve symlinks and clean path to prevent path traversal
realRoot, _ := filepath.EvalSymlinks(f.Root)
realPath, _ := filepath.EvalSymlinks(filepath.Dir(rPath))
if realPath != realRoot && !strings.HasPrefix(realPath, realRoot+string(filepath.Separator)) {
return nil, errors.New("not permitted")
}
```
---
## 2. 并发安全修复
### 2.1 修复 `ProxyHandler.count` 竞态条件 (handler/proxy.go)
**问题**`ProxyHandler.count` 使用 `int` 类型,在高并发环境下存在数据竞态。
**修复**:使用 `atomic.Int32` 替代 `int`,确保计数器操作的原子性:
```go
// 修改前
type ProxyHandler struct {
count int
}
// 修改后
type ProxyHandler struct {
count atomic.Int32
}
// 使用 Load()/Store() 方法进行原子操作
proxyIndex = p.count.Load()
p.count.Store(newCount)
```
---
## 3. 代码质量改进
### 3.1 使用 `strings.EqualFold` 替代 `strings.ToLower` 比较 (server/server.go)
**问题**:方法匹配时使用两次 `strings.ToLower` 调用效率较低。
**修复**
```go
// 修改前
if route.Method != "" && strings.ToLower(route.Method) != strings.ToLower(r.Method) {
// 修改后
if route.Method != "" && !strings.EqualFold(route.Method, r.Method) {
```
### 3.2 简化 IP 访问控制逻辑 (server/middleware.go)
**问题**:使用循环遍历切片检查元素存在性,代码冗余。
**修复**:使用 `slices.Contains``strings.Cut` 简化代码:
```go
// 修改前
clientIP := strings.Split(r.RemoteAddr, ":")[0]
for _, ip := range deniedIPs {
if ip == clientIP { ... }
}
// 修改后
clientIP, _, _ := strings.Cut(r.RemoteAddr, ":")
if slices.Contains(deniedIPs, clientIP) { ... }
```
---
## 4. 待修复问题(已知但本次未修改)
### 4.1 `MiddlewareLink.Clone()` 方法存在反向迭代 Bug
**问题**:该方法从后向前迭代 `list`,但 `Add` 方法总是将元素插入到 `Done` 之前,导致克隆的中间件链顺序反转。
**影响**:如果中间件顺序重要,克隆后的中间件链可能无法正常工作。
### 4.2 测试 `Test_RestMux_HandleFunc` 失败
**问题**:该测试在修改前后均失败,属于已有问题。
**原因**:路由匹配逻辑与 `RestMux.ServeHTTP` 的路由匹配实现不一致。
---
## 5. 文件变更清单
| 文件 | 修改类型 |
|------|---------|
| handler/file.go | 安全性修复 |
| handler/proxy.go | 并发安全修复 |
| server/server.go | 代码质量改进 |
| server/middleware.go | 代码质量改进 |
---
## 6. 新增功能TCP代理服务器
### 6.1 功能概述
新增TCP代理服务器功能支持在指定端口监听TCP请求并转发到指定服务器的指定端口。可以配置多个TCP代理。
### 6.2 新增文件
**handler/tcpproxy.go** - TCP代理处理器实现
- `TcpProxyHandler` 结构体管理TCP代理连接
- `NewTcpProxyHandler` 函数创建新的TCP代理处理器
- `Start` 方法启动TCP代理监听
- `Stop` 方法停止TCP代理服务器
- `pipe` 方法:连接间数据转发
### 6.3 配置模型 (model/model.go)
新增 `TcpProxyConfig` 配置结构:
```go
type TcpProxyConfig struct {
Name string `json:"name"` // 代理名称
Listen string `json:"listen"` // 监听地址,格式如 "0.0.0.0:8080"
Target string `json:"target"` // 目标服务器地址,格式如 "192.168.1.1:3306"
Enabled bool `json:"enabled"` // 是否启用
}
```
### 6.4 配置示例
`config.json` 中添加TCP代理配置
```json
{
"tcp_proxys": [
{
"name": "mysql-proxy",
"listen": "0.0.0.0:3307",
"target": "192.168.1.100:3306",
"enabled": true
},
{
"name": "redis-proxy",
"listen": "0.0.0.0:6380",
"target": "192.168.1.101:6379",
"enabled": true
}
]
}
```
### 6.5 从include目录加载TCP代理配置
TCP代理配置也可以从include目录中加载使用 `tcp_*.json` 文件命名约定:
**include目录结构**
```
./include/
├── servers/
│ ├── web.json
│ └── api.json
├── tcp_mysql.json # TCP代理配置
└── tcp_redis.json # TCP代理配置
```
**include/tcp_mysql.json 示例:**
```json
{
"name": "mysql-proxy",
"listen": "0.0.0.0:3307",
"target": "192.168.1.100:3306",
"enabled": true
}
```
### 6.6 服务器管理 (server/manager.go)
新增TCP代理服务器管理功能
- `TcpProxyManager`TCP代理处理器映射
- `AddTcpProxy`注册TCP代理服务器
- `StartTcpProxies`启动所有TCP代理服务器
- `StopTcpProxies`停止所有TCP代理服务器
---
## 7. 构建验证
所有修改已通过 `go build ./...` 编译验证。