@@ -14,6 +14,8 @@ use crate::{Client, Connection, Error};
14
14
use std:: borrow:: Cow ;
15
15
#[ cfg( unix) ]
16
16
use std:: ffi:: OsStr ;
17
+ use std:: net:: IpAddr ;
18
+ use std:: ops:: Deref ;
17
19
#[ cfg( unix) ]
18
20
use std:: os:: unix:: ffi:: OsStrExt ;
19
21
#[ cfg( unix) ]
@@ -92,6 +94,19 @@ pub enum Host {
92
94
/// path to the directory containing Unix domain sockets. Otherwise, it is treated as a hostname. Multiple hosts
93
95
/// can be specified, separated by commas. Each host will be tried in turn when connecting. Required if connecting
94
96
/// with the `connect` method.
97
+ /// * `hostaddr` - Numeric IP address of host to connect to. This should be in the standard IPv4 address format,
98
+ /// e.g., 172.28.40.9. If your machine supports IPv6, you can also use those addresses.
99
+ /// If this parameter is not specified, the value of `host` will be looked up to find the corresponding IP address,
100
+ /// - or if host specifies an IP address, that value will be used directly.
101
+ /// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications
102
+ /// with time constraints. However, a host name is required for verify-full SSL certificate verification.
103
+ /// Specifically:
104
+ /// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address.
105
+ /// The connection attempt will fail if the authentication method requires a host name;
106
+ /// * If `host` is specified without `hostaddr`, a host name lookup occurs;
107
+ /// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address.
108
+ /// The value for `host` is ignored unless the authentication method requires it,
109
+ /// in which case it will be used as the host name.
95
110
/// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be
96
111
/// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if
97
112
/// omitted or the empty string.
@@ -126,6 +141,10 @@ pub enum Host {
126
141
/// ```
127
142
///
128
143
/// ```not_rust
144
+ /// host=host1,host2,host3 port=1234,,5678 hostaddr=127.0.0.1,127.0.0.2,127.0.0.3 user=postgres target_session_attrs=read-write
145
+ /// ```
146
+ ///
147
+ /// ```not_rust
129
148
/// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write
130
149
/// ```
131
150
///
@@ -162,6 +181,7 @@ pub struct Config {
162
181
pub ( crate ) application_name : Option < String > ,
163
182
pub ( crate ) ssl_mode : SslMode ,
164
183
pub ( crate ) host : Vec < Host > ,
184
+ pub ( crate ) hostaddr : Vec < IpAddr > ,
165
185
pub ( crate ) port : Vec < u16 > ,
166
186
pub ( crate ) connect_timeout : Option < Duration > ,
167
187
pub ( crate ) tcp_user_timeout : Option < Duration > ,
@@ -189,6 +209,7 @@ impl Config {
189
209
application_name : None ,
190
210
ssl_mode : SslMode :: Prefer ,
191
211
host : vec ! [ ] ,
212
+ hostaddr : vec ! [ ] ,
192
213
port : vec ! [ ] ,
193
214
connect_timeout : None ,
194
215
tcp_user_timeout : None ,
@@ -288,6 +309,7 @@ impl Config {
288
309
///
289
310
/// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix
290
311
/// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets.
312
+ /// There must be either no hosts, or the same number of hosts as hostaddrs.
291
313
pub fn host ( & mut self , host : & str ) -> & mut Config {
292
314
#[ cfg( unix) ]
293
315
{
@@ -305,6 +327,11 @@ impl Config {
305
327
& self . host
306
328
}
307
329
330
+ /// Gets the hostaddrs that have been added to the configuration with `hostaddr`.
331
+ pub fn get_hostaddrs ( & self ) -> & [ IpAddr ] {
332
+ self . hostaddr . deref ( )
333
+ }
334
+
308
335
/// Adds a Unix socket host to the configuration.
309
336
///
310
337
/// Unlike `host`, this method allows non-UTF8 paths.
@@ -317,6 +344,15 @@ impl Config {
317
344
self
318
345
}
319
346
347
+ /// Adds a hostaddr to the configuration.
348
+ ///
349
+ /// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order.
350
+ /// There must be either no hostaddrs, or the same number of hostaddrs as hosts.
351
+ pub fn hostaddr ( & mut self , hostaddr : IpAddr ) -> & mut Config {
352
+ self . hostaddr . push ( hostaddr) ;
353
+ self
354
+ }
355
+
320
356
/// Adds a port to the configuration.
321
357
///
322
358
/// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which
@@ -484,6 +520,14 @@ impl Config {
484
520
self . host ( host) ;
485
521
}
486
522
}
523
+ "hostaddr" => {
524
+ for hostaddr in value. split ( ',' ) {
525
+ let addr = hostaddr
526
+ . parse ( )
527
+ . map_err ( |_| Error :: config_parse ( Box :: new ( InvalidValue ( "hostaddr" ) ) ) ) ?;
528
+ self . hostaddr ( addr) ;
529
+ }
530
+ }
487
531
"port" => {
488
532
for port in value. split ( ',' ) {
489
533
let port = if port. is_empty ( ) {
@@ -635,6 +679,7 @@ impl fmt::Debug for Config {
635
679
. field ( "application_name" , & self . application_name )
636
680
. field ( "ssl_mode" , & self . ssl_mode )
637
681
. field ( "host" , & self . host )
682
+ . field ( "hostaddr" , & self . hostaddr )
638
683
. field ( "port" , & self . port )
639
684
. field ( "connect_timeout" , & self . connect_timeout )
640
685
. field ( "tcp_user_timeout" , & self . tcp_user_timeout )
@@ -1025,3 +1070,41 @@ impl<'a> UrlParser<'a> {
1025
1070
. map_err ( |e| Error :: config_parse ( e. into ( ) ) )
1026
1071
}
1027
1072
}
1073
+
1074
+ #[ cfg( test) ]
1075
+ mod tests {
1076
+ use std:: net:: IpAddr ;
1077
+
1078
+ use crate :: { config:: Host , Config } ;
1079
+
1080
+ #[ test]
1081
+ fn test_simple_parsing ( ) {
1082
+ let s = "user=pass_user dbname=postgres host=host1,host2 hostaddr=127.0.0.1,127.0.0.2 port=26257" ;
1083
+ let config = s. parse :: < Config > ( ) . unwrap ( ) ;
1084
+ assert_eq ! ( Some ( "pass_user" ) , config. get_user( ) ) ;
1085
+ assert_eq ! ( Some ( "postgres" ) , config. get_dbname( ) ) ;
1086
+ assert_eq ! (
1087
+ [
1088
+ Host :: Tcp ( "host1" . to_string( ) ) ,
1089
+ Host :: Tcp ( "host2" . to_string( ) )
1090
+ ] ,
1091
+ config. get_hosts( ) ,
1092
+ ) ;
1093
+
1094
+ assert_eq ! (
1095
+ [
1096
+ "127.0.0.1" . parse:: <IpAddr >( ) . unwrap( ) ,
1097
+ "127.0.0.2" . parse:: <IpAddr >( ) . unwrap( )
1098
+ ] ,
1099
+ config. get_hostaddrs( ) ,
1100
+ ) ;
1101
+
1102
+ assert_eq ! ( 1 , 1 ) ;
1103
+ }
1104
+
1105
+ #[ test]
1106
+ fn test_invalid_hostaddr_parsing ( ) {
1107
+ let s = "user=pass_user dbname=postgres host=host1 hostaddr=127.0.0 port=26257" ;
1108
+ s. parse :: < Config > ( ) . err ( ) . unwrap ( ) ;
1109
+ }
1110
+ }
0 commit comments