0
Fork 0
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:
Mohammed Al Sahaf 2022-06-12 22:35:11 +03:00
parent b16aba5c27
commit 5e24e84288
17 changed files with 180 additions and 0 deletions

View file

@ -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

View file

@ -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

View 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)
}

View file

@ -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().

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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{})
}

View file

@ -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.

View file

@ -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)
}
}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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
}