@@ -6,13 +6,19 @@ use quinn::{Connection, Endpoint};
66use std:: net:: { SocketAddr , SocketAddrV4 , SocketAddrV6 } ;
77use std:: ops:: Deref ;
88use std:: sync:: Arc ;
9+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
910use tracing:: { debug, instrument, warn} ;
1011use url:: Host ;
1112
1213#[ derive( Clone ) ]
1314pub struct QuicConnection {
15+ inner : Arc < QuicConnectionInner > ,
16+ }
17+
18+ pub struct QuicConnectionInner {
1419 config : Arc < WsClientConfig > ,
1520 endpoint : Endpoint ,
21+ is_broken : AtomicBool ,
1622}
1723
1824impl QuicConnection {
@@ -30,22 +36,20 @@ impl QuicConnection {
3036 let _ = socket. set_send_buffer_size ( requested_size) ;
3137 let _ = socket. set_recv_buffer_size ( requested_size) ;
3238
33- if let Ok ( size) = socket. send_buffer_size ( ) {
34- if size < requested_size && config. quic_socket_buffer_size > 0 {
39+ if let Ok ( size) = socket. send_buffer_size ( )
40+ && size < requested_size && config. quic_socket_buffer_size > 0 {
3541 warn ! (
3642 "QUIC UDP send buffer size is small: {} bytes. This may limit throughput. Consider increasing net.core.wmem_max." ,
3743 size
3844 ) ;
3945 }
40- }
41- if let Ok ( size) = socket. recv_buffer_size ( ) {
42- if size < requested_size && config. quic_socket_buffer_size > 0 {
46+ if let Ok ( size) = socket. recv_buffer_size ( )
47+ && size < requested_size && config. quic_socket_buffer_size > 0 {
4348 warn ! (
4449 "QUIC UDP recv buffer size is small: {} bytes. This may limit throughput. Consider increasing net.core.rmem_max." ,
4550 size
4651 ) ;
4752 }
48- }
4953
5054 let addr = SocketAddr :: V4 ( SocketAddrV4 :: new ( std:: net:: Ipv4Addr :: UNSPECIFIED , 0 ) ) ;
5155 socket. bind ( & addr. into ( ) ) . expect ( "Failed to bind UDP socket" ) ;
@@ -55,15 +59,21 @@ impl QuicConnection {
5559 let endpoint = Endpoint :: new ( quinn:: EndpointConfig :: default ( ) , None , socket, Arc :: new ( quinn:: TokioRuntime ) )
5660 . expect ( "Failed to create QUIC endpoint" ) ;
5761
58- Self { config, endpoint }
62+ Self {
63+ inner : Arc :: new ( QuicConnectionInner {
64+ config,
65+ endpoint,
66+ is_broken : AtomicBool :: new ( false ) ,
67+ } ) ,
68+ }
5969 }
6070}
6171
6272impl Deref for QuicConnection {
63- type Target = WsClientConfig ;
73+ type Target = QuicConnectionInner ;
6474
6575 fn deref ( & self ) -> & Self :: Target {
66- & self . config
76+ & self . inner
6777 }
6878}
6979
@@ -74,12 +84,15 @@ impl ManageConnection for QuicConnection {
7484 #[ instrument( level = "trace" , name = "quic_cnx_server" , skip_all) ]
7585 async fn connect ( & self ) -> Result < Self :: Connection , Self :: Error > {
7686 // 1. Resolve DNS
77- let host = self . remote_addr . host ( ) ;
78- let port = self . remote_addr . port ( ) ;
87+ self . inner . is_broken . store ( false , Ordering :: SeqCst ) ;
88+ let host = self . inner . config . remote_addr . host ( ) ;
89+ let port = self . inner . config . remote_addr . port ( ) ;
7990
8091 let remote_addr = match host {
8192 Host :: Domain ( domain) => {
8293 let addrs = self
94+ . inner
95+ . config
8396 . dns_resolver
8497 . lookup_host ( domain, port)
8598 . await
@@ -95,6 +108,8 @@ impl ManageConnection for QuicConnection {
95108
96109 // 2. Get TLS configuration
97110 let tls_config = self
111+ . inner
112+ . config
98113 . remote_addr
99114 . tls ( )
100115 . ok_or_else ( || anyhow ! ( "QUIC requires TLS configuration" ) ) ?;
@@ -111,7 +126,7 @@ impl ManageConnection for QuicConnection {
111126 debug ! (
112127 "Creating QUIC client config for {} (SNI: {:?}), mTLS: {}" ,
113128 remote_addr,
114- self . tls_server_name( ) ,
129+ self . inner . config . tls_server_name( ) ,
115130 tls_client_certificate. is_some( )
116131 ) ;
117132
@@ -134,6 +149,8 @@ impl ManageConnection for QuicConnection {
134149 // Configure max idle timeout
135150 // Use 10 minutes by default to support long-lived reverse tunnels and file transfers
136151 let idle_timeout = self
152+ . inner
153+ . config
137154 . quic_max_idle_timeout
138155 . unwrap_or ( std:: time:: Duration :: from_secs ( 600 ) ) ;
139156 debug ! ( "QUIC idle timeout: {}s" , idle_timeout. as_secs( ) ) ;
@@ -142,13 +159,19 @@ impl ManageConnection for QuicConnection {
142159 ) ) ) ;
143160
144161 // Configure keep-alive interval
145- debug ! ( "QUIC keep-alive interval: {}s" , self . quic_keep_alive_interval. as_secs( ) ) ;
146- transport_config. keep_alive_interval ( Some ( self . quic_keep_alive_interval ) ) ;
162+ debug ! (
163+ "QUIC keep-alive interval: {}s" ,
164+ self . inner. config. quic_keep_alive_interval. as_secs( )
165+ ) ;
166+ transport_config. keep_alive_interval ( Some ( self . inner . config . quic_keep_alive_interval ) ) ;
147167
148168 // Configure stream limits
149- debug ! ( "QUIC concurrent streams: {} bidirectional" , self . quic_max_concurrent_bi_streams) ;
169+ debug ! (
170+ "QUIC concurrent streams: {} bidirectional" ,
171+ self . inner. config. quic_max_concurrent_bi_streams
172+ ) ;
150173 transport_config. max_concurrent_bidi_streams (
151- quinn:: VarInt :: from_u64 ( self . quic_max_concurrent_bi_streams )
174+ quinn:: VarInt :: from_u64 ( self . inner . config . quic_max_concurrent_bi_streams )
152175 . expect ( "QUIC concurrent bidirectional streams limit too large" ) ,
153176 ) ;
154177 transport_config. max_concurrent_uni_streams ( 0u32 . into ( ) ) ; // We don't use unidirectional streams
@@ -157,20 +180,21 @@ impl ManageConnection for QuicConnection {
157180 // Connection-level flow control (total data across all streams)
158181 debug ! (
159182 "QUIC flow control - connection: {} bytes, stream: {} bytes" ,
160- self . quic_initial_max_data, self . quic_initial_max_stream_data
183+ self . inner . config . quic_initial_max_data, self . inner . config . quic_initial_max_stream_data
161184 ) ;
162185 transport_config. receive_window (
163- quinn:: VarInt :: from_u64 ( self . quic_initial_max_data ) . expect ( "QUIC initial max data limit too large" ) ,
186+ quinn:: VarInt :: from_u64 ( self . inner . config . quic_initial_max_data )
187+ . expect ( "QUIC initial max data limit too large" ) ,
164188 ) ;
165- transport_config. send_window ( self . quic_initial_max_data ) ;
189+ transport_config. send_window ( self . inner . config . quic_initial_max_data ) ;
166190
167191 // Per-stream flow control
168192 transport_config. stream_receive_window (
169- quinn:: VarInt :: from_u64 ( self . quic_initial_max_stream_data )
193+ quinn:: VarInt :: from_u64 ( self . inner . config . quic_initial_max_stream_data )
170194 . expect ( "QUIC initial max stream data limit too large" ) ,
171195 ) ;
172196
173- if let Some ( mtu) = self . quic_initial_mtu {
197+ if let Some ( mtu) = self . inner . config . quic_initial_mtu {
174198 transport_config. initial_mtu ( mtu) ;
175199 }
176200
@@ -180,11 +204,13 @@ impl ManageConnection for QuicConnection {
180204 debug ! (
181205 "Initiating QUIC connection to {} (SNI: {:?})" ,
182206 remote_addr,
183- self . tls_server_name( )
207+ self . inner . config . tls_server_name( )
184208 ) ;
185- let connecting =
186- self . endpoint
187- . connect_with ( client_config, remote_addr, self . tls_server_name ( ) . to_str ( ) . as_ref ( ) ) ?;
209+ let connecting = self . endpoint . connect_with (
210+ client_config,
211+ remote_addr,
212+ self . inner . config . tls_server_name ( ) . to_str ( ) . as_ref ( ) ,
213+ ) ?;
188214
189215 debug ! ( "Waiting for QUIC handshake to complete..." ) ;
190216 let connection = match connecting. await {
@@ -223,9 +249,20 @@ impl ManageConnection for QuicConnection {
223249 }
224250
225251 fn has_broken ( & self , conn : & mut Self :: Connection ) -> bool {
252+ if self . inner . is_broken . load ( Ordering :: SeqCst ) {
253+ warn ! ( "Connection pool: Connection marked as broken, discarding" ) ;
254+ return true ;
255+ }
256+
226257 match conn {
227- Some ( c) => c. close_reason ( ) . is_some ( ) ,
228- None => true ,
258+ Some ( c) => {
259+ if c. close_reason ( ) . is_some ( ) {
260+ warn ! ( "Connection pool: Connection has close_reason, discarding" ) ;
261+ return true ;
262+ }
263+ false
264+ }
265+ None => true , // No connection, so it's "broken"
229266 }
230267 }
231268}
0 commit comments