mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-30 22:34:15 -05:00
core: add type registry
Facilitates validation of type adherence to namespace requirements
This commit is contained in:
parent
b16aba5c27
commit
5e24e84288
17 changed files with 180 additions and 0 deletions
4
admin.go
4
admin.go
|
@ -34,6 +34,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -55,6 +56,9 @@ func init() {
|
|||
if env, exists := os.LookupEnv("CADDY_ADMIN"); exists {
|
||||
DefaultAdminListen = env
|
||||
}
|
||||
RegisterType("caddy.config_loaders", []reflect.Type{
|
||||
reflect.TypeOf((*ConfigLoader)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// AdminConfig configures Caddy's API endpoint, which is used
|
||||
|
|
14
caddy.go
14
caddy.go
|
@ -27,6 +27,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -41,6 +42,15 @@ import (
|
|||
"github.com/caddyserver/caddy/v2/notify"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterType("", []reflect.Type{
|
||||
reflect.TypeOf((*App)(nil)).Elem(),
|
||||
})
|
||||
RegisterType("caddy.storage", []reflect.Type{
|
||||
reflect.TypeOf((*StorageConverter)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// Config is the top (or beginning) of the Caddy configuration structure.
|
||||
// Caddy config is expressed natively as a JSON document. If you prefer
|
||||
// not to work with JSON directly, there are [many config adapters](/docs/config-adapters)
|
||||
|
@ -72,11 +82,15 @@ type Config struct {
|
|||
// module is `caddy.storage.file_system` (the local file system),
|
||||
// and the default path
|
||||
// [depends on the OS and environment](/docs/conventions#data-directory).
|
||||
// A storage `module` should implement the following interfaces:
|
||||
// - [StorageConverter](https://pkg.go.dev/github.com/caddyserver/caddy/v2#StorageConverter)
|
||||
StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
|
||||
|
||||
// AppsRaw are the apps that Caddy will load and run. The
|
||||
// app module name is the key, and the app's config is the
|
||||
// associated value.
|
||||
// An `app` should implement the following interfaces:
|
||||
// - [caddy.App](https://pkg.go.dev/github.com/caddyserver/caddy/v2?tab=doc#App)
|
||||
AppsRaw ModuleMap `json:"apps,omitempty" caddy:"namespace="`
|
||||
|
||||
apps map[string]App
|
||||
|
|
22
caddytest/integration/types_test.go
Normal file
22
caddytest/integration/types_test.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
_ "github.com/caddyserver/caddy/v2/modules/standard"
|
||||
)
|
||||
|
||||
// Validates Caddy's the registered internal types implement the necessary interfaces of their
|
||||
// namespaces
|
||||
func TestTypes(t *testing.T) {
|
||||
var i int
|
||||
for _, v := range caddy.Modules() {
|
||||
mod, _ := caddy.GetModule(v)
|
||||
if ok, err := caddy.ConformsToNamespace(mod.New(), mod.ID.Namespace()); !ok {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
t.Logf("Passed through %d modules", i)
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -37,6 +38,12 @@ import (
|
|||
"github.com/caddyserver/caddy/v2/internal"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterType("caddy.listeners", []reflect.Type{
|
||||
reflect.TypeOf((*ListenerWrapper)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// NetworkAddress represents one or more network addresses.
|
||||
// It contains the individual components for a parsed network
|
||||
// address of the form accepted by ParseNetworkAddress().
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -33,6 +34,12 @@ func init() {
|
|||
RegisterModule(StdoutWriter{})
|
||||
RegisterModule(StderrWriter{})
|
||||
RegisterModule(DiscardWriter{})
|
||||
RegisterType("caddy.logging.encoders", []reflect.Type{
|
||||
reflect.TypeOf((*zapcore.Encoder)(nil)).Elem(),
|
||||
})
|
||||
RegisterType("caddy.logging.writers", []reflect.Type{
|
||||
reflect.TypeOf((*WriterOpener)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// Logging facilitates logging within Caddy. The default log is
|
||||
|
@ -265,6 +272,8 @@ type BaseLog struct {
|
|||
WriterRaw json.RawMessage `json:"writer,omitempty" caddy:"namespace=caddy.logging.writers inline_key=output"`
|
||||
|
||||
// The encoder is how the log entries are formatted or encoded.
|
||||
// An `encoder` should implement the following interfaces:
|
||||
// - [zapcore.Encoder](https://pkg.go.dev/go.uber.org/zap/zapcore#Encoder)
|
||||
EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
|
||||
|
||||
// Level is the minimum level to emit, and is inclusive.
|
||||
|
|
|
@ -21,8 +21,10 @@ import (
|
|||
"fmt"
|
||||
weakrand "math/rand"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
|
@ -31,6 +33,15 @@ import (
|
|||
|
||||
func init() {
|
||||
caddy.RegisterModule(HTTPBasicAuth{})
|
||||
|
||||
caddy.RegisterType("http.authentication.hashes", []reflect.Type{
|
||||
reflect.TypeOf((*Comparer)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("http.authentication.providers", []reflect.Type{
|
||||
reflect.TypeOf((*Authenticator)(nil)).Elem(),
|
||||
})
|
||||
|
||||
weakrand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// HTTPBasicAuth facilitates HTTP basic authentication.
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -37,6 +38,9 @@ import (
|
|||
|
||||
func init() {
|
||||
caddy.RegisterModule(Encode{})
|
||||
caddy.RegisterType("http.encoders", []reflect.Type{
|
||||
reflect.TypeOf((*Encoding)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// Encode is a middleware which can encode responses.
|
||||
|
|
|
@ -26,9 +26,11 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
|
@ -38,6 +40,12 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
weakrand.Seed(time.Now().UnixNano())
|
||||
|
||||
caddy.RegisterType("http.precompressed", []reflect.Type{
|
||||
reflect.TypeOf((*encode.Precompressed)(nil)).Elem(),
|
||||
})
|
||||
|
||||
caddy.RegisterModule(FileServer{})
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,8 @@ type (
|
|||
// where each of the array elements is a matcher set, i.e. an
|
||||
// object keyed by matcher name.
|
||||
MatchNot struct {
|
||||
// A `matcher` should implement the following interfaces:
|
||||
// - [caddyhttp.RequestMatcher](https://pkg.go.dev/github.com/caddyserver/caddy/v2/modules/caddyhttp?tab=doc#RequestMatcher)
|
||||
MatcherSetsRaw []caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"`
|
||||
MatcherSets []MatcherSet `json:"-"`
|
||||
}
|
||||
|
@ -212,6 +214,9 @@ func init() {
|
|||
caddy.RegisterModule(MatchHeaderRE{})
|
||||
caddy.RegisterModule(new(MatchProtocol))
|
||||
caddy.RegisterModule(MatchNot{})
|
||||
caddy.RegisterType("http.matchers", []reflect.Type{
|
||||
reflect.TypeOf((*RequestMatcher)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// CaddyModule returns the Caddy module information.
|
||||
|
|
|
@ -1145,3 +1145,9 @@ func BenchmarkHostMatcherWithPlaceholder(b *testing.B) {
|
|||
match.Match(req)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConformsToNamespace(t *testing.T) {
|
||||
if ok, err := caddy.ConformsToNamespace(new(StaticResponse), "http.matchers"); !ok || err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"net/netip"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -45,6 +46,19 @@ import (
|
|||
|
||||
func init() {
|
||||
caddy.RegisterModule(Handler{})
|
||||
|
||||
caddy.RegisterType("http.reverse_proxy.circuit_breakers", []reflect.Type{
|
||||
reflect.TypeOf((*CircuitBreaker)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("http.reverse_proxy.selection_policies", []reflect.Type{
|
||||
reflect.TypeOf((*Selector)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("http.reverse_proxy.transport", []reflect.Type{
|
||||
reflect.TypeOf((*http.RoundTripper)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("http.reverse_proxy.upstreams", []reflect.Type{
|
||||
reflect.TypeOf((*UpstreamSource)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// Handler implements a highly configurable and production-ready reverse proxy.
|
||||
|
|
|
@ -18,10 +18,17 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterType("http.handlers", []reflect.Type{
|
||||
reflect.TypeOf((*MiddlewareHandler)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// Route consists of a set of rules for matching HTTP requests,
|
||||
// a list of handlers to execute, and optional flow control
|
||||
// parameters which customize the handling of HTTP requests
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -30,6 +31,18 @@ import (
|
|||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterType("dns.provider", []reflect.Type{
|
||||
reflect.TypeOf((*acmez.Solver)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("tls.get_certificate", []reflect.Type{
|
||||
reflect.TypeOf((*certmagic.Manager)(nil)).Elem(),
|
||||
})
|
||||
caddy.RegisterType("tls.issuance", []reflect.Type{
|
||||
reflect.TypeOf((*certmagic.Issuer)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// AutomationConfig governs the automated management of TLS certificates.
|
||||
type AutomationConfig struct {
|
||||
// The list of automation policies. The first policy matching
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/acmez"
|
||||
|
@ -33,6 +34,9 @@ import (
|
|||
|
||||
func init() {
|
||||
caddy.RegisterModule(LeafCertClientAuth{})
|
||||
caddy.RegisterType("tls.handshake_match", []reflect.Type{
|
||||
reflect.TypeOf((*ConnectionMatcher)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// ConnectionPolicies govern the establishment of TLS connections. It is
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -28,6 +29,12 @@ import (
|
|||
"github.com/caddyserver/caddy/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterType("tls.stek", []reflect.Type{
|
||||
reflect.TypeOf((*STEKProvider)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
// SessionTicketService configures and manages TLS session tickets.
|
||||
type SessionTicketService struct {
|
||||
// KeySource is the method by which Caddy produces or obtains
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -35,6 +36,10 @@ import (
|
|||
func init() {
|
||||
caddy.RegisterModule(TLS{})
|
||||
caddy.RegisterModule(AutomateLoader{})
|
||||
|
||||
caddy.RegisterType("tls.certificates", []reflect.Type{
|
||||
reflect.TypeOf((*CertificateLoader)(nil)).Elem(),
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -652,6 +657,11 @@ func (AutomateLoader) CaddyModule() caddy.ModuleInfo {
|
|||
}
|
||||
}
|
||||
|
||||
// LoadCertificates is a stub so AutomateLoader can implement CertificateLoader
|
||||
func (AutomateLoader) LoadCertificates() ([]Certificate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CertCacheOptions configures the certificate cache.
|
||||
type CertCacheOptions struct {
|
||||
// Maximum number of certificates to allow in the
|
||||
|
|
35
types.go
Normal file
35
types.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package caddy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var namespaceTypes map[string][]reflect.Type = make(map[string][]reflect.Type)
|
||||
|
||||
func RegisterType(namespace string, types []reflect.Type) {
|
||||
if _, ok := namespaceTypes[namespace]; ok {
|
||||
panic("namespace is already registered")
|
||||
}
|
||||
namespaceTypes[namespace] = types
|
||||
}
|
||||
|
||||
// NamespaceTypes returns a copy of Caddy's namespace->type registry
|
||||
func NamespaceTypes() map[string][]reflect.Type {
|
||||
copy := make(map[string][]reflect.Type)
|
||||
for namespace, typeSlice := range namespaceTypes {
|
||||
copy[namespace] = typeSlice
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// ConformsToNamespace validates the given module implements all the mandatory types of a given namespace
|
||||
func ConformsToNamespace(mod Module, namespace string) (bool, error) {
|
||||
modType := reflect.TypeOf(mod)
|
||||
for _, t := range namespaceTypes[namespace] {
|
||||
if !modType.Implements(t) {
|
||||
return false, fmt.Errorf("%s does not implement %s", modType, t)
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
Loading…
Reference in a new issue