CI / test (push) Successful in 2m32s
Routes Java Edition connections by the hostname in the handshake, with wildcard domains, backend failover, PROXY protocol v2, cached server-list status from health probes, hot-reloaded config, and an admin page and metrics endpoint. Includes CI, tagged releases and a container image. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
// Package router maps the hostname a client typed to a configured server.
|
|
package router
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.veillax.com/Veillax/spinner/internal/config"
|
|
)
|
|
|
|
type Router struct {
|
|
exact map[string]*config.Server
|
|
wild []wildcard
|
|
}
|
|
|
|
type wildcard struct {
|
|
suffix string // ".example.com"
|
|
srv *config.Server
|
|
}
|
|
|
|
// Normalize lowercases a handshake host and strips the trailing dot and any
|
|
// Forge "\0FML\0" suffix.
|
|
func Normalize(host string) string {
|
|
if i := strings.IndexByte(host, 0); i >= 0 {
|
|
host = host[:i]
|
|
}
|
|
return strings.ToLower(strings.TrimRight(host, "."))
|
|
}
|
|
|
|
// New builds a router. Exact domains win; wildcards (*.example.com) match any
|
|
// depth of subdomain but not the apex, and longer suffixes win over shorter.
|
|
func New(cfg *config.Config) *Router {
|
|
r := &Router{exact: map[string]*config.Server{}}
|
|
for i := range cfg.Servers {
|
|
s := &cfg.Servers[i]
|
|
for _, d := range s.Domains {
|
|
d = strings.ToLower(strings.TrimRight(d, "."))
|
|
if rest, ok := strings.CutPrefix(d, "*."); ok {
|
|
r.wild = append(r.wild, wildcard{"." + rest, s})
|
|
} else if _, dup := r.exact[d]; !dup {
|
|
r.exact[d] = s
|
|
}
|
|
}
|
|
}
|
|
sort.SliceStable(r.wild, func(i, j int) bool { return len(r.wild[i].suffix) > len(r.wild[j].suffix) })
|
|
return r
|
|
}
|
|
|
|
func (r *Router) Match(host string) *config.Server {
|
|
host = Normalize(host)
|
|
if s, ok := r.exact[host]; ok {
|
|
return s
|
|
}
|
|
for _, w := range r.wild {
|
|
if strings.HasSuffix(host, w.suffix) && len(host) > len(w.suffix) {
|
|
return w.srv
|
|
}
|
|
}
|
|
return nil
|
|
}
|