-
Notifications
You must be signed in to change notification settings - Fork 741
Expand file tree
/
Copy pathheaders.go
More file actions
92 lines (86 loc) · 3.02 KB
/
Copy pathheaders.go
File metadata and controls
92 lines (86 loc) · 3.02 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
// Package headers resolves per-request HTTP headers from the incoming A2A
// call context. It is shared by the MCP tool transport (allowedHeaders) and
// the model transport (passthroughHeaders) so both forward caller-supplied
// headers with identical semantics.
package headers
import (
"context"
"strings"
"github.com/a2aproject/a2a-go/v2/a2asrv"
)
// restrictedPassthroughHeaders are names that must never be forwarded from a
// caller onto another hop:
// - credential headers (Authorization, Proxy-Authorization, Cookie) would
// forward a caller credential onward or clobber the credential the
// receiving hop manages itself; apiKeyPassthrough is the supported
// mechanism for credential forwarding;
// - the rest are hop-by-hop or message-framing headers per RFC 9110, plus
// the non-standard Proxy-Connection.
//
// Must stay in sync with RESTRICTED_PASSTHROUGH_HEADERS in
// python/packages/kagent-adk/src/kagent/adk/_llm_header_passthrough_plugin.py.
var restrictedPassthroughHeaders = map[string]struct{}{
"authorization": {},
"connection": {},
"content-length": {},
"cookie": {},
"host": {},
"keep-alive": {},
"proxy-authenticate": {},
"proxy-authorization": {},
"proxy-connection": {},
"te": {},
"trailer": {},
"transfer-encoding": {},
"upgrade": {},
}
// IsRestricted reports whether a header name (case-insensitively) must never
// be forwarded from a caller onto another hop.
func IsRestricted(name string) bool {
_, restricted := restrictedPassthroughHeaders[strings.ToLower(name)]
return restricted
}
// FilterRestricted drops restricted names (case-insensitively) from a
// configured pass-through header list.
func FilterRestricted(names []string) []string {
var out []string
for _, n := range names {
if !IsRestricted(n) {
out = append(out, n)
}
}
return out
}
// AllowedRequestHeaders reads the incoming A2A request metadata from ctx and
// returns only the header key/value pairs whose names appear in allowed.
// It reads directly from the A2A CallContext that is already present in the Go
// context, avoiding a redundant copy.
//
// Lookup relies on ServiceParams.Get, which does a case-insensitive lookup
// (NewServiceParams lowercases keys at construction). Keys in the result
// preserve the casing from the allowed list so the receiving server sees the
// header names the operator configured. When a header has multiple values only
// the first one is forwarded; additional values are intentionally dropped.
func AllowedRequestHeaders(ctx context.Context, allowed []string) map[string]string {
if len(allowed) == 0 {
return nil
}
callCtx, ok := a2asrv.CallContextFrom(ctx)
if !ok {
return nil
}
meta := callCtx.ServiceParams()
if meta == nil {
return nil
}
result := make(map[string]string)
for _, name := range allowed {
if vals, ok := meta.Get(name); ok && len(vals) > 0 && vals[0] != "" {
result[name] = vals[0]
}
}
if len(result) == 0 {
return nil
}
return result
}