@marcel91200 / caddy Mirror · read-only / commits / 2faeac0a
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 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: