fix(server): handle LocationEngine registration errors properly
Add typed ConflictError for path conflicts, change register functions to return errors, handle conflicts as warnings and fatal errors as startup failures. Remove all 20 instances of ignored Add* return values.
This commit is contained in:
parent
ac66ea5534
commit
38bb743781
@ -1,6 +1,7 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
@ -107,4 +108,8 @@ func TestLocationEngine_PathConflict(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Error("should fail on path conflict")
|
||||
}
|
||||
var ce *ConflictError
|
||||
if !errors.As(err, &ce) {
|
||||
t.Errorf("expected *ConflictError, got %T: %v", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,6 +240,21 @@ func (e *LocationEngine) MarkInitialized() {
|
||||
e.prefixTree.MarkInitialized()
|
||||
}
|
||||
|
||||
// ConflictError 路径冲突错误。
|
||||
//
|
||||
// 当同一路径被重复注册为不同类型的 location 时返回此错误。
|
||||
// 调用方可通过 errors.As 检测此类型,区分冲突与致命错误。
|
||||
type ConflictError struct {
|
||||
Path string
|
||||
ExistingType string
|
||||
NewType string
|
||||
}
|
||||
|
||||
func (e *ConflictError) Error() string {
|
||||
return fmt.Sprintf("path conflict: '%s' already registered as '%s', trying to register as '%s'",
|
||||
e.Path, e.ExistingType, e.NewType)
|
||||
}
|
||||
|
||||
// checkConflict 检查路径冲突。
|
||||
//
|
||||
// 参数:
|
||||
@ -247,11 +262,10 @@ func (e *LocationEngine) MarkInitialized() {
|
||||
// - locationType: location 类型
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 路径已存在时返回冲突错误
|
||||
// - error: 路径已存在时返回 *ConflictError
|
||||
func (e *LocationEngine) checkConflict(path, locationType string) error {
|
||||
if existing, ok := e.registeredPaths[path]; ok {
|
||||
return fmt.Errorf("path conflict: '%s' already registered as '%s', trying to register as '%s'",
|
||||
path, existing, locationType)
|
||||
return &ConflictError{Path: path, ExistingType: existing, NewType: locationType}
|
||||
}
|
||||
e.registeredPaths[path] = locationType
|
||||
return nil
|
||||
|
||||
@ -67,7 +67,7 @@ func (s *Server) createProxyForConfig(proxyCfg *config.ProxyConfig) *proxy.Proxy
|
||||
//
|
||||
// 根据配置为 LocationEngine 注册代理路径,创建代理处理器和健康检查器。
|
||||
// 支持通过 LocationType 配置不同的匹配方式。
|
||||
func (s *Server) registerProxyRoutesWithLocationEngine(serverCfg *config.ServerConfig) {
|
||||
func (s *Server) registerProxyRoutesWithLocationEngine(serverCfg *config.ServerConfig) error {
|
||||
for i := range serverCfg.Proxy {
|
||||
proxyCfg := &serverCfg.Proxy[i]
|
||||
|
||||
@ -76,7 +76,6 @@ func (s *Server) registerProxyRoutesWithLocationEngine(serverCfg *config.ServerC
|
||||
continue
|
||||
}
|
||||
|
||||
// 根据 LocationType 注册路由
|
||||
locType := proxyCfg.LocationType
|
||||
if locType == "" {
|
||||
locType = matcher.LocationTypePrefix
|
||||
@ -84,22 +83,47 @@ func (s *Server) registerProxyRoutesWithLocationEngine(serverCfg *config.ServerC
|
||||
|
||||
switch locType {
|
||||
case matcher.LocationTypeExact:
|
||||
_ = s.locationEngine.AddExact(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal)
|
||||
if err := s.locationEngine.AddExact(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", proxyCfg.Path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypePrefixPriority:
|
||||
_ = s.locationEngine.AddPrefixPriority(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal)
|
||||
if err := s.locationEngine.AddPrefixPriority(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", proxyCfg.Path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypeRegex, matcher.LocationTypeRegexCaseless:
|
||||
caseInsensitive := locType == matcher.LocationTypeRegexCaseless
|
||||
_ = s.locationEngine.AddRegex(proxyCfg.Path, p.ServeHTTP, caseInsensitive, proxyCfg.Internal)
|
||||
if err := s.locationEngine.AddRegex(proxyCfg.Path, p.ServeHTTP, caseInsensitive, proxyCfg.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", proxyCfg.Path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypeNamed:
|
||||
if proxyCfg.LocationName != "" {
|
||||
_ = s.locationEngine.AddNamed(proxyCfg.LocationName, p.ServeHTTP)
|
||||
if err := s.locationEngine.AddNamed(proxyCfg.LocationName, p.ServeHTTP); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", "@"+proxyCfg.LocationName, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypePrefix:
|
||||
_ = s.locationEngine.AddPrefix(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal)
|
||||
if err := s.locationEngine.AddPrefix(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", proxyCfg.Path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
_ = s.locationEngine.AddPrefix(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal)
|
||||
if err := s.locationEngine.AddPrefix(proxyCfg.Path, p.ServeHTTP, proxyCfg.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("proxy", proxyCfg.Path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureStaticHandler 配置静态文件处理器。
|
||||
@ -156,7 +180,7 @@ func (s *Server) configureStaticHandler(static *config.StaticConfig, cfg *config
|
||||
}
|
||||
|
||||
// registerStaticHandlersWithLocationEngine 使用 LocationEngine 注册静态文件处理器。
|
||||
func (s *Server) registerStaticHandlersWithLocationEngine(cfg *config.ServerConfig) {
|
||||
func (s *Server) registerStaticHandlersWithLocationEngine(cfg *config.ServerConfig) error {
|
||||
for _, static := range cfg.Static {
|
||||
staticHandler := s.configureStaticHandler(&static, cfg)
|
||||
path := static.Path
|
||||
@ -164,7 +188,6 @@ func (s *Server) registerStaticHandlersWithLocationEngine(cfg *config.ServerConf
|
||||
path = "/"
|
||||
}
|
||||
|
||||
// 根据 LocationType 注册路由
|
||||
locType := static.LocationType
|
||||
if locType == "" {
|
||||
locType = matcher.LocationTypePrefix
|
||||
@ -172,15 +195,32 @@ func (s *Server) registerStaticHandlersWithLocationEngine(cfg *config.ServerConf
|
||||
|
||||
switch locType {
|
||||
case matcher.LocationTypeExact:
|
||||
_ = s.locationEngine.AddExact(path, staticHandler.Handle, static.Internal)
|
||||
if err := s.locationEngine.AddExact(path, staticHandler.Handle, static.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("static", path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypePrefixPriority:
|
||||
_ = s.locationEngine.AddPrefixPriority(path, staticHandler.Handle, static.Internal)
|
||||
if err := s.locationEngine.AddPrefixPriority(path, staticHandler.Handle, static.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("static", path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypePrefix:
|
||||
_ = s.locationEngine.AddPrefix(path, staticHandler.Handle, static.Internal)
|
||||
if err := s.locationEngine.AddPrefix(path, staticHandler.Handle, static.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("static", path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
_ = s.locationEngine.AddPrefix(path, staticHandler.Handle, static.Internal)
|
||||
if err := s.locationEngine.AddPrefix(path, staticHandler.Handle, static.Internal); err != nil {
|
||||
if err := s.handleRegistrationError("static", path, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// registerProxyRoutes 注册代理路由。
|
||||
@ -324,9 +364,9 @@ func (s *Server) registerLuaRoutes(router *handler.Router, serverCfg *config.Ser
|
||||
// - 只有设置了 Route 字段的脚本才会被注册
|
||||
// - 路由脚本不经过完整中间件链,只应用 accesslog 和 errorintercept
|
||||
// - 支持 exact、prefix、prefix_priority、regex、regex_caseless 匹配类型
|
||||
func (s *Server) registerLuaRoutesWithLocationEngine(serverCfg *config.ServerConfig) {
|
||||
func (s *Server) registerLuaRoutesWithLocationEngine(serverCfg *config.ServerConfig) error {
|
||||
if s.luaEngine == nil || serverCfg.Lua == nil || !serverCfg.Lua.Enabled {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, script := range serverCfg.Lua.Scripts {
|
||||
@ -348,17 +388,38 @@ func (s *Server) registerLuaRoutesWithLocationEngine(serverCfg *config.ServerCon
|
||||
|
||||
switch routeType {
|
||||
case matcher.LocationTypeExact:
|
||||
_ = s.locationEngine.AddExact(script.Route, handler, false)
|
||||
if err := s.locationEngine.AddExact(script.Route, handler, false); err != nil {
|
||||
if err := s.handleRegistrationError("lua", script.Route, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypePrefixPriority:
|
||||
_ = s.locationEngine.AddPrefixPriority(script.Route, handler, false)
|
||||
if err := s.locationEngine.AddPrefixPriority(script.Route, handler, false); err != nil {
|
||||
if err := s.handleRegistrationError("lua", script.Route, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypeRegex:
|
||||
_ = s.locationEngine.AddRegex(script.Route, handler, false, false)
|
||||
if err := s.locationEngine.AddRegex(script.Route, handler, false, false); err != nil {
|
||||
if err := s.handleRegistrationError("lua", script.Route, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case matcher.LocationTypeRegexCaseless:
|
||||
_ = s.locationEngine.AddRegex(script.Route, handler, true, false)
|
||||
if err := s.locationEngine.AddRegex(script.Route, handler, true, false); err != nil {
|
||||
if err := s.handleRegistrationError("lua", script.Route, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
_ = s.locationEngine.AddPrefix(script.Route, handler, false)
|
||||
if err := s.locationEngine.AddPrefix(script.Route, handler, false); err != nil {
|
||||
if err := s.handleRegistrationError("lua", script.Route, err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrapRoutedHandler 为路由处理器包装基础中间件链。
|
||||
|
||||
@ -21,6 +21,7 @@ package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
@ -96,6 +97,15 @@ func New(cfg *config.Config) *Server {
|
||||
return &Server{config: cfg}
|
||||
}
|
||||
|
||||
func (s *Server) handleRegistrationError(source, path string, err error) error {
|
||||
var ce *matcher.ConflictError
|
||||
if errors.As(err, &ce) {
|
||||
logging.Warn().Msgf("Route registration skipped (%s %s): %s", source, path, err)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s route %s: %w", source, path, err)
|
||||
}
|
||||
|
||||
// getServerName 根据配置返回服务器名称。
|
||||
//
|
||||
// 当 ServerTokens 为 false 时隐藏版本号,仅返回 "lolly"。
|
||||
@ -382,39 +392,56 @@ func (s *Server) startSingleMode() error {
|
||||
if err != nil {
|
||||
logging.Error().Msg("Failed to create status handler: " + err.Error())
|
||||
} else {
|
||||
_ = s.locationEngine.AddExact(statusHandler.Path(), statusHandler.ServeHTTP, false)
|
||||
if err := s.locationEngine.AddExact(statusHandler.Path(), statusHandler.ServeHTTP, false); err != nil {
|
||||
if err := s.handleRegistrationError("status", statusHandler.Path(), err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册 pprof 性能分析端点(如果配置)
|
||||
if s.config.Monitoring.Pprof.Enabled {
|
||||
pprofHandler, err := NewPprofHandler(&s.config.Monitoring.Pprof)
|
||||
if err != nil {
|
||||
logging.Error().Msg("Failed to create pprof handler: " + err.Error())
|
||||
} else {
|
||||
_ = s.locationEngine.AddExact(pprofHandler.Path(), pprofHandler.ServeHTTP, false)
|
||||
_ = s.locationEngine.AddPrefixPriority(pprofHandler.Path()+"/", pprofHandler.ServeHTTP, false)
|
||||
if err := s.locationEngine.AddExact(pprofHandler.Path(), pprofHandler.ServeHTTP, false); err != nil {
|
||||
if err := s.handleRegistrationError("pprof", pprofHandler.Path(), err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.locationEngine.AddPrefixPriority(pprofHandler.Path()+"/", pprofHandler.ServeHTTP, false); err != nil {
|
||||
if err := s.handleRegistrationError("pprof", pprofHandler.Path()+"/", err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册缓存清理 API(如果配置)
|
||||
if serverCfg.CacheAPI != nil && serverCfg.CacheAPI.Enabled {
|
||||
purgeHandler, err := NewPurgeHandler(s, serverCfg.CacheAPI)
|
||||
if err != nil {
|
||||
logging.Error().Msg("Failed to create cache purge handler: " + err.Error())
|
||||
} else {
|
||||
_ = s.locationEngine.AddExact(purgeHandler.Path(), purgeHandler.ServeHTTP, false)
|
||||
if err := s.locationEngine.AddExact(purgeHandler.Path(), purgeHandler.ServeHTTP, false); err != nil {
|
||||
if err := s.handleRegistrationError("cache-purge", purgeHandler.Path(), err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册代理路由
|
||||
s.registerProxyRoutesWithLocationEngine(serverCfg)
|
||||
if err := s.registerProxyRoutesWithLocationEngine(serverCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lua 路由
|
||||
s.registerLuaRoutesWithLocationEngine(serverCfg)
|
||||
if err := s.registerLuaRoutesWithLocationEngine(serverCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 静态文件服务
|
||||
s.registerStaticHandlersWithLocationEngine(serverCfg)
|
||||
if err := s.registerStaticHandlersWithLocationEngine(serverCfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 标记 LocationEngine 初始化完成
|
||||
s.locationEngine.MarkInitialized()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user