-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
error.go
285 lines (262 loc) · 7.79 KB
/
error.go
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
274
275
276
277
278
279
280
281
282
283
284
285
package variable
import (
"io"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
"github.com/ysugimoto/falco/interpreter/context"
"github.com/ysugimoto/falco/interpreter/limitations"
"github.com/ysugimoto/falco/interpreter/value"
)
type ErrorScopeVariables struct {
Variable
base *AllScopeVariables
ctx *context.Context
}
func NewErrorScopeVariables(ctx *context.Context) *ErrorScopeVariables {
return &ErrorScopeVariables{
base: NewAllScopeVariables(ctx),
ctx: ctx,
}
}
func (v *ErrorScopeVariables) Get(s context.Scope, name string) (value.Value, error) {
switch name {
case CLIENT_SOCKET_CONGESTION_ALGORITHM:
return v.ctx.ClientSocketCongestionAlgorithm, nil
case CLIENT_SOCKET_CWND:
// Sometimes change this value but we don't know how change it without set statement
return &value.Integer{Value: 60}, nil
case CLIENT_SOCKET_NEXTHOP:
return &value.IP{Value: net.IPv4(127, 0, 0, 1)}, nil
case CLIENT_SOCKET_PACE:
return &value.Integer{Value: 0}, nil
case CLIENT_SOCKET_PLOSS:
return &value.Float{Value: 0}, nil
case ESI_ALLOW_INSIDE_CDATA:
return v.ctx.EsiAllowInsideCData, nil
// TODO: should be able to get from context after object checked
case OBJ_AGE:
if v.ctx.CacheHitItem != nil {
return &value.RTime{Value: time.Since(v.ctx.CacheHitItem.EntryTime)}, nil
}
return &value.RTime{Value: 0}, nil // 0s
case OBJ_CACHEABLE:
return v.ctx.BackendResponseCacheable, nil
case OBJ_ENTERED:
if v.ctx.CacheHitItem != nil {
return &value.RTime{Value: time.Since(v.ctx.CacheHitItem.EntryTime)}, nil
}
return &value.RTime{Value: 0}, nil
case OBJ_GRACE:
return v.ctx.ObjectGrace, nil
case OBJ_HITS:
if v.ctx.CacheHitItem != nil {
return &value.Integer{Value: int64(v.ctx.CacheHitItem.Hits)}, nil
}
return &value.Integer{Value: 0}, nil
case OBJ_IS_PCI:
return &value.Boolean{Value: false}, nil // fixed value
case OBJ_LASTUSE:
if v.ctx.CacheHitItem != nil {
return &value.RTime{Value: v.ctx.CacheHitItem.LastUsed}, nil
}
return &value.RTime{Value: 0}, nil
case OBJ_PROTO:
return &value.String{Value: v.ctx.Object.Proto}, nil
case OBJ_RESPONSE:
return v.ctx.ObjectResponse, nil
case OBJ_STALE_IF_ERROR:
// alias for obj.grace
return v.ctx.ObjectGrace, nil
case OBJ_STALE_WHILE_REVALIDATE:
// Return fixed value because we don't support SWR yet
return &value.RTime{Value: 60 * time.Second}, nil
case OBJ_STATUS:
return &value.Integer{Value: int64(v.ctx.Object.StatusCode)}, nil
case OBJ_TTL:
return v.ctx.ObjectTTL, nil
case REQ_BACKEND_IP:
return &value.IP{Value: net.IPv4(127, 0, 0, 1)}, nil
case REQ_BACKEND_IS_CLUSTER:
return &value.Boolean{Value: false}, nil
case REQ_BACKEND_NAME:
var name string
if v.ctx.Backend != nil {
name = v.ctx.Backend.Value.Name.Value
}
return &value.String{Value: name}, nil
case REQ_BACKEND_PORT:
if v.ctx.Backend == nil {
return &value.Integer{Value: 0}, nil
}
var port int64
for _, p := range v.ctx.Backend.Value.Properties {
if p.Key.Value != PORT {
continue
}
n, err := strconv.ParseInt(p.Value.String(), 10, 64)
if err != nil {
return value.Null, errors.WithStack(err)
}
port = n
break
}
return &value.Integer{Value: port}, nil
case REQ_ESI:
return v.ctx.EnableSSI, nil
case REQ_HASH:
return v.ctx.RequestHash, nil
// Digest ratio will return fixed value
case REQ_DIGEST_RATIO:
return &value.Float{Value: 0.4}, nil
// Limited waf related variables could get
case WAF_BLOCKED:
return v.ctx.WafBlocked, nil
case WAF_EXECUTED:
return v.ctx.WafExecuted, nil
case WAF_FAILURES:
return &value.Integer{Value: 0}, nil
case WAF_LOGGED:
return v.ctx.WafLogged, nil
case WAF_PASSED:
return v.ctx.WafPassed, nil
}
// Look up shared variables
if val, err := GetTCPInfoVariable(name); err != nil {
return value.Null, errors.WithStack(err)
} else if val != nil {
return val, nil
}
if val := v.getFromRegex(name); val != nil {
return val, nil
}
// If not found, also look up all scope value
val, err := v.base.Get(s, name)
if err != nil {
return value.Null, errors.WithStack(err)
}
return val, nil
}
func (v *ErrorScopeVariables) getFromRegex(name string) value.Value {
// HTTP response header matching
match := objectHttpHeaderRegex.FindStringSubmatch(name)
if match == nil {
return v.base.getFromRegex(name)
}
return getResponseHeaderValue(v.ctx.Object, match[1])
}
func (v *ErrorScopeVariables) Set(s context.Scope, name, operator string, val value.Value) error {
switch name {
case CLIENT_SOCKET_CONGESTION_ALGORITHM:
if err := doAssign(v.ctx.ClientSocketCongestionAlgorithm, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case CLIENT_SOCKET_CWND:
if err := doAssign(v.ctx.ClientSocketCwnd, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case CLIENT_SOCKET_PACE:
if err := doAssign(v.ctx.ClientSocketPace, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case ESI_ALLOW_INSIDE_CDATA:
if err := doAssign(v.ctx.EsiAllowInsideCData, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case OBJ_GRACE:
if err := doAssign(v.ctx.ObjectGrace, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case OBJ_RESPONSE:
if err := doAssign(v.ctx.ObjectResponse, operator, val); err != nil {
return errors.WithStack(err)
}
v.ctx.Object.Body = io.NopCloser(strings.NewReader(v.ctx.ObjectResponse.Value))
return nil
case OBJ_STATUS:
i := &value.Integer{Value: 0}
if err := doAssign(i, operator, val); err != nil {
return errors.WithStack(err)
}
v.ctx.Object.StatusCode = int(i.Value)
v.ctx.Object.Status = http.StatusText(int(i.Value))
return nil
case OBJ_TTL:
if err := doAssign(v.ctx.ObjectTTL, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case REQ_ESI:
if err := doAssign(v.ctx.EnableSSI, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case REQ_HASH:
if err := doAssign(v.ctx.RequestHash, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case WAF_BLOCKED:
if err := doAssign(v.ctx.WafBlocked, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case WAF_EXECUTED:
if err := doAssign(v.ctx.WafExecuted, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case WAF_LOGGED:
if err := doAssign(v.ctx.WafLogged, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
case WAF_PASSED:
if err := doAssign(v.ctx.WafPassed, operator, val); err != nil {
return errors.WithStack(err)
}
return nil
}
if match := objectHttpHeaderRegex.FindStringSubmatch(name); match != nil {
if err := limitations.CheckProtectedHeader(match[1]); err != nil {
return errors.WithStack(err)
}
setResponseHeaderValue(v.ctx.Object, match[1], val)
return nil
}
// If not found, pass to all scope value
return v.base.Set(s, name, operator, val)
}
func (v *ErrorScopeVariables) Add(s context.Scope, name string, val value.Value) error {
// Add statement could be use only for HTTP header
match := objectHttpHeaderRegex.FindStringSubmatch(name)
if match == nil {
// Nothing values to be enable to add in ERROR, pass to base
return v.base.Add(s, name, val)
}
if err := limitations.CheckProtectedHeader(match[1]); err != nil {
return errors.WithStack(err)
}
v.ctx.Object.Header.Add(match[1], val.String())
return nil
}
func (v *ErrorScopeVariables) Unset(s context.Scope, name string) error {
match := objectHttpHeaderRegex.FindStringSubmatch(name)
if match == nil {
// Nothing values to be enable to unset in ERROR, pass to base
return v.base.Unset(s, name)
}
if err := limitations.CheckProtectedHeader(match[1]); err != nil {
return errors.WithStack(err)
}
unsetResponseHeaderValue(v.ctx.Object, match[1])
return nil
}