This commit is contained in:
2025-11-17 15:39:44 +08:00
parent abfa51f12e
commit 1ddda89499
46 changed files with 2185 additions and 751 deletions

View File

@@ -1,12 +1,5 @@
package hooks
import (
"net/http"
"net/url"
"strings"
"sync"
)
// CachePolicy mirrors the proxy cache policy structure.
type CachePolicy struct {
AllowCache bool
@@ -27,34 +20,9 @@ type RequestContext struct {
// Hooks describes customization points for module-specific behavior.
type Hooks struct {
NormalizePath func(ctx *RequestContext, cleanPath string) string
ResolveUpstream func(ctx *RequestContext, base *url.URL, cleanPath string, rawQuery []byte) *url.URL
RewriteResponse func(ctx *RequestContext, resp *http.Response, cleanPath string) (*http.Response, error)
NormalizePath func(ctx *RequestContext, cleanPath string, rawQuery []byte) (string, []byte)
ResolveUpstream func(ctx *RequestContext, baseURL string, path string, rawQuery []byte) string
RewriteResponse func(ctx *RequestContext, status int, headers map[string]string, body []byte, path string) (int, map[string]string, []byte, error)
CachePolicy func(ctx *RequestContext, locatorPath string, current CachePolicy) CachePolicy
ContentType func(ctx *RequestContext, locatorPath string) string
}
var registry sync.Map
// Register stores hooks for the given module key.
func Register(moduleKey string, hooks Hooks) {
key := strings.ToLower(strings.TrimSpace(moduleKey))
if key == "" {
return
}
registry.Store(key, hooks)
}
// For retrieves hooks associated with a module key.
func For(moduleKey string) (Hooks, bool) {
key := strings.ToLower(strings.TrimSpace(moduleKey))
if key == "" {
return Hooks{}, false
}
if value, ok := registry.Load(key); ok {
if hooks, ok := value.(Hooks); ok {
return hooks, true
}
}
return Hooks{}, false
}

View File

@@ -0,0 +1,68 @@
package hooks
import (
"errors"
"strings"
"sync"
)
var registry sync.Map
// ErrDuplicateHook indicates a module key already has hooks registered.
var ErrDuplicateHook = errors.New("hook already registered")
// Register stores hooks for the given module key.
func Register(moduleKey string, hooks Hooks) error {
key := normalizeKey(moduleKey)
if key == "" {
return errors.New("module key required")
}
if _, loaded := registry.LoadOrStore(key, hooks); loaded {
return ErrDuplicateHook
}
return nil
}
// MustRegister panics on registration failure.
func MustRegister(moduleKey string, hooks Hooks) {
if err := Register(moduleKey, hooks); err != nil {
panic(err)
}
}
// Fetch retrieves hooks associated with a module key.
func Fetch(moduleKey string) (Hooks, bool) {
key := normalizeKey(moduleKey)
if key == "" {
return Hooks{}, false
}
if value, ok := registry.Load(key); ok {
if hooks, ok := value.(Hooks); ok {
return hooks, true
}
}
return Hooks{}, false
}
// Status returns hook registration status for a module key.
func Status(moduleKey string) string {
if _, ok := Fetch(moduleKey); ok {
return "registered"
}
return "missing"
}
// Snapshot returns status for a list of module keys.
func Snapshot(keys []string) map[string]string {
out := make(map[string]string, len(keys))
for _, key := range keys {
if normalized := normalizeKey(key); normalized != "" {
out[normalized] = Status(normalized)
}
}
return out
}
func normalizeKey(key string) string {
return strings.ToLower(strings.TrimSpace(key))
}

View File

@@ -0,0 +1,45 @@
package hooks
import (
"sync"
"testing"
)
func TestRegisterAndFetch(t *testing.T) {
registry = sync.Map{}
h := Hooks{ContentType: func(*RequestContext, string) string { return "ok" }}
if err := Register("test", h); err != nil {
t.Fatalf("register failed: %v", err)
}
if _, ok := Fetch("test"); !ok {
t.Fatalf("expected fetch ok")
}
if Status("test") != "registered" {
t.Fatalf("expected registered status")
}
if Status("missing") != "missing" {
t.Fatalf("expected missing status")
}
}
func TestRegisterDuplicate(t *testing.T) {
registry = sync.Map{}
if err := Register("dup", Hooks{}); err != nil {
t.Fatalf("first register failed: %v", err)
}
if err := Register("dup", Hooks{}); err != ErrDuplicateHook {
t.Fatalf("expected ErrDuplicateHook, got %v", err)
}
}
func TestSnapshot(t *testing.T) {
registry = sync.Map{}
_ = Register("a", Hooks{})
snap := Snapshot([]string{"a", "b"})
if snap["a"] != "registered" {
t.Fatalf("expected a registered, got %s", snap["a"])
}
if snap["b"] != "missing" {
t.Fatalf("expected b missing, got %s", snap["b"])
}
}