initial commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Protocol constants (mirror of the Java Protocol class; see PROTOCOL.md).
|
||||
const (
|
||||
IntentRedapricot = 17
|
||||
ProtocolVersion = 767 // arbitrary; the hub ignores it
|
||||
|
||||
MagicControl = 0x01
|
||||
MagicWorker = 0x02
|
||||
|
||||
CIDLen = 16
|
||||
|
||||
CtlSessionReady = 0x00
|
||||
CtlRegister = 0x01
|
||||
CtlUnregister = 0x02
|
||||
CtlRegisterAck = 0x03
|
||||
CtlControlRequest = 0x04
|
||||
CtlPing = 0x05
|
||||
CtlPong = 0x06
|
||||
|
||||
MuxSyn = 0x00
|
||||
MuxData = 0x01
|
||||
MuxFin = 0x02
|
||||
MuxRst = 0x03
|
||||
|
||||
FrameError = 0x7F
|
||||
|
||||
SaturationThreshold = 8
|
||||
)
|
||||
|
||||
// Mapping routes a registered pattern to a real destination.
|
||||
type Mapping struct {
|
||||
Pattern string `json:"pattern"`
|
||||
Destination string `json:"destination"`
|
||||
ProxyProtocol bool `json:"proxyProtocol"`
|
||||
}
|
||||
|
||||
// Config is the client configuration (PROTOCOL.md §9.2).
|
||||
type Config struct {
|
||||
Server string `json:"server"`
|
||||
PSK string `json:"psk"`
|
||||
MaxConn int `json:"maxConn"`
|
||||
PingIntervalMs int `json:"pingIntervalMs"`
|
||||
Mappings []Mapping `json:"mappings"`
|
||||
}
|
||||
|
||||
// LoadConfig reads and validates a JSON config file.
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var c Config
|
||||
if err := json.Unmarshal(data, &c); err != nil {
|
||||
return nil, fmt.Errorf("parse config: %w", err)
|
||||
}
|
||||
if c.Server == "" {
|
||||
return nil, fmt.Errorf("server is required")
|
||||
}
|
||||
if c.PSK == "" {
|
||||
return nil, fmt.Errorf("psk is required")
|
||||
}
|
||||
if c.MaxConn < 1 {
|
||||
c.MaxConn = 1
|
||||
}
|
||||
if c.MaxConn > 8 {
|
||||
c.MaxConn = 8
|
||||
}
|
||||
if c.PingIntervalMs <= 0 {
|
||||
c.PingIntervalMs = 20000
|
||||
}
|
||||
if len(c.Mappings) == 0 {
|
||||
return nil, fmt.Errorf("at least one mapping is required")
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// NormalizeAddress matches the hub's normalization: lower-cased, FML-suffix and
|
||||
// trailing-dot stripped.
|
||||
func NormalizeAddress(addr string) string {
|
||||
if i := strings.IndexByte(addr, 0); i >= 0 {
|
||||
addr = addr[:i]
|
||||
}
|
||||
addr = strings.ToLower(addr)
|
||||
addr = strings.TrimRight(addr, ".")
|
||||
return addr
|
||||
}
|
||||
Reference in New Issue
Block a user