···
| Invalid_config of string
| Invalid_endpoint of string
let pp_error ppf = function
| Dns_resolution_failed { hostname } ->
Fmt.pf ppf "DNS resolution failed for hostname: %s" hostname
···
let resolve_endpoint (pool : ('clock, 'net) internal) endpoint =
Log.debug (fun m -> m "Resolving %a..." Endpoint.pp endpoint);
+
Eio.Net.getaddrinfo_stream pool.net (Endpoint.host endpoint)
+
~service:(string_of_int (Endpoint.port endpoint))
+
Log.debug (fun m -> m "Got address list for %a" Endpoint.pp endpoint);
+
m "Resolved %a to %a" Endpoint.pp endpoint Eio.Net.Sockaddr.pp addr);
+
m "Failed to resolve hostname: %s" (Endpoint.host endpoint));
+
raise (err (Dns_resolution_failed { hostname = Endpoint.host endpoint }))
+
let bt = Printexc.get_raw_backtrace () in
+
Eio.Exn.reraise_with_context ex bt "resolving %a" Endpoint.pp endpoint
(** {1 Connection Creation with Retry} *)
···
m "Failed to connect to %a after %d attempts" Endpoint.pp endpoint
+
raise (err (Connection_failed { endpoint; attempts = retry_count; last_error }))
···
(* Connect with optional timeout *)
+
match Config.connect_timeout pool.config with
+
Eio.Time.with_timeout_exn pool.clock timeout (fun () ->
+
Eio.Net.connect ~sw:pool.sw pool.net addr)
+
| None -> Eio.Net.connect ~sw:pool.sw pool.net addr
+
let bt = Printexc.get_raw_backtrace () in
+
Eio.Exn.reraise_with_context ex bt "connecting to %a" Endpoint.pp endpoint
···
+
m "Initiating TLS handshake with %a" Endpoint.pp endpoint);
+
Domain_name.(host_exn (of_string_exn (Endpoint.host endpoint)))
+
let tls_flow = Tls_eio.client_of_flow ~host tls_config socket in
+
m "TLS connection established to %a" Endpoint.pp endpoint);
+
(tls_flow :> connection)
+
let bt = Printexc.get_raw_backtrace () in
+
Eio.Exn.reraise_with_context ex bt "TLS handshake with %a" Endpoint.pp endpoint
let now = get_time pool in
···
mutex = Eio.Mutex.create ();
m "Connection timeout to %a (attempt %d)" Endpoint.pp endpoint attempt);
if attempt >= Config.connect_retry_count pool.config then
(* Last attempt - convert to our error type *)
match Config.connect_timeout pool.config with
+
raise (err (Connection_timeout { endpoint; timeout }))
+
raise (err (Connection_failed
+
{ endpoint; attempts = attempt; last_error = "Timeout" }))
(* Retry with exponential backoff *)
···
*. (2.0 ** float_of_int (attempt - 1))
Eio.Time.sleep pool.clock delay;
+
create_connection_with_retry pool endpoint (attempt + 1) "Timeout"
+
(* Eio IO errors - retry with backoff and add context on final failure *)
+
let error_msg = Printexc.to_string ex in
m "Connection attempt %d to %a failed: %s" attempt Endpoint.pp
···
Eio.Time.sleep pool.clock delay;
create_connection_with_retry pool endpoint (attempt + 1) error_msg)
+
let bt = Printexc.get_raw_backtrace () in
+
Eio.Exn.reraise_with_context ex bt "after %d retry attempts" attempt
let create_connection (pool : ('clock, 'net) internal) endpoint =
create_connection_with_retry pool endpoint 1 "No attempts made"
···
let with_connection t endpoint f =
Eio.Switch.run (fun sw -> f (connection ~sw t endpoint))
(** {1 Public API - Statistics} *)