-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathoptions.go
More file actions
273 lines (245 loc) · 8.66 KB
/
options.go
File metadata and controls
273 lines (245 loc) · 8.66 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
package fastlike
import (
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
)
// Option is a functional option applied to an Instance at creation time
type Option func(*Instance)
// WithBackend registers an `http.Handler` identified by `name` used for subrequests targeting that
// backend. This creates a simple backend with default settings.
func WithBackend(name string, h http.Handler) Option {
return func(i *Instance) {
// Parse a default URL from the backend name
u, err := url.Parse("http://" + name)
if err != nil {
// Fallback to a simple localhost URL
u, _ = url.Parse("http://localhost")
}
backend := &Backend{
Name: name,
URL: u,
Handler: h,
}
i.addBackend(name, backend)
}
}
// WithBackendConfig registers a fully configured Backend
func WithBackendConfig(backend *Backend) Option {
return func(i *Instance) {
i.addBackend(backend.Name, backend)
}
}
// WithUnreliableBackend registers a backend with simulated reliability. The
// uptime parameter is the percentage of requests that reach the backend
// normally; the rest are answered with a synthetic 502 that mimics a real
// upstream failure. Values must be in [0, 100]; 0 means the backend always
// appears down, 100 means no simulation. Out-of-range values panic at
// configuration time so misconfigurations are caught immediately rather than
// silently masked.
func WithUnreliableBackend(name string, h http.Handler, uptime uint8) Option {
if uptime > 100 {
panic(fmt.Sprintf("WithUnreliableBackend: uptime %d out of range, must be 0..100", uptime))
}
return func(i *Instance) {
u, err := url.Parse("http://" + name)
if err != nil {
u, _ = url.Parse("http://localhost")
}
pct := uptime
i.addBackend(name, &Backend{
Name: name,
URL: u,
Handler: h,
UptimePercent: &pct,
})
}
}
// WithDefaultBackend sets a fallback handler for backend requests to undefined backends.
// The function receives the backend name and returns an http.Handler.
// If not set, undefined backends return 502 Bad Gateway.
func WithDefaultBackend(fn func(name string) http.Handler) Option {
return func(i *Instance) {
i.defaultBackend = fn
}
}
// WithUnreliableDefaultBackend is the catch-all counterpart of
// WithUnreliableBackend. The supplied factory's output is wrapped with the
// same reliability simulation used by named backends, so catch-all dispatch
// honors the percentage even though it never flows through addBackend.
func WithUnreliableDefaultBackend(fn func(name string) http.Handler, uptime uint8) Option {
if uptime > 100 {
panic(fmt.Sprintf("WithUnreliableDefaultBackend: uptime %d out of range, must be 0..100", uptime))
}
pct := uptime
return func(i *Instance) {
i.defaultBackend = func(name string) http.Handler {
return wrapWithReliability(fn(name), &pct)
}
}
}
// WithGeo replaces the default geographic lookup function
func WithGeo(fn func(net.IP) Geo) Option {
return func(i *Instance) {
i.geolookup = fn
}
}
// WithLogger registers a new log endpoint usable from a wasm guest
func WithLogger(name string, w io.Writer) Option {
return func(i *Instance) {
i.addLogger(name, w)
}
}
// WithDefaultLogger sets a fallback logger for log endpoints not registered with WithLogger.
// The function receives the log endpoint name and returns an io.Writer.
// This differs from WithLogger in that it's called dynamically when the guest requests
// an undefined log endpoint, allowing custom implementations to print the endpoint name.
func WithDefaultLogger(fn func(name string) io.Writer) Option {
return func(i *Instance) {
i.defaultLogger = fn
}
}
// WithDictionary registers a new dictionary with a corresponding lookup function
func WithDictionary(name string, fn LookupFunc) Option {
return func(i *Instance) {
i.addDictionary(name, fn)
}
}
// WithConfigStore registers a new config store with a corresponding lookup function
func WithConfigStore(name string, fn LookupFunc) Option {
return func(i *Instance) {
i.addConfigStore(name, fn)
}
}
// WithKVStore registers a new KV store with the given name
func WithKVStore(name string) Option {
return func(i *Instance) {
store := NewKVStore(name)
i.addKVStore(name, store)
}
}
// WithKVStoreData registers a pre-populated KV store with the given name
func WithKVStoreData(name string, store *KVStore) Option {
return func(i *Instance) {
i.addKVStore(name, store)
}
}
// WithSecretStore registers a new secret store with a corresponding lookup function
func WithSecretStore(name string, fn SecretLookupFunc) Option {
return func(i *Instance) {
i.addSecretStore(name, fn)
}
}
// WithSecureFunc sets a custom function to determine if a request should be considered "secure".
// When the function returns true, the request will have:
// - URL scheme set to "https"
// - "fastly-ssl" header set to "1"
//
// This affects how the wasm guest program sees the request.
// The default implementation checks if req.TLS is non-nil.
func WithSecureFunc(fn func(*http.Request) bool) Option {
return func(i *Instance) {
i.secureFn = fn
}
}
// WithUserAgentParser is an Option that converts user agent header values into UserAgent structs,
// called when the guest code uses the user agent parser XQD call.
func WithUserAgentParser(fn UserAgentParser) Option {
return func(i *Instance) {
i.uaparser = fn
}
}
// WithDeviceDetection is an Option that provides device detection data for user agents.
// The function takes a user agent string and returns device detection data as a JSON string.
// If no data is available for a given user agent, the function should return an empty string.
func WithDeviceDetection(fn DeviceLookupFunc) Option {
return func(i *Instance) {
i.deviceDetection = fn
}
}
// WithImageOptimizer is an Option that provides image transformation functionality.
// The function receives the origin request, body, backend name, and transformation config,
// and returns a transformed image response.
// By default, image optimization returns an error indicating it's not configured.
func WithImageOptimizer(fn ImageOptimizerTransformFunc) Option {
return func(i *Instance) {
i.imageOptimizer = fn
}
}
// WithBotDetection is an Option that provides bot detection data for requests.
// The function takes an HTTP request and returns bot detection info.
// If not set, bot analysis always returns false (matching Viceroy's default behavior).
func WithBotDetection(fn BotDetectionFunc) Option {
return func(i *Instance) {
i.botDetection = fn
}
}
// WithVpnProxy is an Option that provides VPN/proxy intelligence data for requests.
// The function takes an HTTP request and returns VPN/proxy info.
// If not set, all VPN proxy hostcalls return NONE (matching Viceroy's stub behavior).
func WithVpnProxy(fn VpnProxyFunc) Option {
return func(i *Instance) {
i.vpnProxy = fn
}
}
// WithShield registers a shield POP configuration for the shielding module.
func WithShield(name string, shield *Shield) Option {
return func(i *Instance) {
i.shields[name] = shield
}
}
// WithVerbosity controls logging verbosity for ABI calls and system-level operations.
// - Level 0 (default): No logging
// - Level 1: System-level logs to stderr
// - Level 2: All XQD ABI calls from guest to host logged to stderr
func WithVerbosity(v int) Option {
return func(i *Instance) {
if v >= 2 {
i.abilog.SetOutput(os.Stderr)
}
if v >= 1 {
i.log.SetOutput(os.Stderr)
}
}
}
// WithComplianceRegion sets the compliance region identifier for data locality requirements.
// Valid values include "none", "us-eu", "us", etc.
// This is used by guest programs to implement GDPR compliance and data residency policies.
func WithComplianceRegion(region string) Option {
return func(i *Instance) {
i.complianceRegion = region
}
}
// WithACL registers a new ACL (Access Control List) with the given name.
// The ACL data should be in Fastly's JSON format with entries containing
// IP prefix/mask and actions (ALLOW/BLOCK).
func WithACL(name string, acl *Acl) Option {
return func(i *Instance) {
i.addACL(name, acl)
}
}
// WithRateCounter registers a new rate counter for Edge Rate Limiting.
// Rate counters track request counts over time windows to calculate rates.
// If counter is nil, a new rate counter is created.
func WithRateCounter(name string, counter *RateCounter) Option {
return func(i *Instance) {
if counter == nil {
counter = NewRateCounter()
}
i.addRateCounter(name, counter)
}
}
// WithPenaltyBox registers a new penalty box for Edge Rate Limiting.
// Penalty boxes track entries that have exceeded rate limits.
// If box is nil, a new penalty box is created.
func WithPenaltyBox(name string, box *PenaltyBox) Option {
return func(i *Instance) {
if box == nil {
box = NewPenaltyBox()
}
i.addPenaltyBox(name, box)
}
}