diff --git a/lib/gat/gatcaddyfile/discoverer.go b/lib/gat/gatcaddyfile/discoverer.go new file mode 100644 index 0000000000000000000000000000000000000000..ec4fb4225c09bbcf9f713a126ed16ea45b8bcfef --- /dev/null +++ b/lib/gat/gatcaddyfile/discoverer.go @@ -0,0 +1,25 @@ +package gatcaddyfile + +import ( + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + + "gfx.cafe/gfx/pggat/lib/gat/handlers/discovery/discoverers/digitalocean" +) + +func init() { + RegisterDirective(Discoverer, "digitalocean", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + + apiKey := d.Val() + + return &digitalocean.Discoverer{ + Config: digitalocean.Config{ + APIKey: apiKey, + }, + }, nil + }) +} diff --git a/lib/gat/gatcaddyfile/gattype.go b/lib/gat/gatcaddyfile/gattype.go index 1114f41870316cd997bdec921c3fe6137f4a9599..db839f49f00a91d507e95e354b5af433787c95da 100644 --- a/lib/gat/gatcaddyfile/gattype.go +++ b/lib/gat/gatcaddyfile/gattype.go @@ -2,7 +2,6 @@ package gatcaddyfile import ( "encoding/json" - "log" "strings" "github.com/caddyserver/caddy/v2" @@ -66,15 +65,10 @@ func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*cadd &warnings, ) } else { - unmarshaller, ok := sslServers[d.Val()] - if !ok { - return nil, nil, d.Errf(`unknown ssl server "%s"`, d.Val()) - } - var err error - val, err = unmarshaller.JSONModuleObject( + val, err = UnmarshalDirectiveJSONModuleObject( d, - "pggat.ssl.servers", + SSLServer, "provider", &warnings, ) @@ -105,26 +99,57 @@ func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*cadd // read named matcher if d.NextArg() { // inline + var err error + matcher, err = UnmarshalDirectiveJSONModuleObject( + d, + Matcher, + "matcher", + &warnings, + ) + if err != nil { + return nil, nil, err + } } else { // block if !d.NextBlock(0) { return nil, nil, d.ArgErr() } + var and matchers.And + for { if d.Val() == "}" { break } - log.Println(d.Val()) - for d.NextArg() { - log.Println(d.Val()) + cond, err := UnmarshalDirectiveJSONModuleObject( + d, + Matcher, + "matcher", + &warnings, + ) + if err != nil { + return nil, nil, err } + and.And = append(and.And, cond) if !d.NextLine() { return nil, nil, d.EOFErr() } } + + if len(and.And) == 0 { + matcher = nil + } else if len(and.And) == 1 { + matcher = and.And[0] + } else { + matcher = caddyconfig.JSONModuleObject( + and, + Matcher, + "matcher", + &warnings, + ) + } } if d.CountRemainingArgs() > 0 { @@ -136,7 +161,7 @@ func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*cadd } namedMatchers[name] = matcher default: - unmarshaller, ok := handlers[d.Val()] + unmarshaller, ok := LookupDirective(Handler, d.Val()) if !ok { return nil, nil, d.Errf(`unknown handler "%s"`, d.Val()) } @@ -169,7 +194,7 @@ func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*cadd var err error route.Handle, err = unmarshaller.JSONModuleObject( d, - "pggat.handlers", + Handler, "handler", &warnings, ) diff --git a/lib/gat/gatcaddyfile/handler.go b/lib/gat/gatcaddyfile/handler.go index c3f628bbfc8c3bca4b8d7e1f21ed971c048f82b4..06504036fe43a33a7ced31c40ee11e9de8fd04d1 100644 --- a/lib/gat/gatcaddyfile/handler.go +++ b/lib/gat/gatcaddyfile/handler.go @@ -1,31 +1,32 @@ package gatcaddyfile import ( - "fmt" + "strings" + "time" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "gfx.cafe/gfx/pggat/lib/bouncer" + "gfx.cafe/gfx/pggat/lib/gat/handlers/allowed_startup_parameters" + "gfx.cafe/gfx/pggat/lib/gat/handlers/discovery" "gfx.cafe/gfx/pggat/lib/gat/handlers/pgbouncer" "gfx.cafe/gfx/pggat/lib/gat/handlers/require_ssl" + "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_database" + "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_parameter" + "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_password" + "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_user" + "gfx.cafe/gfx/pggat/lib/gat/poolers/transaction" + "gfx.cafe/gfx/pggat/lib/gat/ssl/clients/insecure_skip_verify" + "gfx.cafe/gfx/pggat/lib/util/dur" + "gfx.cafe/gfx/pggat/lib/util/strutil" ) -var handlers map[string]Unmarshaller - -func RegisterHandlerDirective(directive string, unmarshaller Unmarshaller) { - if _, ok := handlers[directive]; ok { - panic(fmt.Sprintf(`duplicate handler directive "%s"`, directive)) - } - if handlers == nil { - handlers = make(map[string]Unmarshaller) - } - handlers[directive] = unmarshaller -} - func init() { - RegisterHandlerDirective("require_ssl", func(d *caddyfile.Dispenser) (caddy.Module, error) { + RegisterDirective(Handler, "require_ssl", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { var ssl = true - if d.Next() { + if d.NextArg() { switch d.Val() { case "true": ssl = true @@ -39,13 +40,209 @@ func init() { SSL: ssl, }, nil }) - RegisterHandlerDirective("pgbouncer", func(d *caddyfile.Dispenser) (caddy.Module, error) { + RegisterDirective(Handler, "allowed_startup_parameters", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextBlock(d.Nesting()) { + return nil, d.ArgErr() + } + + var module allowed_startup_parameters.Module + + for { + if d.Val() == "}" { + break + } + + module.Parameters = append(module.Parameters, strutil.MakeCIString(d.Val())) + + if !d.NextLine() { + return nil, d.EOFErr() + } + } + + return &module, nil + }) + RegisterDirective(Handler, "user", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + + return &rewrite_user.Module{ + User: d.Val(), + }, nil + }) + RegisterDirective(Handler, "password", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + + return &rewrite_password.Module{ + Password: d.Val(), + }, nil + }) + RegisterDirective(Handler, "database", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + + return &rewrite_database.Module{ + Database: d.Val(), + }, nil + }) + RegisterDirective(Handler, "parameter", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + + keyValue := d.Val() + key, value, ok := strings.Cut(keyValue, "=") + if !ok { + return nil, d.SyntaxErr("key=value") + } + + return &rewrite_parameter.Module{ + Key: strutil.MakeCIString(key), + Value: value, + }, nil + }) + RegisterDirective(Handler, "pgbouncer", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { var config = "pgbouncer.ini" - if d.Next() { + if d.NextArg() { config = d.Val() } return &pgbouncer.Module{ ConfigFile: config, }, nil }) + RegisterDirective(Handler, "discovery", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + module := discovery.Module{ + Config: discovery.Config{ + ReconcilePeriod: dur.Duration(5 * time.Minute), + Pooler: JSONModuleObject( + &transaction.Pool{ + ManagementConfig: defaultPoolManagementConfig, + }, + Pooler, + "pooler", + warnings, + ), + ServerSSLMode: bouncer.SSLModePrefer, + ServerSSL: JSONModuleObject( + &insecure_skip_verify.Client{}, + SSLClient, + "provider", + warnings, + ), + }, + } + + if d.NextArg() { + // discoverer + var err error + module.Discoverer, err = UnmarshalDirectiveJSONModuleObject( + d, + Discoverer, + "discoverer", + warnings, + ) + + if err != nil { + return nil, err + } + } else { + if !d.NextBlock(d.Nesting()) { + return nil, d.ArgErr() + } + + for { + if d.Val() == "}" { + break + } + + directive := d.Val() + switch directive { + case "reconcile_period": + if !d.NextArg() { + return nil, d.ArgErr() + } + + val, err := time.ParseDuration(d.Val()) + if err != nil { + return nil, d.WrapErr(err) + } + module.ReconcilePeriod = dur.Duration(val) + case "discoverer": + if !d.NextArg() { + return nil, d.ArgErr() + } + + var err error + module.Discoverer, err = UnmarshalDirectiveJSONModuleObject( + d, + Discoverer, + "discoverer", + warnings, + ) + if err != nil { + return nil, err + } + case "pooler": + if !d.NextArg() { + return nil, d.ArgErr() + } + + var err error + module.Pooler, err = UnmarshalDirectiveJSONModuleObject( + d, + Pooler, + "pooler", + warnings, + ) + if err != nil { + return nil, err + } + case "ssl": + if !d.NextArg() { + return nil, d.ArgErr() + } + + module.ServerSSLMode = bouncer.SSLMode(d.Val()) + + if !d.NextArg() { + return nil, d.ArgErr() + } + + var err error + module.ServerSSL, err = UnmarshalDirectiveJSONModuleObject( + d, + SSLClient, + "provider", + warnings, + ) + if err != nil { + return nil, err + } + case "parameter": + if !d.NextArg() { + return nil, d.ArgErr() + } + + keyValue := d.Val() + key, value, ok := strings.Cut(keyValue, "=") + if !ok { + return nil, d.SyntaxErr("key=value") + } + if module.ServerStartupParameters == nil { + module.ServerStartupParameters = make(map[string]string) + } + module.ServerStartupParameters[key] = value + } + + if !d.NextLine() { + return nil, d.EOFErr() + } + } + } + + return &module, nil + }) } diff --git a/lib/gat/gatcaddyfile/matcher.go b/lib/gat/gatcaddyfile/matcher.go new file mode 100644 index 0000000000000000000000000000000000000000..07e6822f6b2f350fb92f0ac5bb0cf420e49302a1 --- /dev/null +++ b/lib/gat/gatcaddyfile/matcher.go @@ -0,0 +1,194 @@ +package gatcaddyfile + +import ( + "encoding/json" + "strings" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + + "gfx.cafe/gfx/pggat/lib/gat/matchers" + "gfx.cafe/gfx/pggat/lib/util/strutil" +) + +func init() { + RegisterDirective(Matcher, "user", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + user := d.Val() + return &matchers.User{ + User: user, + }, nil + }) + RegisterDirective(Matcher, "database", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + database := d.Val() + return &matchers.Database{ + Database: database, + }, nil + }) + RegisterDirective(Matcher, "local_address", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + address := d.Val() + var network string + if strings.HasPrefix(address, "/") { + network = "unix" + } else { + network = "tcp" + } + return &matchers.LocalAddress{ + Network: network, + Address: address, + }, nil + }) + RegisterDirective(Matcher, "parameter", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.ArgErr() + } + keyValue := d.Val() + key, value, ok := strings.Cut(keyValue, "=") + if !ok { + return nil, d.SyntaxErr("key=value") + } + return &matchers.StartupParameter{ + Key: strutil.MakeCIString(key), + Value: value, + }, nil + }) + RegisterDirective(Matcher, "ssl", func(d *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + var ssl = true + if d.NextArg() { + val := d.Val() + switch val { + case "true": + ssl = true + case "false": + ssl = false + default: + return nil, d.SyntaxErr("boolean") + } + } + + return &matchers.SSL{ + SSL: ssl, + }, nil + }) + RegisterDirective(Matcher, "not", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextArg() { + return nil, d.SyntaxErr("matcher directive") + } + + matcher, err := UnmarshalDirectiveJSONModuleObject( + d, + Matcher, + "matcher", + warnings, + ) + if err != nil { + return nil, err + } + + return &matchers.Not{ + Not: matcher, + }, nil + }) + RegisterDirective(Matcher, "and", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextBlock(d.Nesting()) { + return nil, d.ArgErr() + } + + var and []caddy.Module + + for { + if d.Val() == "}" { + break + } + + unmarshaller, ok := LookupDirective(Matcher, d.Val()) + if !ok { + return nil, d.Errf(`unknown matcher "%s"`, d.Val()) + } + + val, err := unmarshaller(d, warnings) + if err != nil { + return nil, err + } + and = append(and, val) + + if !d.NextLine() { + return nil, d.EOFErr() + } + } + + if len(and) == 0 { + return nil, nil + } + if len(and) == 1 { + return and[0], nil + } + + var raw = make([]json.RawMessage, 0, len(and)) + for _, val := range and { + raw = append(raw, JSONModuleObject( + val, + Matcher, + "matcher", + warnings, + )) + } + return &matchers.And{ + And: raw, + }, nil + }) + RegisterDirective(Matcher, "or", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + if !d.NextBlock(d.Nesting()) { + return nil, d.ArgErr() + } + + var or []caddy.Module + + for { + if d.Val() == "}" { + break + } + + unmarshaller, ok := LookupDirective(Matcher, d.Val()) + if !ok { + return nil, d.Errf(`unknown matcher "%s"`, d.Val()) + } + + val, err := unmarshaller(d, warnings) + if err != nil { + return nil, err + } + or = append(or, val) + + if !d.NextLine() { + return nil, d.EOFErr() + } + } + + if len(or) == 1 { + return or[0], nil + } + + var raw = make([]json.RawMessage, 0, len(or)) + for _, val := range or { + raw = append(raw, JSONModuleObject( + val, + Matcher, + "matcher", + warnings, + )) + } + return &matchers.Or{ + Or: raw, + }, nil + }) +} diff --git a/lib/gat/gatcaddyfile/pooler.go b/lib/gat/gatcaddyfile/pooler.go new file mode 100644 index 0000000000000000000000000000000000000000..fe5ac9b86e1b83fe78bd3db58fdaecf83330cee5 --- /dev/null +++ b/lib/gat/gatcaddyfile/pooler.go @@ -0,0 +1,55 @@ +package gatcaddyfile + +import ( + "time" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + + "gfx.cafe/gfx/pggat/lib/gat/pool" + "gfx.cafe/gfx/pggat/lib/gat/poolers/session" + "gfx.cafe/gfx/pggat/lib/gat/poolers/transaction" + "gfx.cafe/gfx/pggat/lib/util/dur" + "gfx.cafe/gfx/pggat/lib/util/strutil" +) + +var defaultPoolManagementConfig = pool.ManagementConfig{ + ServerIdleTimeout: dur.Duration(5 * time.Minute), + ServerReconnectInitialTime: dur.Duration(5 * time.Second), + ServerReconnectMaxTime: dur.Duration(1 * time.Minute), + TrackedParameters: []strutil.CIString{ + strutil.MakeCIString("client_encoding"), + strutil.MakeCIString("datestyle"), + strutil.MakeCIString("timezone"), + strutil.MakeCIString("standard_conforming_strings"), + strutil.MakeCIString("application_name"), + }, +} + +func init() { + RegisterDirective(Pooler, "transaction", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + module := transaction.Pool{ + ManagementConfig: defaultPoolManagementConfig, + } + + if !d.NextBlock(d.Nesting()) { + return &module, nil + } + + // TODO(garet) + panic("TODO(garet)") + }) + RegisterDirective(Pooler, "session", func(d *caddyfile.Dispenser, warnings *[]caddyconfig.Warning) (caddy.Module, error) { + module := session.Pool{ + ManagementConfig: defaultPoolManagementConfig, + } + + if !d.NextBlock(d.Nesting()) { + return &module, nil + } + + // TODO(garet) + panic("TODO(garet)") + }) +} diff --git a/lib/gat/gatcaddyfile/ssl.go b/lib/gat/gatcaddyfile/ssl.go index 01f35ffa3a378b1b3a20725ab0f8124316bdb19f..798d6bc95f2b5c948055b2e73b9eb3081f5c1d1f 100644 --- a/lib/gat/gatcaddyfile/ssl.go +++ b/lib/gat/gatcaddyfile/ssl.go @@ -1,28 +1,20 @@ package gatcaddyfile import ( - "fmt" - "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "gfx.cafe/gfx/pggat/lib/gat/ssl/clients/insecure_skip_verify" "gfx.cafe/gfx/pggat/lib/gat/ssl/servers/self_signed" ) -var sslServers map[string]Unmarshaller - -func RegisterSSLServerDirective(directive string, unmarshaller Unmarshaller) { - if _, ok := sslServers[directive]; ok { - panic(fmt.Sprintf(`duplicate ssl server directive "%s"`, directive)) - } - if sslServers == nil { - sslServers = make(map[string]Unmarshaller) - } - sslServers[directive] = unmarshaller -} - func init() { - RegisterSSLServerDirective("self_signed", func(_ *caddyfile.Dispenser) (caddy.Module, error) { + RegisterDirective(SSLServer, "self_signed", func(_ *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { return &self_signed.Server{}, nil }) + + RegisterDirective(SSLClient, "insecure_skip_verify", func(_ *caddyfile.Dispenser, _ *[]caddyconfig.Warning) (caddy.Module, error) { + return &insecure_skip_verify.Client{}, nil + }) } diff --git a/lib/gat/gatcaddyfile/unmarshaller.go b/lib/gat/gatcaddyfile/unmarshaller.go index c5edbcd83f076aad137277ad4944cb0d19046676..e75b67241eabbb60cd0afda77097bcc87492e540 100644 --- a/lib/gat/gatcaddyfile/unmarshaller.go +++ b/lib/gat/gatcaddyfile/unmarshaller.go @@ -10,7 +10,7 @@ import ( "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) -type Unmarshaller func(*caddyfile.Dispenser) (caddy.Module, error) +type Unmarshaller func(*caddyfile.Dispenser, *[]caddyconfig.Warning) (caddy.Module, error) func (T Unmarshaller) JSONModuleObject( d *caddyfile.Dispenser, @@ -18,16 +18,35 @@ func (T Unmarshaller) JSONModuleObject( inlineKey string, warnings *[]caddyconfig.Warning, ) (json.RawMessage, error) { - module, err := T(d) + module, err := T(d, warnings) if err != nil { return nil, err } + return JSONModuleObject( + module, + namespace, + inlineKey, + warnings, + ), nil +} + +func JSONModuleObject( + module caddy.Module, + namespace string, + inlineKey string, + warnings *[]caddyconfig.Warning, +) json.RawMessage { rawModuleID := string(module.CaddyModule().ID) dotModuleID := strings.TrimPrefix(rawModuleID, namespace) moduleID := strings.TrimPrefix(dotModuleID, ".") if rawModuleID == dotModuleID || dotModuleID == moduleID { - return nil, fmt.Errorf(`expected item in namespace "%s" but got "%s"`, namespace, rawModuleID) + if warnings != nil { + *warnings = append(*warnings, caddyconfig.Warning{ + Message: fmt.Sprintf(`expected item in namespace "%s" but got "%s"`, namespace, rawModuleID), + }) + } + return nil } return caddyconfig.JSONModuleObject( @@ -35,5 +54,5 @@ func (T Unmarshaller) JSONModuleObject( inlineKey, moduleID, warnings, - ), nil + ) } diff --git a/lib/gat/gatcaddyfile/unmarshallers.go b/lib/gat/gatcaddyfile/unmarshallers.go new file mode 100644 index 0000000000000000000000000000000000000000..0d1f0cd75e45ff374721313958d0c3f431122d36 --- /dev/null +++ b/lib/gat/gatcaddyfile/unmarshallers.go @@ -0,0 +1,52 @@ +package gatcaddyfile + +import ( + "encoding/json" + "fmt" + + "github.com/caddyserver/caddy/v2/caddyconfig" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + + "gfx.cafe/gfx/pggat/lib/util/maps" +) + +const ( + Discoverer = "pggat.handlers.discovery.discoverers" + Handler = "pggat.handlers" + Matcher = "pggat.matchers" + Pooler = "pggat.poolers" + SSLServer = "pggat.ssl.servers" + SSLClient = "pggat.ssl.clients" +) + +var unmarshallers maps.TwoKey[string, string, Unmarshaller] + +func RegisterDirective(namespace, directive string, unmarshaller Unmarshaller) { + if _, ok := unmarshallers.Load(namespace, directive); ok { + panic(fmt.Sprintf(`directive "%s" already exists`, directive)) + } + unmarshallers.Store(namespace, directive, unmarshaller) +} + +func LookupDirective(namespace, directive string) (Unmarshaller, bool) { + return unmarshallers.Load(namespace, directive) +} + +func UnmarshalDirectiveJSONModuleObject( + d *caddyfile.Dispenser, + namespace string, + inlineKey string, + warnings *[]caddyconfig.Warning, +) (json.RawMessage, error) { + unmarshaller, ok := LookupDirective(namespace, d.Val()) + if !ok { + return nil, d.Errf(`unknown directive in %s: "%s"`, namespace, d.Val()) + } + + return unmarshaller.JSONModuleObject( + d, + namespace, + inlineKey, + warnings, + ) +} diff --git a/lib/gat/handlers/discovery/config.go b/lib/gat/handlers/discovery/config.go index 2d9c1581c7994c3d4801e3c8b256fed919b9ed42..d21a5533ae395c025a5ebe4278e81ab84e20ad2b 100644 --- a/lib/gat/handlers/discovery/config.go +++ b/lib/gat/handlers/discovery/config.go @@ -18,5 +18,5 @@ type Config struct { ServerSSLMode bouncer.SSLMode `json:"server_ssl_mode"` ServerSSL json.RawMessage `json:"server_ssl" caddy:"namespace=pggat.ssl.clients inline_key=provider"` - ServerStartupParameters map[string]string `json:"server_startup_parameters"` + ServerStartupParameters map[string]string `json:"server_startup_parameters,omitempty"` } diff --git a/lib/gat/handlers/rewrite_parameters/module.go b/lib/gat/handlers/rewrite_parameter/module.go similarity index 53% rename from lib/gat/handlers/rewrite_parameters/module.go rename to lib/gat/handlers/rewrite_parameter/module.go index 44a988179bd80af82e666f8087cfdb0c2d4ea2fc..89b056fb57c4ef8248bcbcfc454e17270dc207aa 100644 --- a/lib/gat/handlers/rewrite_parameters/module.go +++ b/lib/gat/handlers/rewrite_parameter/module.go @@ -1,4 +1,4 @@ -package rewrite_parameters +package rewrite_parameter import ( "github.com/caddyserver/caddy/v2" @@ -13,42 +13,27 @@ func init() { } type Module struct { - Parameters map[string]string `json:"parameters"` - - parameters map[strutil.CIString]string + Key strutil.CIString `json:"key"` + Value string `json:"value"` } func (T *Module) CaddyModule() caddy.ModuleInfo { return caddy.ModuleInfo{ - ID: "pggat.handlers.rewrite_parameters", + ID: "pggat.handlers.rewrite_parameter", New: func() caddy.Module { return new(Module) }, } } -func (T *Module) Provision(ctx caddy.Context) error { - T.parameters = make(map[strutil.CIString]string, len(T.Parameters)) - - for key, value := range T.Parameters { - T.parameters[strutil.MakeCIString(key)] = value - } - - return nil -} - func (T *Module) Handle(conn *fed.Conn) error { if conn.InitialParameters == nil { conn.InitialParameters = make(map[strutil.CIString]string) } - - for key, value := range T.parameters { - conn.InitialParameters[key] = value - } + conn.InitialParameters[T.Key] = T.Value return nil } var _ gat.Handler = (*Module)(nil) var _ caddy.Module = (*Module)(nil) -var _ caddy.Provisioner = (*Module)(nil) diff --git a/lib/gat/matchers/not.go b/lib/gat/matchers/not.go new file mode 100644 index 0000000000000000000000000000000000000000..22e7facd65365abc35524ef2f09b436fec1044e1 --- /dev/null +++ b/lib/gat/matchers/not.go @@ -0,0 +1,51 @@ +package matchers + +import ( + "encoding/json" + "fmt" + + "github.com/caddyserver/caddy/v2" + + "gfx.cafe/gfx/pggat/lib/fed" + "gfx.cafe/gfx/pggat/lib/gat" +) + +func init() { + caddy.RegisterModule((*Not)(nil)) +} + +type Not struct { + Not json.RawMessage `json:"not" caddy:"namespace=pggat.matchers inline_key=matcher"` + + not gat.Matcher +} + +func (T *Not) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "pggat.matchers.not", + New: func() caddy.Module { + return new(Not) + }, + } +} + +func (T *Not) Provision(ctx caddy.Context) error { + if T.Not != nil { + val, err := ctx.LoadModule(T, "Not") + if err != nil { + return fmt.Errorf("loading matcher module: %v", err) + } + + T.not = val.(gat.Matcher) + } + + return nil +} + +func (T *Not) Matches(conn *fed.Conn) bool { + return !T.not.Matches(conn) +} + +var _ gat.Matcher = (*Not)(nil) +var _ caddy.Module = (*Not)(nil) +var _ caddy.Provisioner = (*Not)(nil) diff --git a/lib/gat/matchers/startupparameter.go b/lib/gat/matchers/startupparameter.go new file mode 100644 index 0000000000000000000000000000000000000000..2cbacf2425e96c8f32ae5f53c8ba0418ba717471 --- /dev/null +++ b/lib/gat/matchers/startupparameter.go @@ -0,0 +1,34 @@ +package matchers + +import ( + "github.com/caddyserver/caddy/v2" + + "gfx.cafe/gfx/pggat/lib/fed" + "gfx.cafe/gfx/pggat/lib/gat" + "gfx.cafe/gfx/pggat/lib/util/strutil" +) + +func init() { + caddy.RegisterModule((*StartupParameter)(nil)) +} + +type StartupParameter struct { + Key strutil.CIString `json:"key"` + Value string `json:"value"` +} + +func (T *StartupParameter) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "pggat.matchers.startup_parameter", + New: func() caddy.Module { + return new(StartupParameter) + }, + } +} + +func (T *StartupParameter) Matches(conn *fed.Conn) bool { + return conn.InitialParameters[T.Key] == T.Value +} + +var _ gat.Matcher = (*StartupParameter)(nil) +var _ caddy.Module = (*StartupParameter)(nil) diff --git a/lib/gat/matchers/startupparameters.go b/lib/gat/matchers/startupparameters.go deleted file mode 100644 index 975e4d6e7d27248d7c8f42fc31cf62578ce01cfe..0000000000000000000000000000000000000000 --- a/lib/gat/matchers/startupparameters.go +++ /dev/null @@ -1,50 +0,0 @@ -package matchers - -import ( - "github.com/caddyserver/caddy/v2" - - "gfx.cafe/gfx/pggat/lib/fed" - "gfx.cafe/gfx/pggat/lib/gat" - "gfx.cafe/gfx/pggat/lib/util/strutil" -) - -func init() { - caddy.RegisterModule((*StartupParameters)(nil)) -} - -type StartupParameters struct { - Parameters map[string]string `json:"startup_parameters"` - - parameters map[strutil.CIString]string -} - -func (T *StartupParameters) CaddyModule() caddy.ModuleInfo { - return caddy.ModuleInfo{ - ID: "pggat.matchers.startup_parameters", - New: func() caddy.Module { - return new(StartupParameters) - }, - } -} - -func (T *StartupParameters) Provision(ctx caddy.Context) error { - T.parameters = make(map[strutil.CIString]string, len(T.Parameters)) - for key, value := range T.Parameters { - T.parameters[strutil.MakeCIString(key)] = value - } - - return nil -} - -func (T *StartupParameters) Matches(conn *fed.Conn) bool { - for key, value := range T.parameters { - if conn.InitialParameters[key] != value { - return false - } - } - return true -} - -var _ gat.Matcher = (*StartupParameters)(nil) -var _ caddy.Module = (*StartupParameters)(nil) -var _ caddy.Provisioner = (*StartupParameters)(nil) diff --git a/lib/gat/standard/standard.go b/lib/gat/standard/standard.go index da50e5d3733f7971e8d74355f175b52ce4f6a389..bbe9dfe73b22fd7d956113e782c164779c3a4285 100644 --- a/lib/gat/standard/standard.go +++ b/lib/gat/standard/standard.go @@ -17,7 +17,7 @@ import ( _ "gfx.cafe/gfx/pggat/lib/gat/handlers/allowed_startup_parameters" _ "gfx.cafe/gfx/pggat/lib/gat/handlers/require_ssl" _ "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_database" - _ "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_parameters" + _ "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_parameter" _ "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_password" _ "gfx.cafe/gfx/pggat/lib/gat/handlers/rewrite_user"