A route may have its own listener: every request there is that route, paths unprefixed
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -2,8 +2,10 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -22,6 +24,7 @@ type Route struct {
|
||||
Peers []string `toml:"peers"`
|
||||
Affinity string `toml:"affinity"` // "" or "conversation" (the default), or "route"
|
||||
Queue *bool `toml:"queue"` // nil means true
|
||||
Listen string `toml:"listen"` // "" = none; else host:port of the route's own listener
|
||||
}
|
||||
|
||||
// PerRoute reports affinity = "route": every request on the route (chat or control) shares one
|
||||
@@ -80,6 +83,9 @@ func (c *Config) checkRoutes(peersDefined map[string]bool, identityDefined bool)
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
// A route's own listener address, keyed for the uniqueness check: the value is the
|
||||
// route that first claimed it, so the second route in sorted order reports the miss.
|
||||
seenListen := make(map[string]string, len(c.Routes))
|
||||
for _, name := range names {
|
||||
r := c.Routes[name]
|
||||
|
||||
@@ -124,6 +130,39 @@ func (c *Config) checkRoutes(peersDefined map[string]bool, identityDefined bool)
|
||||
if e := checkPeers(name, r.Peers, peersDefined[name], identityDefined, c.Identity); e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
if e := checkListen(name, r.Listen, c.Listen, seenListen); e != nil {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkListen validates one route's own listener. The rules, in order, each naming the key
|
||||
// routes.<name>.listen: the value must split into host and a numeric port 1-65535, it must not be on
|
||||
// a template route, it must not be the top-level listen, and it must be unique across routes (the
|
||||
// second route in sorted name order reports the clash and the other route's name).
|
||||
func checkListen(name, listen, mainListen string, seenListen map[string]string) *Error {
|
||||
if listen == "" {
|
||||
return nil
|
||||
}
|
||||
_, port, err := net.SplitHostPort(listen)
|
||||
if err != nil {
|
||||
return &Error{Field: fmt.Sprintf("routes.%s.listen", name), Msg: "must be host:port"}
|
||||
}
|
||||
n, err := strconv.Atoi(port)
|
||||
if err != nil || n < 1 || n > 65535 {
|
||||
return &Error{Field: fmt.Sprintf("routes.%s.listen", name), Msg: "port must be 1-65535"}
|
||||
}
|
||||
if templateName.MatchString(name) {
|
||||
return &Error{Field: fmt.Sprintf("routes.%s.listen", name), Msg: "a template route cannot have its own listener"}
|
||||
}
|
||||
if listen == mainListen {
|
||||
return &Error{Field: fmt.Sprintf("routes.%s.listen", name), Msg: "cannot be the main listen address"}
|
||||
}
|
||||
if other, dup := seenListen[listen]; dup {
|
||||
return &Error{Field: fmt.Sprintf("routes.%s.listen", name), Msg: fmt.Sprintf("already used by route %s", other)}
|
||||
}
|
||||
seenListen[listen] = name
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user