Repository navigation
Expand file tree
/
Copy pathadapter.go
More file actions
127 lines (117 loc) · 4.63 KB
/
Copy pathadapter.go
File metadata and controls
127 lines (117 loc) · 4.63 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
// Package edge adapts AWS API Gateway Lambda proxy events to net/http handlers.
// It supports raw JSON v2 and already typed AWS events with optional native gateway identity.
// Buffered and REST streaming responses share request and identity policies;
// each has its own response and invocation boundary.
package edge
import (
"context"
"errors"
"fmt"
"net/http"
"reflect"
)
// Adapter holds an HTTP handler and its API Gateway adaptation configuration.
// Construct an Adapter with [New];
// its zero value is not usable.
// Configuration is fixed at construction.
// Calls own separate request state;
// a shared handler must be safe for concurrent use.
type Adapter struct {
handler http.Handler
config config
}
type config struct {
gatewayIdentity bool
identityClaimsBudget int
responseHeaderBudget int
streamReporter func(context.Context, error)
streamReporterSet bool
}
// Option configures an [Adapter] or [StreamingAdapter] during construction.
// Use the option functions provided by this package.
// A nil Option is invalid.
// Options apply in argument order;
// the last assignment to a scalar setting wins.
type Option func(*config)
// WithGatewayIdentity configures whether gateway assertions establish identity.
// The default is false.
// When enabled, the policy covers native IAM and JWT/Cognito assertions;
// it does not configure authorization in AWS or enable local token verification.
// Custom-authorizer contexts need an explicit mapper.
// Unsupported or conflicting assertions fail before running the HTTP handler.
func WithGatewayIdentity(enabled bool) Option {
return func(c *config) {
c.gatewayIdentity = enabled
}
}
// WithIdentityClaimsBudget sets the gateway identity claims allowance in bytes.
// The default is 256 KiB;
// New and [NewStreaming] reject settings outside 1..6 MiB.
// Each claim value costs 64 bytes plus name, string and exact-number text bytes.
// Dedicated gateway scopes consume the same allowance.
// Raw claim JSON is also limited to this byte length.
// This is a resource policy, not a heap cap or AWS quota.
// It does not enable gateway identity.
func WithIdentityClaimsBudget(bytes int) Option {
return func(c *config) {
c.identityClaimsBudget = bytes
}
}
// WithResponseHeaderBudget sets the response header resource budget in bytes.
// The default is 256 KiB.
// Both constructors reject settings outside 1..6 MiB;
// zero does not disable the bound.
// Each original value costs its name length, value length and 32 bytes;
// nil/empty slices cost their name length and 32.
// Generated headers also consume the budget.
// This is a library resource policy, not an AWS quota or a bound on total heap usage.
// It does not change the separate buffered envelope or streaming metadata limits.
func WithResponseHeaderBudget(bytes int) Option {
return func(c *config) {
c.responseHeaderBudget = bytes
}
}
// New validates handler and opts and returns an Adapter without performing I/O or invoking handler.
// Nil handlers, including typed nil handlers, and nil options are rejected.
// On error, the returned Adapter is nil.
//
// The caller retains ownership of handler and is responsible for its concurrency safety when sharing it.
// New does not use [http.DefaultServeMux].
func New(handler http.Handler, opts ...Option) (*Adapter, error) {
cfg, err := configureAdapter(handler, opts)
if err != nil {
return nil, err
}
if cfg.streamReporterSet {
return nil, errors.New("edge: stream error reporter requires NewStreaming")
}
return &Adapter{handler: handler, config: cfg}, nil
}
func configureAdapter(handler http.Handler, opts []Option) (config, error) {
if handler == nil {
return config{}, errors.New("edge: nil HTTP handler")
}
// An interface containing a typed nil is non-nil.
// Check nil-capable kinds before accepting a handler that would defer a startup fault to invocation.
v := reflect.ValueOf(handler)
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice:
if v.IsNil() {
return config{}, errors.New("edge: nil HTTP handler")
}
}
cfg := config{identityClaimsBudget: 256 * 1024, responseHeaderBudget: defaultResponseHeaderBudget}
for i, opt := range opts {
if opt == nil {
return config{}, fmt.Errorf("edge: nil option at index %d", i)
}
opt(&cfg)
}
if cfg.responseHeaderBudget <= 0 || cfg.responseHeaderBudget > maxResponseBytes {
return config{}, errors.New("edge: response header budget must be between 1 and 6291456 bytes")
}
if cfg.identityClaimsBudget <= 0 || cfg.identityClaimsBudget > maxResponseBytes {
return config{}, errors.New("edge: identity claims budget must be between 1 and 6291456 bytes")
}
return cfg, nil
}