chore: Use slices package where possible (#6585)

* chore: Use slices package where possible

* More, mostly using ContainsFunc

* Even more slice operations
This commit is contained in:
Francis Lavoie 2024-09-25 16:30:56 -04:00 committed by GitHub
parent 9dda8fbf84
commit 2faeac0a10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 142 additions and 268 deletions

View file

@ -34,6 +34,7 @@ import (
"os" "os"
"path" "path"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -675,13 +676,7 @@ func (remote RemoteAdmin) enforceAccessControls(r *http.Request) error {
// key recognized; make sure its HTTP request is permitted // key recognized; make sure its HTTP request is permitted
for _, accessPerm := range adminAccess.Permissions { for _, accessPerm := range adminAccess.Permissions {
// verify method // verify method
methodFound := accessPerm.Methods == nil methodFound := accessPerm.Methods == nil || slices.Contains(accessPerm.Methods, r.Method)
for _, method := range accessPerm.Methods {
if method == r.Method {
methodFound = true
break
}
}
if !methodFound { if !methodFound {
return APIError{ return APIError{
HTTPStatus: http.StatusForbidden, HTTPStatus: http.StatusForbidden,
@ -877,13 +872,9 @@ func (h adminHandler) handleError(w http.ResponseWriter, r *http.Request, err er
// a trustworthy/expected value. This helps to mitigate DNS // a trustworthy/expected value. This helps to mitigate DNS
// rebinding attacks. // rebinding attacks.
func (h adminHandler) checkHost(r *http.Request) error { func (h adminHandler) checkHost(r *http.Request) error {
var allowed bool allowed := slices.ContainsFunc(h.allowedOrigins, func(u *url.URL) bool {
for _, allowedOrigin := range h.allowedOrigins { return r.Host == u.Host
if r.Host == allowedOrigin.Host { })
allowed = true
break
}
}
if !allowed { if !allowed {
return APIError{ return APIError{
HTTPStatus: http.StatusForbidden, HTTPStatus: http.StatusForbidden,

View file

@ -16,6 +16,7 @@ package caddyfile
import ( import (
"fmt" "fmt"
"slices"
) )
type adjacency map[string][]string type adjacency map[string][]string
@ -91,12 +92,7 @@ func (i *importGraph) areConnected(from, to string) bool {
if !ok { if !ok {
return false return false
} }
for _, v := range al { return slices.Contains(al, to)
if v == to {
return true
}
}
return false
} }
func (i *importGraph) willCycle(from, to string) bool { func (i *importGraph) willCycle(from, to string) bool {

View file

@ -17,6 +17,7 @@ package httpcaddyfile
import ( import (
"encoding/json" "encoding/json"
"net" "net"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -100,17 +101,6 @@ var defaultDirectiveOrder = []string{
// plugins or by the user via the "order" global option. // plugins or by the user via the "order" global option.
var directiveOrder = defaultDirectiveOrder var directiveOrder = defaultDirectiveOrder
// directiveIsOrdered returns true if dir is
// a known, ordered (sorted) directive.
func directiveIsOrdered(dir string) bool {
for _, d := range directiveOrder {
if d == dir {
return true
}
}
return false
}
// RegisterDirective registers a unique directive dir with an // RegisterDirective registers a unique directive dir with an
// associated unmarshaling (setup) function. When directive dir // associated unmarshaling (setup) function. When directive dir
// is encountered in a Caddyfile, setupFunc will be called to // is encountered in a Caddyfile, setupFunc will be called to
@ -161,7 +151,7 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
// EXPERIMENTAL: This API may change or be removed. // EXPERIMENTAL: This API may change or be removed.
func RegisterDirectiveOrder(dir string, position Positional, standardDir string) { func RegisterDirectiveOrder(dir string, position Positional, standardDir string) {
// check if directive was already ordered // check if directive was already ordered
if directiveIsOrdered(dir) { if slices.Contains(directiveOrder, dir) {
panic("directive '" + dir + "' already ordered") panic("directive '" + dir + "' already ordered")
} }
@ -172,12 +162,7 @@ func RegisterDirectiveOrder(dir string, position Positional, standardDir string)
// check if directive exists in standard distribution, since // check if directive exists in standard distribution, since
// we can't allow plugins to depend on one another; we can't // we can't allow plugins to depend on one another; we can't
// guarantee the order that plugins are loaded in. // guarantee the order that plugins are loaded in.
foundStandardDir := false foundStandardDir := slices.Contains(defaultDirectiveOrder, standardDir)
for _, d := range defaultDirectiveOrder {
if d == standardDir {
foundStandardDir = true
}
}
if !foundStandardDir { if !foundStandardDir {
panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy") panic("the 3rd argument '" + standardDir + "' must be a directive that exists in the standard distribution of Caddy")
} }
@ -603,23 +588,17 @@ func (sb serverBlock) hostsFromKeysNotHTTP(httpPort string) []string {
// hasHostCatchAllKey returns true if sb has a key that // hasHostCatchAllKey returns true if sb has a key that
// omits a host portion, i.e. it "catches all" hosts. // omits a host portion, i.e. it "catches all" hosts.
func (sb serverBlock) hasHostCatchAllKey() bool { func (sb serverBlock) hasHostCatchAllKey() bool {
for _, addr := range sb.keys { return slices.ContainsFunc(sb.keys, func(addr Address) bool {
if addr.Host == "" { return addr.Host == ""
return true })
}
}
return false
} }
// isAllHTTP returns true if all sb keys explicitly specify // isAllHTTP returns true if all sb keys explicitly specify
// the http:// scheme // the http:// scheme
func (sb serverBlock) isAllHTTP() bool { func (sb serverBlock) isAllHTTP() bool {
for _, addr := range sb.keys { return !slices.ContainsFunc(sb.keys, func(addr Address) bool {
if addr.Scheme != "http" { return addr.Scheme != "http"
return false })
}
}
return true
} }
// Positional are the supported modes for ordering directives. // Positional are the supported modes for ordering directives.

View file

@ -536,7 +536,7 @@ func (st *ServerType) serversFromPairings(
if k == j { if k == j {
continue continue
} }
if sliceContains(sblock2.block.GetKeysText(), key) { if slices.Contains(sblock2.block.GetKeysText(), key) {
return nil, fmt.Errorf("ambiguous site definition: %s", key) return nil, fmt.Errorf("ambiguous site definition: %s", key)
} }
} }
@ -720,7 +720,7 @@ func (st *ServerType) serversFromPairings(
if srv.AutoHTTPS == nil { if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig) srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
} }
if !sliceContains(srv.AutoHTTPS.Skip, addr.Host) { if !slices.Contains(srv.AutoHTTPS.Skip, addr.Host) {
srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host) srv.AutoHTTPS.Skip = append(srv.AutoHTTPS.Skip, addr.Host)
} }
} }
@ -734,7 +734,7 @@ func (st *ServerType) serversFromPairings(
// https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761 // https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761
createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"] createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"]
hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) || hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) ||
(addr.Host != "" && srv.AutoHTTPS != nil && !sliceContains(srv.AutoHTTPS.Skip, addr.Host)) (addr.Host != "" && srv.AutoHTTPS != nil && !slices.Contains(srv.AutoHTTPS.Skip, addr.Host))
// we'll need to remember if the address qualifies for auto-HTTPS, so we // we'll need to remember if the address qualifies for auto-HTTPS, so we
// can add a TLS conn policy if necessary // can add a TLS conn policy if necessary
@ -1061,7 +1061,7 @@ func consolidateConnPolicies(cps caddytls.ConnectionPolicies) (caddytls.Connecti
} else if cps[i].CertSelection != nil && cps[j].CertSelection != nil { } else if cps[i].CertSelection != nil && cps[j].CertSelection != nil {
// if both have one, then combine AnyTag // if both have one, then combine AnyTag
for _, tag := range cps[j].CertSelection.AnyTag { for _, tag := range cps[j].CertSelection.AnyTag {
if !sliceContains(cps[i].CertSelection.AnyTag, tag) { if !slices.Contains(cps[i].CertSelection.AnyTag, tag) {
cps[i].CertSelection.AnyTag = append(cps[i].CertSelection.AnyTag, tag) cps[i].CertSelection.AnyTag = append(cps[i].CertSelection.AnyTag, tag)
} }
} }
@ -1144,7 +1144,7 @@ func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) { func buildSubroute(routes []ConfigValue, groupCounter counter, needsSorting bool) (*caddyhttp.Subroute, error) {
if needsSorting { if needsSorting {
for _, val := range routes { for _, val := range routes {
if !directiveIsOrdered(val.directive) { if !slices.Contains(directiveOrder, val.directive) {
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) 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)
} }
} }
@ -1354,17 +1354,8 @@ func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.Mod
// add this server block's keys to the matcher // add this server block's keys to the matcher
// pair if it doesn't already exist // pair if it doesn't already exist
if addr.Host != "" { if addr.Host != "" && !slices.Contains(chosenMatcherPair.hostm, addr.Host) {
var found bool chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
for _, h := range chosenMatcherPair.hostm {
if h == addr.Host {
found = true
break
}
}
if !found {
chosenMatcherPair.hostm = append(chosenMatcherPair.hostm, addr.Host)
}
} }
} }
@ -1540,16 +1531,6 @@ func tryDuration(val any, warnings *[]caddyconfig.Warning) caddy.Duration {
return durationVal return durationVal
} }
// sliceContains returns true if needle is in haystack.
func sliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
// listenersUseAnyPortOtherThan returns true if there are any // listenersUseAnyPortOtherThan returns true if there are any
// listeners in addresses that use a port which is not otherPort. // listeners in addresses that use a port which is not otherPort.
// Mostly borrowed from unexported method in caddyhttp package. // Mostly borrowed from unexported method in caddyhttp package.

View file

@ -15,6 +15,7 @@
package httpcaddyfile package httpcaddyfile
import ( import (
"slices"
"strconv" "strconv"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
@ -110,17 +111,12 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
pos := Positional(d.Val()) pos := Positional(d.Val())
newOrder := directiveOrder // if directive already had an order, drop it
newOrder := slices.DeleteFunc(directiveOrder, func(d string) bool {
return d == dirName
})
// if directive exists, first remove it // act on the positional; if it's First or Last, we're done right away
for i, d := range newOrder {
if d == dirName {
newOrder = append(newOrder[:i], newOrder[i+1:]...)
break
}
}
// act on the positional
switch pos { switch pos {
case First: case First:
newOrder = append([]string{dirName}, newOrder...) newOrder = append([]string{dirName}, newOrder...)
@ -129,6 +125,7 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
directiveOrder = newOrder directiveOrder = newOrder
return newOrder, nil return newOrder, nil
case Last: case Last:
newOrder = append(newOrder, dirName) newOrder = append(newOrder, dirName)
if d.NextArg() { if d.NextArg() {
@ -136,8 +133,11 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
} }
directiveOrder = newOrder directiveOrder = newOrder
return newOrder, nil return newOrder, nil
// if it's Before or After, continue
case Before: case Before:
case After: case After:
default: default:
return nil, d.Errf("unknown positional '%s'", pos) return nil, d.Errf("unknown positional '%s'", pos)
} }
@ -151,17 +151,17 @@ func parseOptOrder(d *caddyfile.Dispenser, _ any) (any, error) {
return nil, d.ArgErr() return nil, d.ArgErr()
} }
// insert directive into proper position // get the position of the target directive
for i, d := range newOrder { targetIndex := slices.Index(newOrder, otherDir)
if d == otherDir { if targetIndex == -1 {
if pos == Before { return nil, d.Errf("directive '%s' not found", otherDir)
newOrder = append(newOrder[:i], append([]string{dirName}, newOrder[i:]...)...)
} else if pos == After {
newOrder = append(newOrder[:i+1], append([]string{dirName}, newOrder[i+1:]...)...)
}
break
}
} }
// if we're inserting after, we need to increment the index to go after
if pos == After {
targetIndex++
}
// insert the directive into the new order
newOrder = slices.Insert(newOrder, targetIndex, dirName)
directiveOrder = newOrder directiveOrder = newOrder

View file

@ -17,6 +17,7 @@ package httpcaddyfile
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
@ -180,7 +181,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" { if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto) return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
} }
if sliceContains(serverOpts.Protocols, proto) { if slices.Contains(serverOpts.Protocols, proto) {
return nil, d.Errf("protocol %s specified more than once", proto) return nil, d.Errf("protocol %s specified more than once", proto)
} }
serverOpts.Protocols = append(serverOpts.Protocols, proto) serverOpts.Protocols = append(serverOpts.Protocols, proto)
@ -229,7 +230,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
case "client_ip_headers": case "client_ip_headers":
headers := d.RemainingArgs() headers := d.RemainingArgs()
for _, header := range headers { for _, header := range headers {
if sliceContains(serverOpts.ClientIPHeaders, header) { if slices.Contains(serverOpts.ClientIPHeaders, header) {
return nil, d.Errf("client IP header %s specified more than once", header) return nil, d.Errf("client IP header %s specified more than once", header)
} }
serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header) serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
@ -288,24 +289,15 @@ func applyServerOptions(
for key, server := range servers { for key, server := range servers {
// find the options that apply to this server // find the options that apply to this server
opts := func() *serverOptions { optsIndex := slices.IndexFunc(serverOpts, func(s serverOptions) bool {
for _, entry := range serverOpts { return s.ListenerAddress == "" || slices.Contains(server.Listen, s.ListenerAddress)
if entry.ListenerAddress == "" { })
return &entry
}
for _, listener := range server.Listen {
if entry.ListenerAddress == listener {
return &entry
}
}
}
return nil
}()
// if none apply, then move to the next server // if none apply, then move to the next server
if opts == nil { if optsIndex == -1 {
continue continue
} }
opts := serverOpts[optsIndex]
// set all the options // set all the options
server.ListenerWrappersRaw = opts.ListenerWrappersRaw server.ListenerWrappersRaw = opts.ListenerWrappersRaw

View file

@ -19,6 +19,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -57,19 +58,20 @@ func (st ServerType) buildTLSApp(
for _, pair := range pairings { for _, pair := range pairings {
for _, sb := range pair.serverBlocks { for _, sb := range pair.serverBlocks {
for _, addr := range sb.keys { for _, addr := range sb.keys {
if addr.Host == "" { if addr.Host != "" {
// this server block has a hostless key, now continue
// go through and add all the hosts to the set
for _, otherAddr := range sb.keys {
if otherAddr.Original == addr.Original {
continue
}
if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
}
}
break
} }
// this server block has a hostless key, now
// go through and add all the hosts to the set
for _, otherAddr := range sb.keys {
if otherAddr.Original == addr.Original {
continue
}
if otherAddr.Host != "" && otherAddr.Scheme != "http" && otherAddr.Port != httpPort {
httpsHostsSharedWithHostlessKey[otherAddr.Host] = struct{}{}
}
}
break
} }
} }
} }
@ -465,7 +467,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalACMECA != nil && acmeIssuer.CA == "" { if globalACMECA != nil && acmeIssuer.CA == "" {
acmeIssuer.CA = globalACMECA.(string) acmeIssuer.CA = globalACMECA.(string)
} }
if globalACMECARoot != nil && !sliceContains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) { if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
} }
if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) { if globalACMEDNS != nil && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) {
@ -580,7 +582,7 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
if !automationPolicyHasAllPublicNames(aps[i]) { if !automationPolicyHasAllPublicNames(aps[i]) {
// if this automation policy has internal names, we might as well remove it // if this automation policy has internal names, we might as well remove it
// so auto-https can implicitly use the internal issuer // so auto-https can implicitly use the internal issuer
aps = append(aps[:i], aps[i+1:]...) aps = slices.Delete(aps, i, i+1)
i-- i--
} }
} }
@ -597,7 +599,7 @@ outer:
for j := i + 1; j < len(aps); j++ { for j := i + 1; j < len(aps); j++ {
// if they're exactly equal in every way, just keep one of them // if they're exactly equal in every way, just keep one of them
if reflect.DeepEqual(aps[i], aps[j]) { if reflect.DeepEqual(aps[i], aps[j]) {
aps = append(aps[:j], aps[j+1:]...) aps = slices.Delete(aps, j, j+1)
// must re-evaluate current i against next j; can't skip it! // must re-evaluate current i against next j; can't skip it!
// even if i decrements to -1, will be incremented to 0 immediately // even if i decrements to -1, will be incremented to 0 immediately
i-- i--
@ -627,18 +629,18 @@ outer:
// cause example.com to be served by the less specific policy for // cause example.com to be served by the less specific policy for
// '*.com', which might be different (yes we've seen this happen) // '*.com', which might be different (yes we've seen this happen)
if automationPolicyShadows(i, aps) >= j { if automationPolicyShadows(i, aps) >= j {
aps = append(aps[:i], aps[i+1:]...) aps = slices.Delete(aps, i, i+1)
i-- i--
continue outer continue outer
} }
} else { } else {
// avoid repeated subjects // avoid repeated subjects
for _, subj := range aps[j].SubjectsRaw { for _, subj := range aps[j].SubjectsRaw {
if !sliceContains(aps[i].SubjectsRaw, subj) { if !slices.Contains(aps[i].SubjectsRaw, subj) {
aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj) aps[i].SubjectsRaw = append(aps[i].SubjectsRaw, subj)
} }
} }
aps = append(aps[:j], aps[j+1:]...) aps = slices.Delete(aps, j, j+1)
j-- j--
} }
} }
@ -658,13 +660,9 @@ func automationPolicyIsSubset(a, b *caddytls.AutomationPolicy) bool {
return false return false
} }
for _, aSubj := range a.SubjectsRaw { for _, aSubj := range a.SubjectsRaw {
var inSuperset bool inSuperset := slices.ContainsFunc(b.SubjectsRaw, func(bSubj string) bool {
for _, bSubj := range b.SubjectsRaw { return certmagic.MatchWildcard(aSubj, bSubj)
if certmagic.MatchWildcard(aSubj, bSubj) { })
inSuperset = true
break
}
}
if !inSuperset { if !inSuperset {
return false return false
} }
@ -709,12 +707,9 @@ func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) b
// automationPolicyHasAllPublicNames returns true if all the names on the policy // automationPolicyHasAllPublicNames returns true if all the names on the policy
// do NOT qualify for public certs OR are tailscale domains. // do NOT qualify for public certs OR are tailscale domains.
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool { func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
for _, subj := range ap.SubjectsRaw { return !slices.ContainsFunc(ap.SubjectsRaw, func(i string) bool {
if !subjectQualifiesForPublicCert(ap, subj) || isTailscaleDomain(subj) { return !subjectQualifiesForPublicCert(ap, i) || isTailscaleDomain(i)
return false })
}
}
return true
} }
func isTailscaleDomain(name string) bool { func isTailscaleDomain(name string) bool {

View file

@ -124,18 +124,19 @@ func (app *App) Provision(ctx caddy.Context) error {
app.subscriptions = make(map[string]map[caddy.ModuleID][]Handler) app.subscriptions = make(map[string]map[caddy.ModuleID][]Handler)
for _, sub := range app.Subscriptions { for _, sub := range app.Subscriptions {
if sub.HandlersRaw != nil { if sub.HandlersRaw == nil {
handlersIface, err := ctx.LoadModule(sub, "HandlersRaw") continue
if err != nil { }
return fmt.Errorf("loading event subscriber modules: %v", err) handlersIface, err := ctx.LoadModule(sub, "HandlersRaw")
} if err != nil {
for _, h := range handlersIface.([]any) { return fmt.Errorf("loading event subscriber modules: %v", err)
sub.Handlers = append(sub.Handlers, h.(Handler)) }
} for _, h := range handlersIface.([]any) {
if len(sub.Handlers) == 0 { sub.Handlers = append(sub.Handlers, h.(Handler))
// pointless to bind without any handlers }
return fmt.Errorf("no handlers defined") if len(sub.Handlers) == 0 {
} // pointless to bind without any handlers
return fmt.Errorf("no handlers defined")
} }
} }

View file

@ -17,6 +17,7 @@ package caddyhttp
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
@ -66,17 +67,6 @@ type AutoHTTPSConfig struct {
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"` IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
} }
// Skipped returns true if name is in skipSlice, which
// should be either the Skip or SkipCerts field on ahc.
func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool {
for _, n := range skipSlice {
if name == n {
return true
}
}
return false
}
// automaticHTTPSPhase1 provisions all route matchers, determines // automaticHTTPSPhase1 provisions all route matchers, determines
// which domain names found in the routes qualify for automatic // which domain names found in the routes qualify for automatic
// HTTPS, and sets up HTTP->HTTPS redirects. This phase must occur // 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
return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v", return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err) srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
} }
if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.Skip) { if !slices.Contains(srv.AutoHTTPS.Skip, d) {
serverDomainSet[d] = struct{}{} serverDomainSet[d] = struct{}{}
} }
} }
@ -193,7 +183,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
} else { } else {
for d := range serverDomainSet { for d := range serverDomainSet {
if certmagic.SubjectQualifiesForCert(d) && if certmagic.SubjectQualifiesForCert(d) &&
!srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) { !slices.Contains(srv.AutoHTTPS.SkipCerts, d) {
// if a certificate for this name is already loaded, // if a certificate for this name is already loaded,
// don't obtain another one for it, unless we are // don't obtain another one for it, unless we are
// supposed to ignore loaded certificates // supposed to ignore loaded certificates

View file

@ -24,6 +24,7 @@ import (
"io" "io"
"math" "math"
"net/http" "net/http"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -441,12 +442,9 @@ func AcceptedEncodings(r *http.Request, preferredOrder []string) []string {
} }
// set server preference // set server preference
prefOrder := -1 prefOrder := slices.Index(preferredOrder, encName)
for i, p := range preferredOrder { if prefOrder > -1 {
if encName == p { prefOrder = len(preferredOrder) - prefOrder
prefOrder = len(preferredOrder) - i
break
}
} }
prefs = append(prefs, encodingPreference{ prefs = append(prefs, encodingPreference{

View file

@ -21,6 +21,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -281,12 +282,9 @@ type fileInfo struct {
// HasExt returns true if the filename has any of the given suffixes, case-insensitive. // HasExt returns true if the filename has any of the given suffixes, case-insensitive.
func (fi fileInfo) HasExt(exts ...string) bool { func (fi fileInfo) HasExt(exts ...string) bool {
for _, ext := range exts { return slices.ContainsFunc(exts, func(ext string) bool {
if strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext)) { return strings.HasSuffix(strings.ToLower(fi.Name), strings.ToLower(ext))
return true })
}
}
return false
} }
// HumanSize returns the size of the file as a // HumanSize returns the size of the file as a

View file

@ -135,13 +135,14 @@ type HeaderOps struct {
func (ops *HeaderOps) Provision(_ caddy.Context) error { func (ops *HeaderOps) Provision(_ caddy.Context) error {
for fieldName, replacements := range ops.Replace { for fieldName, replacements := range ops.Replace {
for i, r := range replacements { for i, r := range replacements {
if r.SearchRegexp != "" { if r.SearchRegexp == "" {
re, err := regexp.Compile(r.SearchRegexp) continue
if err != nil {
return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
}
replacements[i].re = re
} }
re, err := regexp.Compile(r.SearchRegexp)
if err != nil {
return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
}
replacements[i].re = re
} }
} }
return nil return nil

View file

@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"regexp" "regexp"
"slices"
"strings" "strings"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
@ -126,7 +127,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
// defer work until a variable is actually evaluated by using replacer's Map callback // defer work until a variable is actually evaluated by using replacer's Map callback
repl.Map(func(key string) (any, bool) { repl.Map(func(key string) (any, bool) {
// return early if the variable is not even a configured destination // return early if the variable is not even a configured destination
destIdx := h.destinationIndex(key) destIdx := slices.Index(h.Destinations, key)
if destIdx < 0 { if destIdx < 0 {
return nil, false return nil, false
} }
@ -170,17 +171,6 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhtt
return next.ServeHTTP(w, r) return next.ServeHTTP(w, r)
} }
// destinationIndex returns the positional index of the destination
// is name is a known destination; otherwise it returns -1.
func (h Handler) destinationIndex(name string) int {
for i, dest := range h.Destinations {
if dest == name {
return i
}
}
return -1
}
// Mapping describes a mapping from input to outputs. // Mapping describes a mapping from input to outputs.
type Mapping struct { type Mapping struct {
// The input value to match. Must be distinct from other mappings. // The input value to match. Must be distinct from other mappings.

View file

@ -764,12 +764,7 @@ func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
// Match returns true if r matches m. // Match returns true if r matches m.
func (m MatchMethod) Match(r *http.Request) bool { func (m MatchMethod) Match(r *http.Request) bool {
for _, method := range m { return slices.Contains(m, r.Method)
if r.Method == method {
return true
}
}
return false
} }
// CELLibrary produces options that expose this matcher for use in CEL // CELLibrary produces options that expose this matcher for use in CEL

View file

@ -23,6 +23,7 @@ import (
"net/url" "net/url"
"regexp" "regexp"
"runtime/debug" "runtime/debug"
"slices"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -397,12 +398,8 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
u.Scheme = "https" u.Scheme = "https"
// if the port is in the except list, flip back to HTTP // if the port is in the except list, flip back to HTTP
if ht, ok := h.Transport.(*HTTPTransport); ok { if ht, ok := h.Transport.(*HTTPTransport); ok && slices.Contains(ht.TLS.ExceptPorts, port) {
for _, exceptPort := range ht.TLS.ExceptPorts { u.Scheme = "http"
if exceptPort == port {
u.Scheme = "http"
}
}
} }
} }

View file

@ -27,6 +27,7 @@ import (
"net/url" "net/url"
"os" "os"
"reflect" "reflect"
"slices"
"strings" "strings"
"time" "time"
@ -381,7 +382,7 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
rt.DisableCompression = !*h.Compression rt.DisableCompression = !*h.Compression
} }
if sliceContains(h.Versions, "2") { if slices.Contains(h.Versions, "2") {
if err := http2.ConfigureTransport(rt); err != nil { if err := http2.ConfigureTransport(rt); err != nil {
return nil, err return nil, err
} }
@ -400,13 +401,13 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err) return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err)
} }
} }
} else if len(h.Versions) > 1 && sliceContains(h.Versions, "3") { } else if len(h.Versions) > 1 && slices.Contains(h.Versions, "3") {
return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported") return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported")
} }
// if h2c is enabled, configure its transport (std lib http.Transport // if h2c is enabled, configure its transport (std lib http.Transport
// does not "HTTP/2 over cleartext TCP") // does not "HTTP/2 over cleartext TCP")
if sliceContains(h.Versions, "h2c") { if slices.Contains(h.Versions, "h2c") {
// crafting our own http2.Transport doesn't allow us to utilize // crafting our own http2.Transport doesn't allow us to utilize
// most of the customizations/preferences on the http.Transport, // most of the customizations/preferences on the http.Transport,
// because, for some reason, only http2.ConfigureTransport() // because, for some reason, only http2.ConfigureTransport()
@ -783,16 +784,6 @@ func decodeBase64DERCert(certStr string) (*x509.Certificate, error) {
return x509.ParseCertificate(derBytes) return x509.ParseCertificate(derBytes)
} }
// sliceContains returns true if needle is in haystack.
func sliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
// Interface guards // Interface guards
var ( var (
_ caddy.Provisioner = (*HTTPTransport)(nil) _ caddy.Provisioner = (*HTTPTransport)(nil)

View file

@ -25,6 +25,7 @@ import (
"net/netip" "net/netip"
"net/url" "net/url"
"runtime" "runtime"
"slices"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -543,12 +544,9 @@ func (s *Server) hasListenerAddress(fullAddr string) bool {
} }
func (s *Server) hasTLSClientAuth() bool { func (s *Server) hasTLSClientAuth() bool {
for _, cp := range s.TLSConnPolicies { return slices.ContainsFunc(s.TLSConnPolicies, func(cp *caddytls.ConnectionPolicy) bool {
if cp.ClientAuthentication != nil && cp.ClientAuthentication.Active() { return cp.ClientAuthentication != nil && cp.ClientAuthentication.Active()
return true })
}
}
return false
} }
// findLastRouteWithHostMatcher returns the index of the last route // findLastRouteWithHostMatcher returns the index of the last route
@ -849,12 +847,7 @@ func (s *Server) logRequest(
// protocol returns true if the protocol proto is configured/enabled. // protocol returns true if the protocol proto is configured/enabled.
func (s *Server) protocol(proto string) bool { func (s *Server) protocol(proto string) bool {
for _, p := range s.Protocols { return slices.Contains(s.Protocols, proto)
if p == proto {
return true
}
}
return false
} }
// Listeners returns the server's listeners. These are active listeners, // Listeners returns the server's listeners. These are active listeners,
@ -959,12 +952,9 @@ func determineTrustedProxy(r *http.Request, s *Server) (bool, string) {
// isTrustedClientIP returns true if the given IP address is // isTrustedClientIP returns true if the given IP address is
// in the list of trusted IP ranges. // in the list of trusted IP ranges.
func isTrustedClientIP(ipAddr netip.Addr, trusted []netip.Prefix) bool { func isTrustedClientIP(ipAddr netip.Addr, trusted []netip.Prefix) bool {
for _, ipRange := range trusted { return slices.ContainsFunc(trusted, func(prefix netip.Prefix) bool {
if ipRange.Contains(ipAddr) { return prefix.Contains(ipAddr)
return true })
}
}
return false
} }
// trustedRealClientIP finds the client IP from the request assuming it is // trustedRealClientIP finds the client IP from the request assuming it is

View file

@ -40,6 +40,7 @@ func extractFrontMatter(input string) (map[string]any, string, error) {
if firstLine == fmType.FenceOpen { if firstLine == fmType.FenceOpen {
closingFence = fmType.FenceClose closingFence = fmType.FenceClose
fmParser = fmType.ParseFunc fmParser = fmType.ParseFunc
break
} }
} }

View file

@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"slices"
"strings" "strings"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
@ -373,12 +374,9 @@ func (ap *AutomationPolicy) Subjects() []string {
// AllInternalSubjects returns true if all the subjects on this policy are internal. // AllInternalSubjects returns true if all the subjects on this policy are internal.
func (ap *AutomationPolicy) AllInternalSubjects() bool { func (ap *AutomationPolicy) AllInternalSubjects() bool {
for _, subj := range ap.subjects { return !slices.ContainsFunc(ap.subjects, func(s string) bool {
if !certmagic.SubjectIsInternal(subj) { return !certmagic.SubjectIsInternal(s)
return false })
}
}
return true
} }
func (ap *AutomationPolicy) onlyInternalIssuer() bool { func (ap *AutomationPolicy) onlyInternalIssuer() bool {

View file

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big" "math/big"
"slices"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
@ -72,15 +73,9 @@ nextChoice:
} }
if len(p.SubjectOrganization) > 0 { if len(p.SubjectOrganization) > 0 {
var found bool found := slices.ContainsFunc(p.SubjectOrganization, func(s string) bool {
for _, subjOrg := range p.SubjectOrganization { return slices.Contains(cert.Leaf.Subject.Organization, s)
for _, org := range cert.Leaf.Subject.Organization { })
if subjOrg == org {
found = true
break
}
}
}
if !found { if !found {
continue continue
} }

View file

@ -20,6 +20,7 @@ import (
"net" "net"
"net/netip" "net/netip"
"regexp" "regexp"
"slices"
"strconv" "strconv"
"strings" "strings"
@ -321,12 +322,9 @@ func (MatchRemoteIP) parseIPRange(str string) ([]netip.Prefix, error) {
} }
func (MatchRemoteIP) matches(ip netip.Addr, ranges []netip.Prefix) bool { func (MatchRemoteIP) matches(ip netip.Addr, ranges []netip.Prefix) bool {
for _, ipRange := range ranges { return slices.ContainsFunc(ranges, func(prefix netip.Prefix) bool {
if ipRange.Contains(ip) { return prefix.Contains(ip)
return true })
}
}
return false
} }
// UnmarshalCaddyfile sets up the MatchRemoteIP from Caddyfile tokens. Syntax: // UnmarshalCaddyfile sets up the MatchRemoteIP from Caddyfile tokens. Syntax:
@ -439,12 +437,9 @@ func (MatchLocalIP) parseIPRange(str string) ([]netip.Prefix, error) {
} }
func (MatchLocalIP) matches(ip netip.Addr, ranges []netip.Prefix) bool { func (MatchLocalIP) matches(ip netip.Addr, ranges []netip.Prefix) bool {
for _, ipRange := range ranges { return slices.ContainsFunc(ranges, func(prefix netip.Prefix) bool {
if ipRange.Contains(ip) { return prefix.Contains(ip)
return true })
}
}
return false
} }
// UnmarshalCaddyfile sets up the MatchLocalIP from Caddyfile tokens. Syntax: // UnmarshalCaddyfile sets up the MatchLocalIP from Caddyfile tokens. Syntax: