From 74359955adea9a139eecc11985ca47b1e9268dde Mon Sep 17 00:00:00 2001 From: Pulse Monitor Date: Sat, 6 Sep 2025 10:07:10 +0000 Subject: [PATCH] fix: properly handle PBS connection timeouts with granular timeout settings The real issue was not the overall timeout duration, but that DNS resolution and TLS handshake could hang indefinitely. Added specific timeouts for: - DNS resolution/connection: 10 seconds - TLS handshake: 10 seconds - Response headers: 10 seconds This prevents the connection from hanging on DNS lookup (like with pve-backup.lan) or during TLS negotiation, which was causing the 'context deadline exceeded' errors. (addresses #424) --- internal/api/diagnostics.go | 11 +++-------- pkg/tlsutil/fingerprint.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/internal/api/diagnostics.go b/internal/api/diagnostics.go index f1f4581cd..72d0f2af7 100644 --- a/internal/api/diagnostics.go +++ b/internal/api/diagnostics.go @@ -226,11 +226,7 @@ func (r *Router) handleDiagnostics(w http.ResponseWriter, req *http.Request) { Host: pbsNode.Host, } - // Test connection with a shorter timeout for PBS specifically - // PBS can be slow to respond, so we use a separate 30-second timeout - pbsCtx, pbsCancel := context.WithTimeout(context.Background(), 30*time.Second) - defer pbsCancel() - + // Test connection testCfg := pbs.ClientConfig{ Host: pbsNode.Host, User: pbsNode.User, @@ -239,7 +235,6 @@ func (r *Router) handleDiagnostics(w http.ResponseWriter, req *http.Request) { TokenValue: pbsNode.TokenValue, Fingerprint: pbsNode.Fingerprint, VerifySSL: pbsNode.VerifySSL, - Timeout: 30 * time.Second, // Set explicit timeout in client config } client, err := pbs.NewClient(testCfg) @@ -247,8 +242,8 @@ func (r *Router) handleDiagnostics(w http.ResponseWriter, req *http.Request) { pbsDiag.Connected = false pbsDiag.Error = err.Error() } else { - // Try to get version with PBS-specific context - if version, err := client.GetVersion(pbsCtx); err != nil { + // Try to get version + if version, err := client.GetVersion(ctx); err != nil { pbsDiag.Connected = false pbsDiag.Error = "Connection established but version check failed: " + err.Error() } else { diff --git a/pkg/tlsutil/fingerprint.go b/pkg/tlsutil/fingerprint.go index 53c68183b..f6ac0c49b 100644 --- a/pkg/tlsutil/fingerprint.go +++ b/pkg/tlsutil/fingerprint.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/hex" "fmt" + "net" "net/http" "strings" "time" @@ -52,6 +53,15 @@ func CreateHTTPClientWithTimeout(verifySSL bool, fingerprint string, timeout tim MaxConnsPerHost: 20, // Limit concurrent connections per host IdleConnTimeout: 90 * time.Second, DisableCompression: true, // Disable compression for lower latency + // Add specific timeouts for DNS, TLS handshake, and response headers + // These prevent hanging on DNS resolution or TLS negotiation + DialContext: (&net.Dialer{ + Timeout: 10 * time.Second, // Connection timeout + KeepAlive: 30 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 10 * time.Second, // TLS handshake timeout + ResponseHeaderTimeout: 10 * time.Second, // Time to wait for response headers + ExpectContinueTimeout: 1 * time.Second, } if !verifySSL && fingerprint == "" {