Compare commits

...

5 Commits

Author SHA1 Message Date
Mohammed Al Sahaf 18b346f6f9 r/RegisterType/RegisterNamespace/g
Co-Author: Matt Holt
2023-12-14 23:50:24 +03:00
Mohammed Al Sahaf 52441e3037 follow the linter's commands 2023-12-14 23:38:08 +03:00
Mohammed Al Sahaf b825a10927 own the usage of reflection into the RegisterType
allowing the users to only pass instances of the interfaces
2023-12-14 18:14:18 +03:00
Mohammed Al Sahaf 52f43d2f4c remove invalid test 2023-12-14 18:02:38 +03:00
Mohammed Al Sahaf 5e24e84288 core: add type registry
Facilitates validation of type adherence to namespace requirements
2023-12-14 18:02:15 +03:00
16 changed files with 157 additions and 0 deletions
+1
View File
@@ -55,6 +55,7 @@ func init() {
if env, exists := os.LookupEnv("CADDY_ADMIN"); exists { if env, exists := os.LookupEnv("CADDY_ADMIN"); exists {
DefaultAdminListen = env DefaultAdminListen = env
} }
RegisterNamespace("caddy.config_loaders", []interface{}{(*ConfigLoader)(nil)})
} }
// AdminConfig configures Caddy's API endpoint, which is used // AdminConfig configures Caddy's API endpoint, which is used
+13
View File
@@ -41,6 +41,15 @@ import (
"github.com/caddyserver/caddy/v2/notify" "github.com/caddyserver/caddy/v2/notify"
) )
func init() {
RegisterNamespace("", []interface{}{
(*App)(nil),
})
RegisterNamespace("caddy.storage", []interface{}{
(*StorageConverter)(nil),
})
}
// Config is the top (or beginning) of the Caddy configuration structure. // Config is the top (or beginning) of the Caddy configuration structure.
// Caddy config is expressed natively as a JSON document. If you prefer // 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) // not to work with JSON directly, there are [many config adapters](/docs/config-adapters)
@@ -72,11 +81,15 @@ type Config struct {
// module is `caddy.storage.file_system` (the local file system), // module is `caddy.storage.file_system` (the local file system),
// and the default path // and the default path
// [depends on the OS and environment](/docs/conventions#data-directory). // [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"` StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
// AppsRaw are the apps that Caddy will load and run. The // AppsRaw are the apps that Caddy will load and run. The
// app module name is the key, and the app's config is the // app module name is the key, and the app's config is the
// associated value. // 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="` AppsRaw ModuleMap `json:"apps,omitempty" caddy:"namespace="`
apps map[string]App apps map[string]App
+22
View File
@@ -0,0 +1,22 @@
package integration
import (
"testing"
"github.com/caddyserver/caddy/v2"
_ "github.com/caddyserver/caddy/v2/modules/standard"
)
// Validates that Caddy's registered internal modules implement the necessary interfaces of their
// respective 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)
}
+6
View File
@@ -37,6 +37,12 @@ import (
"github.com/caddyserver/caddy/v2/internal" "github.com/caddyserver/caddy/v2/internal"
) )
func init() {
RegisterNamespace("caddy.listeners", []interface{}{
(*ListenerWrapper)(nil),
})
}
// NetworkAddress represents one or more network addresses. // NetworkAddress represents one or more network addresses.
// It contains the individual components for a parsed network // It contains the individual components for a parsed network
// address of the form accepted by ParseNetworkAddress(). // address of the form accepted by ParseNetworkAddress().
+8
View File
@@ -33,6 +33,12 @@ func init() {
RegisterModule(StdoutWriter{}) RegisterModule(StdoutWriter{})
RegisterModule(StderrWriter{}) RegisterModule(StderrWriter{})
RegisterModule(DiscardWriter{}) RegisterModule(DiscardWriter{})
RegisterNamespace("caddy.logging.encoders", []interface{}{
(*zapcore.Encoder)(nil),
})
RegisterNamespace("caddy.logging.writers", []interface{}{
(*WriterOpener)(nil),
})
} }
// Logging facilitates logging within Caddy. The default log is // Logging facilitates logging within Caddy. The default log is
@@ -265,6 +271,8 @@ type BaseLog struct {
WriterRaw json.RawMessage `json:"writer,omitempty" caddy:"namespace=caddy.logging.writers inline_key=output"` 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. // 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"` EncoderRaw json.RawMessage `json:"encoder,omitempty" caddy:"namespace=caddy.logging.encoders inline_key=format"`
// Level is the minimum level to emit, and is inclusive. // Level is the minimum level to emit, and is inclusive.
+7
View File
@@ -31,6 +31,13 @@ import (
func init() { func init() {
caddy.RegisterModule(HTTPBasicAuth{}) caddy.RegisterModule(HTTPBasicAuth{})
caddy.RegisterNamespace("http.authentication.hashes", []interface{}{
(*Comparer)(nil),
})
caddy.RegisterNamespace("http.authentication.providers", []interface{}{
(*Authenticator)(nil),
})
} }
// HTTPBasicAuth facilitates HTTP basic authentication. // HTTPBasicAuth facilitates HTTP basic authentication.
+3
View File
@@ -37,6 +37,9 @@ import (
func init() { func init() {
caddy.RegisterModule(Encode{}) caddy.RegisterModule(Encode{})
caddy.RegisterNamespace("http.encoders", []interface{}{
(*Encoding)(nil),
})
} }
// Encode is a middleware which can encode responses. // Encode is a middleware which can encode responses.
@@ -38,6 +38,10 @@ import (
) )
func init() { func init() {
caddy.RegisterNamespace("http.precompressed", []interface{}{
(*encode.Precompressed)(nil),
})
caddy.RegisterModule(FileServer{}) caddy.RegisterModule(FileServer{})
} }
+5
View File
@@ -197,6 +197,8 @@ type (
// where each of the array elements is a matcher set, i.e. an // where each of the array elements is a matcher set, i.e. an
// object keyed by matcher name. // object keyed by matcher name.
MatchNot struct { 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"` MatcherSetsRaw []caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"`
MatcherSets []MatcherSet `json:"-"` MatcherSets []MatcherSet `json:"-"`
} }
@@ -212,6 +214,9 @@ func init() {
caddy.RegisterModule(MatchHeaderRE{}) caddy.RegisterModule(MatchHeaderRE{})
caddy.RegisterModule(new(MatchProtocol)) caddy.RegisterModule(new(MatchProtocol))
caddy.RegisterModule(MatchNot{}) caddy.RegisterModule(MatchNot{})
caddy.RegisterNamespace("http.matchers", []interface{}{
(*RequestMatcher)(nil),
})
} }
// CaddyModule returns the Caddy module information. // CaddyModule returns the Caddy module information.
@@ -45,6 +45,19 @@ import (
func init() { func init() {
caddy.RegisterModule(Handler{}) caddy.RegisterModule(Handler{})
caddy.RegisterNamespace("http.reverse_proxy.circuit_breakers", []interface{}{
(*CircuitBreaker)(nil),
})
caddy.RegisterNamespace("http.reverse_proxy.selection_policies", []interface{}{
(*Selector)(nil),
})
caddy.RegisterNamespace("http.reverse_proxy.transport", []interface{}{
(*http.RoundTripper)(nil),
})
caddy.RegisterNamespace("http.reverse_proxy.upstreams", []interface{}{
(*UpstreamSource)(nil),
})
} }
// Handler implements a highly configurable and production-ready reverse proxy. // Handler implements a highly configurable and production-ready reverse proxy.
+6
View File
@@ -22,6 +22,12 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
) )
func init() {
caddy.RegisterNamespace("http.handlers", []interface{}{
(*MiddlewareHandler)(nil),
})
}
// Route consists of a set of rules for matching HTTP requests, // Route consists of a set of rules for matching HTTP requests,
// a list of handlers to execute, and optional flow control // a list of handlers to execute, and optional flow control
// parameters which customize the handling of HTTP requests // parameters which customize the handling of HTTP requests
+12
View File
@@ -30,6 +30,18 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
) )
func init() {
caddy.RegisterNamespace("dns.provider", []interface{}{
(*acmez.Solver)(nil),
})
caddy.RegisterNamespace("tls.get_certificate", []interface{}{
(*certmagic.Manager)(nil),
})
caddy.RegisterNamespace("tls.issuance", []interface{}{
(*certmagic.Issuer)(nil),
})
}
// AutomationConfig governs the automated management of TLS certificates. // AutomationConfig governs the automated management of TLS certificates.
type AutomationConfig struct { type AutomationConfig struct {
// The list of automation policies. The first policy matching // The list of automation policies. The first policy matching
+3
View File
@@ -33,6 +33,9 @@ import (
func init() { func init() {
caddy.RegisterModule(LeafCertClientAuth{}) caddy.RegisterModule(LeafCertClientAuth{})
caddy.RegisterNamespace("tls.handshake_match", []interface{}{
(*ConnectionMatcher)(nil),
})
} }
// ConnectionPolicies govern the establishment of TLS connections. It is // ConnectionPolicies govern the establishment of TLS connections. It is
+6
View File
@@ -28,6 +28,12 @@ import (
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
) )
func init() {
caddy.RegisterNamespace("tls.stek", []interface{}{
(*STEKProvider)(nil),
})
}
// SessionTicketService configures and manages TLS session tickets. // SessionTicketService configures and manages TLS session tickets.
type SessionTicketService struct { type SessionTicketService struct {
// KeySource is the method by which Caddy produces or obtains // KeySource is the method by which Caddy produces or obtains
+9
View File
@@ -35,6 +35,10 @@ import (
func init() { func init() {
caddy.RegisterModule(TLS{}) caddy.RegisterModule(TLS{})
caddy.RegisterModule(AutomateLoader{}) caddy.RegisterModule(AutomateLoader{})
caddy.RegisterNamespace("tls.certificates", []interface{}{
(*CertificateLoader)(nil),
})
} }
var ( var (
@@ -652,6 +656,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. // CertCacheOptions configures the certificate cache.
type CertCacheOptions struct { type CertCacheOptions struct {
// Maximum number of certificates to allow in the // Maximum number of certificates to allow in the
+39
View File
@@ -0,0 +1,39 @@
package caddy
import (
"fmt"
"reflect"
)
var namespaceTypes map[string][]reflect.Type = make(map[string][]reflect.Type)
func RegisterNamespace(namespace string, vals []interface{}) {
var types []reflect.Type
for _, v := range vals {
reflect.TypeOf(v).Elem()
}
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
}