Initial commit: stats-gateway gRPC service + dashboard + CLI

This commit is contained in:
2026-05-05 21:04:32 +05:00
commit 4059e94759
44 changed files with 2513 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
package probes
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/http/httptrace"
"time"
)
// Result holds timing measurements from a single HTTP probe.
type Result struct {
Node string // populated by storage on read; ignored by InsertSample (uses separate arg)
URL string
HTTPLatencyMs float64
TCPRttMs float64
DNSResolveMs float64
TLSHandshakeMs float64
ThroughputKbps int64
Timestamp time.Time
}
// ProbeHTTP performs an HTTP HEAD request to url, recording timing via httptrace.
func ProbeHTTP(ctx context.Context, url string) (*Result, error) {
r := &Result{URL: url, Timestamp: time.Now().UTC()}
var dnsStart, connStart, tlsStart time.Time
trace := &httptrace.ClientTrace{
DNSStart: func(_ httptrace.DNSStartInfo) { dnsStart = time.Now() },
DNSDone: func(_ httptrace.DNSDoneInfo) {
if !dnsStart.IsZero() {
r.DNSResolveMs = float64(time.Since(dnsStart).Microseconds()) / 1000
}
},
ConnectStart: func(_, _ string) { connStart = time.Now() },
ConnectDone: func(_, _ string, _ error) {
if !connStart.IsZero() {
r.TCPRttMs = float64(time.Since(connStart).Microseconds()) / 1000
}
},
TLSHandshakeStart: func() { tlsStart = time.Now() },
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
if !tlsStart.IsZero() {
r.TLSHandshakeMs = float64(time.Since(tlsStart).Microseconds()) / 1000
}
},
}
req, err := http.NewRequestWithContext(httptrace.WithClientTrace(ctx, trace), "HEAD", url, nil)
if err != nil {
return nil, fmt.Errorf("new request: %w", err)
}
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{Timeout: 5 * time.Second}).DialContext,
},
}
start := time.Now()
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("http: %w", err)
}
defer resp.Body.Close()
r.HTTPLatencyMs = float64(time.Since(start).Microseconds()) / 1000
return r, nil
}