From bdb774510fc5f2b02f2a90324669d0259022b7d0 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 2 Apr 2026 13:16:22 +0800 Subject: [PATCH] docs(plan): refine development plan with detailed implementation - Add middleware framework as Phase 2 prerequisite - Move basic logging system to Phase 2 for debugging support - Add TLS security defaults (disable TLSv1.0/1.1, secure ciphers) - Expand security headers (CSP, HSTS, Referrer-Policy, Permissions-Policy) - Add cache lock mechanism to prevent cache stampede - Detail performance optimization (sendfile, goroutine pool, buffer pool) - Update dependency graph Co-Authored-By: Claude --- docs/plan.md | 447 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 390 insertions(+), 57 deletions(-) diff --git a/docs/plan.md b/docs/plan.md index 8d24628..b17bb42 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -27,7 +27,17 @@ lolly/ │ ├── config/ # 配置解析模块 │ ├── server/ # HTTP 服务器核心 │ ├── handler/ # 请求处理器 -│ └── middleware/ # 中间件系统 +│ ├── middleware/ # 中间件系统(框架) +│ │ ├── security/ # 安全中间件(access、ratelimit、auth) +│ │ ├── compression/ # 压缩中间件(gzip、brotli) +│ │ ├── logging/ # 日志中间件 +│ │ └── rewrite/ # URL 重写中间件 +│ ├── proxy/ # 反向代理模块 +│ ├── loadbalance/ # 负载均衡模块 +│ ├── ssl/ # SSL/TLS 模块 +│ ├── cache/ # 缓存模块 +│ ├── stream/ # TCP/UDP Stream 代理 +│ └── logging/ # 日志系统核心 ├── pkg/ │ └── utils/ # 公共工具函数 ├── configs/ @@ -41,6 +51,7 @@ lolly/ **关键文件**: - `cmd/lolly/main.go` - 入口点,初始化和启动逻辑 - `internal/config/config.go` - 配置结构体定义和解析 +- `internal/middleware/middleware.go` - 中间件框架接口定义 #### 1.2 YAML 配置解析 @@ -50,12 +61,18 @@ lolly/ // Config 根配置结构 type Config struct { - Server ServerConfig `yaml:"server"` - Servers []ServerConfig `yaml:"servers"` // 多虚拟主机 - Logging LoggingConfig `yaml:"logging"` - Performance PerformanceConfig `yaml:"performance"` + DefaultServer ServerConfig `yaml:"server"` // 默认服务器配置(单服务器场景) + Servers []ServerConfig `yaml:"servers"` // 多虚拟主机配置(可选) + Logging LoggingConfig `yaml:"logging"` + Performance PerformanceConfig `yaml:"performance"` } +// 配置字段语义说明: +// - 若只配置 `server` 字段,则作为单一服务器运行 +// - 若配置 `servers` 字段,则按虚拟主机模式运行(按 Host 头匹配) +// - 若同时配置两者,`server` 作为默认 fallback 主机,`servers` 按名称匹配 +// - 建议使用场景:简单部署用 `server`,多站点部署用 `servers` + // ServerConfig 服务器配置 type ServerConfig struct { Listen string `yaml:"listen"` // 监听地址 ":8080" @@ -132,10 +149,36 @@ feat(config): 实现项目骨架和 YAML 配置解析 ## 第二阶段:HTTP 核心功能 ### 目标 -实现基础 HTTP 服务器、静态文件服务、请求路由。 +实现基础 HTTP 服务器、静态文件服务、请求路由、基础日志系统。 ### 任务列表 +#### 2.0 中间件框架(前置依赖) + +**实现**: +```go +// internal/middleware/middleware.go + +// Middleware 中间件接口 +type Middleware interface { + Name() string + Process(next http.Handler) http.Handler +} + +// Chain 中间件链 +type Chain struct { + middlewares []Middleware +} + +// Apply 应用中间件链 +func (c *Chain) Apply(final http.Handler) http.Handler +``` + +**设计要点**: +- 定义统一的中间件接口,所有中间件(security、compression、logging 等)实现此接口 +- 支持链式组合,按注册顺序执行 +- Phase 1 建立框架,后续阶段填充具体中间件实现 + #### 2.1 基础 HTTP 服务器 **核心实现**: @@ -241,6 +284,38 @@ type VHostManager struct { - 默认主机fallback - SNI 支持(SSL) +#### 2.5 基础日志系统(Phase 2 必需) + +**原因**:调试 Phase 2-4 功能需要日志支持,将日志系统基础版本提前实现。 + +**实现**: +```go +// internal/logging/logging.go + +// Logger 日志管理器 +type Logger struct { + level LogLevel + output io.Writer +} + +// LogLevel 日志级别 +type LogLevel int +const ( + LogLevelDebug LogLevel = iota + LogLevelInfo + LogLevelWarn + LogLevelError +) + +// AccessLogger 访问日志(基础版) +func LogAccess(r *http.Request, status int, size int64, duration time.Duration) +``` + +**Phase 2 实现范围**: +- 基础请求日志:记录请求方法、路径、状态码 +- 控制台输出:开发阶段便于调试 +- Phase 5 将扩展为完整日志系统(文件输出、自定义格式) + ### 验证方法 ```bash # 启动服务器 @@ -258,11 +333,13 @@ curl http://localhost:8080/api/health # 应返回 404(代理未实现) ``` feat(server): 实现 HTTP 服务器核心功能 +- 实现中间件框架(Middleware 接口、Chain 链式组合) - 实现基础 HTTP 服务器(启动/停止/优雅关闭) - 实现静态文件服务(MIME 类型、索引文件、Range 请求) - 实现请求路由器(精确/前缀/正则匹配) - 支持多虚拟主机配置 - 支持 keep-alive 长连接配置 +- 实现基础日志系统(控制台输出、请求日志) ``` --- @@ -441,31 +518,53 @@ feat(proxy): 实现反向代理和负载均衡功能 // SSLConfig SSL 配置 type SSLConfig struct { - Cert string `yaml:"cert"` // 证书路径 - Key string `yaml:"key"` // 私钥路径 - Protocols []string `yaml:"protocols"` // TLS 版本 - Ciphers []string `yaml:"ciphers"` // 加密套件 + Cert string `yaml:"cert"` // 证书路径 + Key string `yaml:"key"` // 私钥路径 + CertChain string `yaml:"cert_chain"` // 证书链路径(可选,用于中间证书) + Protocols []string `yaml:"protocols"` // TLS 版本,默认 ["TLSv1.2", "TLSv1.3"] + Ciphers []string `yaml:"ciphers"` // 加密套件(仅 TLS 1.2 有效) + OCSPStapling bool `yaml:"ocsp_stapling"` // OCSP Stapling 支持(默认 false) } // TLSManager TLS 管理器 type TLSManager struct { - configs map[string*tls.Config // 按 server_name + configs map[string]*tls.Config // 按 server_name } ``` +**安全默认配置**: +- **TLS 版本**:默认仅允许 TLSv1.2 和 TLSv1.3,**强制禁用 TLSv1.0/TLSv1.1** +- **加密套件默认值**(TLS 1.2,按优先级排序): + ```yaml + # 默认安全加密套件,无需手动配置 + ciphers: + - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 # 推荐,性能好 + - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # 推荐,更安全 + - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 # 推荐,移动端友好 + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 # ECDSA 证书专用 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 # ECDSA 证书专用 + - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 # ECDSA 证书专用 + ``` +- **TLS 1.3**:自动使用 Go 标准库的安全套件,配置无效 + +**安全校验**: +- 启用 Basic Auth 时,**强制要求 SSL 配置**,否则拒绝启动 +- 拒绝配置不安全的加密套件(如 RC4、DES、3DES) + **功能清单**: -- 证书加载:PEM 格式 -- TLS 版本控制:TLSv1.2、TLSv1.3 -- 加密套件配置 -- SSL 会话缓存(减少握手开销) -- SNI 支持(多证书) -- HTTP/2 自动启用(TLS 时) +- 证书加载:PEM 格式,支持证书链合并 +- TLS 版本控制:TLSv1.2、TLSv1.3,**默认禁用不安全版本** +- 加密套件配置:提供安全默认值,拒绝不安全套件 +- SSL 会话缓存:减少握手开销(LRU 缓存,默认 128 条) +- SNI 支持:多证书,通过 GetCertificate 回调实现 +- HTTP/2 自动启用:TLS 时自动启用 +- OCSP Stapling:减少客户端 CA 查询延迟和隐私风险 #### 4.2 IP 访问控制 **实现**: ```go -// internal/security/access.go +// internal/middleware/security/access.go // AccessControl IP 访问控制 type AccessControl struct { @@ -478,29 +577,49 @@ type AccessControl struct { func (a *AccessControl) Check(ip net.IP) bool ``` +**性能优化建议**: +- 使用 CIDR 树结构(radix tree)优化大规模 ACL 匹配 +- 预编译匹配规则,减少运行时开销 +- 支持 IPv4 和 IPv6 双栈匹配 + **配置示例**: ```yaml security: access: - allow: [192.168.1.0/24, 10.0.0.0/8] - deny: [192.168.2.100] + allow: [192.168.1.0/24, 10.0.0.0/8, "2001:db8::/32"] # 支持 IPv6 + deny: [192.168.2.100/32] default: deny + # 可选:使用高性能匹配模式 + optimize: true # 启用 CIDR 树优化(适用于 >100 条规则) ``` #### 4.3 请求限制 **实现**: ```go -// internal/security/ratelimit.go +// internal/middleware/security/ratelimit.go -// RateLimiter 速率限制器 +// RateLimiter 速率限制器(令牌桶算法) type RateLimiter struct { - requests int - per time.Duration - buckets map[string]*Bucket + rate int // 令牌生成速率(请求/秒) + burst int // 桶容量(突发流量上限) + buckets map[string]*TokenBucket mu sync.RWMutex } +// TokenBucket 令牌桶 +type TokenBucket struct { + tokens float64 + lastUpdate time.Time +} + +// SlidingWindowLimiter 滑动窗口限流器(可选,解决边界突发问题) +type SlidingWindowLimiter struct { + window time.Duration + limit int + requests map[string][]time.Time +} + // ConnLimiter 连接数限制器 type ConnLimiter struct { max int @@ -509,45 +628,115 @@ type ConnLimiter struct { } ``` +**算法选择**: +| 算法 | 适用场景 | 特点 | +|------|----------|------| +| 令牌桶 (Token Bucket) | API 请求限流 | 允许突发流量,推荐默认使用 | +| 滑动窗口 (Sliding Window) | 精确限流 | 解决固定窗口边界问题,无突发 | + **功能**: - 请求速率限制(`limit_req`) - 连接数限制(`limit_conn`) - 按 IP 或按 key 限制 - 超限响应:429 Too Many Requests +- 支持 `Retry-After` 响应头告知等待时间 #### 4.4 基础认证 **实现**: ```go -// internal/security/auth.go +// internal/middleware/security/auth.go // BasicAuth 基础认证 type BasicAuth struct { - users map[string]string // username -> hashed_password + users map[string]string // username -> hashed_password + algorithm HashAlgorithm // 哈希算法:bcrypt(默认)或 argon2id + realm string + requireTLS bool // 强制 HTTPS(默认 true) } +// HashAlgorithm 哈希算法类型 +type HashAlgorithm int +const ( + HashBcrypt HashAlgorithm = iota // bcrypt(默认,推荐) + HashArgon2id // Argon2id(更安全,计算密集) +) + // Authenticate 验证认证信息 func (b *BasicAuth) Authenticate(r *http.Request) bool ``` +**安全要求**: +- **强制 HTTPS**:启用 Basic Auth 时必须配置 SSL,否则拒绝启动 +- **安全哈希**:默认使用 bcrypt(成本因子 12),可选 Argon2id +- **弃用 apr1**:不再支持不安全的 MD5-based apr1 哈希 +- **密码强度**:配置验证,拒绝弱密码 + **配置示例**: ```yaml security: auth: type: basic + require_tls: true # 强制 HTTPS(默认 true) + algorithm: bcrypt # bcrypt(默认)或 argon2id users: - name: admin - password: $apr1$... # htpasswd 格式 + password: $2b$12$... # bcrypt 哈希(推荐) + - name: api_user + password: $argon2id$... # Argon2id 哈希(可选) realm: "Restricted Area" + min_password_length: 12 # 密码最小长度 ``` #### 4.5 安全头部 -**自动添加的安全头**: -- `X-Frame-Options: DENY` -- `X-Content-Type-Options: nosniff` -- `X-XSS-Protection: 1; mode=block` -- `Strict-Transport-Security: max-age=31536000` +**实现**: +```go +// internal/middleware/security/headers.go + +// SecurityHeaders 安全头部配置 +type SecurityHeaders struct { + XFrameOptions string `yaml:"x_frame_options"` // DENY/SAMEORIGIN/ALLOW-FROM + XContentTypeOptions string `yaml:"x_content_type_options"` // nosniff(默认) + ContentSecurityPolicy string `yaml:"content_security_policy"` // CSP 策略 + HSTS HSTSConfig `yaml:"hsts"` // HSTS 配置 + ReferrerPolicy string `yaml:"referrer_policy"` // 推荐值 + PermissionsPolicy string `yaml:"permissions_policy"` // 权限策略 +} + +// HSTSConfig HSTS 配置 +type HSTSConfig struct { + MaxAge int `yaml:"max_age"` // 过期时间(秒),默认 31536000(1年) + IncludeSubDomains bool `yaml:"include_sub_domains"` // 包含子域名,默认 true + Preload bool `yaml:"preload"` // HSTS 预加载列表,默认 false +} +``` + +**默认安全头部**: +| 头部 | 默认值 | 说明 | +|------|--------|------| +| X-Frame-Options | DENY | 防止点击劫持,可配置为 SAMEORIGIN | +| X-Content-Type-Options | nosniff | 防止 MIME 类型嗅探 | +| Content-Security-Policy | 可配置 | **关键**:防止 XSS 攻击 | +| Strict-Transport-Security | max-age=31536000; includeSubDomains | HSTS,强制 HTTPS | +| Referrer-Policy | strict-origin-when-cross-origin | 控制引用信息泄露 | +| Permissions-Policy | 可配置 | 控制浏览器功能权限 | + +**注意**:`X-XSS-Protection` 已被现代浏览器弃用,不再默认添加,重点依赖 CSP 防护。 + +**配置示例**: +```yaml +security: + headers: + x_frame_options: SAMEORIGIN # 或 DENY(默认) + content_security_policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" + hsts: + max_age: 31536000 # 1年 + include_sub_domains: true # 包含子域名 + preload: false # 不加入预加载列表(需用户显式启用) + referrer_policy: strict-origin-when-cross-origin + permissions_policy: "geolocation=(), microphone=(), camera=()" +``` ### 验证方法 ```bash @@ -569,14 +758,14 @@ curl -u admin:password http://localhost:8080/protected/ ``` feat(security): 实现 SSL/TLS 和安全访问控制 -- 实现 SSL/TLS 支持(证书加载、TLS版本、加密套件) -- 实现 SSL 会话缓存 -- 实现 SNI 多证书支持 -- 实现 IP 访问控制(白名单/黑名单) -- 实现请求速率限制 +- 实现 SSL/TLS 支持(证书加载、TLS版本、加密套件、OCSP Stapling) +- 提供安全默认加密套件,强制禁用 TLSv1.0/TLSv1.1 +- 实现 SSL 会话缓存和 SNI 多证书支持 +- 实现 IP 访问控制(白名单/黑名单,支持 IPv6,CIDR 树优化) +- 实现请求速率限制(令牌桶算法,支持滑动窗口) - 实现连接数限制 -- 实现基础认证(Basic Auth) -- 添加安全响应头部 +- 实现基础认证(bcrypt/Argon2id,强制 HTTPS) +- 实现完整安全头部(CSP、HSTS、Referrer-Policy 等) ``` --- @@ -653,8 +842,18 @@ compression: // FileCache 文件描述符缓存 type FileCache struct { maxEntries int + maxSize int64 // 内存上限(新增) inactive time.Duration entries map[string]*FileEntry + lruList *list.List // LRU 淘汰链表(新增) +} + +// FileEntry 缓存条目 +type FileEntry struct { + fd *os.File + size int64 + modTime time.Time + lastAccess time.Time } ``` @@ -664,10 +863,48 @@ type FileCache struct { // ProxyCache 代理缓存 type ProxyCache struct { - storage Storage // 内存/磁盘存储 - rules []CacheRule - maxAge time.Duration + storage Storage // 内存/磁盘存储 + rules []CacheRule + maxAge time.Duration + cacheLock bool // 缓存锁开关(默认 true) + lock *sync.RWMutex // 缓存锁,防止击穿 + pending map[string]*chan struct{} // 正在生成的缓存项 } + +// CacheRule 缓存规则 +type CacheRule struct { + Path string + Methods []string + Statuses []int // 可缓存的响应状态码 + MaxAge time.Duration +} +``` + +**缓存锁机制(防击穿)**: +- 当多个请求同时请求同一个未缓存的资源时,只让一个请求去后端获取 +- 其他请求等待第一个请求完成后从缓存读取 +- 防止缓存击穿导致后端压力骤增 + +**配置示例**: +```yaml +cache: + file: + max_entries: 10000 + max_size: 256MB # 内存上限 + inactive: 20s + lru_eviction: true # 启用 LRU 淘汰 + + proxy: + enabled: true + storage: memory # memory/disk + max_size: 1GB + cache_lock: true # 防止缓存击穿 + stale_while_revalidate: 60s # 过期缓存复用 + rules: + - path: /api/cacheable + methods: [GET] + statuses: [200, 301, 302] + max_age: 10m ``` #### 5.4 日志系统 @@ -763,8 +1000,8 @@ feat(enhance): 实现重写、压缩、缓存和日志功能 - 实现 URL 重写规则(正则匹配、301/302重定向) - 实现 Gzip 响应压缩 -- 实现静态文件缓存(文件描述符缓存) -- 实现代理响应缓存 +- 实现静态文件缓存(LRU 淘汰、内存上限控制) +- 实现代理响应缓存(缓存锁防击穿) - 实现访问日志(可定制格式) - 实现分级错误日志 - 实现状态监控端点 @@ -840,11 +1077,97 @@ func GracefulUpgrade(newBinary string) error #### 6.3 性能优化 **优化点**: -- 连接复用:`http.Transport` 配置 -- 缓冲池:减少内存分配 -- 零拷贝:`io.Copy` 使用 `sendfile`(Linux) -- Goroutine 池:控制并发数(可选) -- 对象池:`sync.Pool` 复用对象 + +##### 6.3.1 连接复用 +```go +// http.Transport 连接池配置 +transport := &http.Transport{ + MaxIdleConns: 100, // 最大空闲连接数 + MaxIdleConnsPerHost: 32, // 每主机最大空闲连接 + IdleConnTimeout: 90 * time.Second, // 空闲连接超时 + MaxConnsPerHost: 0, // 每主机最大连接数(0=无限制) +} +``` + +##### 6.3.2 缓冲池 +```go +// 使用 sync.Pool 实现分级缓冲池 +var bufferPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 32*1024) // 32KB 缓冲区 + }, +} +``` + +##### 6.3.3 零拷贝(sendfile) +```go +// internal/handler/sendfile.go + +// SendFile 零拷贝文件传输(仅 Linux) +// 大文件(>= 8KB)使用 sendfile 系统调用 +func SendFile(w http.ResponseWriter, f *os.File, offset, length int64) error { + // Linux: syscall.Sendfile + // macOS: syscall.Sendfile(不同签名) + // Windows: syscall.TransmitFile + // 其他平台: 降级为 io.Copy +} + +// 跨平台兼容方案 +// - Linux: sendfile(out_fd, in_fd, offset, count) +// - macOS: sendfile(in_fd, out_fd, offset, &len, sf_hdtr, flags) +// - Windows: TransmitFile(socket, handle, bytes_to_write, ... +// - Fallback: io.CopyBuffer +``` + +##### 6.3.4 Goroutine 池(可选) +```go +// internal/server/pool.go + +// GoroutinePool Goroutine 池配置 +type GoroutinePool struct { + maxWorkers int // 最大 worker 数 + minWorkers int // 最小 worker 数(预热) + idleTimeout time.Duration // 空闲超时 + taskQueue chan Task // 任务队列 +} + +// 配置示例 +performance: + goroutine_pool: + enabled: true // 启用池化(高 QPS 场景推荐) + max_workers: 10000 // 最大并发数 + min_workers: 100 // 预热 worker 数 + idle_timeout: 60s // 空闲超时 +``` + +##### 6.3.5 对象池 +```go +// 使用 sync.Pool 复用对象 +var requestPool = sync.Pool{ + New: func() interface{} { + return new(Request) + }, +} +``` + +##### 6.3.6 代理缓存锁(防击穿) +```go +// internal/cache/proxy_cache.go + +// ProxyCache 代理缓存(增加缓存锁) +type ProxyCache struct { + storage Storage + rules []CacheRule + maxAge time.Duration + lock *sync.RWMutex // 缓存锁,防止缓存击穿 + pending map[string]*chan struct{} // 正在生成的缓存项 +} + +// 缓存锁机制: +// 1. 请求到达时检查是否有 pending 请求 +// 2. 有则等待 pending 完成 +// 3. 无则创建 pending,生成缓存后广播 +``` #### 6.4 信号处理完善 @@ -880,7 +1203,12 @@ feat(advanced): 实现 Stream 代理和高级功能 - 实现 UDP Stream 代理 - 实现优雅升级(热升级) - 完善信号处理(SIGHUP、SIGUSR1、SIGUSR2) -- 实现性能优化(连接复用、缓冲池) +- 实现性能优化: + - 连接池复用(http.Transport 配置) + - 分级缓冲池(sync.Pool) + - 零拷贝 sendfile(跨平台兼容) + - Goroutine 池(可选,高 QPS 场景) + - 代理缓存锁(防止缓存击穿) ``` --- @@ -890,11 +1218,14 @@ feat(advanced): 实现 Stream 代理和高级功能 ``` Phase 1: cmd/lolly/main.go → internal/config/config.go + cmd/lolly/main.go → internal/middleware/middleware.go Phase 2: internal/server/server.go → internal/config/config.go internal/server/server.go → internal/handler/router.go + internal/server/server.go → internal/logging/logging.go internal/handler/router.go → internal/handler/static.go + internal/handler/router.go → internal/middleware/middleware.go Phase 3: internal/handler/router.go → internal/proxy/proxy.go @@ -903,19 +1234,21 @@ Phase 3: Phase 4: internal/server/server.go → internal/ssl/ssl.go - internal/handler/router.go → internal/security/access.go - internal/handler/router.go → internal/security/ratelimit.go - internal/handler/router.go → internal/security/auth.go + internal/middleware/middleware.go → internal/middleware/security/access.go + internal/middleware/middleware.go → internal/middleware/security/ratelimit.go + internal/middleware/middleware.go → internal/middleware/security/auth.go + internal/middleware/middleware.go → internal/middleware/security/headers.go Phase 5: - internal/handler/router.go → internal/rewrite/rewrite.go - internal/server/server.go → internal/compression/compression.go + internal/middleware/middleware.go → internal/middleware/rewrite/rewrite.go + internal/middleware/middleware.go → internal/middleware/compression/compression.go internal/proxy/proxy.go → internal/cache/proxy_cache.go - internal/server/server.go → internal/logging/logging.go + internal/server/server.go → internal/logging/logging.go(扩展) Phase 6: cmd/lolly/main.go → internal/stream/stream.go internal/server/server.go → internal/server/upgrade.go + internal/server/server.go → internal/server/pool.go(可选) ``` ---