Skip to content

Commit ba93f23

Browse files
committed
Add optional client_name property to RedisConnectionInfo, which will be used with 'CLIENT SETNAME' command during connection setup.
1 parent 46e8a9d commit ba93f23

File tree

6 files changed

+123
-44
lines changed

6 files changed

+123
-44
lines changed

redis/src/aio/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ where
140140
}
141141
}
142142

143+
if connection_info.client_name.is_some() {
144+
match cmd("CLIENT")
145+
.arg("SETNAME")
146+
.arg(connection_info.client_name.as_ref().unwrap())
147+
.query_async(con).await
148+
{
149+
Ok(Value::Okay) => {}
150+
_ => fail!((
151+
ErrorKind::ResponseError,
152+
"Redis server refused to set client name"
153+
)),
154+
}
155+
}
156+
143157
// result is ignored, as per the command's instructions.
144158
// https://redis.io/commands/client-setinfo/
145159
let _: RedisResult<()> = crate::connection::client_set_info_pipeline()

redis/src/connection.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ pub struct RedisConnectionInfo {
227227
pub password: Option<String>,
228228
/// Use RESP 3 mode, Redis 6 or newer is required.
229229
pub use_resp3: bool,
230+
/// Optionally a pass a client name that should be used for connection
231+
pub client_name: Option<String>,
230232
}
231233

232234
impl FromStr for ConnectionInfo {
@@ -387,6 +389,7 @@ fn url_to_tcp_connection_info(url: url::Url) -> RedisResult<ConnectionInfo> {
387389
Some(v) => v == "true",
388390
_ => false,
389391
},
392+
client_name: None
390393
},
391394
})
392395
}
@@ -413,6 +416,7 @@ fn url_to_unix_connection_info(url: url::Url) -> RedisResult<ConnectionInfo> {
413416
Some(v) => v == "true",
414417
_ => false,
415418
},
419+
client_name: None
416420
},
417421
})
418422
}
@@ -979,6 +983,20 @@ fn setup_connection(
979983
}
980984
}
981985

986+
if connection_info.client_name.is_some() {
987+
match cmd("CLIENT")
988+
.arg("SETNAME")
989+
.arg(connection_info.client_name.as_ref().unwrap())
990+
.query::<Value>(&mut rv)
991+
{
992+
Ok(Value::Okay) => {}
993+
_ => fail!((
994+
ErrorKind::ResponseError,
995+
"Redis server refused to set client name"
996+
)),
997+
}
998+
}
999+
9821000
// result is ignored, as per the command's instructions.
9831001
// https://redis.io/commands/client-setinfo/
9841002
let _: RedisResult<()> = client_set_info_pipeline().query(&mut rv);
@@ -1708,6 +1726,7 @@ mod tests {
17081726
username: Some("%johndoe%".to_string()),
17091727
password: Some("#@<>$".to_string()),
17101728
use_resp3: false,
1729+
client_name: None
17111730
},
17121731
},
17131732
),
@@ -1775,6 +1794,7 @@ mod tests {
17751794
username: None,
17761795
password: None,
17771796
use_resp3: false,
1797+
client_name: None
17781798
},
17791799
},
17801800
),
@@ -1787,6 +1807,7 @@ mod tests {
17871807
username: None,
17881808
password: None,
17891809
use_resp3: false,
1810+
client_name: None
17901811
},
17911812
},
17921813
),
@@ -1802,6 +1823,7 @@ mod tests {
18021823
username: Some("%johndoe%".to_string()),
18031824
password: Some("#@<>$".to_string()),
18041825
use_resp3: false,
1826+
client_name: None
18051827
},
18061828
},
18071829
),
@@ -1817,6 +1839,7 @@ mod tests {
18171839
username: Some("%johndoe%".to_string()),
18181840
password: Some("&?= *+".to_string()),
18191841
use_resp3: false,
1842+
client_name: None
18201843
},
18211844
},
18221845
),

redis/src/sentinel.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
//! username: Some(String::from("foo")),
6060
//! password: Some(String::from("bar")),
6161
//! use_resp3: false,
62+
//! client_name: None
6263
//! }),
6364
//! }),
6465
//! )
@@ -95,6 +96,7 @@
9596
//! username: Some(String::from("user")),
9697
//! password: Some(String::from("pass")),
9798
//! use_resp3: false,
99+
//! client_name: None
98100
//! }),
99101
//! }),
100102
//! redis::sentinel::SentinelServerType::Master,

redis/tests/support/mod.rs

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -349,28 +349,7 @@ impl TestContext {
349349
Self::with_modules(&[], true)
350350
}
351351

352-
pub fn with_tls(tls_files: TlsFilePaths, mtls_enabled: bool) -> TestContext {
353-
let redis_port = get_random_available_port();
354-
let addr = RedisServer::get_addr(redis_port);
355-
356-
let server = RedisServer::new_with_addr_tls_modules_and_spawner(
357-
addr,
358-
None,
359-
Some(tls_files),
360-
mtls_enabled,
361-
&[],
362-
|cmd| {
363-
cmd.spawn()
364-
.unwrap_or_else(|err| panic!("Failed to run {cmd:?}: {err}"))
365-
},
366-
);
367-
368-
#[cfg(feature = "tls-rustls")]
369-
let client =
370-
build_single_client(server.connection_info(), &server.tls_paths, mtls_enabled).unwrap();
371-
#[cfg(not(feature = "tls-rustls"))]
372-
let client = redis::Client::open(server.connection_info()).unwrap();
373-
352+
fn connect_with_retries(client: &redis::Client) {
374353
let mut con;
375354

376355
let millisecond = Duration::from_millis(1);
@@ -395,6 +374,31 @@ impl TestContext {
395374
}
396375
}
397376
redis::cmd("FLUSHDB").execute(&mut con);
377+
}
378+
379+
pub fn with_tls(tls_files: TlsFilePaths, mtls_enabled: bool) -> TestContext {
380+
let redis_port = get_random_available_port();
381+
let addr: ConnectionAddr = RedisServer::get_addr(redis_port);
382+
383+
let server = RedisServer::new_with_addr_tls_modules_and_spawner(
384+
addr,
385+
None,
386+
Some(tls_files),
387+
mtls_enabled,
388+
&[],
389+
|cmd| {
390+
cmd.spawn()
391+
.unwrap_or_else(|err| panic!("Failed to run {cmd:?}: {err}"))
392+
},
393+
);
394+
395+
#[cfg(feature = "tls-rustls")]
396+
let client =
397+
build_single_client(server.connection_info(), &server.tls_paths, mtls_enabled).unwrap();
398+
#[cfg(not(feature = "tls-rustls"))]
399+
let client = redis::Client::open(server.connection_info()).unwrap();
400+
401+
Self::connect_with_retries(&client);
398402

399403
TestContext {
400404
server,
@@ -412,30 +416,35 @@ impl TestContext {
412416
#[cfg(not(feature = "tls-rustls"))]
413417
let client = redis::Client::open(server.connection_info()).unwrap();
414418

415-
let mut con;
419+
Self::connect_with_retries(&client);
416420

417-
let millisecond = Duration::from_millis(1);
418-
let mut retries = 0;
419-
loop {
420-
match client.get_connection() {
421-
Err(err) => {
422-
if err.is_connection_refusal() {
423-
sleep(millisecond);
424-
retries += 1;
425-
if retries > 100000 {
426-
panic!("Tried to connect too many times, last error: {err}");
427-
}
428-
} else {
429-
panic!("Could not connect: {err}");
430-
}
431-
}
432-
Ok(x) => {
433-
con = x;
434-
break;
435-
}
436-
}
421+
TestContext {
422+
server,
423+
client,
424+
use_resp3: use_resp3(),
437425
}
438-
redis::cmd("FLUSHDB").execute(&mut con);
426+
}
427+
428+
pub fn with_client_name(clientname: &str) -> TestContext {
429+
let server = RedisServer::with_modules(&[], false);
430+
let con_info = redis::ConnectionInfo {
431+
addr: server.client_addr().clone(),
432+
redis: redis::RedisConnectionInfo {
433+
db: Default::default(),
434+
username: None,
435+
password: None,
436+
use_resp3: Default::default(),
437+
client_name: Some(clientname.to_string())
438+
}
439+
};
440+
441+
#[cfg(feature = "tls-rustls")]
442+
let client =
443+
build_single_client(con_info, &server.tls_paths, false).unwrap();
444+
#[cfg(not(feature = "tls-rustls"))]
445+
let client = redis::Client::open(con_info).unwrap();
446+
447+
Self::connect_with_retries(&client);
439448

440449
TestContext {
441450
server,

redis/tests/test_async.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ async fn invalid_password_issue_343() {
472472
username: None,
473473
password: Some("asdcasc".to_string()),
474474
use_resp3: false,
475+
client_name: None
475476
},
476477
};
477478
let client = redis::Client::open(coninfo).unwrap();

redis/tests/test_basic.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(clippy::let_unit_value)]
22

3+
34
use redis::{
45
Commands, ConnectionInfo, ConnectionLike, ControlFlow, ErrorKind, ExistenceCheck, Expiry,
56
PubSubCommands, RedisResult, SetExpiry, SetOptions, ToRedisArgs,
@@ -1448,3 +1449,32 @@ fn test_blocking_sorted_set_api() {
14481449
);
14491450
}
14501451
}
1452+
1453+
#[test]
1454+
fn test_set_client_name_by_config() {
1455+
const CLIENT_NAME: &str = "TEST_CLIENT_NAME";
1456+
1457+
let ctx = TestContext::with_client_name(CLIENT_NAME);
1458+
let mut con = ctx.connection();
1459+
1460+
let client_list: redis::InfoDict = redis::cmd("CLIENT").arg("LIST").query(&mut con).unwrap();
1461+
assert!(!client_list.is_empty(), "No clients present in CLIENT LIST result");
1462+
1463+
let val: &redis::Value = client_list.values().next().unwrap();
1464+
match val {
1465+
redis::Value::SimpleString(s) => {
1466+
let client_attr: Vec<&str> = s.split(' ').collect();
1467+
for i in client_attr.iter() {
1468+
let this_attr: Vec<&str> = i.split('=').collect();
1469+
if this_attr[0] == "name" {
1470+
assert!(this_attr[1] == CLIENT_NAME, "Incorrect client name, expecting: {}, got {}", CLIENT_NAME, this_attr[1]);
1471+
return;
1472+
}
1473+
}
1474+
},
1475+
_ => {
1476+
assert!(false, "Unexpected Enum type returned by CLIENT LIST command (expected String)");
1477+
}
1478+
}
1479+
assert!(false, "Could not detect the expected client name {} in CLIENT LIST output", CLIENT_NAME);
1480+
}

0 commit comments

Comments
 (0)