Skip to content

Commit

Permalink
Merge pull request #71 from ikolomi/add_clientname_support
Browse files Browse the repository at this point in the history
Add support for client name configuration
  • Loading branch information
ikolomi authored Dec 18, 2023
2 parents e99c565 + 41e6e19 commit 896fce1
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 44 deletions.
1 change: 1 addition & 0 deletions redis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ tokio = { version = "1", features = ["rt", "macros", "rt-multi-thread", "time"]
tempfile = "=3.6.0"
once_cell = "1"
anyhow = "1"
sscanf = "0.4.1"

[[test]]
name = "test_async"
Expand Down
15 changes: 15 additions & 0 deletions redis/src/aio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,21 @@ where
}
}

if let Some(client_name) = &connection_info.client_name {
match cmd("CLIENT")
.arg("SETNAME")
.arg(client_name)
.query_async(con)
.await
{
Ok(Value::Okay) => {}
_ => fail!((
ErrorKind::ResponseError,
"Redis server refused to set client name"
)),
}
}

// result is ignored, as per the command's instructions.
// https://redis.io/commands/client-setinfo/
let _: RedisResult<()> = crate::connection::client_set_info_pipeline()
Expand Down
1 change: 1 addition & 0 deletions redis/src/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ pub(crate) fn get_connection_info(
redis: RedisConnectionInfo {
password: cluster_params.password,
username: cluster_params.username,
client_name: cluster_params.client_name,
use_resp3: cluster_params.use_resp3,
..Default::default()
},
Expand Down
18 changes: 18 additions & 0 deletions redis/src/cluster_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct BuilderParams {
retries_configuration: RetryParams,
connection_timeout: Option<Duration>,
topology_checks_interval: Option<Duration>,
client_name: Option<String>,
use_resp3: bool,
}

Expand Down Expand Up @@ -87,6 +88,7 @@ pub(crate) struct ClusterParams {
pub(crate) connection_timeout: Duration,
pub(crate) topology_checks_interval: Option<Duration>,
pub(crate) tls_params: Option<TlsConnParams>,
pub(crate) client_name: Option<String>,
pub(crate) use_resp3: bool,
}

Expand All @@ -111,6 +113,7 @@ impl ClusterParams {
connection_timeout: value.connection_timeout.unwrap_or(Duration::MAX),
topology_checks_interval: value.topology_checks_interval,
tls_params,
client_name: value.client_name,
use_resp3: value.use_resp3,
})
}
Expand Down Expand Up @@ -212,6 +215,15 @@ impl ClusterClientBuilder {
)));
}

if node.redis.client_name.is_some()
&& node.redis.client_name != cluster_params.client_name
{
return Err(RedisError::from((
ErrorKind::InvalidClientConfig,
"Cannot use different client_name among initial nodes.",
)));
}

nodes.push(node);
}

Expand All @@ -221,6 +233,12 @@ impl ClusterClientBuilder {
})
}

/// Sets client name for the new ClusterClient.
pub fn client_name(mut self, client_name: String) -> ClusterClientBuilder {
self.builder_params.client_name = Some(client_name);
self
}

/// Sets password for the new ClusterClient.
pub fn password(mut self, password: String) -> ClusterClientBuilder {
self.builder_params.password = Some(password);
Expand Down
23 changes: 23 additions & 0 deletions redis/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ pub struct RedisConnectionInfo {
pub password: Option<String>,
/// Use RESP 3 mode, Redis 6 or newer is required.
pub use_resp3: bool,
/// Optionally a pass a client name that should be used for connection
pub client_name: Option<String>,
}

impl FromStr for ConnectionInfo {
Expand Down Expand Up @@ -387,6 +389,7 @@ fn url_to_tcp_connection_info(url: url::Url) -> RedisResult<ConnectionInfo> {
Some(v) => v == "true",
_ => false,
},
client_name: None,
},
})
}
Expand All @@ -413,6 +416,7 @@ fn url_to_unix_connection_info(url: url::Url) -> RedisResult<ConnectionInfo> {
Some(v) => v == "true",
_ => false,
},
client_name: None,
},
})
}
Expand Down Expand Up @@ -979,6 +983,20 @@ fn setup_connection(
}
}

if connection_info.client_name.is_some() {
match cmd("CLIENT")
.arg("SETNAME")
.arg(connection_info.client_name.as_ref().unwrap())
.query::<Value>(&mut rv)
{
Ok(Value::Okay) => {}
_ => fail!((
ErrorKind::ResponseError,
"Redis server refused to set client name"
)),
}
}

// result is ignored, as per the command's instructions.
// https://redis.io/commands/client-setinfo/
let _: RedisResult<()> = client_set_info_pipeline().query(&mut rv);
Expand Down Expand Up @@ -1708,6 +1726,7 @@ mod tests {
username: Some("%johndoe%".to_string()),
password: Some("#@<>$".to_string()),
use_resp3: false,
client_name: None,
},
},
),
Expand Down Expand Up @@ -1775,6 +1794,7 @@ mod tests {
username: None,
password: None,
use_resp3: false,
client_name: None,
},
},
),
Expand All @@ -1787,6 +1807,7 @@ mod tests {
username: None,
password: None,
use_resp3: false,
client_name: None,
},
},
),
Expand All @@ -1802,6 +1823,7 @@ mod tests {
username: Some("%johndoe%".to_string()),
password: Some("#@<>$".to_string()),
use_resp3: false,
client_name: None,
},
},
),
Expand All @@ -1817,6 +1839,7 @@ mod tests {
username: Some("%johndoe%".to_string()),
password: Some("&?= *+".to_string()),
use_resp3: false,
client_name: None,
},
},
),
Expand Down
2 changes: 2 additions & 0 deletions redis/src/sentinel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
//! username: Some(String::from("foo")),
//! password: Some(String::from("bar")),
//! use_resp3: false,
//! client_name: None
//! }),
//! }),
//! )
Expand Down Expand Up @@ -95,6 +96,7 @@
//! username: Some(String::from("user")),
//! password: Some(String::from("pass")),
//! use_resp3: false,
//! client_name: None
//! }),
//! }),
//! redis::sentinel::SentinelServerType::Master,
Expand Down
4 changes: 4 additions & 0 deletions redis/tests/support/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ impl RedisCluster {
"world"
}

pub fn client_name() -> &'static str {
"test_cluster_client"
}

pub fn new(nodes: u16, replicas: u16) -> RedisCluster {
RedisCluster::with_modules(nodes, replicas, &[], false)
}
Expand Down
97 changes: 53 additions & 44 deletions redis/tests/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ mod cluster;
mod mock_cluster;

mod util;
pub use self::util::*;

#[cfg(any(feature = "cluster", feature = "cluster-async"))]
pub use self::cluster::*;
Expand Down Expand Up @@ -349,28 +350,7 @@ impl TestContext {
Self::with_modules(&[], true)
}

pub fn with_tls(tls_files: TlsFilePaths, mtls_enabled: bool) -> TestContext {
let redis_port = get_random_available_port();
let addr = RedisServer::get_addr(redis_port);

let server = RedisServer::new_with_addr_tls_modules_and_spawner(
addr,
None,
Some(tls_files),
mtls_enabled,
&[],
|cmd| {
cmd.spawn()
.unwrap_or_else(|err| panic!("Failed to run {cmd:?}: {err}"))
},
);

#[cfg(feature = "tls-rustls")]
let client =
build_single_client(server.connection_info(), &server.tls_paths, mtls_enabled).unwrap();
#[cfg(not(feature = "tls-rustls"))]
let client = redis::Client::open(server.connection_info()).unwrap();

fn connect_with_retries(client: &redis::Client) {
let mut con;

let millisecond = Duration::from_millis(1);
Expand All @@ -395,6 +375,31 @@ impl TestContext {
}
}
redis::cmd("FLUSHDB").execute(&mut con);
}

pub fn with_tls(tls_files: TlsFilePaths, mtls_enabled: bool) -> TestContext {
let redis_port = get_random_available_port();
let addr: ConnectionAddr = RedisServer::get_addr(redis_port);

let server = RedisServer::new_with_addr_tls_modules_and_spawner(
addr,
None,
Some(tls_files),
mtls_enabled,
&[],
|cmd| {
cmd.spawn()
.unwrap_or_else(|err| panic!("Failed to run {cmd:?}: {err}"))
},
);

#[cfg(feature = "tls-rustls")]
let client =
build_single_client(server.connection_info(), &server.tls_paths, mtls_enabled).unwrap();
#[cfg(not(feature = "tls-rustls"))]
let client = redis::Client::open(server.connection_info()).unwrap();

Self::connect_with_retries(&client);

TestContext {
server,
Expand All @@ -412,30 +417,34 @@ impl TestContext {
#[cfg(not(feature = "tls-rustls"))]
let client = redis::Client::open(server.connection_info()).unwrap();

let mut con;
Self::connect_with_retries(&client);

let millisecond = Duration::from_millis(1);
let mut retries = 0;
loop {
match client.get_connection() {
Err(err) => {
if err.is_connection_refusal() {
sleep(millisecond);
retries += 1;
if retries > 100000 {
panic!("Tried to connect too many times, last error: {err}");
}
} else {
panic!("Could not connect: {err}");
}
}
Ok(x) => {
con = x;
break;
}
}
TestContext {
server,
client,
use_resp3: use_resp3(),
}
redis::cmd("FLUSHDB").execute(&mut con);
}

pub fn with_client_name(clientname: &str) -> TestContext {
let server = RedisServer::with_modules(&[], false);
let con_info = redis::ConnectionInfo {
addr: server.client_addr().clone(),
redis: redis::RedisConnectionInfo {
db: Default::default(),
username: None,
password: None,
use_resp3: Default::default(),
client_name: Some(clientname.to_string()),
},
};

#[cfg(feature = "tls-rustls")]
let client = build_single_client(con_info, &server.tls_paths, false).unwrap();
#[cfg(not(feature = "tls-rustls"))]
let client = redis::Client::open(con_info).unwrap();

Self::connect_with_retries(&client);

TestContext {
server,
Expand Down
13 changes: 13 additions & 0 deletions redis/tests/support/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

#[macro_export]
macro_rules! assert_args {
($value:expr, $($args:expr),+) => {
Expand All @@ -8,3 +10,14 @@ macro_rules! assert_args {
assert_eq!(strings, vec![$($args),+]);
}
}

pub fn parse_client_info(client_info: &str) -> HashMap<String, String> {
let mut res = HashMap::new();

for line in client_info.split(' ') {
let this_attr: Vec<&str> = line.split('=').collect();
res.insert(this_attr[0].to_string(), this_attr[1].to_string());
}

res
}
33 changes: 33 additions & 0 deletions redis/tests/test_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ async fn invalid_password_issue_343() {
username: None,
password: Some("asdcasc".to_string()),
use_resp3: false,
client_name: None,
},
};
let client = redis::Client::open(coninfo).unwrap();
Expand Down Expand Up @@ -781,3 +782,35 @@ mod mtls_test {
}
}
}

#[test]
fn test_set_client_name_by_config() {
const CLIENT_NAME: &str = "TEST_CLIENT_NAME";
use redis::RedisError;
let ctx = TestContext::with_client_name(CLIENT_NAME);

block_on_all(async move {
let mut con = ctx.async_connection().await?;

let client_info: String = redis::cmd("CLIENT")
.arg("INFO")
.query_async(&mut con)
.await
.unwrap();

let client_attrs = parse_client_info(&client_info);

assert!(
client_attrs.contains_key("name"),
"Could not detect the 'name' attribute in CLIENT INFO output"
);

assert_eq!(
client_attrs["name"], CLIENT_NAME,
"Incorrect client name, expecting: {}, got {}",
CLIENT_NAME, client_attrs["name"]
);
Ok::<_, RedisError>(())
})
.unwrap();
}
Loading

0 comments on commit 896fce1

Please sign in to comment.