Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 12, 08:15 UTC
chore: Use slices package where possible (#6585)
* chore: Use slices package where possible * More, mostly using ContainsFunc * Even more slice operations
Francis Lavoie committed
Sep 25, 2024 at 16:30 UTC
2faeac0a104c72e9396717406ecb7c353bf9aa64
21 files changed
+141
-267
admin.go
+5
-14
@@ -34,6 +34,7 @@ import (
34
"os"
35
"path"
36
"regexp"
37
+ "slices"
38
"strconv"
39
"strings"
40
"sync"
@@ -675,13 +676,7 @@ func (remote RemoteAdmin) enforceAccessControls(r *http.Request) error {
676
// key recognized; make sure its HTTP request is permitted
677
for _, accessPerm := range adminAccess.Permissions {
678
// verify method
678
- methodFound := accessPerm.Methods == nil
679
- for _, method := range accessPerm.Methods {
680
- if method == r.Method {
681
- methodFound = true
682
- break
683
- }
684
- }
679
+ methodFound := accessPerm.Methods == nil || slices.Contains(accessPerm.Methods, r.Method)
680
if !methodFound {
681
return APIError{
682
HTTPStatus: http.StatusForbidden,
@@ -877,13 +872,9 @@ func (h adminHandler) handleError(w http.ResponseWriter, r *http.Request, err er
872
// a trustworthy/expected value. This helps to mitigate DNS
873
// rebinding attacks.
874
func (h adminHandler) checkHost(r *http.Request) error {
880
- var allowed bool
881
- for _, allowedOrigin := range h.allowedOrigins {
882
- if r.Host == allowedOrigin.Host {
883
- allowed = true
884
- break
885
- }
886
- }
875
+ allowed := slices.ContainsFunc(h.allowedOrigins, func(u *url.URL) bool {
876
+ return r.Host == u.Host
877
+ })
878
if !allowed {
879
return APIError{
880
HTTPStatus: http.StatusForbidden,
caddyconfig/caddyfile/importgraph.go
+2
-6
@@ -16,6 +16,7 @@ package caddyfile
16
17
import (
18
"fmt"
19
+ "slices"
20
)
21
22
type adjacency map[string][]string
@@ -91,12 +92,7 @@ func (i *importGraph) areConnected(from, to string) bool {
92
if !ok {
93
return false
94
}
94
- for _, v := range al {
95
- if v == to {
96
- return true
97
- }
98
- }
99
- return false
95
+ return slices.Contains(al, to)
96
}
97
98
func (i *importGraph) willCycle(from, to string) bool {
caddyconfig/httpcaddyfile/directives.go
+9
-30
@@ -17,6 +17,7 @@ package httpcaddyfile
17
import (
18
"encoding/json"
19
"net"
20
+ "slices"
21
"sort"
22
"strconv"
23
"strings"
@@ -100,17 +101,6 @@ var defaultDirectiveOrder = []string{
101
// plugins or by the user via the "order" global option.
102
var directiveOrder = defaultDirectiveOrder
103
103
-// directiveIsOrdered returns true if dir is
104
-// a known, ordered (sorted) directive.
105
-func directiveIsOrdered(dir string) bool {
106
- for _, d := range directiveOrder {
107
- if d == dir {
108
- return true
109
- }
110
- }
111
- return false
112
-}
113
-
104
// RegisterDirective registers a unique directive dir with an
105
// associated unmarshaling (setup) function. When directive dir
106
// is encountered in a Caddyfile, setupFunc will be called to
@@ -161,7 +151,7 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
151
// EXPERIMENTAL: This API may change or be removed.
152
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
153
// check if directive was already ordered
164
- if directiveIsOrdered(dir) {
154
+ if slices.Contains(directiveOrder, dir) {
155
panic("directive '" + dir + "' already ordered")
156
}
157
@@ -172,12 +162,7 @@ func RegisterDirectiveOrder(dir string, position Positional, standardDir string)
162
// check if directive exists in standard distribution, since
163
// we can't allow plugins to depend on one another; we can't
164
// guarantee the order that plugins are loaded in.
175
- foundStandardDir := false
176
- for _, d := range defaultDirectiveOrder {
177
- if d == standardDir {
178
- foundStandardDir = true
179
- }
180
- }
165
+ foundStandardDir := slices.Contains(defaultDirectiveOrder, standardDir)
166
if !foundStandardDir {
167
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
168
}
@@ -603,23 +588,17 @@ func (sb serverBlock) hostsFromKeysNotHTTP(httpPort string) []string {
588
// hasHostCatchAllKey returns true if sb has a key that
589
// omits a host portion, i.e. it "catches all" hosts.
590
func (sb serverBlock) hasHostCatchAllKey() bool {
606
- for _, addr := range sb.keys {
607
- if addr.Host == "" {
608
- return true
609
- }
610
- }
611
- return false
591
+ return slices.ContainsFunc(sb.keys, func(addr Address) bool {
592
+ return addr.Host == ""
593
+ })
594
}
595
596
// isAllHTTP returns true if all sb keys explicitly specify
597
// the http:// scheme
598
func (sb serverBlock) isAllHTTP() bool {
617
- for _, addr := range sb.keys {
618
- if addr.Scheme != "http" {
619
- return false
620
- }
621
- }
622
- return true
599
+ return !slices.ContainsFunc(sb.keys, func(addr Address) bool {
600
+ return addr.Scheme != "http"
601
+ })
602
}
603
604
// Positional are the supported modes for ordering directives.
caddyconfig/httpcaddyfile/httptype.go
+7
-26
@@ -536,7 +536,7 @@ func (st *ServerType) serversFromPairings(
536
if k == j {
537
continue
538
}
539
- if sliceContains(sblock2.block.GetKeysText(), key) {
539
+ if slices.Contains(sblock2.block.GetKeysText(), key) {
540
return nil, fmt.Errorf("ambiguous site definition: %s", key)
541
}
542
}
@@ -720,7 +720,7 @@ func (st *ServerType) serversFromPairings(
720
if srv.AutoHTTPS == nil {
721
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
722
}
723
- if !sliceContains(srv.AutoHTTPS.Skip, addr.Host) {
723
+ if !slices.Contains(srv.AutoHTTPS.Skip, addr.Host) {
724
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
725
}
726
}
@@ -734,7 +734,7 @@ func (st *ServerType) serversFromPairings(
734
// https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761
735
createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"]
736
hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) ||
737
- (addr.Host != "" && srv.AutoHTTPS != nil && !sliceContains(srv.AutoHTTPS.Skip, addr.Host))
737
+ (addr.Host != "" && srv.AutoHTTPS != nil && !slices.Contains(srv.AutoHTTPS.Skip, addr.Host))
738
739
// we'll need to remember if the address qualifies for auto-HTTPS, so we
740
// can add a TLS conn policy if necessary
@@ -1061,7 +1061,7 @@ func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.Connecti
1061
} else if cps[i].CertSelection != nil && cps[j].CertSelection != nil {
1062
// if both have one, then combine AnyTag
1063
for _, tag := range cps[j].CertSelection.AnyTag {
1064
- if !sliceContains(cps[i].CertSelection.AnyTag, tag) {
1064
+ if !slices.Contains(cps[i].CertSelection.AnyTag, tag) {
1065
cps[i].CertSelection.AnyTag = append(cps[i].CertSelection.AnyTag, tag)
1066
}
1067
}
@@ -1144,7 +1144,7 @@ func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
1144
func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) {
1145
if needsSorting {
1146
for _, val := range routes {
1147
- if !directiveIsOrdered(val.directive) {
1147
+ if !slices.Contains(directiveOrder, val.directive) {
1148
return nil, fmt.Errorf("directive '%s' is not an ordered HTTP handler, so it cannot be used here - try placing within a route block or using the order global option", val.directive)
1149
}
1150
}
@@ -1354,17 +1354,8 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
1354
1355
// add this server block's keys to the matcher
1356
// pair if it doesn't already exist
1357
- if addr.Host != "" {
1358
- var found bool
1359
- for _, h := range chosenMatcherPair.hostm {
1360
- if h == addr.Host {
1361
- found = true
1362
- break
1363
- }
1364
- }
1365
- if !found {
1366
- chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
1367
- }
1357
+ if addr.Host != "" && !slices.Contains(chosenMatcherPair.hostm, addr.Host) {
1358
+ chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
1359
}
1360
}
1361
@@ -1540,16 +1531,6 @@ func tryDuration(val any, warnings *[]caddyconfig.Warning) caddy.Duration {
1531
return durationVal
1532
}
1533
1543
-// sliceContains returns true if needle is in haystack.
1544
-func sliceContains(haystack []string, needle string) bool {
1545
- for _, s := range haystack {
1546
- if s == needle {
1547
- return true
1548
- }
1549
- }
1550
- return false
1551
-}
1552
-
1534
// listenersUseAnyPortOtherThan returns true if there are any
1535
// listeners in addresses that use a port which is not otherPort.
1536
// Mostly borrowed from unexported method in caddyhttp package.
caddyconfig/httpcaddyfile/options.go
+20
-20
@@ -15,6 +15,7 @@
15
package httpcaddyfile
16
17
import (
18
+ "slices"
19
"strconv"
20
21
"github.com/caddyserver/certmagic"
@@ -110,17 +111,12 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
111
}
112
pos := Positional(d.Val())
113
113
- newOrder := directiveOrder
114
+ // if directive already had an order, drop it
115
+ newOrder := slices.DeleteFunc(directiveOrder, func(d string) bool {
116
+ return d == dirName
117
+ })
118
115
- // if directive exists, first remove it
116
- for i, d := range newOrder {
117
- if d == dirName {
118
- newOrder = append(newOrder[:i], newOrder[i+1:]...)
119
- break
120
- }
121
- }
122
-
123
- // act on the positional
119
+ // act on the positional; if it's First or Last, we're done right away
120
switch pos {
121
case First:
122
newOrder = append([]string{dirName}, newOrder...)
@@ -129,6 +125,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
125
}
126
directiveOrder = newOrder
127
return newOrder, nil
128
+
129
case Last:
130
newOrder = append(newOrder, dirName)
131
if d.NextArg() {
@@ -136,8 +133,11 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
133
}
134
directiveOrder = newOrder
135
return newOrder, nil
136
+
137
+ // if it's Before or After, continue
138
case Before:
139
case After:
140
+
141
default:
142
return nil, d.Errf("unknown positional '%s'", pos)
143
}
@@ -151,17 +151,17 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
151
return nil, d.ArgErr()
152
}
153
154
- // insert directive into proper position
155
- for i, d := range newOrder {
156
- if d == otherDir {
157
- if pos == Before {
158
- newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
159
- } else if pos == After {
160
- newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
161
- }
162
- break
163
- }
154
+ // get the position of the target directive
155
+ targetIndex := slices.Index(newOrder, otherDir)
156
+ if targetIndex == -1 {
157
+ return nil, d.Errf("directive '%s' not found", otherDir)
158
+ }
159
+ // if we're inserting after, we need to increment the index to go after
160
+ if pos == After {
161
+ targetIndex++
162
}
163
+ // insert the directive into the new order
164
+ newOrder = slices.Insert(newOrder, targetIndex, dirName)
165
166
directiveOrder = newOrder
167
caddyconfig/httpcaddyfile/serveroptions.go
+8
-16
@@ -17,6 +17,7 @@ package httpcaddyfile
17
import (
18
"encoding/json"
19
"fmt"
20
+ "slices"
21
22
"github.com/dustin/go-humanize"
23
@@ -180,7 +181,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
181
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
182
return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
183
}
183
- if sliceContains(serverOpts.Protocols, proto) {
184
+ if slices.Contains(serverOpts.Protocols, proto) {
185
return nil, d.Errf("protocol %s specified more than once", proto)
186
}
187
serverOpts.Protocols = append(serverOpts.Protocols, proto)
@@ -229,7 +230,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
230
case "client_ip_headers":
231
headers := d.RemainingArgs()
232
for _, header := range headers {
232
- if sliceContains(serverOpts.ClientIPHeaders, header) {
233
+ if slices.Contains(serverOpts.ClientIPHeaders, header) {
234
return nil, d.Errf("client IP header %s specified more than once", header)
235
}
236
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
@@ -288,24 +289,15 @@ func applyServerOptions(
289
290
for key, server := range servers {
291
// find the options that apply to this server
291
- opts := func() *serverOptions {
292
- for _, entry := range serverOpts {
293
- if entry.ListenerAddress == "" {
294
- return &entry
295
- }
296
- for _, listener := range server.Listen {
297
- if entry.ListenerAddress == listener {
298
- return &entry
299
- }
300
- }
301
- }
302
- return nil
303
- }()
292
+ optsIndex := slices.IndexFunc(serverOpts, func(s serverOptions) bool {
293
+ return s.ListenerAddress == "" || slices.Contains(server.Listen, s.ListenerAddress)
294
+ })
295
296
// if none apply, then move to the next server
306
- if opts == nil {
297
+ if optsIndex == -1 {
298
continue
299
}
300
+ opts := serverOpts[optsIndex]
301
302
// set all the options
303
server.ListenerWrappersRaw = opts.ListenerWrappersRaw
caddyconfig/httpcaddyfile/tlsapp.go
+25
-30
@@ -19,6 +19,7 @@ import (
19
"encoding/json"
20
"fmt"
21
"reflect"
22
+ "slices"
23
"sort"
24
"strconv"
25
"strings"
@@ -57,19 +58,20 @@ func (st ServerType) buildTLSApp(
58
for _, pair := range pairings {
59
for _, sb := range pair.serverBlocks {
60
for _, addr := range sb.keys {
60
- if addr.Host == "" {
61
- // this server block has a hostless key, now
62
- // go through and add all the hosts to the set
63
- for _, otherAddr := range sb.keys {
64
- if otherAddr.Original == addr.Original {
65
- continue
66
- }
67
- if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
68
- httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
69
- }
61
+ if addr.Host != "" {
62
+ continue
63
+ }
64
+ // this server block has a hostless key, now
65
+ // go through and add all the hosts to the set
66
+ for _, otherAddr := range sb.keys {
67
+ if otherAddr.Original == addr.Original {
68
+ continue
69
+ }
70
+ if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
71
+ httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
72
}
71
- break
73
}
74
+ break
75
}
76
}
77
}
@@ -465,7 +467,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
467
if globalACMECA != nil && acmeIssuer.CA == "" {
468
acmeIssuer.CA = globalACMECA.(string)
469
}
468
- if globalACMECARoot != nil && !sliceContains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
470
+ if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
471
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
472
}
473
if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) {
@@ -580,7 +582,7 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
582
if !automationPolicyHasAllPublicNames(aps[i]) {
583
// if this automation policy has internal names, we might as well remove it
584
// so auto-https can implicitly use the internal issuer
583
- aps = append(aps[:i], aps[i+1:]...)
585
+ aps = slices.Delete(aps, i, i+1)
586
i--
587
}
588
}
@@ -597,7 +599,7 @@ outer:
599
for j := i + 1; j < len(aps); j++ {
600
// if they're exactly equal in every way, just keep one of them
601
if reflect.DeepEqual(aps[i], aps[j]) {
600
- aps = append(aps[:j], aps[j+1:]...)
602
+ aps = slices.Delete(aps, j, j+1)
603
// must re-evaluate current i against next j; can't skip it!
604
// even if i decrements to -1, will be incremented to 0 immediately
605
i--
@@ -627,18 +629,18 @@ outer:
629
// cause example.com to be served by the less specific policy for
630
// '*.com', which might be different (yes we've seen this happen)
631
if automationPolicyShadows(i, aps) >= j {
630
- aps = append(aps[:i], aps[i+1:]...)
632
+ aps = slices.Delete(aps, i, i+1)
633
i--
634
continue outer
635
}
636
} else {
637
// avoid repeated subjects
638
for _, subj := range aps[j].SubjectsRaw {
637
- if !sliceContains(aps[i].SubjectsRaw, subj) {
639
+ if !slices.Contains(aps[i].SubjectsRaw, subj) {
640
aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj)
641
}
642
}
641
- aps = append(aps[:j], aps[j+1:]...)
643
+ aps = slices.Delete(aps, j, j+1)
644
j--
645
}
646
}
@@ -658,13 +660,9 @@ func automationPolicyIsSubset(a, b *caddytls.AutomationPolicy) bool {
660
return false
661
}
662
for _, aSubj := range a.SubjectsRaw {
661
- var inSuperset bool
662
- for _, bSubj := range b.SubjectsRaw {
663
- if certmagic.MatchWildcard(aSubj, bSubj) {
664
- inSuperset = true
665
- break
666
- }
667
- }
663
+ inSuperset := slices.ContainsFunc(b.SubjectsRaw, func(bSubj string) bool {
664
+ return certmagic.MatchWildcard(aSubj, bSubj)
665
+ })
666
if !inSuperset {
667
return false
668
}
@@ -709,12 +707,9 @@ func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) b
707
// automationPolicyHasAllPublicNames returns true if all the names on the policy
708
// do NOT qualify for public certs OR are tailscale domains.
709
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
712
- for _, subj := range ap.SubjectsRaw {
713
- if !subjectQualifiesForPublicCert(ap, subj) || isTailscaleDomain(subj) {
714
- return false
715
- }
716
- }
717
- return true
710
+ return !slices.ContainsFunc(ap.SubjectsRaw, func(i string) bool {
711
+ return !subjectQualifiesForPublicCert(ap, i) || isTailscaleDomain(i)
712
+ })
713
}
714
715
func isTailscaleDomain(name string) bool {
modules/caddyevents/app.go
+13
-12
@@ -124,18 +124,19 @@ func (app *App) Provision(ctx caddy.Context) error {
124
app.subscriptions = make(map[string]map[caddy.ModuleID][]Handler)
125
126
for _, sub := range app.Subscriptions {
127
- if sub.HandlersRaw != nil {
128
- handlersIface, err := ctx.LoadModule(sub, "HandlersRaw")
129
- if err != nil {
130
- return fmt.Errorf("loading event subscriber modules: %v", err)
131
- }
132
- for _, h := range handlersIface.([]any) {
133
- sub.Handlers = append(sub.Handlers, h.(Handler))
134
- }
135
- if len(sub.Handlers) == 0 {
136
- // pointless to bind without any handlers
137
- return fmt.Errorf("no handlers defined")
138
- }
127
+ if sub.HandlersRaw == nil {
128
+ continue
129
+ }
130
+ handlersIface, err := ctx.LoadModule(sub, "HandlersRaw")
131
+ if err != nil {
132
+ return fmt.Errorf("loading event subscriber modules: %v", err)
133
+ }
134
+ for _, h := range handlersIface.([]any) {
135
+ sub.Handlers = append(sub.Handlers, h.(Handler))
136
+ }
137
+ if len(sub.Handlers) == 0 {
138
+ // pointless to bind without any handlers
139
+ return fmt.Errorf("no handlers defined")
140
}
141
}
142
modules/caddyhttp/autohttps.go
+3
-13
@@ -17,6 +17,7 @@ package caddyhttp
17
import (
18
"fmt"
19
"net/http"
20
+ "slices"
21
"strconv"
22
"strings"
23
@@ -66,17 +67,6 @@ type AutoHTTPSConfig struct {
67
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
68
}
69
69
-// Skipped returns true if name is in skipSlice, which
70
-// should be either the Skip or SkipCerts field on ahc.
71
-func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool {
72
- for _, n := range skipSlice {
73
- if name == n {
74
- return true
75
- }
76
- }
77
- return false
78
-}
79
-
70
// automaticHTTPSPhase1 provisions all route matchers, determines
71
// which domain names found in the routes qualify for automatic
72
// HTTPS, and sets up HTTP->HTTPS redirects. This phase must occur
@@ -158,7 +148,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
148
return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
149
srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
150
}
161
- if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.Skip) {
151
+ if !slices.Contains(srv.AutoHTTPS.Skip, d) {
152
serverDomainSet[d] = struct{}{}
153
}
154
}
@@ -193,7 +183,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
183
} else {
184
for d := range serverDomainSet {
185
if certmagic.SubjectQualifiesForCert(d) &&
196
- !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
186
+ !slices.Contains(srv.AutoHTTPS.SkipCerts, d) {
187
// if a certificate for this name is already loaded,
188
// don't obtain another one for it, unless we are
189
// supposed to ignore loaded certificates
modules/caddyhttp/encode/encode.go
+4
-6
@@ -24,6 +24,7 @@ import (
24
"io"
25
"math"
26
"net/http"
27
+ "slices"
28
"sort"
29
"strconv"
30
"strings"
@@ -441,12 +442,9 @@ func AcceptedEncodings(r *http.Request, preferredOrder []string) []string {
442
}
443
444
// set server preference
444
- prefOrder := -1
445
- for i, p := range preferredOrder {
446
- if encName == p {
447
- prefOrder = len(preferredOrder) - i
448
- break
449
- }
445
+ prefOrder := slices.Index(preferredOrder, encName)
446
+ if prefOrder > -1 {
447
+ prefOrder = len(preferredOrder) - prefOrder
448
}
449
450
prefs = append(prefs, encodingPreference{
modules/caddyhttp/fileserver/browsetplcontext.go
+4
-6
@@ -21,6 +21,7 @@ import (
21
"os"
22
"path"
23
"path/filepath"
24
+ "slices"
25
"sort"
26
"strconv"
27
"strings"
@@ -281,12 +282,9 @@ type fileInfo struct {
282
283
// HasExt returns true if the filename has any of the given suffixes, case-insensitive.
284
func (fi fileInfo) HasExt(exts ...string) bool {
284
- for _, ext := range exts {
285
- if strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext)) {
286
- return true
287
- }
288
- }
289
- return false
285
+ return slices.ContainsFunc(exts, func(ext string) bool {
286
+ return strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext))
287
+ })
288
}
289
290
// HumanSize returns the size of the file as a
modules/caddyhttp/headers/headers.go
+7
-6
@@ -135,13 +135,14 @@ type HeaderOps struct {
135
func (ops *HeaderOps) Provision(_ caddy.Context) error {
136
for fieldName, replacements := range ops.Replace {
137
for i, r := range replacements {
138
- if r.SearchRegexp != "" {
139
- re, err := regexp.Compile(r.SearchRegexp)
140
- if err != nil {
141
- return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
142
- }
143
- replacements[i].re = re
138
+ if r.SearchRegexp == "" {
139
+ continue
140
+ }
141
+ re, err := regexp.Compile(r.SearchRegexp)
142
+ if err != nil {
143
+ return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
144
}
145
+ replacements[i].re = re
146
}
147
}
148
return nil
modules/caddyhttp/map/map.go
+2
-12
@@ -18,6 +18,7 @@ import (
18
"fmt"
19
"net/http"
20
"regexp"
21
+ "slices"
22
"strings"
23
24
"github.com/caddyserver/caddy/v2"
@@ -126,7 +127,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
127
// defer work until a variable is actually evaluated by using replacer's Map callback
128
repl.Map(func(key string) (any, bool) {
129
// return early if the variable is not even a configured destination
129
- destIdx := h.destinationIndex(key)
130
+ destIdx := slices.Index(h.Destinations, key)
131
if destIdx < 0 {
132
return nil, false
133
}
@@ -170,17 +171,6 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
171
return next.ServeHTTP(w, r)
172
}
173
173
-// destinationIndex returns the positional index of the destination
174
-// is name is a known destination; otherwise it returns -1.
175
-func (h Handler) destinationIndex(name string) int {
176
- for i, dest := range h.Destinations {
177
- if dest == name {
178
- return i
179
- }
180
- }
181
- return -1
182
-}
183
-
174
// Mapping describes a mapping from input to outputs.
175
type Mapping struct {
176
// The input value to match. Must be distinct from other mappings.
modules/caddyhttp/matchers.go
+1
-6
@@ -764,12 +764,7 @@ func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
764
765
// Match returns true if r matches m.
766
func (m MatchMethod) Match(r *http.Request) bool {
767
- for _, method := range m {
768
- if r.Method == method {
769
- return true
770
- }
771
- }
772
- return false
767
+ return slices.Contains(m, r.Method)
768
}
769
770
// CELLibrary produces options that expose this matcher for use in CEL
modules/caddyhttp/reverseproxy/healthchecks.go
+3
-6
@@ -23,6 +23,7 @@ import (
23
"net/url"
24
"regexp"
25
"runtime/debug"
26
+ "slices"
27
"strconv"
28
"strings"
29
"time"
@@ -397,12 +398,8 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
398
u.Scheme = "https"
399
400
// if the port is in the except list, flip back to HTTP
400
- if ht, ok := h.Transport.(*HTTPTransport); ok {
401
- for _, exceptPort := range ht.TLS.ExceptPorts {
402
- if exceptPort == port {
403
- u.Scheme = "http"
404
- }
405
- }
401
+ if ht, ok := h.Transport.(*HTTPTransport); ok && slices.Contains(ht.TLS.ExceptPorts, port) {
402
+ u.Scheme = "http"
403
}
404
}
405
modules/caddyhttp/reverseproxy/httptransport.go
+4
-13
@@ -27,6 +27,7 @@ import (
27
"net/url"
28
"os"
29
"reflect"
30
+ "slices"
31
"strings"
32
"time"
33
@@ -381,7 +382,7 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
382
rt.DisableCompression = !*h.Compression
383
}
384
384
- if sliceContains(h.Versions, "2") {
385
+ if slices.Contains(h.Versions, "2") {
386
if err := http2.ConfigureTransport(rt); err != nil {
387
return nil, err
388
}
@@ -400,13 +401,13 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
401
return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err)
402
}
403
}
403
- } else if len(h.Versions) > 1 && sliceContains(h.Versions, "3") {
404
+ } else if len(h.Versions) > 1 && slices.Contains(h.Versions, "3") {
405
return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported")
406
}
407
408
// if h2c is enabled, configure its transport (std lib http.Transport
409
// does not "HTTP/2 over cleartext TCP")
409
- if sliceContains(h.Versions, "h2c") {
410
+ if slices.Contains(h.Versions, "h2c") {
411
// crafting our own http2.Transport doesn't allow us to utilize
412
// most of the customizations/preferences on the http.Transport,
413
// because, for some reason, only http2.ConfigureTransport()
@@ -783,16 +784,6 @@ func decodeBase64DERCert(certStr string) (*x509.Certificate, error) {
784
return x509.ParseCertificate(derBytes)
785
}
786
786
-// sliceContains returns true if needle is in haystack.
787
-func sliceContains(haystack []string, needle string) bool {
788
- for _, s := range haystack {
789
- if s == needle {
790
- return true
791
- }
792
- }
793
- return false
794
-}
795
-
787
// Interface guards
788
var (
789
_ caddy.Provisioner = (*HTTPTransport)(nil)
modules/caddyhttp/server.go
+8
-18
@@ -25,6 +25,7 @@ import (
25
"net/netip"
26
"net/url"
27
"runtime"
28
+ "slices"
29
"strings"
30
"sync"
31
"time"
@@ -543,12 +544,9 @@ func (s *Server) hasListenerAddress(fullAddr string) bool {
544
}
545
546
func (s *Server) hasTLSClientAuth() bool {
546
- for _, cp := range s.TLSConnPolicies {
547
- if cp.ClientAuthentication != nil && cp.ClientAuthentication.Active() {
548
- return true
549
- }
550
- }
551
- return false
547
+ return slices.ContainsFunc(s.TLSConnPolicies, func(cp *caddytls.ConnectionPolicy) bool {
548
+ return cp.ClientAuthentication != nil && cp.ClientAuthentication.Active()
549
+ })
550
}
551
552
// findLastRouteWithHostMatcher returns the index of the last route
@@ -849,12 +847,7 @@ func (s *Server) logRequest(
847
848
// protocol returns true if the protocol proto is configured/enabled.
849
func (s *Server) protocol(proto string) bool {
852
- for _, p := range s.Protocols {
853
- if p == proto {
854
- return true
855
- }
856
- }
857
- return false
850
+ return slices.Contains(s.Protocols, proto)
851
}
852
853
// Listeners returns the server's listeners. These are active listeners,
@@ -959,12 +952,9 @@ func determineTrustedProxy(r *http.Request, s *Server) (bool, string) {
952
// isTrustedClientIP returns true if the given IP address is
953
// in the list of trusted IP ranges.
954
func isTrustedClientIP(ipAddr netip.Addr, trusted []netip.Prefix) bool {
962
- for _, ipRange := range trusted {
963
- if ipRange.Contains(ipAddr) {
964
- return true
965
- }
966
- }
967
- return false
955
+ return slices.ContainsFunc(trusted, func(prefix netip.Prefix) bool {
956
+ return prefix.Contains(ipAddr)
957
+ })
958
}
959
960
// trustedRealClientIP finds the client IP from the request assuming it is
modules/caddyhttp/templates/frontmatter.go
+1
@@ -40,6 +40,7 @@ func extractFrontMatter(input string) (map[string]any, string, error) {
40
if firstLine == fmType.FenceOpen {
41
closingFence = fmType.FenceClose
42
fmParser = fmType.ParseFunc
43
+ break
44
}
45
}
46
modules/caddytls/automation.go
+4
-6
@@ -21,6 +21,7 @@ import (
21
"errors"
22
"fmt"
23
"net"
24
+ "slices"
25
"strings"
26
27
"github.com/caddyserver/certmagic"
@@ -373,12 +374,9 @@ func (ap *AutomationPolicy) Subjects() []string {
374
375
// AllInternalSubjects returns true if all the subjects on this policy are internal.
376
func (ap *AutomationPolicy) AllInternalSubjects() bool {
376
- for _, subj := range ap.subjects {
377
- if !certmagic.SubjectIsInternal(subj) {
378
- return false
379
- }
380
- }
381
- return true
377
+ return !slices.ContainsFunc(ap.subjects, func(s string) bool {
378
+ return !certmagic.SubjectIsInternal(s)
379
+ })
380
}
381
382
func (ap *AutomationPolicy) onlyInternalIssuer() bool {
modules/caddytls/certselection.go
+4
-9
@@ -20,6 +20,7 @@ import (
20
"encoding/json"
21
"fmt"
22
"math/big"
23
+ "slices"
24
25
"github.com/caddyserver/certmagic"
26
@@ -72,15 +73,9 @@ nextChoice:
73
}
74
75
if len(p.SubjectOrganization) > 0 {
75
- var found bool
76
- for _, subjOrg := range p.SubjectOrganization {
77
- for _, org := range cert.Leaf.Subject.Organization {
78
- if subjOrg == org {
79
- found = true
80
- break
81
- }
82
- }
83
- }
76
+ found := slices.ContainsFunc(p.SubjectOrganization, func(s string) bool {
77
+ return slices.Contains(cert.Leaf.Subject.Organization, s)
78
+ })
79
if !found {
80
continue
81
}
modules/caddytls/matchers.go
+7
-12
@@ -20,6 +20,7 @@ import (
20
"net"
21
"net/netip"
22
"regexp"
23
+ "slices"
24
"strconv"
25
"strings"
26
@@ -321,12 +322,9 @@ func (MatchRemoteIP) parseIPRange(str string) ([]netip.Prefix, error) {
322
}
323
324
func (MatchRemoteIP) matches(ip netip.Addr, ranges []netip.Prefix) bool {
324
- for _, ipRange := range ranges {
325
- if ipRange.Contains(ip) {
326
- return true
327
- }
328
- }
329
- return false
325
+ return slices.ContainsFunc(ranges, func(prefix netip.Prefix) bool {
326
+ return prefix.Contains(ip)
327
+ })
328
}
329
330
// UnmarshalCaddyfile sets up the MatchRemoteIP from Caddyfile tokens. Syntax:
@@ -439,12 +437,9 @@ func (MatchLocalIP) parseIPRange(str string) ([]netip.Prefix, error) {
437
}
438
439
func (MatchLocalIP) matches(ip netip.Addr, ranges []netip.Prefix) bool {
442
- for _, ipRange := range ranges {
443
- if ipRange.Contains(ip) {
444
- return true
445
- }
446
- }
447
- return false
440
+ return slices.ContainsFunc(ranges, func(prefix netip.Prefix) bool {
441
+ return prefix.Contains(ip)
442
+ })
443
}
444
445
// UnmarshalCaddyfile sets up the MatchLocalIP from Caddyfile tokens. Syntax: