Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4f41157

Browse files
authored
Merge pull request#945 from trungda/tdinh/hostaddr
Add hostaddr support
2 parents790af54 +b0596f7 commit4f41157

File tree

4 files changed

+213
-10
lines changed

4 files changed

+213
-10
lines changed

‎postgres/src/config.rs‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::connection::Connection;
66
usecrate::Client;
77
use log::info;
88
use std::fmt;
9+
use std::net::IpAddr;
910
use std::path::Path;
1011
use std::str::FromStr;
1112
use std::sync::Arc;
@@ -39,6 +40,19 @@ use tokio_postgres::{Error, Socket};
3940
/// path to the directory containing Unix domain sockets. Otherwise, it is treated as a hostname. Multiple hosts
4041
/// can be specified, separated by commas. Each host will be tried in turn when connecting. Required if connecting
4142
/// with the `connect` method.
43+
/// * `hostaddr` - Numeric IP address of host to connect to. This should be in the standard IPv4 address format,
44+
/// e.g., 172.28.40.9. If your machine supports IPv6, you can also use those addresses.
45+
/// If this parameter is not specified, the value of `host` will be looked up to find the corresponding IP address,
46+
/// - or if host specifies an IP address, that value will be used directly.
47+
/// Using `hostaddr` allows the application to avoid a host name look-up, which might be important in applications
48+
/// with time constraints. However, a host name is required for verify-full SSL certificate verification.
49+
/// Specifically:
50+
/// * If `hostaddr` is specified without `host`, the value for `hostaddr` gives the server network address.
51+
/// The connection attempt will fail if the authentication method requires a host name;
52+
/// * If `host` is specified without `hostaddr`, a host name lookup occurs;
53+
/// * If both `host` and `hostaddr` are specified, the value for `hostaddr` gives the server network address.
54+
/// The value for `host` is ignored unless the authentication method requires it,
55+
/// in which case it will be used as the host name.
4256
/// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be
4357
/// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if
4458
/// omitted or the empty string.
@@ -70,6 +84,10 @@ use tokio_postgres::{Error, Socket};
7084
/// ```
7185
///
7286
/// ```not_rust
87+
/// 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
88+
/// ```
89+
///
90+
/// ```not_rust
7391
/// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write
7492
/// ```
7593
///
@@ -207,6 +225,7 @@ impl Config {
207225
///
208226
/// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix
209227
/// systems, a host starting with a `/` is interpreted as a path to a directory containing Unix domain sockets.
228+
/// There must be either no hosts, or the same number of hosts as hostaddrs.
210229
pubfnhost(&mutself,host:&str) ->&mutConfig{
211230
self.config.host(host);
212231
self
@@ -217,6 +236,11 @@ impl Config {
217236
self.config.get_hosts()
218237
}
219238

239+
/// Gets the hostaddrs that have been added to the configuration with `hostaddr`.
240+
pubfnget_hostaddrs(&self) ->&[IpAddr]{
241+
self.config.get_hostaddrs()
242+
}
243+
220244
/// Adds a Unix socket host to the configuration.
221245
///
222246
/// Unlike `host`, this method allows non-UTF8 paths.
@@ -229,6 +253,15 @@ impl Config {
229253
self
230254
}
231255

256+
/// Adds a hostaddr to the configuration.
257+
///
258+
/// Multiple hostaddrs can be specified by calling this method multiple times, and each will be tried in order.
259+
/// There must be either no hostaddrs, or the same number of hostaddrs as hosts.
260+
pubfnhostaddr(&mutself,hostaddr:IpAddr) ->&mutConfig{
261+
self.config.hostaddr(hostaddr);
262+
self
263+
}
264+
232265
/// Adds a port to the configuration.
233266
///
234267
/// Multiple ports can be specified by calling this method multiple times. There must either be no ports, in which

‎tokio-postgres/src/config.rs‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use crate::{Client, Connection, Error};
1414
use std::borrow::Cow;
1515
#[cfg(unix)]
1616
use std::ffi::OsStr;
17+
use std::net::IpAddr;
18+
use std::ops::Deref;
1719
#[cfg(unix)]
1820
use std::os::unix::ffi::OsStrExt;
1921
#[cfg(unix)]
@@ -92,6 +94,19 @@ pub enum Host {
9294
/// path to the directory containing Unix domain sockets. Otherwise, it is treated as a hostname. Multiple hosts
9395
/// can be specified, separated by commas. Each host will be tried in turn when connecting. Required if connecting
9496
/// 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.
95110
/// * `port` - The port to connect to. Multiple ports can be specified, separated by commas. The number of ports must be
96111
/// either 1, in which case it will be used for all hosts, or the same as the number of hosts. Defaults to 5432 if
97112
/// omitted or the empty string.
@@ -126,6 +141,10 @@ pub enum Host {
126141
/// ```
127142
///
128143
/// ```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
129148
/// host=host1,host2,host3 port=1234,,5678 user=postgres target_session_attrs=read-write
130149
/// ```
131150
///
@@ -162,6 +181,7 @@ pub struct Config {
162181
pub(crate)application_name:Option<String>,
163182
pub(crate)ssl_mode:SslMode,
164183
pub(crate)host:Vec<Host>,
184+
pub(crate)hostaddr:Vec<IpAddr>,
165185
pub(crate)port:Vec<u16>,
166186
pub(crate)connect_timeout:Option<Duration>,
167187
pub(crate)tcp_user_timeout:Option<Duration>,
@@ -189,6 +209,7 @@ impl Config {
189209
application_name:None,
190210
ssl_mode:SslMode::Prefer,
191211
host:vec![],
212+
hostaddr:vec![],
192213
port:vec![],
193214
connect_timeout:None,
194215
tcp_user_timeout:None,
@@ -288,6 +309,7 @@ impl Config {
288309
///
289310
/// Multiple hosts can be specified by calling this method multiple times, and each will be tried in order. On Unix
290311
/// 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.
291313
pubfnhost(&mutself,host:&str) ->&mutConfig{
292314
#[cfg(unix)]
293315
{
@@ -305,6 +327,11 @@ impl Config {
305327
&self.host
306328
}
307329

330+
/// Gets the hostaddrs that have been added to the configuration with `hostaddr`.
331+
pubfnget_hostaddrs(&self) ->&[IpAddr]{
332+
self.hostaddr.deref()
333+
}
334+
308335
/// Adds a Unix socket host to the configuration.
309336
///
310337
/// Unlike `host`, this method allows non-UTF8 paths.
@@ -317,6 +344,15 @@ impl Config {
317344
self
318345
}
319346

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+
pubfnhostaddr(&mutself,hostaddr:IpAddr) ->&mutConfig{
352+
self.hostaddr.push(hostaddr);
353+
self
354+
}
355+
320356
/// Adds a port to the configuration.
321357
///
322358
/// 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 {
484520
self.host(host);
485521
}
486522
}
523+
"hostaddr" =>{
524+
for hostaddrin value.split(','){
525+
let addr = hostaddr
526+
.parse()
527+
.map_err(|_|Error::config_parse(Box::new(InvalidValue("hostaddr"))))?;
528+
self.hostaddr(addr);
529+
}
530+
}
487531
"port" =>{
488532
for portin value.split(','){
489533
let port =if port.is_empty(){
@@ -635,6 +679,7 @@ impl fmt::Debug for Config {
635679
.field("application_name",&self.application_name)
636680
.field("ssl_mode",&self.ssl_mode)
637681
.field("host",&self.host)
682+
.field("hostaddr",&self.hostaddr)
638683
.field("port",&self.port)
639684
.field("connect_timeout",&self.connect_timeout)
640685
.field("tcp_user_timeout",&self.tcp_user_timeout)
@@ -1025,3 +1070,41 @@ impl<'a> UrlParser<'a> {
10251070
.map_err(|e|Error::config_parse(e.into()))
10261071
}
10271072
}
1073+
1074+
#[cfg(test)]
1075+
mod tests{
1076+
use std::net::IpAddr;
1077+
1078+
usecrate::{config::Host,Config};
1079+
1080+
#[test]
1081+
fntest_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+
fntest_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+
}

‎tokio-postgres/src/connect.rs‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::connect_socket::connect_socket;
55
usecrate::tls::{MakeTlsConnect,TlsConnect};
66
usecrate::{Client,Config,Connection,Error,SimpleQueryMessage,Socket};
77
use futures_util::{future, pin_mut,Future,FutureExt,Stream};
8-
use std::io;
98
use std::task::Poll;
9+
use std::{cmp, io};
1010

1111
pubasyncfnconnect<T>(
1212
muttls:T,
@@ -15,35 +15,70 @@ pub async fn connect<T>(
1515
where
1616
T:MakeTlsConnect<Socket>,
1717
{
18-
if config.host.is_empty(){
19-
returnErr(Error::config("host missing".into()));
18+
if config.host.is_empty()&& config.hostaddr.is_empty(){
19+
returnErr(Error::config("bothhost and hostaddr are missing".into()));
2020
}
2121

22-
if config.port.len() >1 && config.port.len() != config.host.len(){
22+
if !config.host.is_empty()
23+
&& !config.hostaddr.is_empty()
24+
&& config.host.len() != config.hostaddr.len()
25+
{
26+
let msg =format!(
27+
"number of hosts ({}) is different from number of hostaddrs ({})",
28+
config.host.len(),
29+
config.hostaddr.len(),
30+
);
31+
returnErr(Error::config(msg.into()));
32+
}
33+
34+
// At this point, either one of the following two scenarios could happen:
35+
// (1) either config.host or config.hostaddr must be empty;
36+
// (2) if both config.host and config.hostaddr are NOT empty; their lengths must be equal.
37+
let num_hosts = cmp::max(config.host.len(), config.hostaddr.len());
38+
39+
if config.port.len() >1 && config.port.len() != num_hosts{
2340
returnErr(Error::config("invalid number of ports".into()));
2441
}
2542

2643
letmut error =None;
27-
for(i, host)in config.host.iter().enumerate(){
44+
for iin0..num_hosts{
45+
let host = config.host.get(i);
46+
let hostaddr = config.hostaddr.get(i);
2847
let port = config
2948
.port
3049
.get(i)
3150
.or_else(|| config.port.first())
3251
.copied()
3352
.unwrap_or(5432);
3453

54+
// The value of host is used as the hostname for TLS validation,
55+
// if it's not present, use the value of hostaddr.
3556
let hostname =match host{
36-
Host::Tcp(host) => host.as_str(),
57+
Some(Host::Tcp(host)) => host.clone(),
3758
// postgres doesn't support TLS over unix sockets, so the choice here doesn't matter
3859
#[cfg(unix)]
39-
Host::Unix(_) =>"",
60+
Some(Host::Unix(_)) =>"".to_string(),
61+
None => hostaddr.map_or("".to_string(), |ipaddr| ipaddr.to_string()),
4062
};
41-
4263
let tls = tls
43-
.make_tls_connect(hostname)
64+
.make_tls_connect(&hostname)
4465
.map_err(|e|Error::tls(e.into()))?;
4566

46-
matchconnect_once(host, port, tls, config).await{
67+
// Try to use the value of hostaddr to establish the TCP connection,
68+
// fallback to host if hostaddr is not present.
69+
let addr =match hostaddr{
70+
Some(ipaddr) =>Host::Tcp(ipaddr.to_string()),
71+
None =>{
72+
ifletSome(host) = host{
73+
host.clone()
74+
}else{
75+
// This is unreachable.
76+
returnErr(Error::config("both host and hostaddr are empty".into()));
77+
}
78+
}
79+
};
80+
81+
matchconnect_once(&addr, port, tls, config).await{
4782
Ok((client, connection)) =>returnOk((client, connection)),
4883
Err(e) => error =Some(e),
4984
}

‎tokio-postgres/tests/test/runtime.rs‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,58 @@ async fn target_session_attrs_err() {
6666
.unwrap();
6767
}
6868

69+
#[tokio::test]
70+
asyncfnhost_only_ok(){
71+
let _ = tokio_postgres::connect(
72+
"host=localhost port=5433 user=pass_user dbname=postgres password=password",
73+
NoTls,
74+
)
75+
.await
76+
.unwrap();
77+
}
78+
79+
#[tokio::test]
80+
asyncfnhostaddr_only_ok(){
81+
let _ = tokio_postgres::connect(
82+
"hostaddr=127.0.0.1 port=5433 user=pass_user dbname=postgres password=password",
83+
NoTls,
84+
)
85+
.await
86+
.unwrap();
87+
}
88+
89+
#[tokio::test]
90+
asyncfnhostaddr_and_host_ok(){
91+
let _ = tokio_postgres::connect(
92+
"hostaddr=127.0.0.1 host=localhost port=5433 user=pass_user dbname=postgres password=password",
93+
NoTls,
94+
)
95+
.await
96+
.unwrap();
97+
}
98+
99+
#[tokio::test]
100+
asyncfnhostaddr_host_mismatch(){
101+
let _ = tokio_postgres::connect(
102+
"hostaddr=127.0.0.1,127.0.0.2 host=localhost port=5433 user=pass_user dbname=postgres password=password",
103+
NoTls,
104+
)
105+
.await
106+
.err()
107+
.unwrap();
108+
}
109+
110+
#[tokio::test]
111+
asyncfnhostaddr_host_both_missing(){
112+
let _ = tokio_postgres::connect(
113+
"port=5433 user=pass_user dbname=postgres password=password",
114+
NoTls,
115+
)
116+
.await
117+
.err()
118+
.unwrap();
119+
}
120+
69121
#[tokio::test]
70122
asyncfncancel_query(){
71123
let client =connect("host=localhost port=5433 user=postgres").await;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp