A TLS probe result depends only on the set of endpoints actually dialed and the probe knobs, never on which domain or service published them: the observation is a map keyed by each endpoint's contract Ref (host|port|effective SNI|STARTTLS|require). Implement sdk.ObservationSharer so the host dials a host:port once and serves every target that resolves to the same endpoint set, instead of re-handshaking per record. This is the highest-value case among the checkers, since dane, xmpp, srv, dav, … all funnel their endpoints into this single checker. The share key sorts the endpoint Refs and folds in the probe timeout and the cipher-enumeration flag, since both change what is collected (a tighter timeout can fail a slow handshake; enumeration adds the Enum block). An empty or unparseable entry set yields "" so the host falls back to per-target caching.
81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
package checker
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
"git.happydns.org/checker-tls/contract"
|
|
)
|
|
|
|
func mustEntry(t *testing.T, ep contract.TLSEndpoint) sdk.DiscoveryEntry {
|
|
t.Helper()
|
|
e, err := contract.NewEntry(ep)
|
|
if err != nil {
|
|
t.Fatalf("NewEntry(%v): %v", ep, err)
|
|
}
|
|
return e
|
|
}
|
|
|
|
func TestShareKey_StableRegardlessOfOrder(t *testing.T) {
|
|
p := &tlsProvider{}
|
|
e1 := mustEntry(t, contract.TLSEndpoint{Host: "a.example.net", Port: 443})
|
|
e2 := mustEntry(t, contract.TLSEndpoint{Host: "b.example.net", Port: 25, STARTTLS: "smtp"})
|
|
|
|
a, err := p.ShareKey(sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{e1, e2}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := p.ShareKey(sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{e2, e1}})
|
|
if a != b {
|
|
t.Fatalf("key must be order-independent: %q vs %q", a, b)
|
|
}
|
|
if !strings.HasPrefix(a, "tls:") {
|
|
t.Fatalf("missing prefix: %q", a)
|
|
}
|
|
}
|
|
|
|
func TestShareKey_DiffersByEndpointSet(t *testing.T) {
|
|
p := &tlsProvider{}
|
|
e1 := mustEntry(t, contract.TLSEndpoint{Host: "a.example.net", Port: 443})
|
|
e2 := mustEntry(t, contract.TLSEndpoint{Host: "a.example.net", Port: 8443})
|
|
|
|
a, _ := p.ShareKey(sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{e1}})
|
|
b, _ := p.ShareKey(sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{e2}})
|
|
if a == b {
|
|
t.Fatalf("different endpoints must yield different keys")
|
|
}
|
|
}
|
|
|
|
func TestShareKey_DiffersByProbeKnobs(t *testing.T) {
|
|
p := &tlsProvider{}
|
|
e1 := mustEntry(t, contract.TLSEndpoint{Host: "a.example.net", Port: 443})
|
|
base := func(extra map[string]any) sdk.CheckerOptions {
|
|
o := sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{e1}}
|
|
for k, v := range extra {
|
|
o[k] = v
|
|
}
|
|
return o
|
|
}
|
|
|
|
def, _ := p.ShareKey(base(nil))
|
|
timeout, _ := p.ShareKey(base(map[string]any{OptionProbeTimeoutMs: float64(1000)}))
|
|
enum, _ := p.ShareKey(base(map[string]any{OptionEnumerateCiphers: true}))
|
|
|
|
if def == timeout {
|
|
t.Fatalf("probe timeout must affect the key")
|
|
}
|
|
if def == enum {
|
|
t.Fatalf("cipher enumeration must affect the key")
|
|
}
|
|
}
|
|
|
|
func TestShareKey_EmptyWhenNoEndpoints(t *testing.T) {
|
|
p := &tlsProvider{}
|
|
if sk, err := p.ShareKey(sdk.CheckerOptions{}); err != nil || sk != "" {
|
|
t.Fatalf("expected empty key, got %q (err=%v)", sk, err)
|
|
}
|
|
if sk, _ := p.ShareKey(sdk.CheckerOptions{OptionEndpoints: []sdk.DiscoveryEntry{}}); sk != "" {
|
|
t.Fatalf("expected empty key for empty entry set, got %q", sk)
|
|
}
|
|
}
|