209 lines
4.5 KiB
Go
209 lines
4.5 KiB
Go
// Package http3 提供 HTTP/3 (QUIC) 协议支持。
|
||
//
|
||
// 该文件包含 HTTP/3 服务器的核心实现,包括:
|
||
// - 基于 quic-go 的 HTTP/3 服务器
|
||
// - 支持 0-RTT 连接
|
||
// - 优雅关闭支持
|
||
// - 与现有 fasthttp handler 的集成
|
||
//
|
||
// 主要用途:
|
||
//
|
||
// 用于提供 HTTP/3 协议支持,提升网站性能和用户体验。
|
||
//
|
||
// 作者:xfy
|
||
package http3
|
||
|
||
import (
|
||
"crypto/tls"
|
||
"fmt"
|
||
"net"
|
||
"sync"
|
||
"sync/atomic"
|
||
"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 atomic.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.Load() {
|
||
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.Store(true)
|
||
|
||
logging.Info().
|
||
Str("listen", listenAddr).
|
||
Bool("0rtt", s.config.Enable0RTT).
|
||
Msg("HTTP/3 server started")
|
||
|
||
http3Server := s.http3Server
|
||
listener := s.listener
|
||
go func() {
|
||
if err := http3Server.ServeListener(listener); err != nil {
|
||
if s.running.Load() {
|
||
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.Load() {
|
||
return nil
|
||
}
|
||
|
||
s.running.Store(false)
|
||
|
||
if s.http3Server != nil {
|
||
if err := s.http3Server.Close(); err != nil {
|
||
logging.Error().Err(err).Msg("HTTP/3 server close error")
|
||
}
|
||
}
|
||
|
||
logging.Info().Msg("HTTP/3 server stopped")
|
||
return nil
|
||
}
|