Import upstream v0.16.22, stripped

Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+301
View File
@@ -0,0 +1,301 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{RedisPool, RedisStore, into_error};
use crate::{Deserialize, write::now};
use redis::AsyncCommands;
impl RedisStore {
pub async fn key_set(&self, key: &[u8], value: &[u8], expires: Option<u64>) -> trc::Result<()> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_set_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
RedisPool::Cluster(pool) => {
self.key_set_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
RedisPool::Sentinel(pool) => {
self.key_set_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
}
}
pub async fn key_incr(&self, key: &[u8], value: i64, expires: Option<u64>) -> trc::Result<i64> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_incr_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
RedisPool::Cluster(pool) => {
self.key_incr_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
RedisPool::Sentinel(pool) => {
self.key_incr_(
pool.get().await.map_err(into_error)?.as_mut(),
key,
value,
expires,
)
.await
}
}
}
pub async fn try_lock(&self, key: &[u8], expires: u64) -> trc::Result<bool> {
match &self.pool {
RedisPool::Single(pool) => {
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
.await
}
RedisPool::Cluster(pool) => {
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
.await
}
RedisPool::Sentinel(pool) => {
self.try_lock_(pool.get().await.map_err(into_error)?.as_mut(), key, expires)
.await
}
}
}
pub async fn key_delete(&self, key: &[u8]) -> trc::Result<()> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Cluster(pool) => {
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Sentinel(pool) => {
self.key_delete_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
}
}
pub async fn key_delete_prefix(&self, prefix: &[u8]) -> trc::Result<()> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
.await
}
RedisPool::Cluster(pool) => {
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
.await
}
RedisPool::Sentinel(pool) => {
self.key_delete_prefix_(pool.get().await.map_err(into_error)?.as_mut(), prefix)
.await
}
}
}
pub async fn key_get<T: Deserialize + std::fmt::Debug + 'static>(
&self,
key: &[u8],
) -> trc::Result<Option<T>> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Cluster(pool) => {
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Sentinel(pool) => {
self.key_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
}
}
pub async fn counter_get(&self, key: &[u8]) -> trc::Result<i64> {
match &self.pool {
RedisPool::Single(pool) => {
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Cluster(pool) => {
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Sentinel(pool) => {
self.counter_get_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
}
}
pub async fn key_exists(&self, key: &[u8]) -> trc::Result<bool> {
match &self.pool {
RedisPool::Single(pool) => {
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Cluster(pool) => {
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
RedisPool::Sentinel(pool) => {
self.key_exists_(pool.get().await.map_err(into_error)?.as_mut(), key)
.await
}
}
}
async fn key_get_<T: Deserialize + std::fmt::Debug + 'static>(
&self,
conn: &mut impl AsyncCommands,
key: &[u8],
) -> trc::Result<Option<T>> {
if let Some(value) = redis::cmd("GET")
.arg(key)
.query_async::<Option<Vec<u8>>>(conn)
.await
.map_err(into_error)?
{
T::deserialize_owned(value).map(Some)
} else {
Ok(None)
}
}
async fn counter_get_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<i64> {
redis::cmd("GET")
.arg(key)
.query_async::<Option<i64>>(conn)
.await
.map(|x| x.unwrap_or(0))
.map_err(into_error)
}
async fn key_exists_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<bool> {
conn.exists(key).await.map_err(into_error)
}
async fn key_set_(
&self,
conn: &mut impl AsyncCommands,
key: &[u8],
value: &[u8],
expires: Option<u64>,
) -> trc::Result<()> {
if let Some(expires) = expires {
conn.set_ex(key, value, expires).await.map_err(into_error)
} else {
conn.set(key, value).await.map_err(into_error)
}
}
async fn key_incr_(
&self,
conn: &mut impl AsyncCommands,
key: &[u8],
value: i64,
expires: Option<u64>,
) -> trc::Result<i64> {
if let Some(expires) = expires {
redis::pipe()
.atomic()
.incr(key, value)
.expire(key, expires as i64)
.ignore()
.query_async::<Vec<i64>>(conn)
.await
.map_err(into_error)
.map(|v| v.first().copied().unwrap_or(0))
} else {
conn.incr(key, value).await.map_err(into_error)
}
}
async fn try_lock_(
&self,
conn: &mut impl AsyncCommands,
key: &[u8],
expires: u64,
) -> trc::Result<bool> {
redis::cmd("SET")
.arg(key)
.arg(now() + expires)
.arg("NX")
.arg("EX")
.arg(expires as i64)
.query_async::<Option<String>>(conn)
.await
.map(|reply| reply.is_some())
.map_err(into_error)
}
async fn key_delete_(&self, conn: &mut impl AsyncCommands, key: &[u8]) -> trc::Result<()> {
conn.del(key).await.map_err(into_error)
}
async fn key_delete_prefix_(
&self,
conn: &mut impl AsyncCommands,
prefix: &[u8],
) -> trc::Result<()> {
let mut pattern = Vec::with_capacity(prefix.len() + 1);
pattern.extend_from_slice(prefix);
pattern.push(b'*');
let mut cursor = 0;
loop {
let (new_cursor, keys): (u64, Vec<Vec<u8>>) = redis::cmd("SCAN")
.cursor_arg(cursor)
.arg("MATCH")
.arg(&pattern)
.arg("COUNT")
.arg(100)
.query_async(conn)
.await
.map_err(into_error)?;
if !keys.is_empty() {
conn.del::<_, ()>(&keys).await.map_err(into_error)?;
}
if new_cursor != 0 {
cursor = new_cursor;
} else {
return Ok(());
}
}
}
}
+215
View File
@@ -0,0 +1,215 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::InMemoryStore;
use deadpool::{
Runtime,
managed::{Manager, Pool},
};
use redis::{
Client, ConnectionAddr, IntoConnectionInfo, ProtocolVersion, TlsMode,
cluster::{ClusterClient, ClusterClientBuilder},
cluster_read_routing::RandomReplicaStrategy,
sentinel::{SentinelClient, SentinelClientBuilder, SentinelServerType},
};
use registry::{
schema::{enums::RedisProtocol, structs},
types::duration::Duration,
};
use std::{fmt::Display, sync::Arc};
pub mod lookup;
pub mod pool;
#[derive(Debug)]
pub struct RedisStore {
pub pool: RedisPool,
}
pub struct RedisConnectionManager {
pub client: Client,
timeout: std::time::Duration,
}
pub struct RedisClusterConnectionManager {
pub client: ClusterClient,
timeout: std::time::Duration,
}
pub struct RedisSentinelConnectionManager {
pub client: tokio::sync::Mutex<SentinelClient>,
timeout: std::time::Duration,
}
pub enum RedisPool {
Single(Pool<RedisConnectionManager>),
Cluster(Pool<RedisClusterConnectionManager>),
Sentinel(Pool<RedisSentinelConnectionManager>),
}
impl RedisStore {
pub async fn open_single(config: structs::RedisStore) -> Result<InMemoryStore, String> {
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
pool: RedisPool::Single(build_pool(
RedisConnectionManager {
client: Client::open(config.url)
.map_err(|err| format!("Failed to open Redis client: {err:?}"))?,
timeout: config.timeout.into_inner(),
},
config.pool_max_connections,
config.pool_timeout_create,
config.pool_timeout_wait,
config.pool_timeout_recycle,
)?),
})))
}
pub async fn open_cluster(config: structs::RedisClusterStore) -> Result<InMemoryStore, String> {
let mut builder = ClusterClientBuilder::new(config.urls);
if let Some(value) = config.auth_username {
builder = builder.username(value);
}
if let Some(value) = config.auth_secret.secret().await?.map(|v| v.into_owned()) {
builder = builder.password(value);
}
if let Some(value) = config.max_retries {
builder = builder.retries(value as u32);
}
if let Some(value) = config.max_retry_wait {
builder = builder.max_retry_wait(value.as_millis());
}
if let Some(value) = config.min_retry_wait {
builder = builder.min_retry_wait(value.as_millis());
}
if config.read_from_replicas {
builder = builder.read_routing_strategy(RandomReplicaStrategy);
}
if matches!(config.protocol_version, RedisProtocol::Resp3) {
builder = builder.use_protocol(ProtocolVersion::RESP3);
}
let client = builder
.build()
.map_err(|err| format!("Failed to open Redis client: {err:?}"))?;
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
pool: RedisPool::Cluster(build_pool(
RedisClusterConnectionManager {
client,
timeout: config.timeout.into_inner(),
},
config.pool_max_connections,
config.pool_timeout_create,
config.pool_timeout_wait,
config.pool_timeout_recycle,
)?),
})))
}
pub async fn open_sentinel(
config: structs::RedisSentinelStore,
) -> Result<InMemoryStore, String> {
let mut sentinels = Vec::with_capacity(config.urls.len());
let mut tls_mode = None;
for url in config.urls {
let info = url
.into_connection_info()
.map_err(|err| format!("Invalid Redis Sentinel URL: {err}"))?;
let url_tls_mode = match info.addr() {
ConnectionAddr::TcpTls { insecure: true, .. } => Some(TlsMode::Insecure),
ConnectionAddr::TcpTls {
insecure: false, ..
} => Some(TlsMode::Secure),
_ => None,
};
if sentinels.is_empty() {
tls_mode = url_tls_mode;
} else if tls_mode != url_tls_mode {
return Err(
"All Redis Sentinel URLs must use the same scheme and TLS settings".to_string(),
);
}
sentinels.push(info.addr().clone());
}
let mut builder =
SentinelClientBuilder::new(sentinels, config.service_name, SentinelServerType::Master)
.map_err(|err| format!("Failed to create Redis Sentinel client: {err:?}"))?;
if let Some(value) = config.auth_username {
builder = builder.set_client_to_redis_username(value);
}
if let Some(value) = config.auth_secret.secret().await?.map(|v| v.into_owned()) {
builder = builder.set_client_to_redis_password(value);
}
if let Some(value) = config.sentinel_username {
builder = builder.set_client_to_sentinel_username(value);
}
if let Some(value) = config
.sentinel_secret
.secret()
.await?
.map(|v| v.into_owned())
{
builder = builder.set_client_to_sentinel_password(value);
}
if matches!(config.protocol_version, RedisProtocol::Resp3) {
builder = builder.set_client_to_redis_protocol(ProtocolVersion::RESP3);
}
if let Some(tls_mode) = tls_mode {
builder = builder.set_client_to_redis_tls_mode(tls_mode);
}
let client = builder
.build()
.map_err(|err| format!("Failed to open Redis Sentinel client: {err:?}"))?;
Ok(InMemoryStore::Redis(Arc::new(RedisStore {
pool: RedisPool::Sentinel(build_pool(
RedisSentinelConnectionManager {
client: tokio::sync::Mutex::new(client),
timeout: config.timeout.into_inner(),
},
config.pool_max_connections,
config.pool_timeout_create,
config.pool_timeout_wait,
config.pool_timeout_recycle,
)?),
})))
}
}
fn build_pool<M: Manager>(
manager: M,
max_size: u64,
create_timeout: Option<Duration>,
wait_timeout: Option<Duration>,
recycle_timeout: Option<Duration>,
) -> Result<Pool<M>, String> {
Pool::builder(manager)
.runtime(Runtime::Tokio1)
.max_size(max_size as usize)
.create_timeout(create_timeout.map(|v| v.into_inner()))
.wait_timeout(wait_timeout.map(|v| v.into_inner()))
.recycle_timeout(recycle_timeout.map(|v| v.into_inner()))
.build()
.map_err(|err| format!("Failed to build pool: {err}"))
}
#[inline(always)]
fn into_error(err: impl Display) -> trc::Error {
trc::StoreEvent::RedisError.reason(err)
}
impl std::fmt::Debug for RedisPool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Single(_) => f.debug_tuple("Single").finish(),
Self::Cluster(_) => f.debug_tuple("Cluster").finish(),
Self::Sentinel(_) => f.debug_tuple("Sentinel").finish(),
}
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{
RedisClusterConnectionManager, RedisConnectionManager, RedisSentinelConnectionManager,
into_error,
};
use deadpool::managed;
use redis::{
aio::{ConnectionLike, MultiplexedConnection},
cluster_async::ClusterConnection,
};
impl managed::Manager for RedisConnectionManager {
type Type = MultiplexedConnection;
type Error = trc::Error;
async fn create(&self) -> Result<MultiplexedConnection, trc::Error> {
match tokio::time::timeout(self.timeout, self.client.get_multiplexed_async_connection())
.await
{
Ok(conn) => conn.map_err(into_error),
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
}
}
async fn recycle(
&self,
conn: &mut MultiplexedConnection,
_: &managed::Metrics,
) -> managed::RecycleResult<trc::Error> {
conn.req_packed_command(&redis::cmd("PING"))
.await
.map(|_| ())
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
}
}
impl managed::Manager for RedisClusterConnectionManager {
type Type = ClusterConnection;
type Error = trc::Error;
async fn create(&self) -> Result<ClusterConnection, trc::Error> {
match tokio::time::timeout(self.timeout, self.client.get_async_connection()).await {
Ok(conn) => conn.map_err(into_error),
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
}
}
async fn recycle(
&self,
conn: &mut ClusterConnection,
_: &managed::Metrics,
) -> managed::RecycleResult<trc::Error> {
conn.req_packed_command(&redis::cmd("PING"))
.await
.map(|_| ())
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
}
}
impl managed::Manager for RedisSentinelConnectionManager {
type Type = MultiplexedConnection;
type Error = trc::Error;
async fn create(&self) -> Result<MultiplexedConnection, trc::Error> {
let mut client = self.client.lock().await;
match tokio::time::timeout(self.timeout, client.get_async_connection()).await {
Ok(conn) => conn.map_err(into_error),
Err(_) => Err(trc::StoreEvent::RedisError.ctx(trc::Key::Details, "Connection Timeout")),
}
}
async fn recycle(
&self,
conn: &mut MultiplexedConnection,
_: &managed::Metrics,
) -> managed::RecycleResult<trc::Error> {
conn.req_packed_command(&redis::cmd("PING"))
.await
.map(|_| ())
.map_err(|err| managed::RecycleError::Backend(into_error(err)))
}
}