Files
any-hub/internal/proxy/forwarder.go

142 lines
3.7 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 proxy
import (
"fmt"
"strings"
"sync"
"github.com/gofiber/fiber/v3"
"github.com/sirupsen/logrus"
"github.com/any-hub/any-hub/internal/logging"
"github.com/any-hub/any-hub/internal/server"
)
// Forwarder 根据 HubRoute 的 module_key 选择对应的 ProxyHandler默认回退到构造时注入的 handler。
type Forwarder struct {
defaultHandler server.ProxyHandler
logger *logrus.Logger
}
// NewForwarder 创建 ForwarderdefaultHandler 不能为空。
func NewForwarder(defaultHandler server.ProxyHandler, logger *logrus.Logger) *Forwarder {
return &Forwarder{
defaultHandler: defaultHandler,
logger: logger,
}
}
var (
moduleHandlers sync.Map
)
// RegisterModuleHandler is kept for backward compatibility; it panics on invalid input.
func RegisterModuleHandler(key string, handler server.ProxyHandler) {
MustRegisterModule(ModuleRegistration{Key: key, Handler: handler})
}
// Handle 实现 server.ProxyHandler根据 route.Module.Key 选择 handler。
func (f *Forwarder) Handle(c fiber.Ctx, route *server.HubRoute) error {
requestID := server.RequestID(c)
handler := f.lookup(route)
if handler == nil {
return f.respondMissingHandler(c, route, requestID)
}
return f.invokeHandler(c, route, handler, requestID)
}
func (f *Forwarder) respondMissingHandler(c fiber.Ctx, route *server.HubRoute, requestID string) error {
f.logModuleError(route, "module_handler_missing", nil, requestID)
setRequestIDHeader(c, requestID)
return c.Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "module_handler_missing"})
}
func (f *Forwarder) invokeHandler(c fiber.Ctx, route *server.HubRoute, handler server.ProxyHandler, requestID string) (err error) {
defer func() {
if r := recover(); r != nil {
err = f.respondHandlerPanic(c, route, r, requestID)
}
}()
return handler.Handle(c, route)
}
func (f *Forwarder) respondHandlerPanic(c fiber.Ctx, route *server.HubRoute, recovered interface{}, requestID string) error {
f.logModuleError(route, "module_handler_panic", fmt.Errorf("panic: %v", recovered), requestID)
setRequestIDHeader(c, requestID)
return c.Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "module_handler_panic"})
}
func setRequestIDHeader(c fiber.Ctx, requestID string) {
if requestID != "" {
c.Set("X-Request-ID", requestID)
}
}
func (f *Forwarder) logModuleError(route *server.HubRoute, code string, err error, requestID string) {
if f.logger == nil {
return
}
fields := f.routeFields(route, requestID)
fields["action"] = "proxy"
fields["error"] = code
if err != nil {
f.logger.WithFields(fields).Error(err.Error())
return
}
f.logger.WithFields(fields).Error("module handler unavailable")
}
func (f *Forwarder) lookup(route *server.HubRoute) server.ProxyHandler {
if route != nil {
if handler := lookupModuleHandler(route.Module.Key); handler != nil {
return handler
}
}
return f.defaultHandler
}
func lookupModuleHandler(key string) server.ProxyHandler {
normalized := normalizeModuleKey(key)
if normalized == "" {
return nil
}
if value, ok := moduleHandlers.Load(normalized); ok {
if handler, ok := value.(server.ProxyHandler); ok {
return handler
}
}
return nil
}
func normalizeModuleKey(key string) string {
return strings.ToLower(strings.TrimSpace(key))
}
func (f *Forwarder) routeFields(route *server.HubRoute, requestID string) logrus.Fields {
if route == nil {
return logrus.Fields{
"hub": "",
"domain": "",
"hub_type": "",
"auth_mode": "",
"cache_hit": false,
"module_key": "",
}
}
fields := logging.RequestFields(
route.Config.Name,
route.Config.Domain,
route.Config.Type,
route.Config.AuthMode(),
route.Module.Key,
false,
)
if requestID != "" {
fields["request_id"] = requestID
}
return fields
}