From fc2c46a9df34e6582f64a9b91adb5e752a211e1b Mon Sep 17 00:00:00 2001 From: Rogee Date: Tue, 18 Nov 2025 16:11:13 +0800 Subject: [PATCH] Refactor module binding to rely on Type --- internal/hubmodule/debian/hooks.go | 71 +++++++------------ internal/proxy/forwarder.go | 6 +- internal/proxy/forwarder_test.go | 5 +- internal/proxy/handler.go | 14 ++-- internal/server/hub_registry.go | 6 +- internal/server/hub_registry_test.go | 4 +- internal/server/routes/modules.go | 2 +- .../005-proxy-module-delegation/data-model.md | 2 +- specs/006-module-hook-refactor/data-model.md | 2 +- tests/integration/module_routing_test.go | 2 +- 10 files changed, 46 insertions(+), 68 deletions(-) diff --git a/internal/hubmodule/debian/hooks.go b/internal/hubmodule/debian/hooks.go index 892e115..8d59bc0 100644 --- a/internal/hubmodule/debian/hooks.go +++ b/internal/hubmodule/debian/hooks.go @@ -25,72 +25,49 @@ func normalizePath(_ *hooks.RequestContext, p string, rawQuery []byte) (string, func cachePolicy(_ *hooks.RequestContext, locatorPath string, current hooks.CachePolicy) hooks.CachePolicy { clean := canonicalPath(locatorPath) - switch { - case isAptIndexPath(clean): - // 索引类(Release/Packages)需要 If-None-Match/If-Modified-Since 再验证。 - current.AllowCache = true - current.AllowStore = true - current.RequireRevalidate = true - case isAptImmutablePath(clean): + if strings.Contains(clean, "/by-hash/") || strings.Contains(clean, "/pool/") { // pool/*.deb 与 by-hash 路径视为不可变,直接缓存后续不再 HEAD。 current.AllowCache = true current.AllowStore = true current.RequireRevalidate = false - default: - current.AllowCache = false - current.AllowStore = false - current.RequireRevalidate = false + return current } + + if strings.Contains(clean, "/dists/") { + // 索引类(Release/Packages/Contents)需要 If-None-Match/If-Modified-Since 再验证。 + if strings.HasSuffix(clean, "/release") || + strings.HasSuffix(clean, "/inrelease") || + strings.HasSuffix(clean, "/release.gpg") { + current.AllowCache = true + current.AllowStore = true + current.RequireRevalidate = true + return current + } + } + + current.AllowCache = false + current.AllowStore = false + current.RequireRevalidate = false return current } func contentType(_ *hooks.RequestContext, locatorPath string) string { + clean := canonicalPath(locatorPath) switch { - case strings.HasSuffix(locatorPath, ".gz"): + case strings.HasSuffix(clean, ".gz"): return "application/gzip" - case strings.HasSuffix(locatorPath, ".xz"): + case strings.HasSuffix(clean, ".xz"): return "application/x-xz" - case strings.HasSuffix(locatorPath, "Release.gpg"): + case strings.HasSuffix(clean, "release.gpg"): return "application/pgp-signature" - case isAptIndexPath(locatorPath): + case strings.Contains(clean, "/dists/") && + (strings.HasSuffix(clean, "/release") || strings.HasSuffix(clean, "/inrelease")): return "text/plain" default: return "" } } -func isAptIndexPath(p string) bool { - clean := canonicalPath(p) - if isByHashPath(clean) { - return false - } - - if strings.Contains(clean, "/dists/") { - if strings.HasSuffix(clean, "/release") || - strings.HasSuffix(clean, "/inrelease") || - strings.HasSuffix(clean, "/release.gpg") { - return true - } - } - return false -} - -func isAptImmutablePath(p string) bool { - clean := canonicalPath(p) - if isByHashPath(clean) { - return true - } - if strings.Contains(clean, "/pool/") { - return true - } - return false -} - -func isByHashPath(p string) bool { - clean := canonicalPath(p) - return strings.Contains(clean, "/by-hash/") -} - func canonicalPath(p string) string { if p == "" { return "/" diff --git a/internal/proxy/forwarder.go b/internal/proxy/forwarder.go index a566cf2..941ab33 100644 --- a/internal/proxy/forwarder.go +++ b/internal/proxy/forwarder.go @@ -35,7 +35,7 @@ func RegisterModuleHandler(key string, handler server.ProxyHandler) { MustRegisterModule(ModuleRegistration{Key: key, Handler: handler}) } -// Handle 实现 server.ProxyHandler,根据 route.ModuleKey 选择 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) @@ -90,7 +90,7 @@ func (f *Forwarder) logModuleError(route *server.HubRoute, code string, err erro func (f *Forwarder) lookup(route *server.HubRoute) server.ProxyHandler { if route != nil { - if handler := lookupModuleHandler(route.ModuleKey); handler != nil { + if handler := lookupModuleHandler(route.Module.Key); handler != nil { return handler } } @@ -131,7 +131,7 @@ func (f *Forwarder) routeFields(route *server.HubRoute, requestID string) logrus route.Config.Domain, route.Config.Type, route.Config.AuthMode(), - route.ModuleKey, + route.Module.Key, false, ) if requestID != "" { diff --git a/internal/proxy/forwarder_test.go b/internal/proxy/forwarder_test.go index fce96c0..8eea491 100644 --- a/internal/proxy/forwarder_test.go +++ b/internal/proxy/forwarder_test.go @@ -10,6 +10,7 @@ import ( "github.com/valyala/fasthttp" "github.com/any-hub/any-hub/internal/config" + "github.com/any-hub/any-hub/internal/hubmodule" "github.com/any-hub/any-hub/internal/server" ) @@ -102,6 +103,8 @@ func testRouteWithModule(moduleKey string) *server.HubRoute { Domain: "test.local", Type: "custom", }, - ModuleKey: moduleKey, + Module: hubmodule.ModuleMetadata{ + Key: moduleKey, + }, } } diff --git a/internal/proxy/handler.go b/internal/proxy/handler.go index 53959e5..755e240 100644 --- a/internal/proxy/handler.go +++ b/internal/proxy/handler.go @@ -65,7 +65,7 @@ func buildHookContext(route *server.HubRoute, c fiber.Ctx) *hooks.RequestContext HubName: route.Config.Name, Domain: route.Config.Domain, HubType: route.Config.Type, - ModuleKey: route.ModuleKey, + ModuleKey: route.Module.Key, UpstreamHost: baseHost, Method: c.Method(), } @@ -83,7 +83,7 @@ func hasHook(def hooks.Hooks) bool { func (h *Handler) Handle(c fiber.Ctx, route *server.HubRoute) error { started := time.Now() requestID := server.RequestID(c) - hooksDef, ok := hooks.Fetch(route.ModuleKey) + hooksDef, ok := hooks.Fetch(route.Module.Key) hookCtx := buildHookContext(route, c) rawQuery := append([]byte(nil), c.Request().URI().QueryString()...) cleanPath := normalizeRequestPath(route, string(c.Request().URI().Path())) @@ -120,7 +120,7 @@ func (h *Handler) Handle(c fiber.Ctx, route *server.HubRoute) error { // miss, continue default: h.logger.WithError(err). - WithFields(logrus.Fields{"hub": route.Config.Name, "module_key": route.ModuleKey}). + WithFields(logrus.Fields{"hub": route.Config.Name, "module_key": route.Module.Key}). Warn("cache_get_failed") } } @@ -134,7 +134,7 @@ func (h *Handler) Handle(c fiber.Ctx, route *server.HubRoute) error { fresh, err := h.isCacheFresh(c, route, locator, cached.Entry, &hookState) if err != nil { h.logger.WithError(err). - WithFields(logrus.Fields{"hub": route.Config.Name, "module_key": route.ModuleKey}). + WithFields(logrus.Fields{"hub": route.Config.Name, "module_key": route.Module.Key}). Warn("cache_revalidate_failed") serve = false } else if !fresh { @@ -517,7 +517,7 @@ func (h *Handler) logResult( route.Config.Domain, route.Config.Type, route.Config.AuthMode(), - route.ModuleKey, + route.Module.Key, cacheHit, ) fields["action"] = "proxy" @@ -968,7 +968,7 @@ func (h *Handler) logAuthRetry(route *server.HubRoute, upstream string, requestI route.Config.Domain, route.Config.Type, route.Config.AuthMode(), - route.ModuleKey, + route.Module.Key, false, ) fields["action"] = "proxy_retry" @@ -987,7 +987,7 @@ func (h *Handler) logAuthFailure(route *server.HubRoute, upstream string, reques route.Config.Domain, route.Config.Type, route.Config.AuthMode(), - route.ModuleKey, + route.Module.Key, false, ) fields["action"] = "proxy" diff --git a/internal/server/hub_registry.go b/internal/server/hub_registry.go index 5056d5f..1fd0095 100644 --- a/internal/server/hub_registry.go +++ b/internal/server/hub_registry.go @@ -25,9 +25,8 @@ type HubRoute struct { // UpstreamURL/ProxyURL 在构造 Registry 时提前解析完成,便于后续请求快速复用。 UpstreamURL *url.URL ProxyURL *url.URL - // ModuleKey/Module 记录当前 hub 选用的模块及其元数据,便于日志与观测。 - ModuleKey string - Module hubmodule.ModuleMetadata + // Module 记录当前 hub 选用的模块元数据,便于日志与观测。 + Module hubmodule.ModuleMetadata // CacheStrategy 代表模块默认策略与 hub 覆盖后的最终结果。 CacheStrategy hubmodule.CacheStrategyProfile } @@ -134,7 +133,6 @@ func buildHubRoute(cfg *config.Config, hub config.HubConfig) (*HubRoute, error) CacheTTL: effectiveTTL, UpstreamURL: upstreamURL, ProxyURL: proxyURL, - ModuleKey: runtime.Module.Key, Module: runtime.Module, CacheStrategy: runtime.CacheStrategy, }, nil diff --git a/internal/server/hub_registry_test.go b/internal/server/hub_registry_test.go index b173259..264eb45 100644 --- a/internal/server/hub_registry_test.go +++ b/internal/server/hub_registry_test.go @@ -54,8 +54,8 @@ func TestHubRegistryLookupByHost(t *testing.T) { if route.CacheStrategy.ValidationMode == "" { t.Fatalf("cache strategy validation mode should not be empty") } - if route.ModuleKey != "docker" { - t.Fatalf("expected docker module, got %s", route.ModuleKey) + if route.Module.Key != "docker" { + t.Fatalf("expected docker module, got %s", route.Module.Key) } if route.UpstreamURL.String() != "https://registry-1.docker.io" { diff --git a/internal/server/routes/modules.go b/internal/server/routes/modules.go index 7ef5863..7168385 100644 --- a/internal/server/routes/modules.go +++ b/internal/server/routes/modules.go @@ -113,7 +113,7 @@ func encodeHubBindings(routes []server.HubRoute) []hubBindingPayload { for _, route := range routes { result = append(result, hubBindingPayload{ HubName: route.Config.Name, - ModuleKey: route.ModuleKey, + ModuleKey: route.Module.Key, Domain: route.Config.Domain, Port: route.ListenPort, }) diff --git a/specs/005-proxy-module-delegation/data-model.md b/specs/005-proxy-module-delegation/data-model.md index b8dd3a9..6c1800f 100644 --- a/specs/005-proxy-module-delegation/data-model.md +++ b/specs/005-proxy-module-delegation/data-model.md @@ -14,7 +14,7 @@ - **Proxy Dispatcher** - Attributes: handler map (module_key → handler), default handler fallback. - - Behavior: Lookup by route.ModuleKey and invoke handler; wraps errors/logging. + - Behavior: Lookup by the hub's module key (derived from Type / route.Module.Key) and invoke handler; wraps errors/logging. - Constraints: If handler missing, return 5xx with observable logging. - **Cache Policy** diff --git a/specs/006-module-hook-refactor/data-model.md b/specs/006-module-hook-refactor/data-model.md index 3d4ec8a..69fe6cd 100644 --- a/specs/006-module-hook-refactor/data-model.md +++ b/specs/006-module-hook-refactor/data-model.md @@ -27,7 +27,7 @@ ## Relationships - Hub module 注册时同时在 HookRegistry 与 Forwarder handler map 建立关联。 -- ProxyDispatcher 在请求进入后根据 route.ModuleKey 查询 Hook + handler。 +- ProxyDispatcher 在请求进入后根据 route.Module.Key(来自 Hub Type)查询 Hook + handler。 - Diagnostics 依赖 HookRegistry 与 HubRegistry 联合输出状态。 ## Lifecycle diff --git a/tests/integration/module_routing_test.go b/tests/integration/module_routing_test.go index 1f34959..a1b10ac 100644 --- a/tests/integration/module_routing_test.go +++ b/tests/integration/module_routing_test.go @@ -95,6 +95,6 @@ type moduleRecorder struct { func (p *moduleRecorder) Handle(c fiber.Ctx, route *server.HubRoute) error { p.routeName = route.Config.Name - p.moduleKey = route.ModuleKey + p.moduleKey = route.Module.Key return c.SendStatus(fiber.StatusNoContent) }