···13131414type t = {
1515 flow : [Eio.Resource.close_ty | Eio.Flow.two_way_ty] Eio.Resource.t;
1616+ tls_flow : Tls_eio.t option;
1617 created_at : float;
1718 mutable last_used : float;
1819 mutable use_count : int;
···2122}
22232324let flow t = t.flow
2525+let tls_flow t = t.tls_flow
2426let endpoint t = t.endpoint
2527let created_at t = t.created_at
2628let last_used t = t.last_used
+75-4
lib/conpool.ml
···5757type connection_ty = [Eio.Resource.close_ty | Eio.Flow.two_way_ty]
5858type connection = connection_ty Eio.Resource.t
59596060+type connection_with_info = {
6161+ flow : connection;
6262+ tls_epoch : Tls.Core.epoch_data option;
6363+}
6464+6065type endp_stats = {
6166 mutable active : int;
6267 mutable idle : int;
···161166 Log.debug (fun m ->
162167 m "TCP connection established to %a" Endpoint.pp endpoint);
163168164164- let flow =
169169+ let flow, tls_flow =
165170 match pool.tls with
166171 | None ->
167167- (socket :> connection)
172172+ ((socket :> connection), None)
168173 | Some tls_config ->
169174 try
170175 Log.debug (fun m ->
···172177 let host =
173178 Domain_name.(host_exn (of_string_exn (Endpoint.host endpoint)))
174179 in
175175- let tls_flow = Tls_eio.client_of_flow ~host tls_config socket in
180180+ let tls = Tls_eio.client_of_flow ~host tls_config socket in
176181 Log.info (fun m ->
177182 m "TLS connection established to %a" Endpoint.pp endpoint);
178178- (tls_flow :> connection)
183183+ ((tls :> connection), Some tls)
179184 with Eio.Io _ as ex ->
180185 let bt = Printexc.get_raw_backtrace () in
181186 Eio.Exn.reraise_with_context ex bt "TLS handshake with %a" Endpoint.pp endpoint
···185190 Log.info (fun m -> m "Connection created to %a" Endpoint.pp endpoint);
186191 {
187192 Connection.flow;
193193+ tls_flow;
188194 created_at = now;
189195 last_used = now;
190196 use_count = 0;
···507513 Eio.Promise.await conn_promise
508514509515let connection ~sw t endpoint = connection_internal ~sw t endpoint
516516+517517+let connection_with_info_internal ~sw (T pool) endpoint =
518518+ Log.debug (fun m -> m "Acquiring connection with TLS info to %a" Endpoint.pp endpoint);
519519+ let ep_pool = get_or_create_endpoint_pool pool endpoint in
520520+521521+ (* Create promises for connection handoff and cleanup signal *)
522522+ let conn_promise, conn_resolver = Eio.Promise.create () in
523523+ let done_promise, done_resolver = Eio.Promise.create () in
524524+525525+ (* Increment active count *)
526526+ Eio.Mutex.use_rw ~protect:true ep_pool.mutex (fun () ->
527527+ ep_pool.stats.active <- ep_pool.stats.active + 1);
528528+529529+ (* Fork a daemon fiber to manage the connection lifecycle *)
530530+ Eio.Fiber.fork_daemon ~sw:pool.sw (fun () ->
531531+ Fun.protect
532532+ ~finally:(fun () ->
533533+ Eio.Mutex.use_rw ~protect:true ep_pool.mutex (fun () ->
534534+ ep_pool.stats.active <- ep_pool.stats.active - 1);
535535+ Log.debug (fun m -> m "Released connection to %a" Endpoint.pp endpoint))
536536+ (fun () ->
537537+ Eio.Pool.use ep_pool.pool (fun conn ->
538538+ Log.debug (fun m ->
539539+ m "Using connection to %a (uses=%d)" Endpoint.pp endpoint
540540+ (Connection.use_count conn));
541541+542542+ Connection.update_usage conn ~now:(get_time pool);
543543+544544+ Eio.Mutex.use_rw ~protect:true ep_pool.mutex (fun () ->
545545+ ep_pool.stats.idle <- max 0 (ep_pool.stats.idle - 1));
546546+547547+ (* Get TLS epoch if available *)
548548+ let tls_epoch =
549549+ match Connection.tls_flow conn with
550550+ | Some tls_flow -> (
551551+ match Tls_eio.epoch tls_flow with
552552+ | Ok epoch -> Some epoch
553553+ | Error () -> None)
554554+ | None -> None
555555+ in
556556+557557+ (* Hand off connection with TLS info to caller *)
558558+ Eio.Promise.resolve conn_resolver { flow = conn.flow; tls_epoch };
559559+560560+ try
561561+ Eio.Promise.await done_promise;
562562+563563+ Eio.Mutex.use_rw ~protect:true ep_pool.mutex (fun () ->
564564+ ep_pool.stats.idle <- ep_pool.stats.idle + 1);
565565+566566+ `Stop_daemon
567567+ with e ->
568568+ close_internal pool conn;
569569+570570+ Eio.Mutex.use_rw ~protect:true ep_pool.mutex (fun () ->
571571+ ep_pool.stats.errors <- ep_pool.stats.errors + 1);
572572+573573+ raise e)));
574574+575575+ Eio.Switch.on_release sw (fun () ->
576576+ Eio.Promise.resolve done_resolver ());
577577+578578+ Eio.Promise.await conn_promise
579579+580580+let connection_with_info ~sw t endpoint = connection_with_info_internal ~sw t endpoint
510581511582let with_connection t endpoint f =
512583 Eio.Switch.run (fun sw -> f (connection ~sw t endpoint))
+37
lib/conpool.mli
···9898type connection = connection_ty Eio.Resource.t
9999(** A connection resource from the pool. *)
100100101101+type connection_with_info = {
102102+ flow : connection;
103103+ tls_epoch : Tls.Core.epoch_data option;
104104+}
105105+(** A connection with additional TLS information.
106106+107107+ The [tls_epoch] field contains the TLS session data if this connection
108108+ uses TLS, or [None] for plaintext connections. This is needed for
109109+ protocols like ACE-MQTT (RFC 9431) that require access to TLS exporter
110110+ material for proof-of-possession. *)
111111+101112(** {1 Connection Pool} *)
102113103114type t
···146157 Eio.Flow.copy_string "GET / HTTP/1.1\r\n\r\n" conn;
147158 let buf = Eio.Buf_read.of_flow conn ~max_size:4096 in
148159 Eio.Buf_read.take_all buf)
160160+ ]} *)
161161+162162+val connection_with_info : sw:Eio.Switch.t -> t -> Endpoint.t -> connection_with_info
163163+(** [connection_with_info ~sw pool endpoint] acquires a connection with TLS info.
164164+165165+ Like {!connection}, but returns a record containing both the connection flow
166166+ and TLS epoch data (if the connection uses TLS).
167167+168168+ This is useful for protocols that need access to TLS session information,
169169+ such as ACE-MQTT (RFC 9431) which uses TLS exporter material for
170170+ proof-of-possession authentication.
171171+172172+ Example:
173173+ {[
174174+ let endpoint = Conpool.Endpoint.make ~host:"example.com" ~port:443 in
175175+ Eio.Switch.run (fun sw ->
176176+ let info = Conpool.connection_with_info ~sw pool endpoint in
177177+ (* Access TLS epoch for PoP authentication *)
178178+ match info.tls_epoch with
179179+ | Some epoch ->
180180+ let challenge = Tls.Engine.export_key_material epoch label 32 in
181181+ (* Use challenge for authentication *)
182182+ | None ->
183183+ failwith "TLS required for ACE authentication";
184184+ (* Use info.flow for MQTT communication *)
185185+ Eio.Flow.copy_string data info.flow)
149186 ]} *)
150187151188val with_connection : t -> Endpoint.t -> (connection -> 'a) -> 'a