@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 1,762 lines 46.3 KB
Raw
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 "fmt"
19 "net"
20 "net/http"
21 "reflect"
22 "strconv"
23 "strings"
24
25 "github.com/dustin/go-humanize"
26
27 "github.com/caddyserver/caddy/v2"
28 "github.com/caddyserver/caddy/v2/caddyconfig"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
30 "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
31 "github.com/caddyserver/caddy/v2/internal"
32 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
33 "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
34 "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
35 "github.com/caddyserver/caddy/v2/modules/caddytls"
36 "github.com/caddyserver/caddy/v2/modules/internal/network"
37 )
38
39 func init() {
40 httpcaddyfile.RegisterHandlerDirective("reverse_proxy", parseCaddyfile)
41 httpcaddyfile.RegisterHandlerDirective("copy_response", parseCopyResponseCaddyfile)
42 httpcaddyfile.RegisterHandlerDirective("copy_response_headers", parseCopyResponseHeadersCaddyfile)
43 }
44
45 func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
46 rp := new(Handler)
47 err := rp.UnmarshalCaddyfile(h.Dispenser)
48 if err != nil {
49 return nil, err
50 }
51 err = rp.FinalizeUnmarshalCaddyfile(h)
52 if err != nil {
53 return nil, err
54 }
55 return rp, nil
56 }
57
58 // UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
59 //
60 // reverse_proxy [<matcher>] [<upstreams...>] {
61 // # backends
62 // to <upstreams...>
63 // dynamic <name> [...]
64 //
65 // # load balancing
66 // lb_policy <name> [<options...>]
67 // lb_retries <retries>
68 // lb_try_duration <duration>
69 // lb_try_interval <interval>
70 // lb_retry_match <matcher>
71 //
72 // # active health checking
73 // health_uri <uri>
74 // health_port <port>
75 // health_interval <interval>
76 // health_passes <num>
77 // health_fails <num>
78 // health_timeout <duration>
79 // health_status <status>
80 // health_body <regexp>
81 // health_method <value>
82 // health_request_body <value>
83 // health_follow_redirects
84 // health_headers {
85 // <field> [<values...>]
86 // }
87 //
88 // # passive health checking
89 // fail_duration <duration>
90 // max_fails <num>
91 // unhealthy_status <status>
92 // unhealthy_latency <duration>
93 // unhealthy_request_count <num>
94 //
95 // # streaming
96 // flush_interval <duration>
97 // request_buffers <size>
98 // response_buffers <size>
99 // stream_buffer_size <size>
100 // stream_timeout <duration>
101 // stream_close_delay <duration>
102 // stream_detached
103 // stream_logs {
104 // level <debug|info|warn|error>
105 // logger_name <name|access>
106 // skip_handshake
107 // }
108 // verbose_logs
109 //
110 // # reporting
111 // proxy_status_name <name>
112 //
113 // # request manipulation
114 // trusted_proxies [private_ranges] <ranges...>
115 // header_up [+|-]<field> [<value|regexp> [<replacement>]]
116 // header_down [+|-]<field> [<value|regexp> [<replacement>]]
117 // method <method>
118 // rewrite <to>
119 //
120 // # round trip
121 // transport <name> {
122 // ...
123 // }
124 //
125 // # optionally intercept responses from upstream
126 // @name {
127 // status <code...>
128 // header <field> [<value>]
129 // }
130 // replace_status [<matcher>] <status_code>
131 // handle_response [<matcher>] {
132 // <directives...>
133 //
134 // # special directives only available in handle_response
135 // copy_response [<matcher>] [<status>] {
136 // status <status>
137 // }
138 // copy_response_headers [<matcher>] {
139 // include <fields...>
140 // exclude <fields...>
141 // }
142 // }
143 // }
144 //
145 // Proxy upstream addresses should be network dial addresses such
146 // as `host:port`, or a URL such as `scheme://host:port`. Scheme
147 // and port may be inferred from other parts of the address/URL; if
148 // either are missing, defaults to HTTP.
149 //
150 // The FinalizeUnmarshalCaddyfile method should be called after this
151 // to finalize parsing of "handle_response" blocks, if possible.
152 func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
153 // currently, all backends must use the same scheme/protocol (the
154 // underlying JSON does not yet support per-backend transports)
155 var commonScheme string
156
157 // we'll wait until the very end of parsing before
158 // validating and encoding the transport
159 var transport http.RoundTripper
160 var transportModuleName string
161
162 // collect the response matchers defined as subdirectives
163 // prefixed with "@" for use with "handle_response" blocks
164 h.responseMatchers = make(map[string]caddyhttp.ResponseMatcher)
165
166 // appendUpstream creates an upstream for address and adds
167 // it to the list.
168 appendUpstream := func(address string) error {
169 pa, err := parseUpstreamDialAddress(address)
170 if err != nil {
171 return d.WrapErr(err)
172 }
173
174 // the underlying JSON does not yet support different
175 // transports (protocols or schemes) to each backend,
176 // so we remember the last one we see and compare them
177
178 switch pa.scheme {
179 case "wss":
180 return d.Errf("the scheme wss:// is only supported in browsers; use https:// instead")
181 case "ws":
182 return d.Errf("the scheme ws:// is only supported in browsers; use http:// instead")
183 case "https", "http", "h2c", "":
184 // Do nothing or handle the valid schemes
185 default:
186 return d.Errf("unsupported URL scheme %s://", pa.scheme)
187 }
188
189 if commonScheme != "" && pa.scheme != commonScheme {
190 return d.Errf("for now, all proxy upstreams must use the same scheme (transport protocol); expecting '%s://' but got '%s://'",
191 commonScheme, pa.scheme)
192 }
193 commonScheme = pa.scheme
194
195 // if the port of upstream address contains a placeholder, only wrap it with the `Upstream` struct,
196 // delaying actual resolution of the address until request time.
197 if pa.replaceablePort() {
198 h.Upstreams = append(h.Upstreams, &Upstream{Dial: pa.dialAddr()})
199 return nil
200 }
201 parsedAddr, err := caddy.ParseNetworkAddress(pa.dialAddr())
202 if err != nil {
203 return d.WrapErr(err)
204 }
205
206 if pa.isUnix() || !pa.rangedPort() {
207 // unix networks don't have ports
208 h.Upstreams = append(h.Upstreams, &Upstream{
209 Dial: pa.dialAddr(),
210 })
211 } else {
212 // expand a port range into multiple upstreams
213 for i := parsedAddr.StartPort; i <= parsedAddr.EndPort; i++ {
214 h.Upstreams = append(h.Upstreams, &Upstream{
215 Dial: caddy.JoinNetworkAddress("", parsedAddr.Host, fmt.Sprint(i)),
216 })
217 }
218 }
219
220 return nil
221 }
222
223 d.Next() // consume the directive name
224 for _, up := range d.RemainingArgs() {
225 err := appendUpstream(up)
226 if err != nil {
227 return fmt.Errorf("parsing upstream '%s': %w", up, err)
228 }
229 }
230
231 for d.NextBlock(0) {
232 // if the subdirective has an "@" prefix then we
233 // parse it as a response matcher for use with "handle_response"
234 if strings.HasPrefix(d.Val(), matcherPrefix) {
235 err := caddyhttp.ParseNamedResponseMatcher(d.NewFromNextSegment(), h.responseMatchers)
236 if err != nil {
237 return err
238 }
239 continue
240 }
241
242 switch d.Val() {
243 case "to":
244 args := d.RemainingArgs()
245 if len(args) == 0 {
246 return d.ArgErr()
247 }
248 for _, up := range args {
249 err := appendUpstream(up)
250 if err != nil {
251 return fmt.Errorf("parsing upstream '%s': %w", up, err)
252 }
253 }
254
255 case "dynamic":
256 if !d.NextArg() {
257 return d.ArgErr()
258 }
259 if h.DynamicUpstreams != nil {
260 return d.Err("dynamic upstreams already specified")
261 }
262 dynModule := d.Val()
263 modID := "http.reverse_proxy.upstreams." + dynModule
264 unm, err := caddyfile.UnmarshalModule(d, modID)
265 if err != nil {
266 return err
267 }
268 source, ok := unm.(UpstreamSource)
269 if !ok {
270 return d.Errf("module %s (%T) is not an UpstreamSource", modID, unm)
271 }
272 h.DynamicUpstreamsRaw = caddyconfig.JSONModuleObject(source, "source", dynModule, nil)
273
274 case "lb_policy":
275 if !d.NextArg() {
276 return d.ArgErr()
277 }
278 if h.LoadBalancing != nil && h.LoadBalancing.SelectionPolicyRaw != nil {
279 return d.Err("load balancing selection policy already specified")
280 }
281 name := d.Val()
282 modID := "http.reverse_proxy.selection_policies." + name
283 unm, err := caddyfile.UnmarshalModule(d, modID)
284 if err != nil {
285 return err
286 }
287 sel, ok := unm.(Selector)
288 if !ok {
289 return d.Errf("module %s (%T) is not a reverseproxy.Selector", modID, unm)
290 }
291 if h.LoadBalancing == nil {
292 h.LoadBalancing = new(LoadBalancing)
293 }
294 h.LoadBalancing.SelectionPolicyRaw = caddyconfig.JSONModuleObject(sel, "policy", name, nil)
295
296 case "lb_retries":
297 if !d.NextArg() {
298 return d.ArgErr()
299 }
300 tries, err := strconv.Atoi(d.Val())
301 if err != nil {
302 return d.Errf("bad lb_retries number '%s': %v", d.Val(), err)
303 }
304 if h.LoadBalancing == nil {
305 h.LoadBalancing = new(LoadBalancing)
306 }
307 h.LoadBalancing.Retries = tries
308
309 case "lb_try_duration":
310 if !d.NextArg() {
311 return d.ArgErr()
312 }
313 if h.LoadBalancing == nil {
314 h.LoadBalancing = new(LoadBalancing)
315 }
316 dur, err := caddy.ParseDuration(d.Val())
317 if err != nil {
318 return d.Errf("bad duration value %s: %v", d.Val(), err)
319 }
320 h.LoadBalancing.TryDuration = caddy.Duration(dur)
321
322 case "lb_try_interval":
323 if !d.NextArg() {
324 return d.ArgErr()
325 }
326 if h.LoadBalancing == nil {
327 h.LoadBalancing = new(LoadBalancing)
328 }
329 dur, err := caddy.ParseDuration(d.Val())
330 if err != nil {
331 return d.Errf("bad interval value '%s': %v", d.Val(), err)
332 }
333 h.LoadBalancing.TryInterval = caddy.Duration(dur)
334
335 case "lb_retry_match":
336 matcherSet, err := caddyhttp.ParseCaddyfileNestedMatcherSet(d)
337 if err != nil {
338 return d.Errf("failed to parse lb_retry_match: %v", err)
339 }
340 if h.LoadBalancing == nil {
341 h.LoadBalancing = new(LoadBalancing)
342 }
343 h.LoadBalancing.RetryMatchRaw = append(h.LoadBalancing.RetryMatchRaw, matcherSet)
344
345 case "health_uri":
346 if !d.NextArg() {
347 return d.ArgErr()
348 }
349 if h.HealthChecks == nil {
350 h.HealthChecks = new(HealthChecks)
351 }
352 if h.HealthChecks.Active == nil {
353 h.HealthChecks.Active = new(ActiveHealthChecks)
354 }
355 h.HealthChecks.Active.URI = d.Val()
356
357 case "health_path":
358 if !d.NextArg() {
359 return d.ArgErr()
360 }
361 if h.HealthChecks == nil {
362 h.HealthChecks = new(HealthChecks)
363 }
364 if h.HealthChecks.Active == nil {
365 h.HealthChecks.Active = new(ActiveHealthChecks)
366 }
367 h.HealthChecks.Active.Path = d.Val()
368 caddy.Log().Named("config.adapter.caddyfile").Warn("the 'health_path' subdirective is deprecated, please use 'health_uri' instead!")
369
370 case "health_upstream":
371 if !d.NextArg() {
372 return d.ArgErr()
373 }
374 if h.HealthChecks == nil {
375 h.HealthChecks = new(HealthChecks)
376 }
377 if h.HealthChecks.Active == nil {
378 h.HealthChecks.Active = new(ActiveHealthChecks)
379 }
380 _, port, err := net.SplitHostPort(d.Val())
381 if err != nil {
382 return d.Errf("health_upstream is malformed '%s': %v", d.Val(), err)
383 }
384 _, err = strconv.Atoi(port)
385 if err != nil {
386 return d.Errf("bad port number '%s': %v", d.Val(), err)
387 }
388 h.HealthChecks.Active.Upstream = d.Val()
389
390 case "health_port":
391 if !d.NextArg() {
392 return d.ArgErr()
393 }
394 if h.HealthChecks == nil {
395 h.HealthChecks = new(HealthChecks)
396 }
397 if h.HealthChecks.Active == nil {
398 h.HealthChecks.Active = new(ActiveHealthChecks)
399 }
400 if h.HealthChecks.Active.Upstream != "" {
401 return d.Errf("the 'health_port' subdirective is ignored if 'health_upstream' is used!")
402 }
403 portNum, err := strconv.Atoi(d.Val())
404 if err != nil {
405 return d.Errf("bad port number '%s': %v", d.Val(), err)
406 }
407 h.HealthChecks.Active.Port = portNum
408
409 case "health_headers":
410 healthHeaders := make(http.Header)
411 for nesting := d.Nesting(); d.NextBlock(nesting); {
412 key := d.Val()
413 values := d.RemainingArgs()
414 if len(values) == 0 {
415 values = append(values, "")
416 }
417 healthHeaders[key] = append(healthHeaders[key], values...)
418 }
419 if h.HealthChecks == nil {
420 h.HealthChecks = new(HealthChecks)
421 }
422 if h.HealthChecks.Active == nil {
423 h.HealthChecks.Active = new(ActiveHealthChecks)
424 }
425 h.HealthChecks.Active.Headers = healthHeaders
426
427 case "health_method":
428 if !d.NextArg() {
429 return d.ArgErr()
430 }
431 if h.HealthChecks == nil {
432 h.HealthChecks = new(HealthChecks)
433 }
434 if h.HealthChecks.Active == nil {
435 h.HealthChecks.Active = new(ActiveHealthChecks)
436 }
437 h.HealthChecks.Active.Method = d.Val()
438
439 case "health_request_body":
440 if !d.NextArg() {
441 return d.ArgErr()
442 }
443 if h.HealthChecks == nil {
444 h.HealthChecks = new(HealthChecks)
445 }
446 if h.HealthChecks.Active == nil {
447 h.HealthChecks.Active = new(ActiveHealthChecks)
448 }
449 h.HealthChecks.Active.Body = d.Val()
450
451 case "health_interval":
452 if !d.NextArg() {
453 return d.ArgErr()
454 }
455 if h.HealthChecks == nil {
456 h.HealthChecks = new(HealthChecks)
457 }
458 if h.HealthChecks.Active == nil {
459 h.HealthChecks.Active = new(ActiveHealthChecks)
460 }
461 dur, err := caddy.ParseDuration(d.Val())
462 if err != nil {
463 return d.Errf("bad interval value %s: %v", d.Val(), err)
464 }
465 h.HealthChecks.Active.Interval = caddy.Duration(dur)
466
467 case "health_timeout":
468 if !d.NextArg() {
469 return d.ArgErr()
470 }
471 if h.HealthChecks == nil {
472 h.HealthChecks = new(HealthChecks)
473 }
474 if h.HealthChecks.Active == nil {
475 h.HealthChecks.Active = new(ActiveHealthChecks)
476 }
477 dur, err := caddy.ParseDuration(d.Val())
478 if err != nil {
479 return d.Errf("bad timeout value %s: %v", d.Val(), err)
480 }
481 h.HealthChecks.Active.Timeout = caddy.Duration(dur)
482
483 case "health_status":
484 if !d.NextArg() {
485 return d.ArgErr()
486 }
487 if h.HealthChecks == nil {
488 h.HealthChecks = new(HealthChecks)
489 }
490 if h.HealthChecks.Active == nil {
491 h.HealthChecks.Active = new(ActiveHealthChecks)
492 }
493 val := d.Val()
494 if len(val) == 3 && strings.HasSuffix(val, "xx") {
495 val = val[:1]
496 }
497 statusNum, err := strconv.Atoi(val)
498 if err != nil {
499 return d.Errf("bad status value '%s': %v", d.Val(), err)
500 }
501 h.HealthChecks.Active.ExpectStatus = statusNum
502
503 case "health_body":
504 if !d.NextArg() {
505 return d.ArgErr()
506 }
507 if h.HealthChecks == nil {
508 h.HealthChecks = new(HealthChecks)
509 }
510 if h.HealthChecks.Active == nil {
511 h.HealthChecks.Active = new(ActiveHealthChecks)
512 }
513 h.HealthChecks.Active.ExpectBody = d.Val()
514
515 case "health_follow_redirects":
516 if d.NextArg() {
517 return d.ArgErr()
518 }
519 if h.HealthChecks == nil {
520 h.HealthChecks = new(HealthChecks)
521 }
522 if h.HealthChecks.Active == nil {
523 h.HealthChecks.Active = new(ActiveHealthChecks)
524 }
525 h.HealthChecks.Active.FollowRedirects = true
526
527 case "health_passes":
528 if !d.NextArg() {
529 return d.ArgErr()
530 }
531 if h.HealthChecks == nil {
532 h.HealthChecks = new(HealthChecks)
533 }
534 if h.HealthChecks.Active == nil {
535 h.HealthChecks.Active = new(ActiveHealthChecks)
536 }
537 passes, err := strconv.Atoi(d.Val())
538 if err != nil {
539 return d.Errf("invalid passes count '%s': %v", d.Val(), err)
540 }
541 h.HealthChecks.Active.Passes = passes
542
543 case "health_fails":
544 if !d.NextArg() {
545 return d.ArgErr()
546 }
547 if h.HealthChecks == nil {
548 h.HealthChecks = new(HealthChecks)
549 }
550 if h.HealthChecks.Active == nil {
551 h.HealthChecks.Active = new(ActiveHealthChecks)
552 }
553 fails, err := strconv.Atoi(d.Val())
554 if err != nil {
555 return d.Errf("invalid fails count '%s': %v", d.Val(), err)
556 }
557 h.HealthChecks.Active.Fails = fails
558
559 case "max_fails":
560 if !d.NextArg() {
561 return d.ArgErr()
562 }
563 if h.HealthChecks == nil {
564 h.HealthChecks = new(HealthChecks)
565 }
566 if h.HealthChecks.Passive == nil {
567 h.HealthChecks.Passive = new(PassiveHealthChecks)
568 }
569 maxFails, err := strconv.Atoi(d.Val())
570 if err != nil {
571 return d.Errf("invalid maximum fail count '%s': %v", d.Val(), err)
572 }
573 h.HealthChecks.Passive.MaxFails = maxFails
574
575 case "fail_duration":
576 if !d.NextArg() {
577 return d.ArgErr()
578 }
579 if h.HealthChecks == nil {
580 h.HealthChecks = new(HealthChecks)
581 }
582 if h.HealthChecks.Passive == nil {
583 h.HealthChecks.Passive = new(PassiveHealthChecks)
584 }
585 dur, err := caddy.ParseDuration(d.Val())
586 if err != nil {
587 return d.Errf("bad duration value '%s': %v", d.Val(), err)
588 }
589 h.HealthChecks.Passive.FailDuration = caddy.Duration(dur)
590
591 case "unhealthy_request_count":
592 if !d.NextArg() {
593 return d.ArgErr()
594 }
595 if h.HealthChecks == nil {
596 h.HealthChecks = new(HealthChecks)
597 }
598 if h.HealthChecks.Passive == nil {
599 h.HealthChecks.Passive = new(PassiveHealthChecks)
600 }
601 maxConns, err := strconv.Atoi(d.Val())
602 if err != nil {
603 return d.Errf("invalid maximum connection count '%s': %v", d.Val(), err)
604 }
605 h.HealthChecks.Passive.UnhealthyRequestCount = maxConns
606
607 case "unhealthy_status":
608 args := d.RemainingArgs()
609 if len(args) == 0 {
610 return d.ArgErr()
611 }
612 if h.HealthChecks == nil {
613 h.HealthChecks = new(HealthChecks)
614 }
615 if h.HealthChecks.Passive == nil {
616 h.HealthChecks.Passive = new(PassiveHealthChecks)
617 }
618 for _, arg := range args {
619 if len(arg) == 3 && strings.HasSuffix(arg, "xx") {
620 arg = arg[:1]
621 }
622 statusNum, err := strconv.Atoi(arg)
623 if err != nil {
624 return d.Errf("bad status value '%s': %v", d.Val(), err)
625 }
626 h.HealthChecks.Passive.UnhealthyStatus = append(h.HealthChecks.Passive.UnhealthyStatus, statusNum)
627 }
628
629 case "unhealthy_latency":
630 if !d.NextArg() {
631 return d.ArgErr()
632 }
633 if h.HealthChecks == nil {
634 h.HealthChecks = new(HealthChecks)
635 }
636 if h.HealthChecks.Passive == nil {
637 h.HealthChecks.Passive = new(PassiveHealthChecks)
638 }
639 dur, err := caddy.ParseDuration(d.Val())
640 if err != nil {
641 return d.Errf("bad duration value '%s': %v", d.Val(), err)
642 }
643 h.HealthChecks.Passive.UnhealthyLatency = caddy.Duration(dur)
644
645 case "flush_interval":
646 if !d.NextArg() {
647 return d.ArgErr()
648 }
649 if fi, err := strconv.Atoi(d.Val()); err == nil {
650 h.FlushInterval = caddy.Duration(fi)
651 } else {
652 dur, err := caddy.ParseDuration(d.Val())
653 if err != nil {
654 return d.Errf("bad duration value '%s': %v", d.Val(), err)
655 }
656 h.FlushInterval = caddy.Duration(dur)
657 }
658
659 case "request_buffers", "response_buffers", "stream_buffer_size":
660 subdir := d.Val()
661 if !d.NextArg() {
662 return d.ArgErr()
663 }
664 val := d.Val()
665 var size int64
666 if val == "unlimited" {
667 size = -1
668 } else {
669 usize, err := humanize.ParseBytes(val)
670 if err != nil {
671 return d.Errf("invalid byte size '%s': %v", val, err)
672 }
673 size = int64(usize)
674 }
675 if d.NextArg() {
676 return d.ArgErr()
677 }
678 switch subdir {
679 case "request_buffers":
680 h.RequestBuffers = size
681 case "response_buffers":
682 h.ResponseBuffers = size
683 case "stream_buffer_size":
684 h.StreamBufferSize = int(size)
685 }
686
687 case "proxy_status_name":
688 if !d.NextArg() {
689 return d.ArgErr()
690 }
691 h.ProxyStatusName = d.Val()
692 if d.NextArg() {
693 return d.ArgErr()
694 }
695
696 case "stream_timeout":
697 if !d.NextArg() {
698 return d.ArgErr()
699 }
700 if fi, err := strconv.Atoi(d.Val()); err == nil {
701 h.StreamTimeout = caddy.Duration(fi)
702 } else {
703 dur, err := caddy.ParseDuration(d.Val())
704 if err != nil {
705 return d.Errf("bad duration value '%s': %v", d.Val(), err)
706 }
707 h.StreamTimeout = caddy.Duration(dur)
708 }
709
710 case "stream_close_delay":
711 if !d.NextArg() {
712 return d.ArgErr()
713 }
714 if fi, err := strconv.Atoi(d.Val()); err == nil {
715 h.StreamCloseDelay = caddy.Duration(fi)
716 } else {
717 dur, err := caddy.ParseDuration(d.Val())
718 if err != nil {
719 return d.Errf("bad duration value '%s': %v", d.Val(), err)
720 }
721 h.StreamCloseDelay = caddy.Duration(dur)
722 }
723
724 case "stream_detached":
725 if d.NextArg() {
726 return d.ArgErr()
727 }
728 h.StreamDetached = true
729
730 case "stream_logs":
731 if d.NextArg() {
732 return d.ArgErr()
733 }
734 if h.StreamLogs == nil {
735 h.StreamLogs = new(StreamLogs)
736 }
737
738 nesting := d.Nesting()
739 for d.NextBlock(nesting) {
740 switch d.Val() {
741 case "level":
742 if !d.NextArg() {
743 return d.ArgErr()
744 }
745 h.StreamLogs.Level = d.Val()
746 if d.NextArg() {
747 return d.ArgErr()
748 }
749 case "logger_name":
750 if !d.NextArg() {
751 return d.ArgErr()
752 }
753 h.StreamLogs.LoggerName = d.Val()
754 if d.NextArg() {
755 return d.ArgErr()
756 }
757 case "skip_handshake":
758 if d.NextArg() {
759 return d.ArgErr()
760 }
761 h.StreamLogs.SkipHandshake = true
762 default:
763 return d.Errf("unrecognized stream_logs option: %s", d.Val())
764 }
765 }
766
767 case "trusted_proxies":
768 for d.NextArg() {
769 if d.Val() == "private_ranges" {
770 h.TrustedProxies = append(h.TrustedProxies, internal.PrivateRangesCIDR()...)
771 continue
772 }
773 h.TrustedProxies = append(h.TrustedProxies, d.Val())
774 }
775
776 case "header_up":
777 var err error
778
779 if h.Headers == nil {
780 h.Headers = new(headers.Handler)
781 }
782 if h.Headers.Request == nil {
783 h.Headers.Request = new(headers.HeaderOps)
784 }
785 args := d.RemainingArgs()
786
787 switch len(args) {
788 case 1:
789 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", nil)
790 case 2:
791 // some lint checks, I guess
792 if strings.EqualFold(args[0], "x-forwarded-for") && (args[1] == "{remote}" || args[1] == "{http.request.remote}" || args[1] == "{remote_host}" || args[1] == "{http.request.remote.host}") {
793 caddy.Log().Named("caddyfile").Warn("Unnecessary header_up X-Forwarded-For: the reverse proxy's default behavior is to pass headers to the upstream")
794 }
795 if strings.EqualFold(args[0], "x-forwarded-proto") && (args[1] == "{scheme}" || args[1] == "{http.request.scheme}") {
796 caddy.Log().Named("caddyfile").Warn("Unnecessary header_up X-Forwarded-Proto: the reverse proxy's default behavior is to pass headers to the upstream")
797 }
798 if strings.EqualFold(args[0], "x-forwarded-host") && (args[1] == "{host}" || args[1] == "{http.request.host}" || args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {
799 caddy.Log().Named("caddyfile").Warn("Unnecessary header_up X-Forwarded-Host: the reverse proxy's default behavior is to pass headers to the upstream")
800 }
801 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], nil)
802 case 3:
803 err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], args[1], &args[2])
804 default:
805 return d.ArgErr()
806 }
807
808 if err != nil {
809 return d.Err(err.Error())
810 }
811
812 case "header_down":
813 var err error
814
815 if h.Headers == nil {
816 h.Headers = new(headers.Handler)
817 }
818 if h.Headers.Response == nil {
819 h.Headers.Response = &headers.RespHeaderOps{
820 HeaderOps: new(headers.HeaderOps),
821 }
822 }
823 args := d.RemainingArgs()
824
825 switch len(args) {
826 case 1:
827 err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], "", nil)
828 case 2:
829 err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], nil)
830 case 3:
831 err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], &args[2])
832 default:
833 return d.ArgErr()
834 }
835
836 if err != nil {
837 return d.Err(err.Error())
838 }
839
840 case "method":
841 if !d.NextArg() {
842 return d.ArgErr()
843 }
844 if h.Rewrite == nil {
845 h.Rewrite = &rewrite.Rewrite{}
846 }
847 h.Rewrite.Method = d.Val()
848 if d.NextArg() {
849 return d.ArgErr()
850 }
851
852 case "rewrite":
853 if !d.NextArg() {
854 return d.ArgErr()
855 }
856 if h.Rewrite == nil {
857 h.Rewrite = &rewrite.Rewrite{}
858 }
859 h.Rewrite.URI = d.Val()
860 if d.NextArg() {
861 return d.ArgErr()
862 }
863
864 case "transport":
865 if !d.NextArg() {
866 return d.ArgErr()
867 }
868 if h.TransportRaw != nil {
869 return d.Err("transport already specified")
870 }
871 transportModuleName = d.Val()
872 modID := "http.reverse_proxy.transport." + transportModuleName
873 unm, err := caddyfile.UnmarshalModule(d, modID)
874 if err != nil {
875 return err
876 }
877 rt, ok := unm.(http.RoundTripper)
878 if !ok {
879 return d.Errf("module %s (%T) is not a RoundTripper", modID, unm)
880 }
881 transport = rt
882
883 case "handle_response":
884 // delegate the parsing of handle_response to the caller,
885 // since we need the httpcaddyfile.Helper to parse subroutes.
886 // See h.FinalizeUnmarshalCaddyfile
887 h.handleResponseSegments = append(h.handleResponseSegments, d.NewFromNextSegment())
888
889 case "replace_status":
890 args := d.RemainingArgs()
891 if len(args) != 1 && len(args) != 2 {
892 return d.Errf("must have one or two arguments: an optional response matcher, and a status code")
893 }
894
895 responseHandler := caddyhttp.ResponseHandler{}
896
897 if len(args) == 2 {
898 if !strings.HasPrefix(args[0], matcherPrefix) {
899 return d.Errf("must use a named response matcher, starting with '@'")
900 }
901 foundMatcher, ok := h.responseMatchers[args[0]]
902 if !ok {
903 return d.Errf("no named response matcher defined with name '%s'", args[0][1:])
904 }
905 responseHandler.Match = &foundMatcher
906 responseHandler.StatusCode = caddyhttp.WeakString(args[1])
907 } else if len(args) == 1 {
908 responseHandler.StatusCode = caddyhttp.WeakString(args[0])
909 }
910
911 // make sure there's no block, cause it doesn't make sense
912 if nesting := d.Nesting(); d.NextBlock(nesting) {
913 return d.Errf("cannot define routes for 'replace_status', use 'handle_response' instead.")
914 }
915
916 h.HandleResponse = append(
917 h.HandleResponse,
918 responseHandler,
919 )
920
921 case "verbose_logs":
922 if h.VerboseLogs {
923 return d.Err("verbose_logs already specified")
924 }
925 h.VerboseLogs = true
926
927 default:
928 return d.Errf("unrecognized subdirective %s", d.Val())
929 }
930 }
931
932 // if the scheme inferred from the backends' addresses is
933 // HTTPS, we will need a non-nil transport to enable TLS,
934 // or if H2C, to set the transport versions.
935 if (commonScheme == "https" || commonScheme == "h2c") && transport == nil {
936 transport = new(HTTPTransport)
937 transportModuleName = "http"
938 }
939
940 // verify transport configuration, and finally encode it
941 if transport != nil {
942 if te, ok := transport.(TLSTransport); ok {
943 if commonScheme == "https" && !te.TLSEnabled() {
944 err := te.EnableTLS(new(TLSConfig))
945 if err != nil {
946 return err
947 }
948 }
949 // check if the user set 'header_up host upstream_hostport' when proxying to HTTPS
950 // this is unnecessary because it's the default behavior already
951 if te.TLSEnabled() && h.Headers != nil && h.Headers.Request != nil {
952 hostVal := h.Headers.Request.Set.Get("Host")
953 if hostVal == "{upstream_hostport}" || hostVal == "{http.reverse_proxy.upstream.hostport}" {
954 caddy.Log().Named("caddyfile").Warn("Unnecessary header_up Host: the reverse proxy's default behavior is to pass the configured upstream address to the upstream when proxying to HTTPS")
955 }
956 }
957 if commonScheme == "http" && te.TLSEnabled() {
958 return d.Errf("upstream address scheme is HTTP but transport is configured for HTTP+TLS (HTTPS)")
959 }
960 if h2ct, ok := transport.(H2CTransport); ok && commonScheme == "h2c" {
961 err := h2ct.EnableH2C()
962 if err != nil {
963 return err
964 }
965 }
966 } else if commonScheme == "https" {
967 return d.Errf("upstreams are configured for HTTPS but transport module does not support TLS: %T", transport)
968 }
969
970 // no need to encode empty default transport
971 if !reflect.DeepEqual(transport, new(HTTPTransport)) {
972 h.TransportRaw = caddyconfig.JSONModuleObject(transport, "protocol", transportModuleName, nil)
973 }
974 }
975
976 return nil
977 }
978
979 // FinalizeUnmarshalCaddyfile finalizes the Caddyfile parsing which
980 // requires having an httpcaddyfile.Helper to function, to parse subroutes.
981 func (h *Handler) FinalizeUnmarshalCaddyfile(helper httpcaddyfile.Helper) error {
982 for _, d := range h.handleResponseSegments {
983 // consume the "handle_response" token
984 d.Next()
985 args := d.RemainingArgs()
986
987 if len(args) > 1 {
988 return d.Errf("too many arguments for 'handle_response': only a single response matcher name is allowed, but got: %s", args)
989 }
990
991 var matcher *caddyhttp.ResponseMatcher
992 if len(args) == 1 {
993 // the first arg should always be a matcher.
994 if !strings.HasPrefix(args[0], matcherPrefix) {
995 return d.Errf("must use a named response matcher, starting with '@'")
996 }
997
998 foundMatcher, ok := h.responseMatchers[args[0]]
999 if !ok {
1000 return d.Errf("no named response matcher defined with name '%s'", args[0][1:])
1001 }
1002 matcher = &foundMatcher
1003 }
1004
1005 // parse the block as routes
1006 handler, err := httpcaddyfile.ParseSegmentAsSubroute(helper.WithDispenser(d.NewFromNextSegment()))
1007 if err != nil {
1008 return err
1009 }
1010 subroute, ok := handler.(*caddyhttp.Subroute)
1011 if !ok {
1012 return helper.Errf("segment was not parsed as a subroute")
1013 }
1014 h.HandleResponse = append(
1015 h.HandleResponse,
1016 caddyhttp.ResponseHandler{
1017 Match: matcher,
1018 Routes: subroute.Routes,
1019 },
1020 )
1021 }
1022
1023 // move the handle_response entries without a matcher to the end.
1024 // we can't use sort.SliceStable because it will reorder the rest of the
1025 // entries which may be undesirable because we don't have a good
1026 // heuristic to use for sorting.
1027 withoutMatchers := []caddyhttp.ResponseHandler{}
1028 withMatchers := []caddyhttp.ResponseHandler{}
1029 for _, hr := range h.HandleResponse {
1030 if hr.Match == nil {
1031 withoutMatchers = append(withoutMatchers, hr)
1032 } else {
1033 withMatchers = append(withMatchers, hr)
1034 }
1035 }
1036 h.HandleResponse = append(withMatchers, withoutMatchers...)
1037
1038 // clean up the bits we only needed for adapting
1039 h.handleResponseSegments = nil
1040 h.responseMatchers = nil
1041
1042 return nil
1043 }
1044
1045 // UnmarshalCaddyfile deserializes Caddyfile tokens into h.
1046 //
1047 // transport http {
1048 // read_buffer <size>
1049 // write_buffer <size>
1050 // max_response_header <size>
1051 // network_proxy <module> {
1052 // ...
1053 // }
1054 // dial_timeout <duration>
1055 // dial_fallback_delay <duration>
1056 // response_header_timeout <duration>
1057 // expect_continue_timeout <duration>
1058 // resolvers <resolvers...>
1059 // tls
1060 // tls_client_auth <automate_name> | <cert_file> <key_file>
1061 // tls_insecure_skip_verify
1062 // tls_timeout <duration>
1063 // tls_trusted_ca_certs <cert_files...>
1064 // tls_trust_pool <module> {
1065 // ...
1066 // }
1067 // tls_server_name <sni>
1068 // tls_renegotiation <level>
1069 // tls_except_ports <ports...>
1070 // keepalive [off|<duration>]
1071 // keepalive_interval <interval>
1072 // keepalive_idle_conns <max_count>
1073 // keepalive_idle_conns_per_host <count>
1074 // versions <versions...>
1075 // compression off
1076 // max_conns_per_host <count>
1077 // max_idle_conns_per_host <count>
1078 // }
1079 func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1080 d.Next() // consume transport name
1081 for d.NextBlock(0) {
1082 switch d.Val() {
1083 case "read_buffer":
1084 if !d.NextArg() {
1085 return d.ArgErr()
1086 }
1087 size, err := humanize.ParseBytes(d.Val())
1088 if err != nil {
1089 return d.Errf("invalid read buffer size '%s': %v", d.Val(), err)
1090 }
1091 h.ReadBufferSize = int(size)
1092
1093 case "write_buffer":
1094 if !d.NextArg() {
1095 return d.ArgErr()
1096 }
1097 size, err := humanize.ParseBytes(d.Val())
1098 if err != nil {
1099 return d.Errf("invalid write buffer size '%s': %v", d.Val(), err)
1100 }
1101 h.WriteBufferSize = int(size)
1102
1103 case "read_timeout":
1104 if !d.NextArg() {
1105 return d.ArgErr()
1106 }
1107 timeout, err := caddy.ParseDuration(d.Val())
1108 if err != nil {
1109 return d.Errf("invalid read timeout duration '%s': %v", d.Val(), err)
1110 }
1111 h.ReadTimeout = caddy.Duration(timeout)
1112
1113 case "write_timeout":
1114 if !d.NextArg() {
1115 return d.ArgErr()
1116 }
1117 timeout, err := caddy.ParseDuration(d.Val())
1118 if err != nil {
1119 return d.Errf("invalid write timeout duration '%s': %v", d.Val(), err)
1120 }
1121 h.WriteTimeout = caddy.Duration(timeout)
1122
1123 case "max_response_header":
1124 if !d.NextArg() {
1125 return d.ArgErr()
1126 }
1127 size, err := humanize.ParseBytes(d.Val())
1128 if err != nil {
1129 return d.Errf("invalid max response header size '%s': %v", d.Val(), err)
1130 }
1131 h.MaxResponseHeaderSize = int64(size)
1132
1133 case "proxy_protocol":
1134 if !d.NextArg() {
1135 return d.ArgErr()
1136 }
1137 switch proxyProtocol := d.Val(); proxyProtocol {
1138 case "v1", "v2":
1139 h.ProxyProtocol = proxyProtocol
1140 default:
1141 return d.Errf("invalid proxy protocol version '%s'", proxyProtocol)
1142 }
1143
1144 case "forward_proxy_url":
1145 caddy.Log().Warn("The 'forward_proxy_url' field is deprecated. Use 'network_proxy <url>' instead.")
1146 if !d.NextArg() {
1147 return d.ArgErr()
1148 }
1149 u := network.ProxyFromURL{URL: d.Val()}
1150 h.NetworkProxyRaw = caddyconfig.JSONModuleObject(u, "from", "url", nil)
1151
1152 case "network_proxy":
1153 if !d.NextArg() {
1154 return d.ArgErr()
1155 }
1156 modStem := d.Val()
1157 modID := "caddy.network_proxy." + modStem
1158 unm, err := caddyfile.UnmarshalModule(d, modID)
1159 if err != nil {
1160 return err
1161 }
1162 h.NetworkProxyRaw = caddyconfig.JSONModuleObject(unm, "from", modStem, nil)
1163
1164 case "dial_timeout":
1165 if !d.NextArg() {
1166 return d.ArgErr()
1167 }
1168 dur, err := caddy.ParseDuration(d.Val())
1169 if err != nil {
1170 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1171 }
1172 h.DialTimeout = caddy.Duration(dur)
1173
1174 case "dial_fallback_delay":
1175 if !d.NextArg() {
1176 return d.ArgErr()
1177 }
1178 dur, err := caddy.ParseDuration(d.Val())
1179 if err != nil {
1180 return d.Errf("bad fallback delay value '%s': %v", d.Val(), err)
1181 }
1182 h.FallbackDelay = caddy.Duration(dur)
1183
1184 case "response_header_timeout":
1185 if !d.NextArg() {
1186 return d.ArgErr()
1187 }
1188 dur, err := caddy.ParseDuration(d.Val())
1189 if err != nil {
1190 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1191 }
1192 h.ResponseHeaderTimeout = caddy.Duration(dur)
1193
1194 case "expect_continue_timeout":
1195 if !d.NextArg() {
1196 return d.ArgErr()
1197 }
1198 dur, err := caddy.ParseDuration(d.Val())
1199 if err != nil {
1200 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1201 }
1202 h.ExpectContinueTimeout = caddy.Duration(dur)
1203
1204 case "resolvers":
1205 if h.Resolver == nil {
1206 h.Resolver = new(UpstreamResolver)
1207 }
1208 h.Resolver.Addresses = d.RemainingArgs()
1209 if len(h.Resolver.Addresses) == 0 {
1210 return d.Errf("must specify at least one resolver address")
1211 }
1212
1213 case "tls":
1214 if h.TLS == nil {
1215 h.TLS = new(TLSConfig)
1216 }
1217
1218 case "tls_client_auth":
1219 if h.TLS == nil {
1220 h.TLS = new(TLSConfig)
1221 }
1222 args := d.RemainingArgs()
1223 switch len(args) {
1224 case 1:
1225 h.TLS.ClientCertificateAutomate = args[0]
1226 case 2:
1227 h.TLS.ClientCertificateFile = args[0]
1228 h.TLS.ClientCertificateKeyFile = args[1]
1229 default:
1230 return d.ArgErr()
1231 }
1232
1233 case "tls_insecure_skip_verify":
1234 if d.NextArg() {
1235 return d.ArgErr()
1236 }
1237 if h.TLS == nil {
1238 h.TLS = new(TLSConfig)
1239 }
1240 h.TLS.InsecureSkipVerify = true
1241
1242 case "tls_curves":
1243 args := d.RemainingArgs()
1244 if len(args) == 0 {
1245 return d.ArgErr()
1246 }
1247 if h.TLS == nil {
1248 h.TLS = new(TLSConfig)
1249 }
1250 h.TLS.Curves = args
1251
1252 case "tls_timeout":
1253 if !d.NextArg() {
1254 return d.ArgErr()
1255 }
1256 dur, err := caddy.ParseDuration(d.Val())
1257 if err != nil {
1258 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1259 }
1260 if h.TLS == nil {
1261 h.TLS = new(TLSConfig)
1262 }
1263 h.TLS.HandshakeTimeout = caddy.Duration(dur)
1264
1265 case "tls_trusted_ca_certs":
1266 caddy.Log().Warn("The 'tls_trusted_ca_certs' field is deprecated. Use the 'tls_trust_pool' field instead.")
1267 args := d.RemainingArgs()
1268 if len(args) == 0 {
1269 return d.ArgErr()
1270 }
1271 if h.TLS == nil {
1272 h.TLS = new(TLSConfig)
1273 }
1274 if len(h.TLS.CARaw) != 0 {
1275 return d.Err("cannot specify both 'tls_trust_pool' and 'tls_trusted_ca_certs")
1276 }
1277 h.TLS.RootCAPEMFiles = args
1278
1279 case "tls_server_name":
1280 if !d.NextArg() {
1281 return d.ArgErr()
1282 }
1283 if h.TLS == nil {
1284 h.TLS = new(TLSConfig)
1285 }
1286 h.TLS.ServerName = d.Val()
1287
1288 case "tls_renegotiation":
1289 if h.TLS == nil {
1290 h.TLS = new(TLSConfig)
1291 }
1292 if !d.NextArg() {
1293 return d.ArgErr()
1294 }
1295 switch renegotiation := d.Val(); renegotiation {
1296 case "never", "once", "freely":
1297 h.TLS.Renegotiation = renegotiation
1298 default:
1299 return d.ArgErr()
1300 }
1301
1302 case "tls_except_ports":
1303 if h.TLS == nil {
1304 h.TLS = new(TLSConfig)
1305 }
1306 h.TLS.ExceptPorts = d.RemainingArgs()
1307 if len(h.TLS.ExceptPorts) == 0 {
1308 return d.ArgErr()
1309 }
1310
1311 case "keepalive":
1312 if !d.NextArg() {
1313 return d.ArgErr()
1314 }
1315 if h.KeepAlive == nil {
1316 h.KeepAlive = new(KeepAlive)
1317 }
1318 if d.Val() == "off" {
1319 var disable bool
1320 h.KeepAlive.Enabled = &disable
1321 break
1322 }
1323 dur, err := caddy.ParseDuration(d.Val())
1324 if err != nil {
1325 return d.Errf("bad duration value '%s': %v", d.Val(), err)
1326 }
1327 h.KeepAlive.IdleConnTimeout = caddy.Duration(dur)
1328
1329 case "keepalive_interval":
1330 if !d.NextArg() {
1331 return d.ArgErr()
1332 }
1333 dur, err := caddy.ParseDuration(d.Val())
1334 if err != nil {
1335 return d.Errf("bad interval value '%s': %v", d.Val(), err)
1336 }
1337 if h.KeepAlive == nil {
1338 h.KeepAlive = new(KeepAlive)
1339 }
1340 h.KeepAlive.ProbeInterval = caddy.Duration(dur)
1341
1342 case "keepalive_idle_conns":
1343 if !d.NextArg() {
1344 return d.ArgErr()
1345 }
1346 num, err := strconv.Atoi(d.Val())
1347 if err != nil {
1348 return d.Errf("bad integer value '%s': %v", d.Val(), err)
1349 }
1350 if h.KeepAlive == nil {
1351 h.KeepAlive = new(KeepAlive)
1352 }
1353 h.KeepAlive.MaxIdleConns = num
1354
1355 case "keepalive_idle_conns_per_host":
1356 if !d.NextArg() {
1357 return d.ArgErr()
1358 }
1359 num, err := strconv.Atoi(d.Val())
1360 if err != nil {
1361 return d.Errf("bad integer value '%s': %v", d.Val(), err)
1362 }
1363 if h.KeepAlive == nil {
1364 h.KeepAlive = new(KeepAlive)
1365 }
1366 h.KeepAlive.MaxIdleConnsPerHost = num
1367
1368 case "versions":
1369 h.Versions = d.RemainingArgs()
1370 if len(h.Versions) == 0 {
1371 return d.ArgErr()
1372 }
1373
1374 case "compression":
1375 if d.NextArg() {
1376 if d.Val() == "off" {
1377 var disable bool
1378 h.Compression = &disable
1379 }
1380 }
1381
1382 case "max_conns_per_host":
1383 if !d.NextArg() {
1384 return d.ArgErr()
1385 }
1386 num, err := strconv.Atoi(d.Val())
1387 if err != nil {
1388 return d.Errf("bad integer value '%s': %v", d.Val(), err)
1389 }
1390 h.MaxConnsPerHost = num
1391
1392 case "tls_trust_pool":
1393 if !d.NextArg() {
1394 return d.ArgErr()
1395 }
1396 modStem := d.Val()
1397 modID := "tls.ca_pool.source." + modStem
1398 unm, err := caddyfile.UnmarshalModule(d, modID)
1399 if err != nil {
1400 return err
1401 }
1402 ca, ok := unm.(caddytls.CA)
1403 if !ok {
1404 return d.Errf("module %s is not a caddytls.CA", modID)
1405 }
1406 if h.TLS == nil {
1407 h.TLS = new(TLSConfig)
1408 }
1409 if len(h.TLS.RootCAPEMFiles) != 0 {
1410 return d.Err("cannot specify both 'tls_trust_pool' and 'tls_trusted_ca_certs'")
1411 }
1412 if h.TLS.CARaw != nil {
1413 return d.Err("cannot specify \"tls_trust_pool\" twice in caddyfile")
1414 }
1415 h.TLS.CARaw = caddyconfig.JSONModuleObject(ca, "provider", modStem, nil)
1416 case "local_address":
1417 if !d.NextArg() {
1418 return d.ArgErr()
1419 }
1420 h.LocalAddress = d.Val()
1421 default:
1422 return d.Errf("unrecognized subdirective %s", d.Val())
1423 }
1424 }
1425 return nil
1426 }
1427
1428 func parseCopyResponseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
1429 crh := new(CopyResponseHandler)
1430 err := crh.UnmarshalCaddyfile(h.Dispenser)
1431 if err != nil {
1432 return nil, err
1433 }
1434 return crh, nil
1435 }
1436
1437 // UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
1438 //
1439 // copy_response [<matcher>] [<status>] {
1440 // status <status>
1441 // }
1442 func (h *CopyResponseHandler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1443 d.Next() // consume directive name
1444
1445 args := d.RemainingArgs()
1446 if len(args) == 1 {
1447 if num, err := strconv.Atoi(args[0]); err == nil && num > 0 {
1448 h.StatusCode = caddyhttp.WeakString(args[0])
1449 return nil
1450 }
1451 }
1452
1453 for d.NextBlock(0) {
1454 switch d.Val() {
1455 case "status":
1456 if !d.NextArg() {
1457 return d.ArgErr()
1458 }
1459 h.StatusCode = caddyhttp.WeakString(d.Val())
1460 default:
1461 return d.Errf("unrecognized subdirective '%s'", d.Val())
1462 }
1463 }
1464 return nil
1465 }
1466
1467 func parseCopyResponseHeadersCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
1468 crh := new(CopyResponseHeadersHandler)
1469 err := crh.UnmarshalCaddyfile(h.Dispenser)
1470 if err != nil {
1471 return nil, err
1472 }
1473 return crh, nil
1474 }
1475
1476 // UnmarshalCaddyfile sets up the handler from Caddyfile tokens. Syntax:
1477 //
1478 // copy_response_headers [<matcher>] {
1479 // include <fields...>
1480 // exclude <fields...>
1481 // }
1482 func (h *CopyResponseHeadersHandler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1483 d.Next() // consume directive name
1484
1485 args := d.RemainingArgs()
1486 if len(args) > 0 {
1487 return d.ArgErr()
1488 }
1489
1490 for d.NextBlock(0) {
1491 switch d.Val() {
1492 case "include":
1493 h.Include = append(h.Include, d.RemainingArgs()...)
1494
1495 case "exclude":
1496 h.Exclude = append(h.Exclude, d.RemainingArgs()...)
1497
1498 default:
1499 return d.Errf("unrecognized subdirective '%s'", d.Val())
1500 }
1501 }
1502 return nil
1503 }
1504
1505 // UnmarshalCaddyfile deserializes Caddyfile tokens into h.
1506 //
1507 // dynamic srv [<name>] {
1508 // service <service>
1509 // proto <proto>
1510 // name <name>
1511 // refresh <interval>
1512 // resolvers <resolvers...>
1513 // dial_timeout <timeout>
1514 // dial_fallback_delay <timeout>
1515 // grace_period <duration>
1516 // }
1517 func (u *SRVUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1518 d.Next() // consume upstream source name
1519
1520 args := d.RemainingArgs()
1521 if len(args) > 1 {
1522 return d.ArgErr()
1523 }
1524 if len(args) > 0 {
1525 u.Name = args[0]
1526 }
1527
1528 for d.NextBlock(0) {
1529 switch d.Val() {
1530 case "service":
1531 if !d.NextArg() {
1532 return d.ArgErr()
1533 }
1534 if u.Service != "" {
1535 return d.Errf("srv service has already been specified")
1536 }
1537 u.Service = d.Val()
1538
1539 case "proto":
1540 if !d.NextArg() {
1541 return d.ArgErr()
1542 }
1543 if u.Proto != "" {
1544 return d.Errf("srv proto has already been specified")
1545 }
1546 u.Proto = d.Val()
1547
1548 case "name":
1549 if !d.NextArg() {
1550 return d.ArgErr()
1551 }
1552 if u.Name != "" {
1553 return d.Errf("srv name has already been specified")
1554 }
1555 u.Name = d.Val()
1556
1557 case "refresh":
1558 if !d.NextArg() {
1559 return d.ArgErr()
1560 }
1561 dur, err := caddy.ParseDuration(d.Val())
1562 if err != nil {
1563 return d.Errf("parsing refresh interval duration: %v", err)
1564 }
1565 u.Refresh = caddy.Duration(dur)
1566
1567 case "resolvers":
1568 if u.Resolver == nil {
1569 u.Resolver = new(UpstreamResolver)
1570 }
1571 u.Resolver.Addresses = d.RemainingArgs()
1572 if len(u.Resolver.Addresses) == 0 {
1573 return d.Errf("must specify at least one resolver address")
1574 }
1575
1576 case "dial_timeout":
1577 if !d.NextArg() {
1578 return d.ArgErr()
1579 }
1580 dur, err := caddy.ParseDuration(d.Val())
1581 if err != nil {
1582 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1583 }
1584 u.DialTimeout = caddy.Duration(dur)
1585
1586 case "dial_fallback_delay":
1587 if !d.NextArg() {
1588 return d.ArgErr()
1589 }
1590 dur, err := caddy.ParseDuration(d.Val())
1591 if err != nil {
1592 return d.Errf("bad delay value '%s': %v", d.Val(), err)
1593 }
1594 u.FallbackDelay = caddy.Duration(dur)
1595
1596 case "grace_period":
1597 if !d.NextArg() {
1598 return d.ArgErr()
1599 }
1600 dur, err := caddy.ParseDuration(d.Val())
1601 if err != nil {
1602 return d.Errf("bad grace period value '%s': %v", d.Val(), err)
1603 }
1604 u.GracePeriod = caddy.Duration(dur)
1605 default:
1606 return d.Errf("unrecognized srv option '%s'", d.Val())
1607 }
1608 }
1609 return nil
1610 }
1611
1612 // UnmarshalCaddyfile deserializes Caddyfile tokens into h.
1613 //
1614 // dynamic a [<name> <port] {
1615 // name <name>
1616 // port <port>
1617 // refresh <interval>
1618 // resolvers <resolvers...>
1619 // dial_timeout <timeout>
1620 // dial_fallback_delay <timeout>
1621 // versions ipv4|ipv6
1622 // }
1623 func (u *AUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1624 d.Next() // consume upstream source name
1625
1626 args := d.RemainingArgs()
1627 if len(args) > 2 {
1628 return d.ArgErr()
1629 }
1630 if len(args) > 0 {
1631 u.Name = args[0]
1632 if len(args) == 2 {
1633 u.Port = args[1]
1634 }
1635 }
1636
1637 for d.NextBlock(0) {
1638 switch d.Val() {
1639 case "name":
1640 if !d.NextArg() {
1641 return d.ArgErr()
1642 }
1643 if u.Name != "" {
1644 return d.Errf("a name has already been specified")
1645 }
1646 u.Name = d.Val()
1647
1648 case "port":
1649 if !d.NextArg() {
1650 return d.ArgErr()
1651 }
1652 if u.Port != "" {
1653 return d.Errf("a port has already been specified")
1654 }
1655 u.Port = d.Val()
1656
1657 case "refresh":
1658 if !d.NextArg() {
1659 return d.ArgErr()
1660 }
1661 dur, err := caddy.ParseDuration(d.Val())
1662 if err != nil {
1663 return d.Errf("parsing refresh interval duration: %v", err)
1664 }
1665 u.Refresh = caddy.Duration(dur)
1666
1667 case "resolvers":
1668 if u.Resolver == nil {
1669 u.Resolver = new(UpstreamResolver)
1670 }
1671 u.Resolver.Addresses = d.RemainingArgs()
1672 if len(u.Resolver.Addresses) == 0 {
1673 return d.Errf("must specify at least one resolver address")
1674 }
1675
1676 case "dial_timeout":
1677 if !d.NextArg() {
1678 return d.ArgErr()
1679 }
1680 dur, err := caddy.ParseDuration(d.Val())
1681 if err != nil {
1682 return d.Errf("bad timeout value '%s': %v", d.Val(), err)
1683 }
1684 u.DialTimeout = caddy.Duration(dur)
1685
1686 case "dial_fallback_delay":
1687 if !d.NextArg() {
1688 return d.ArgErr()
1689 }
1690 dur, err := caddy.ParseDuration(d.Val())
1691 if err != nil {
1692 return d.Errf("bad delay value '%s': %v", d.Val(), err)
1693 }
1694 u.FallbackDelay = caddy.Duration(dur)
1695
1696 case "versions":
1697 args := d.RemainingArgs()
1698 if len(args) == 0 {
1699 return d.Errf("must specify at least one version")
1700 }
1701
1702 if u.Versions == nil {
1703 u.Versions = &IPVersions{}
1704 }
1705
1706 trueBool := true
1707 for _, arg := range args {
1708 switch arg {
1709 case "ipv4":
1710 u.Versions.IPv4 = &trueBool
1711 case "ipv6":
1712 u.Versions.IPv6 = &trueBool
1713 default:
1714 return d.Errf("unsupported version: '%s'", arg)
1715 }
1716 }
1717
1718 default:
1719 return d.Errf("unrecognized a option '%s'", d.Val())
1720 }
1721 }
1722 return nil
1723 }
1724
1725 // UnmarshalCaddyfile deserializes Caddyfile tokens into h.
1726 //
1727 // dynamic multi {
1728 // <source> [...]
1729 // }
1730 func (u *MultiUpstreams) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
1731 d.Next() // consume upstream source name
1732
1733 if d.NextArg() {
1734 return d.ArgErr()
1735 }
1736
1737 for d.NextBlock(0) {
1738 dynModule := d.Val()
1739 modID := "http.reverse_proxy.upstreams." + dynModule
1740 unm, err := caddyfile.UnmarshalModule(d, modID)
1741 if err != nil {
1742 return err
1743 }
1744 source, ok := unm.(UpstreamSource)
1745 if !ok {
1746 return d.Errf("module %s (%T) is not an UpstreamSource", modID, unm)
1747 }
1748 u.SourcesRaw = append(u.SourcesRaw, caddyconfig.JSONModuleObject(source, "source", dynModule, nil))
1749 }
1750 return nil
1751 }
1752
1753 const matcherPrefix = "@"
1754
1755 // Interface guards
1756 var (
1757 _ caddyfile.Unmarshaler = (*Handler)(nil)
1758 _ caddyfile.Unmarshaler = (*HTTPTransport)(nil)
1759 _ caddyfile.Unmarshaler = (*SRVUpstreams)(nil)
1760 _ caddyfile.Unmarshaler = (*AUpstreams)(nil)
1761 _ caddyfile.Unmarshaler = (*MultiUpstreams)(nil)
1762 )