@marcel91200 / caddy Mirror · read-only / commits / 50e62d06
Fork 0

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 +}