gohttp/changes.md

5.6 KiB
Raw Blame History

代码审查修改记录

修改概述

本次代码审查发现了若干问题并进行了修复,主要涉及安全性、并发安全和代码质量方面的改进。


1. 安全性修复

1.1 修复 FileExists 函数错误处理 (handler/file.go)

问题FileExists 函数在文件不存在时错误地返回 os.IsExist(err),导致调用方无法正确区分"文件不存在"和其他错误。

修复

// 修改前
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)

问题:原有代码仅检查 ../ 前缀,无法防止符号链接导致的路径遍历攻击。

修复:添加了符号链接解析和真实路径验证逻辑:

// 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,确保计数器操作的原子性:

// 修改前
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 调用效率较低。

修复

// 修改前
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.Containsstrings.Cut 简化代码:

// 修改前
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 配置结构:

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代理配置

{
    "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 示例:

{
    "name": "mysql-proxy",
    "listen": "0.0.0.0:3307",
    "target": "192.168.1.100:3306",
    "enabled": true
}

6.6 服务器管理 (server/manager.go)

新增TCP代理服务器管理功能

  • TcpProxyManagerTCP代理处理器映射
  • AddTcpProxy注册TCP代理服务器
  • StartTcpProxies启动所有TCP代理服务器
  • StopTcpProxies停止所有TCP代理服务器

7. 构建验证

所有修改已通过 go build ./... 编译验证。