@@ -62,19 +62,108 @@ pub struct PgConfig {
6262 url : String ,
6363 schema : Option < String > ,
6464 tls : SslMode ,
65+ max_connections : usize ,
66+ connection_timeout : Duration ,
6567}
6668
6769impl DatabaseConfig for PgConfig {
6870 fn default_timeout ( & self ) -> Duration {
69- Duration :: from_secs ( 10 )
71+ self . connection_timeout
7072 }
7173
7274 fn max_size ( & self ) -> usize {
73- 20
75+ self . max_connections
76+ }
77+ }
78+
79+ /// Default maximum number of connections in the pool
80+ const DEFAULT_MAX_CONNECTIONS : usize = 20 ;
81+
82+ /// Default connection timeout in seconds
83+ const DEFAULT_CONNECTION_TIMEOUT_SECS : u64 = 10 ;
84+
85+ /// Build a TLS connector with the given certificate/hostname validation settings.
86+ fn build_tls ( accept_invalid_certs : bool , accept_invalid_hostnames : bool ) -> SslMode {
87+ let mut builder = TlsConnector :: builder ( ) ;
88+ if accept_invalid_certs {
89+ builder. danger_accept_invalid_certs ( true ) ;
90+ }
91+ if accept_invalid_hostnames {
92+ builder. danger_accept_invalid_hostnames ( true ) ;
93+ }
94+
95+ match builder. build ( ) {
96+ Ok ( connector) => {
97+ let make_tls_connector = MakeTlsConnector :: new ( connector) ;
98+ SslMode :: NativeTls ( make_tls_connector)
99+ }
100+ Err ( _) => SslMode :: NoTls ( NoTls { } ) ,
101+ }
102+ }
103+
104+ /// Determine TLS mode from the `sslmode=` parameter in a connection URL.
105+ fn ssl_mode_from_url ( url : & str ) -> SslMode {
106+ if url. contains ( SSLMODE_VERIFY_FULL ) {
107+ // Strict TLS: valid certs and hostnames required
108+ build_tls ( false , false )
109+ } else if url. contains ( SSLMODE_VERIFY_CA ) {
110+ // Verify CA, but allow invalid hostnames
111+ build_tls ( false , true )
112+ } else if url. contains ( SSLMODE_PREFER )
113+ || url. contains ( SSLMODE_ALLOW )
114+ || url. contains ( SSLMODE_REQUIRE )
115+ {
116+ // Lenient TLS for preferred/allow/require: accept invalid certs and hostnames
117+ build_tls ( true , true )
118+ } else {
119+ SslMode :: NoTls ( NoTls { } )
120+ }
121+ }
122+
123+ /// Resolve TLS mode from an explicit `tls_mode` string (from config/env), such
124+ /// as `"disable"`, `"prefer"`, `"require"`, `"verify-ca"`, or `"verify-full"`.
125+ ///
126+ /// If the value is `None`, falls back to parsing `sslmode=` from the URL.
127+ fn ssl_mode_from_config ( tls_mode : Option < & str > , url : & str ) -> SslMode {
128+ match tls_mode {
129+ Some ( mode) => match mode. to_lowercase ( ) . as_str ( ) {
130+ "verify-full" => build_tls ( false , false ) ,
131+ "verify-ca" => build_tls ( false , true ) ,
132+ "require" | "prefer" | "allow" => build_tls ( true , true ) ,
133+ // "disable" or any unrecognised value → no TLS
134+ _ => SslMode :: NoTls ( NoTls { } ) ,
135+ } ,
136+ // No explicit tls_mode: fall back to URL-based detection
137+ None => ssl_mode_from_url ( url) ,
74138 }
75139}
76140
77141impl PgConfig {
142+ /// Create a new `PgConfig` with explicit TLS mode, pool size, and timeout.
143+ ///
144+ /// `tls_mode` accepts the same strings as the configuration file:
145+ /// `"disable"`, `"prefer"`, `"allow"`, `"require"`, `"verify-ca"`,
146+ /// `"verify-full"`. When `None`, the TLS mode is inferred from
147+ /// `sslmode=` in the connection URL (matching the old behaviour).
148+ pub fn new (
149+ conn_str : & str ,
150+ tls_mode : Option < & str > ,
151+ max_connections : Option < usize > ,
152+ connection_timeout_secs : Option < u64 > ,
153+ ) -> Self {
154+ let ( schema, conn_str) = Self :: strip_schema ( conn_str) ;
155+ let tls = ssl_mode_from_config ( tls_mode, & conn_str) ;
156+ PgConfig {
157+ url : conn_str,
158+ schema,
159+ tls,
160+ max_connections : max_connections. unwrap_or ( DEFAULT_MAX_CONNECTIONS ) ,
161+ connection_timeout : Duration :: from_secs (
162+ connection_timeout_secs. unwrap_or ( DEFAULT_CONNECTION_TIMEOUT_SECS ) ,
163+ ) ,
164+ }
165+ }
166+
78167 /// strip schema from the connection string
79168 fn strip_schema ( input : & str ) -> ( Option < String > , String ) {
80169 let mut schema: Option < String > = None ;
@@ -97,44 +186,14 @@ impl PgConfig {
97186impl From < & str > for PgConfig {
98187 fn from ( conn_str : & str ) -> Self {
99188 let ( schema, conn_str) = Self :: strip_schema ( conn_str) ;
100- fn build_tls ( accept_invalid_certs : bool , accept_invalid_hostnames : bool ) -> SslMode {
101- let mut builder = TlsConnector :: builder ( ) ;
102- if accept_invalid_certs {
103- builder. danger_accept_invalid_certs ( true ) ;
104- }
105- if accept_invalid_hostnames {
106- builder. danger_accept_invalid_hostnames ( true ) ;
107- }
108-
109- match builder. build ( ) {
110- Ok ( connector) => {
111- let make_tls_connector = MakeTlsConnector :: new ( connector) ;
112- SslMode :: NativeTls ( make_tls_connector)
113- }
114- Err ( _) => SslMode :: NoTls ( NoTls { } ) ,
115- }
116- }
117-
118- let tls = if conn_str. contains ( SSLMODE_VERIFY_FULL ) {
119- // Strict TLS: valid certs and hostnames required
120- build_tls ( false , false )
121- } else if conn_str. contains ( SSLMODE_VERIFY_CA ) {
122- // Verify CA, but allow invalid hostnames
123- build_tls ( false , true )
124- } else if conn_str. contains ( SSLMODE_PREFER )
125- || conn_str. contains ( SSLMODE_ALLOW )
126- || conn_str. contains ( SSLMODE_REQUIRE )
127- {
128- // Lenient TLS for preferred/allow/require: accept invalid certs and hostnames
129- build_tls ( true , true )
130- } else {
131- SslMode :: NoTls ( NoTls { } )
132- } ;
189+ let tls = ssl_mode_from_url ( & conn_str) ;
133190
134191 PgConfig {
135- url : conn_str. to_owned ( ) ,
192+ url : conn_str,
136193 schema,
137194 tls,
195+ max_connections : DEFAULT_MAX_CONNECTIONS ,
196+ connection_timeout : Duration :: from_secs ( DEFAULT_CONNECTION_TIMEOUT_SECS ) ,
138197 }
139198 }
140199}
0 commit comments