lolly/internal/http3/server.go
xfy d21e27fbac fix(lint): 修复 golangci-lint 错误 (119 -> 0 issues)
主要修复:
- errcheck: defer Close 使用 //nolint:errcheck,类型断言改为 ok 检查
- govet fieldalignment: 调整结构体字段顺序优化内存布局
- revive unused-parameter: 将未使用参数改为 _
- exhaustive: 添加缺失的 switch case 或 default
- goconst: 提取重复字符串为常量 (accessAllow, accessDeny 等)
- staticcheck SA9003: 修复空分支逻辑
- gofmt: 运行 gofmt -w 格式化
- nolintlint: 修复 nolint 注释格式

其他改进:
- 更新 .golangci.yml 配置,启用更严格的检查
- 移除未使用的代码和导入
- 简化测试辅助函数调用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:15:31 +08:00

303 lines
6.3 KiB
Go
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.

// Package http3 提供 HTTP/3 (QUIC) 协议支持。
//
// 该文件包含 HTTP/3 服务器的核心实现,包括:
// - 基于 quic-go 的 HTTP/3 服务器
// - 支持 0-RTT 连接
// - 优雅关闭支持
// - 与现有 fasthttp handler 的集成
//
// 主要用途:
//
// 用于提供 HTTP/3 协议支持,提升网站性能和用户体验。
//
// 作者xfy
package http3
import (
"context"
"crypto/tls"
"fmt"
"net"
"sync"
"time"
"github.com/quic-go/quic-go"
quichttp3 "github.com/quic-go/quic-go/http3"
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/config"
"rua.plus/lolly/internal/logging"
)
const (
defaultHTTP3Listen = ":443"
)
// Server HTTP/3 服务器。
//
// 使用 QUIC 协议提供 HTTP/3 服务,与现有的 TCP 服务器并行运行。
type Server struct {
// config HTTP/3 配置
config *config.HTTP3Config
// http3Server HTTP/3 服务器实例
http3Server *quichttp3.Server
// handler fasthttp 请求处理器
handler fasthttp.RequestHandler
// adapter 请求适配器
adapter *Adapter
// tlsConfig TLS 配置
tlsConfig *tls.Config
// listener QUIC 监听器
listener *quic.EarlyListener
// running 服务器运行状态
running bool
// mu 读写锁
mu sync.RWMutex
}
// NewServer 创建 HTTP/3 服务器。
//
// 参数:
// - cfg: HTTP/3 配置
// - handler: fasthttp 请求处理器
// - tlsConfig: TLS 配置(必须)
//
// 返回值:
// - *Server: HTTP/3 服务器实例
// - error: 配置无效时返回错误
func NewServer(cfg *config.HTTP3Config, handler fasthttp.RequestHandler, tlsConfig *tls.Config) (*Server, error) {
if cfg == nil {
return nil, fmt.Errorf("http3 config is nil")
}
if handler == nil {
return nil, fmt.Errorf("handler is nil")
}
if tlsConfig == nil {
return nil, fmt.Errorf("tls config is required for HTTP/3")
}
adapter := NewAdapter()
return &Server{
config: cfg,
handler: handler,
adapter: adapter,
tlsConfig: tlsConfig,
}, nil
}
// Start 启动 HTTP/3 服务器。
//
// 创建 UDP 监听器并开始接受 QUIC 连接。
//
// 返回值:
// - error: 启动失败时返回错误
func (s *Server) Start() error {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
return fmt.Errorf("server already running")
}
// 创建 QUIC 配置
quicConfig := &quic.Config{
MaxIncomingStreams: int64(s.config.MaxStreams),
MaxIdleTimeout: s.config.IdleTimeout,
KeepAlivePeriod: 30 * time.Second,
Allow0RTT: s.config.Enable0RTT,
}
// 如果启用了 0-RTT输出安全警告
if s.config.Enable0RTT {
logging.Warn().
Msg("HTTP/3 0-RTT is enabled. " +
"For 0-RTT to work, TLS session tickets must be configured " +
"(TLSConfig.ClientSessionCache and TLSConfig.SessionTicketKey). " +
"See documentation for details.")
}
// 设置默认值
if quicConfig.MaxIncomingStreams == 0 {
quicConfig.MaxIncomingStreams = 100
}
if quicConfig.MaxIdleTimeout == 0 {
quicConfig.MaxIdleTimeout = 30 * time.Second
}
// 创建 UDP 监听器
listenAddr := s.config.Listen
if listenAddr == "" {
listenAddr = defaultHTTP3Listen
}
udpAddr, err := net.ResolveUDPAddr("udp", listenAddr)
if err != nil {
return fmt.Errorf("failed to resolve UDP address: %w", err)
}
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
return fmt.Errorf("failed to listen UDP: %w", err)
}
// 创建 QUIC 监听器
s.listener, err = quic.ListenEarly(udpConn, s.tlsConfig, quicConfig)
if err != nil {
_ = udpConn.Close()
return fmt.Errorf("failed to listen QUIC: %w", err)
}
// 创建 HTTP/3 服务器
s.http3Server = &quichttp3.Server{
Handler: s.adapter.Wrap(s.handler),
}
s.running = true
logging.Info().
Str("listen", listenAddr).
Bool("0rtt", s.config.Enable0RTT).
Msg("HTTP/3 server started")
// 开始服务
go func() {
if err := s.http3Server.ServeListener(s.listener); err != nil {
s.mu.RLock()
running := s.running
s.mu.RUnlock()
if running {
logging.Error().Err(err).Msg("HTTP/3 server error")
}
}
}()
return nil
}
// Stop 停止 HTTP/3 服务器。
//
// 优雅关闭服务器,等待现有连接完成。
//
// 返回值:
// - error: 关闭失败时返回错误
func (s *Server) Stop() error {
s.mu.Lock()
defer s.mu.Unlock()
if !s.running {
return nil
}
s.running = false
if s.http3Server != nil {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := s.http3Server.Close(); err != nil {
logging.Error().Err(err).Msg("HTTP/3 server close error")
}
// 等待服务完全停止
<-ctx.Done()
}
logging.Info().Msg("HTTP/3 server stopped")
return nil
}
// GracefulStop 优雅停止服务器。
//
// 等待指定时间让现有连接完成。
//
// 参数:
// - timeout: 等待超时时间
func (s *Server) GracefulStop(timeout time.Duration) error {
s.mu.Lock()
s.running = false
s.mu.Unlock()
if s.http3Server != nil {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
done := make(chan struct{})
go func() {
_ = s.http3Server.Close()
close(done)
}()
select {
case <-done:
logging.Info().Msg("HTTP/3 server graceful stop completed")
case <-ctx.Done():
logging.Warn().Msg("HTTP/3 server graceful stop timeout")
}
}
return nil
}
// IsRunning 检查服务器是否正在运行。
func (s *Server) IsRunning() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.running
}
// GetAltSvcHeader 返回 Alt-Svc 响应头值。
//
// 用于告知客户端可以使用 HTTP/3。
//
// 返回值:
// - string: Alt-Svc 头值,如 `h3=":443"; ma=86400`
func (s *Server) GetAltSvcHeader() string {
if s.config == nil || !s.config.Enabled {
return ""
}
listen := s.config.Listen
if listen == "" {
listen = defaultHTTP3Listen
}
// 移除前导冒号,保留端口
port := listen
if port[0] == ':' {
port = port[1:]
}
return fmt.Sprintf(`h3=":%s"; ma=86400`, port)
}
// Stats 返回服务器统计信息。
type Stats struct {
Listen string // 监听地址
MaxStreams int // 最大并发流
Running bool // 是否运行中
Enable0RTT bool // 是否启用 0-RTT
}
// GetStats 返回服务器统计信息。
func (s *Server) GetStats() Stats {
s.mu.RLock()
defer s.mu.RUnlock()
return Stats{
Running: s.running,
Listen: s.config.Listen,
Enable0RTT: s.config.Enable0RTT,
MaxStreams: s.config.MaxStreams,
}
}