@@ -3,8 +3,10 @@ package fake
33import (
44 "crypto/tls"
55 "encoding/binary"
6+ "encoding/json"
67 "fmt"
78 "net"
9+ "os"
810 "sync"
911 "time"
1012)
@@ -13,15 +15,76 @@ const (
1315 probeDialTimeout = 10 * time .Second
1416 probeHandshakeTimeout = 10 * time .Second
1517 defaultProbeCount = 15
18+ defaultCacheTTL = 24 * time .Hour
1619
1720 tlsTypeChangeCipherSpec = 0x14
1821 tlsTypeApplicationData = 0x17
1922)
2023
2124// CertProbeResult holds the measured encrypted handshake size.
2225type CertProbeResult struct {
23- Mean int
24- Jitter int
26+ Mean int `json:"mean"`
27+ Jitter int `json:"jitter"`
28+ }
29+
30+ // CertProbeCache is the on-disk format for cached probe results.
31+ type CertProbeCache struct {
32+ Hostname string `json:"hostname"`
33+ Port int `json:"port"`
34+ Mean int `json:"mean"`
35+ Jitter int `json:"jitter"`
36+ ProbedAt time.Time `json:"probed_at"`
37+ }
38+
39+ // LoadCachedProbe reads a cached probe result from path. Returns the result
40+ // and true if the cache exists, matches hostname:port, and is younger than ttl.
41+ // Otherwise returns zero value and false.
42+ func LoadCachedProbe (path , hostname string , port int , ttl time.Duration ) (CertProbeResult , bool ) {
43+ if ttl <= 0 {
44+ ttl = defaultCacheTTL
45+ }
46+
47+ data , err := os .ReadFile (path )
48+ if err != nil {
49+ return CertProbeResult {}, false
50+ }
51+
52+ var cache CertProbeCache
53+ if err := json .Unmarshal (data , & cache ); err != nil {
54+ return CertProbeResult {}, false
55+ }
56+
57+ if cache .Hostname != hostname || cache .Port != port {
58+ return CertProbeResult {}, false
59+ }
60+
61+ if time .Since (cache .ProbedAt ) > ttl {
62+ return CertProbeResult {}, false
63+ }
64+
65+ if cache .Mean <= 0 {
66+ return CertProbeResult {}, false
67+ }
68+
69+ return CertProbeResult {Mean : cache .Mean , Jitter : cache .Jitter }, true
70+ }
71+
72+ // SaveCachedProbe writes a probe result to path as JSON.
73+ func SaveCachedProbe (path , hostname string , port int , result CertProbeResult ) error {
74+ cache := CertProbeCache {
75+ Hostname : hostname ,
76+ Port : port ,
77+ Mean : result .Mean ,
78+ Jitter : result .Jitter ,
79+ ProbedAt : time .Now (),
80+ }
81+
82+ data , err := json .MarshalIndent (cache , "" , " " )
83+ if err != nil {
84+ return err
85+ }
86+
87+ return os .WriteFile (path , data , 0o644 ) //nolint: gosec
2588}
2689
2790// ProbeCertSize connects to hostname:port via TLS multiple times and measures
0 commit comments