Refactor module binding to rely on Type
This commit is contained in:
@@ -25,72 +25,49 @@ func normalizePath(_ *hooks.RequestContext, p string, rawQuery []byte) (string,
|
|||||||
|
|
||||||
func cachePolicy(_ *hooks.RequestContext, locatorPath string, current hooks.CachePolicy) hooks.CachePolicy {
|
func cachePolicy(_ *hooks.RequestContext, locatorPath string, current hooks.CachePolicy) hooks.CachePolicy {
|
||||||
clean := canonicalPath(locatorPath)
|
clean := canonicalPath(locatorPath)
|
||||||
switch {
|
if strings.Contains(clean, "/by-hash/") || strings.Contains(clean, "/pool/") {
|
||||||
case isAptIndexPath(clean):
|
|
||||||
// 索引类(Release/Packages)需要 If-None-Match/If-Modified-Since 再验证。
|
|
||||||
current.AllowCache = true
|
|
||||||
current.AllowStore = true
|
|
||||||
current.RequireRevalidate = true
|
|
||||||
case isAptImmutablePath(clean):
|
|
||||||
// pool/*.deb 与 by-hash 路径视为不可变,直接缓存后续不再 HEAD。
|
// pool/*.deb 与 by-hash 路径视为不可变,直接缓存后续不再 HEAD。
|
||||||
current.AllowCache = true
|
current.AllowCache = true
|
||||||
current.AllowStore = true
|
current.AllowStore = true
|
||||||
current.RequireRevalidate = false
|
current.RequireRevalidate = false
|
||||||
default:
|
return current
|
||||||
current.AllowCache = false
|
|
||||||
current.AllowStore = false
|
|
||||||
current.RequireRevalidate = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
func contentType(_ *hooks.RequestContext, locatorPath string) string {
|
func contentType(_ *hooks.RequestContext, locatorPath string) string {
|
||||||
|
clean := canonicalPath(locatorPath)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasSuffix(locatorPath, ".gz"):
|
case strings.HasSuffix(clean, ".gz"):
|
||||||
return "application/gzip"
|
return "application/gzip"
|
||||||
case strings.HasSuffix(locatorPath, ".xz"):
|
case strings.HasSuffix(clean, ".xz"):
|
||||||
return "application/x-xz"
|
return "application/x-xz"
|
||||||
case strings.HasSuffix(locatorPath, "Release.gpg"):
|
case strings.HasSuffix(clean, "release.gpg"):
|
||||||
return "application/pgp-signature"
|
return "application/pgp-signature"
|
||||||
case isAptIndexPath(locatorPath):
|
case strings.Contains(clean, "/dists/") &&
|
||||||
|
(strings.HasSuffix(clean, "/release") || strings.HasSuffix(clean, "/inrelease")):
|
||||||
return "text/plain"
|
return "text/plain"
|
||||||
default:
|
default:
|
||||||
return ""
|
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 {
|
func canonicalPath(p string) string {
|
||||||
if p == "" {
|
if p == "" {
|
||||||
return "/"
|
return "/"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func RegisterModuleHandler(key string, handler server.ProxyHandler) {
|
|||||||
MustRegisterModule(ModuleRegistration{Key: key, Handler: handler})
|
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 {
|
func (f *Forwarder) Handle(c fiber.Ctx, route *server.HubRoute) error {
|
||||||
requestID := server.RequestID(c)
|
requestID := server.RequestID(c)
|
||||||
handler := f.lookup(route)
|
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 {
|
func (f *Forwarder) lookup(route *server.HubRoute) server.ProxyHandler {
|
||||||
if route != nil {
|
if route != nil {
|
||||||
if handler := lookupModuleHandler(route.ModuleKey); handler != nil {
|
if handler := lookupModuleHandler(route.Module.Key); handler != nil {
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ func (f *Forwarder) routeFields(route *server.HubRoute, requestID string) logrus
|
|||||||
route.Config.Domain,
|
route.Config.Domain,
|
||||||
route.Config.Type,
|
route.Config.Type,
|
||||||
route.Config.AuthMode(),
|
route.Config.AuthMode(),
|
||||||
route.ModuleKey,
|
route.Module.Key,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
if requestID != "" {
|
if requestID != "" {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
|
||||||
"github.com/any-hub/any-hub/internal/config"
|
"github.com/any-hub/any-hub/internal/config"
|
||||||
|
"github.com/any-hub/any-hub/internal/hubmodule"
|
||||||
"github.com/any-hub/any-hub/internal/server"
|
"github.com/any-hub/any-hub/internal/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,6 +103,8 @@ func testRouteWithModule(moduleKey string) *server.HubRoute {
|
|||||||
Domain: "test.local",
|
Domain: "test.local",
|
||||||
Type: "custom",
|
Type: "custom",
|
||||||
},
|
},
|
||||||
ModuleKey: moduleKey,
|
Module: hubmodule.ModuleMetadata{
|
||||||
|
Key: moduleKey,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func buildHookContext(route *server.HubRoute, c fiber.Ctx) *hooks.RequestContext
|
|||||||
HubName: route.Config.Name,
|
HubName: route.Config.Name,
|
||||||
Domain: route.Config.Domain,
|
Domain: route.Config.Domain,
|
||||||
HubType: route.Config.Type,
|
HubType: route.Config.Type,
|
||||||
ModuleKey: route.ModuleKey,
|
ModuleKey: route.Module.Key,
|
||||||
UpstreamHost: baseHost,
|
UpstreamHost: baseHost,
|
||||||
Method: c.Method(),
|
Method: c.Method(),
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func hasHook(def hooks.Hooks) bool {
|
|||||||
func (h *Handler) Handle(c fiber.Ctx, route *server.HubRoute) error {
|
func (h *Handler) Handle(c fiber.Ctx, route *server.HubRoute) error {
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
requestID := server.RequestID(c)
|
requestID := server.RequestID(c)
|
||||||
hooksDef, ok := hooks.Fetch(route.ModuleKey)
|
hooksDef, ok := hooks.Fetch(route.Module.Key)
|
||||||
hookCtx := buildHookContext(route, c)
|
hookCtx := buildHookContext(route, c)
|
||||||
rawQuery := append([]byte(nil), c.Request().URI().QueryString()...)
|
rawQuery := append([]byte(nil), c.Request().URI().QueryString()...)
|
||||||
cleanPath := normalizeRequestPath(route, string(c.Request().URI().Path()))
|
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
|
// miss, continue
|
||||||
default:
|
default:
|
||||||
h.logger.WithError(err).
|
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")
|
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)
|
fresh, err := h.isCacheFresh(c, route, locator, cached.Entry, &hookState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.WithError(err).
|
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")
|
Warn("cache_revalidate_failed")
|
||||||
serve = false
|
serve = false
|
||||||
} else if !fresh {
|
} else if !fresh {
|
||||||
@@ -517,7 +517,7 @@ func (h *Handler) logResult(
|
|||||||
route.Config.Domain,
|
route.Config.Domain,
|
||||||
route.Config.Type,
|
route.Config.Type,
|
||||||
route.Config.AuthMode(),
|
route.Config.AuthMode(),
|
||||||
route.ModuleKey,
|
route.Module.Key,
|
||||||
cacheHit,
|
cacheHit,
|
||||||
)
|
)
|
||||||
fields["action"] = "proxy"
|
fields["action"] = "proxy"
|
||||||
@@ -968,7 +968,7 @@ func (h *Handler) logAuthRetry(route *server.HubRoute, upstream string, requestI
|
|||||||
route.Config.Domain,
|
route.Config.Domain,
|
||||||
route.Config.Type,
|
route.Config.Type,
|
||||||
route.Config.AuthMode(),
|
route.Config.AuthMode(),
|
||||||
route.ModuleKey,
|
route.Module.Key,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
fields["action"] = "proxy_retry"
|
fields["action"] = "proxy_retry"
|
||||||
@@ -987,7 +987,7 @@ func (h *Handler) logAuthFailure(route *server.HubRoute, upstream string, reques
|
|||||||
route.Config.Domain,
|
route.Config.Domain,
|
||||||
route.Config.Type,
|
route.Config.Type,
|
||||||
route.Config.AuthMode(),
|
route.Config.AuthMode(),
|
||||||
route.ModuleKey,
|
route.Module.Key,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
fields["action"] = "proxy"
|
fields["action"] = "proxy"
|
||||||
|
|||||||
@@ -25,9 +25,8 @@ type HubRoute struct {
|
|||||||
// UpstreamURL/ProxyURL 在构造 Registry 时提前解析完成,便于后续请求快速复用。
|
// UpstreamURL/ProxyURL 在构造 Registry 时提前解析完成,便于后续请求快速复用。
|
||||||
UpstreamURL *url.URL
|
UpstreamURL *url.URL
|
||||||
ProxyURL *url.URL
|
ProxyURL *url.URL
|
||||||
// ModuleKey/Module 记录当前 hub 选用的模块及其元数据,便于日志与观测。
|
// Module 记录当前 hub 选用的模块元数据,便于日志与观测。
|
||||||
ModuleKey string
|
Module hubmodule.ModuleMetadata
|
||||||
Module hubmodule.ModuleMetadata
|
|
||||||
// CacheStrategy 代表模块默认策略与 hub 覆盖后的最终结果。
|
// CacheStrategy 代表模块默认策略与 hub 覆盖后的最终结果。
|
||||||
CacheStrategy hubmodule.CacheStrategyProfile
|
CacheStrategy hubmodule.CacheStrategyProfile
|
||||||
}
|
}
|
||||||
@@ -134,7 +133,6 @@ func buildHubRoute(cfg *config.Config, hub config.HubConfig) (*HubRoute, error)
|
|||||||
CacheTTL: effectiveTTL,
|
CacheTTL: effectiveTTL,
|
||||||
UpstreamURL: upstreamURL,
|
UpstreamURL: upstreamURL,
|
||||||
ProxyURL: proxyURL,
|
ProxyURL: proxyURL,
|
||||||
ModuleKey: runtime.Module.Key,
|
|
||||||
Module: runtime.Module,
|
Module: runtime.Module,
|
||||||
CacheStrategy: runtime.CacheStrategy,
|
CacheStrategy: runtime.CacheStrategy,
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ func TestHubRegistryLookupByHost(t *testing.T) {
|
|||||||
if route.CacheStrategy.ValidationMode == "" {
|
if route.CacheStrategy.ValidationMode == "" {
|
||||||
t.Fatalf("cache strategy validation mode should not be empty")
|
t.Fatalf("cache strategy validation mode should not be empty")
|
||||||
}
|
}
|
||||||
if route.ModuleKey != "docker" {
|
if route.Module.Key != "docker" {
|
||||||
t.Fatalf("expected docker module, got %s", route.ModuleKey)
|
t.Fatalf("expected docker module, got %s", route.Module.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if route.UpstreamURL.String() != "https://registry-1.docker.io" {
|
if route.UpstreamURL.String() != "https://registry-1.docker.io" {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func encodeHubBindings(routes []server.HubRoute) []hubBindingPayload {
|
|||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
result = append(result, hubBindingPayload{
|
result = append(result, hubBindingPayload{
|
||||||
HubName: route.Config.Name,
|
HubName: route.Config.Name,
|
||||||
ModuleKey: route.ModuleKey,
|
ModuleKey: route.Module.Key,
|
||||||
Domain: route.Config.Domain,
|
Domain: route.Config.Domain,
|
||||||
Port: route.ListenPort,
|
Port: route.ListenPort,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
- **Proxy Dispatcher**
|
- **Proxy Dispatcher**
|
||||||
- Attributes: handler map (module_key → handler), default handler fallback.
|
- 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.
|
- Constraints: If handler missing, return 5xx with observable logging.
|
||||||
|
|
||||||
- **Cache Policy**
|
- **Cache Policy**
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
## Relationships
|
## Relationships
|
||||||
|
|
||||||
- Hub module 注册时同时在 HookRegistry 与 Forwarder handler map 建立关联。
|
- Hub module 注册时同时在 HookRegistry 与 Forwarder handler map 建立关联。
|
||||||
- ProxyDispatcher 在请求进入后根据 route.ModuleKey 查询 Hook + handler。
|
- ProxyDispatcher 在请求进入后根据 route.Module.Key(来自 Hub Type)查询 Hook + handler。
|
||||||
- Diagnostics 依赖 HookRegistry 与 HubRegistry 联合输出状态。
|
- Diagnostics 依赖 HookRegistry 与 HubRegistry 联合输出状态。
|
||||||
|
|
||||||
## Lifecycle
|
## Lifecycle
|
||||||
|
|||||||
@@ -95,6 +95,6 @@ type moduleRecorder struct {
|
|||||||
|
|
||||||
func (p *moduleRecorder) Handle(c fiber.Ctx, route *server.HubRoute) error {
|
func (p *moduleRecorder) Handle(c fiber.Ctx, route *server.HubRoute) error {
|
||||||
p.routeName = route.Config.Name
|
p.routeName = route.Config.Name
|
||||||
p.moduleKey = route.ModuleKey
|
p.moduleKey = route.Module.Key
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user