Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
reverse_proxy: Caddyfile integration (and fix blocks in Dispenser)
Matthew Holt committed
Sep 9, 2019 at 12:23 UTC
50e62d06bcbc6b6486b382a22c633772443cfb6d
3 files changed
+545
-10
caddyconfig/caddyfile/dispenser.go
+5
-10
@@ -236,18 +236,13 @@ func (d *Dispenser) NewFromNextTokens() *Dispenser {
236
for d.NextArg() {
237
tkns = append(tkns, d.Token())
238
}
239
- if d.Next() && d.Val() == "{" {
240
- tkns = append(tkns, d.Token())
241
- for d.NextBlock() {
242
- for d.Nested() {
243
- tkns = append(tkns, d.Token())
244
- d.NextBlock()
245
- }
239
+ for d.NextBlock() {
240
+ for d.Nested() {
241
+ tkns = append(tkns, d.Token())
242
+ d.NextBlock()
243
}
247
- tkns = append(tkns, d.Token())
248
- } else {
249
- d.cursor--
244
}
245
+ tkns = append(tkns, d.Token())
246
return NewDispenser(tkns)
247
}
248
modules/caddyhttp/reverseproxy/caddyfile.go
new
+486
@@ -0,0 +1,486 @@
1
+// Copyright 2015 Matthew Holt and The Caddy Authors
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+// http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package reverseproxy
16
+
17
+import (
18
+ "net/http"
19
+ "strconv"
20
+ "strings"
21
+ "time"
22
+
23
+ "github.com/caddyserver/caddy/v2"
24
+ "github.com/caddyserver/caddy/v2/caddyconfig"
25
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
26
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
27
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
28
+ "github.com/dustin/go-humanize"
29
+)
30
+
31
+func init() {
32
+ httpcaddyfile.RegisterHandlerDirective("reverse_proxy", parseCaddyfile)
33
+}
34
+
35
+func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
36
+ rp := new(Handler)
37
+ err := rp.UnmarshalCaddyfile(h.Dispenser)
38
+ if err != nil {
39
+ return nil, err
40
+ }
41
+ return rp, nil
42
+}
43
+
44
+// UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
45
+//
46
+// reverse_proxy [<matcher>] [<upstreams...>] {
47
+// # upstreams
48
+// to <upstreams...>
49
+//
50
+// # load balancing
51
+// lb_policy <name> [<options...>]
52
+// lb_try_duration <duration>
53
+// lb_try_interval <interval>
54
+//
55
+// # active health checking
56
+// health_path <path>
57
+// health_port <port>
58
+// health_interval <interval>
59
+// health_timeout <duration>
60
+// health_status <status>
61
+// health_body <regexp>
62
+//
63
+// # passive health checking
64
+// max_fails <num>
65
+// fail_duration <duration>
66
+// max_conns <num>
67
+// unhealthy_status <status>
68
+// unhealthy_latency <duration>
69
+//
70
+// # round trip
71
+// transport <name> {
72
+// ...
73
+// }
74
+// }
75
+//
76
+func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
77
+ for d.Next() {
78
+ for _, up := range d.RemainingArgs() {
79
+ h.Upstreams = append(h.Upstreams, &Upstream{
80
+ Dial: up,
81
+ })
82
+ }
83
+
84
+ for d.NextBlock() {
85
+ switch d.Val() {
86
+ case "to":
87
+ args := d.RemainingArgs()
88
+ if len(args) == 0 {
89
+ return d.ArgErr()
90
+ }
91
+ for _, up := range args {
92
+ h.Upstreams = append(h.Upstreams, &Upstream{
93
+ Dial: up,
94
+ })
95
+ }
96
+
97
+ case "lb_policy":
98
+ if !d.NextArg() {
99
+ return d.ArgErr()
100
+ }
101
+ if h.LoadBalancing != nil && h.LoadBalancing.SelectionPolicyRaw != nil {
102
+ return d.Err("load balancing selection policy already specified")
103
+ }
104
+ name := d.Val()
105
+ mod, err := caddy.GetModule("http.handlers.reverse_proxy.selection_policies." + name)
106
+ if err != nil {
107
+ return d.Errf("getting load balancing policy module '%s': %v", mod.Name, err)
108
+ }
109
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
110
+ if !ok {
111
+ return d.Errf("load balancing policy module '%s' is not a Caddyfile unmarshaler", mod.Name)
112
+ }
113
+ err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
114
+ if err != nil {
115
+ return err
116
+ }
117
+ sel, ok := unm.(Selector)
118
+ if !ok {
119
+ return d.Errf("module %s is not a Selector", mod.Name)
120
+ }
121
+ if h.LoadBalancing == nil {
122
+ h.LoadBalancing = new(LoadBalancing)
123
+ }
124
+ h.LoadBalancing.SelectionPolicyRaw = caddyconfig.JSONModuleObject(sel, "policy", name, nil)
125
+
126
+ case "lb_try_duration":
127
+ if !d.NextArg() {
128
+ return d.ArgErr()
129
+ }
130
+ if h.LoadBalancing == nil {
131
+ h.LoadBalancing = new(LoadBalancing)
132
+ }
133
+ dur, err := time.ParseDuration(d.Val())
134
+ if err != nil {
135
+ return d.Errf("bad duration value %s: %v", d.Val(), err)
136
+ }
137
+ h.LoadBalancing.TryDuration = caddy.Duration(dur)
138
+
139
+ case "lb_try_interval":
140
+ if !d.NextArg() {
141
+ return d.ArgErr()
142
+ }
143
+ if h.LoadBalancing == nil {
144
+ h.LoadBalancing = new(LoadBalancing)
145
+ }
146
+ dur, err := time.ParseDuration(d.Val())
147
+ if err != nil {
148
+ return d.Errf("bad interval value '%s': %v", d.Val(), err)
149
+ }
150
+ h.LoadBalancing.TryInterval = caddy.Duration(dur)
151
+
152
+ case "health_path":
153
+ if !d.NextArg() {
154
+ return d.ArgErr()
155
+ }
156
+ if h.HealthChecks == nil {
157
+ h.HealthChecks = new(HealthChecks)
158
+ }
159
+ if h.HealthChecks.Active == nil {
160
+ h.HealthChecks.Active = new(ActiveHealthChecks)
161
+ }
162
+ h.HealthChecks.Active.Path = d.Val()
163
+
164
+ case "health_port":
165
+ if !d.NextArg() {
166
+ return d.ArgErr()
167
+ }
168
+ if h.HealthChecks == nil {
169
+ h.HealthChecks = new(HealthChecks)
170
+ }
171
+ if h.HealthChecks.Active == nil {
172
+ h.HealthChecks.Active = new(ActiveHealthChecks)
173
+ }
174
+ portNum, err := strconv.Atoi(d.Val())
175
+ if err != nil {
176
+ return d.Errf("bad port number '%s': %v", d.Val(), err)
177
+ }
178
+ h.HealthChecks.Active.Port = portNum
179
+
180
+ case "health_interval":
181
+ if !d.NextArg() {
182
+ return d.ArgErr()
183
+ }
184
+ if h.HealthChecks == nil {
185
+ h.HealthChecks = new(HealthChecks)
186
+ }
187
+ if h.HealthChecks.Active == nil {
188
+ h.HealthChecks.Active = new(ActiveHealthChecks)
189
+ }
190
+ dur, err := time.ParseDuration(d.Val())
191
+ if err != nil {
192
+ return d.Errf("bad interval value %s: %v", d.Val(), err)
193
+ }
194
+ h.HealthChecks.Active.Interval = caddy.Duration(dur)
195
+
196
+ case "health_timeout":
197
+ if !d.NextArg() {
198
+ return d.ArgErr()
199
+ }
200
+ if h.HealthChecks == nil {
201
+ h.HealthChecks = new(HealthChecks)
202
+ }
203
+ if h.HealthChecks.Active == nil {
204
+ h.HealthChecks.Active = new(ActiveHealthChecks)
205
+ }
206
+ dur, err := time.ParseDuration(d.Val())
207
+ if err != nil {
208
+ return d.Errf("bad timeout value %s: %v", d.Val(), err)
209
+ }
210
+ h.HealthChecks.Active.Timeout = caddy.Duration(dur)
211
+
212
+ case "health_status":
213
+ if !d.NextArg() {
214
+ return d.ArgErr()
215
+ }
216
+ if h.HealthChecks == nil {
217
+ h.HealthChecks = new(HealthChecks)
218
+ }
219
+ if h.HealthChecks.Active == nil {
220
+ h.HealthChecks.Active = new(ActiveHealthChecks)
221
+ }
222
+ val := d.Val()
223
+ if len(val) == 3 && strings.HasSuffix(val, "xx") {
224
+ val = val[:1]
225
+ }
226
+ statusNum, err := strconv.Atoi(val[:1])
227
+ if err != nil {
228
+ return d.Errf("bad status value '%s': %v", d.Val(), err)
229
+ }
230
+ h.HealthChecks.Active.ExpectStatus = statusNum
231
+
232
+ case "health_body":
233
+ if !d.NextArg() {
234
+ return d.ArgErr()
235
+ }
236
+ if h.HealthChecks == nil {
237
+ h.HealthChecks = new(HealthChecks)
238
+ }
239
+ if h.HealthChecks.Active == nil {
240
+ h.HealthChecks.Active = new(ActiveHealthChecks)
241
+ }
242
+ h.HealthChecks.Active.ExpectBody = d.Val()
243
+
244
+ case "max_fails":
245
+ if !d.NextArg() {
246
+ return d.ArgErr()
247
+ }
248
+ if h.HealthChecks == nil {
249
+ h.HealthChecks = new(HealthChecks)
250
+ }
251
+ if h.HealthChecks.Passive == nil {
252
+ h.HealthChecks.Passive = new(PassiveHealthChecks)
253
+ }
254
+ maxFails, err := strconv.Atoi(d.Val())
255
+ if err != nil {
256
+ return d.Errf("invalid maximum fail count '%s': %v", d.Val(), err)
257
+ }
258
+ h.HealthChecks.Passive.MaxFails = maxFails
259
+
260
+ case "fail_duration":
261
+ if !d.NextArg() {
262
+ return d.ArgErr()
263
+ }
264
+ if h.HealthChecks == nil {
265
+ h.HealthChecks = new(HealthChecks)
266
+ }
267
+ if h.HealthChecks.Passive == nil {
268
+ h.HealthChecks.Passive = new(PassiveHealthChecks)
269
+ }
270
+ dur, err := time.ParseDuration(d.Val())
271
+ if err != nil {
272
+ return d.Errf("bad duration value '%s': %v", d.Val(), err)
273
+ }
274
+ h.HealthChecks.Passive.FailDuration = caddy.Duration(dur)
275
+
276
+ case "unhealthy_request_count":
277
+ if !d.NextArg() {
278
+ return d.ArgErr()
279
+ }
280
+ if h.HealthChecks == nil {
281
+ h.HealthChecks = new(HealthChecks)
282
+ }
283
+ if h.HealthChecks.Passive == nil {
284
+ h.HealthChecks.Passive = new(PassiveHealthChecks)
285
+ }
286
+ maxConns, err := strconv.Atoi(d.Val())
287
+ if err != nil {
288
+ return d.Errf("invalid maximum connection count '%s': %v", d.Val(), err)
289
+ }
290
+ h.HealthChecks.Passive.UnhealthyRequestCount = maxConns
291
+
292
+ case "unhealthy_status":
293
+ args := d.RemainingArgs()
294
+ if len(args) == 0 {
295
+ return d.ArgErr()
296
+ }
297
+ if h.HealthChecks == nil {
298
+ h.HealthChecks = new(HealthChecks)
299
+ }
300
+ if h.HealthChecks.Passive == nil {
301
+ h.HealthChecks.Passive = new(PassiveHealthChecks)
302
+ }
303
+ for _, arg := range args {
304
+ if len(arg) == 3 && strings.HasSuffix(arg, "xx") {
305
+ arg = arg[:1]
306
+ }
307
+ statusNum, err := strconv.Atoi(arg[:1])
308
+ if err != nil {
309
+ return d.Errf("bad status value '%s': %v", d.Val(), err)
310
+ }
311
+ h.HealthChecks.Passive.UnhealthyStatus = append(h.HealthChecks.Passive.UnhealthyStatus, statusNum)
312
+ }
313
+
314
+ case "unhealthy_latency":
315
+ if !d.NextArg() {
316
+ return d.ArgErr()
317
+ }
318
+ if h.HealthChecks == nil {
319
+ h.HealthChecks = new(HealthChecks)
320
+ }
321
+ if h.HealthChecks.Passive == nil {
322
+ h.HealthChecks.Passive = new(PassiveHealthChecks)
323
+ }
324
+ dur, err := time.ParseDuration(d.Val())
325
+ if err != nil {
326
+ return d.Errf("bad duration value '%s': %v", d.Val(), err)
327
+ }
328
+ h.HealthChecks.Passive.UnhealthyLatency = caddy.Duration(dur)
329
+
330
+ case "transport":
331
+ if !d.NextArg() {
332
+ return d.ArgErr()
333
+ }
334
+ if h.TransportRaw != nil {
335
+ return d.Err("transport already specified")
336
+ }
337
+ name := d.Val()
338
+ mod, err := caddy.GetModule("http.handlers.reverse_proxy.transport." + name)
339
+ if err != nil {
340
+ return d.Errf("getting transport module '%s': %v", mod.Name, err)
341
+ }
342
+ unm, ok := mod.New().(caddyfile.Unmarshaler)
343
+ if !ok {
344
+ return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod.Name)
345
+ }
346
+ d.Next() // consume the module name token
347
+ err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
348
+ if err != nil {
349
+ return err
350
+ }
351
+ rt, ok := unm.(http.RoundTripper)
352
+ if !ok {
353
+ return d.Errf("module %s is not a RoundTripper", mod.Name)
354
+ }
355
+ h.TransportRaw = caddyconfig.JSONModuleObject(rt, "protocol", name, nil)
356
+
357
+ default:
358
+ return d.Errf("unrecognized subdirective %s", d.Val())
359
+ }
360
+ }
361
+ }
362
+
363
+ return nil
364
+}
365
+
366
+// UnmarshalCaddyfile deserializes Caddyfile tokens into h.
367
+//
368
+// transport http {
369
+// read_buffer <size>
370
+// write_buffer <size>
371
+// dial_timeout <duration>
372
+// tls_client_auth <cert_file> <key_file>
373
+// tls_insecure_skip_verify
374
+// tls_timeout <duration>
375
+// keepalive [off|<duration>]
376
+// keepalive_idle_conns <max_count>
377
+// }
378
+//
379
+func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
380
+ for d.NextBlock() {
381
+ switch d.Val() {
382
+ case "read_buffer":
383
+ if !d.NextArg() {
384
+ return d.ArgErr()
385
+ }
386
+ size, err := humanize.ParseBytes(d.Val())
387
+ if err != nil {
388
+ return d.Errf("invalid read buffer size '%s': %v", d.Val(), err)
389
+ }
390
+ h.ReadBufferSize = int(size)
391
+
392
+ case "write_buffer":
393
+ if !d.NextArg() {
394
+ return d.ArgErr()
395
+ }
396
+ size, err := humanize.ParseBytes(d.Val())
397
+ if err != nil {
398
+ return d.Errf("invalid write buffer size '%s': %v", d.Val(), err)
399
+ }
400
+ h.WriteBufferSize = int(size)
401
+
402
+ case "dial_timeout":
403
+ if !d.NextArg() {
404
+ return d.ArgErr()
405
+ }
406
+ dur, err := time.ParseDuration(d.Val())
407
+ if err != nil {
408
+ return d.Errf("bad timeout value '%s': %v", d.Val(), err)
409
+ }
410
+ h.DialTimeout = caddy.Duration(dur)
411
+
412
+ case "tls_client_auth":
413
+ args := d.RemainingArgs()
414
+ if len(args) != 2 {
415
+ return d.ArgErr()
416
+ }
417
+ if h.TLS == nil {
418
+ h.TLS = new(TLSConfig)
419
+ }
420
+ h.TLS.ClientCertificateFile = args[0]
421
+ h.TLS.ClientCertificateKeyFile = args[1]
422
+
423
+ case "tls_insecure_skip_verify":
424
+ if d.NextArg() {
425
+ return d.ArgErr()
426
+ }
427
+ if h.TLS == nil {
428
+ h.TLS = new(TLSConfig)
429
+ }
430
+ h.TLS.InsecureSkipVerify = true
431
+
432
+ case "tls_timeout":
433
+ if !d.NextArg() {
434
+ return d.ArgErr()
435
+ }
436
+ dur, err := time.ParseDuration(d.Val())
437
+ if err != nil {
438
+ return d.Errf("bad timeout value '%s': %v", d.Val(), err)
439
+ }
440
+ if h.TLS == nil {
441
+ h.TLS = new(TLSConfig)
442
+ }
443
+ h.TLS.HandshakeTimeout = caddy.Duration(dur)
444
+
445
+ case "keepalive":
446
+ if !d.NextArg() {
447
+ return d.ArgErr()
448
+ }
449
+ if h.KeepAlive == nil {
450
+ h.KeepAlive = new(KeepAlive)
451
+ }
452
+ if d.Val() == "off" {
453
+ var disable bool
454
+ h.KeepAlive.Enabled = &disable
455
+ }
456
+ dur, err := time.ParseDuration(d.Val())
457
+ if err != nil {
458
+ return d.Errf("bad duration value '%s': %v", d.Val(), err)
459
+ }
460
+ h.KeepAlive.IdleConnTimeout = caddy.Duration(dur)
461
+
462
+ case "keepalive_idle_conns":
463
+ if !d.NextArg() {
464
+ return d.ArgErr()
465
+ }
466
+ num, err := strconv.Atoi(d.Val())
467
+ if err != nil {
468
+ return d.Errf("bad integer value '%s': %v", d.Val(), err)
469
+ }
470
+ if h.KeepAlive == nil {
471
+ h.KeepAlive = new(KeepAlive)
472
+ }
473
+ h.KeepAlive.MaxIdleConns = num
474
+
475
+ default:
476
+ return d.Errf("unrecognized subdirective %s", d.Val())
477
+ }
478
+ }
479
+ return nil
480
+}
481
+
482
+// Interface guards
483
+var (
484
+ _ caddyfile.Unmarshaler = (*Handler)(nil)
485
+ _ caddyfile.Unmarshaler = (*HTTPTransport)(nil)
486
+)
modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
new
+54
@@ -0,0 +1,54 @@
1
+// Copyright 2015 Matthew Holt and The Caddy Authors
2
+//
3
+// Licensed under the Apache License, Version 2.0 (the "License");
4
+// you may not use this file except in compliance with the License.
5
+// You may obtain a copy of the License at
6
+//
7
+// http://www.apache.org/licenses/LICENSE-2.0
8
+//
9
+// Unless required by applicable law or agreed to in writing, software
10
+// distributed under the License is distributed on an "AS IS" BASIS,
11
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+// See the License for the specific language governing permissions and
13
+// limitations under the License.
14
+
15
+package fastcgi
16
+
17
+import "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
18
+
19
+// UnmarshalCaddyfile deserializes Caddyfile tokens into h.
20
+//
21
+// transport fastcgi {
22
+// root <path>
23
+// split <at>
24
+// env <key> <value>
25
+// }
26
+//
27
+func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
28
+ for d.NextBlock() {
29
+ switch d.Val() {
30
+ case "root":
31
+ if !d.NextArg() {
32
+ return d.ArgErr()
33
+ }
34
+ t.Root = d.Val()
35
+
36
+ case "split":
37
+ if !d.NextArg() {
38
+ return d.ArgErr()
39
+ }
40
+ t.SplitPath = d.Val()
41
+
42
+ case "env":
43
+ args := d.RemainingArgs()
44
+ if len(args) != 2 {
45
+ return d.ArgErr()
46
+ }
47
+ t.EnvVars = append(t.EnvVars, [2]string{args[0], args[1]})
48
+
49
+ default:
50
+ return d.Errf("unrecognized subdirective %s", d.Val())
51
+ }
52
+ }
53
+ return nil
54
+}