-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrouter.go
More file actions
156 lines (138 loc) · 4.14 KB
/
router.go
File metadata and controls
156 lines (138 loc) · 4.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package agent
import (
"context"
"fmt"
"log/slog"
"sync"
"time"
"github.com/peerclaw/peerclaw-core/envelope"
)
// HandlerFunc processes an inbound envelope and optionally returns a response envelope.
type HandlerFunc func(ctx context.Context, env *envelope.Envelope) (*envelope.Envelope, error)
// Middleware wraps a HandlerFunc to add cross-cutting behavior.
type Middleware func(HandlerFunc) HandlerFunc
const (
// MetadataKeyCapability is the envelope metadata key used for routing.
MetadataKeyCapability = "capability"
// MetadataKeyAction is the envelope metadata key for sub-action routing.
MetadataKeyAction = "action"
)
// Router dispatches inbound envelopes to capability-specific handlers.
type Router struct {
mu sync.RWMutex
handlers map[string]HandlerFunc
middlewares []Middleware
logger *slog.Logger
}
// NewRouter creates a new Router.
func NewRouter(logger *slog.Logger) *Router {
if logger == nil {
logger = slog.Default()
}
return &Router{
handlers: make(map[string]HandlerFunc),
logger: logger,
}
}
// Handle registers a handler for the given capability.
func (r *Router) Handle(capability string, handler HandlerFunc) {
r.mu.Lock()
defer r.mu.Unlock()
r.handlers[capability] = handler
}
// Use appends global middlewares that wrap every handler on dispatch.
func (r *Router) Use(mw ...Middleware) {
r.mu.Lock()
defer r.mu.Unlock()
r.middlewares = append(r.middlewares, mw...)
}
// Capabilities returns the list of registered capability names.
func (r *Router) Capabilities() []string {
r.mu.RLock()
defer r.mu.RUnlock()
caps := make([]string, 0, len(r.handlers))
for c := range r.handlers {
caps = append(caps, c)
}
return caps
}
// Dispatch routes an envelope to the matching capability handler.
// Returns (false, nil, nil) if no capability metadata or no matching handler (fallthrough).
// Returns (true, resp, err) when a handler is matched and executed.
func (r *Router) Dispatch(ctx context.Context, env *envelope.Envelope) (matched bool, resp *envelope.Envelope, err error) {
if env.Metadata == nil {
return false, nil, nil
}
capability, ok := env.Metadata[MetadataKeyCapability]
if !ok || capability == "" {
return false, nil, nil
}
r.mu.RLock()
handler, exists := r.handlers[capability]
mws := make([]Middleware, len(r.middlewares))
copy(mws, r.middlewares)
r.mu.RUnlock()
if !exists {
return false, nil, nil
}
// Build the middleware chain around the handler.
final := Chain(handler, mws...)
resp, err = final(ctx, env)
return true, resp, err
}
// Chain wraps a handler with the given middlewares. Middlewares are applied in
// order: the first middleware is the outermost wrapper.
func Chain(handler HandlerFunc, mws ...Middleware) HandlerFunc {
for i := len(mws) - 1; i >= 0; i-- {
handler = mws[i](handler)
}
return handler
}
// LoggingMiddleware logs capability, source, and duration for each dispatched request.
func LoggingMiddleware(logger *slog.Logger) Middleware {
if logger == nil {
logger = slog.Default()
}
return func(next HandlerFunc) HandlerFunc {
return func(ctx context.Context, env *envelope.Envelope) (*envelope.Envelope, error) {
start := time.Now()
capability := ""
if env.Metadata != nil {
capability = env.Metadata[MetadataKeyCapability]
}
logger.Info("handler start",
"capability", capability,
"source", env.Source,
)
resp, err := next(ctx, env)
logger.Info("handler done",
"capability", capability,
"source", env.Source,
"duration", time.Since(start),
"error", err,
)
return resp, err
}
}
}
// RecoveryMiddleware recovers from panics in handlers and converts them to errors.
func RecoveryMiddleware(logger *slog.Logger) Middleware {
if logger == nil {
logger = slog.Default()
}
return func(next HandlerFunc) HandlerFunc {
return func(ctx context.Context, env *envelope.Envelope) (resp *envelope.Envelope, err error) {
defer func() {
if r := recover(); r != nil {
logger.Error("handler panic recovered",
"capability", env.Metadata[MetadataKeyCapability],
"panic", r,
)
resp = nil
err = fmt.Errorf("handler panic: %v", r)
}
}()
return next(ctx, env)
}
}
}