Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,24 @@ Console.WriteLine($"User: {user}");
### With Authentication and TLS

```csharp
var config = new StandaloneClientConfigurationBuilder()
.WithAddress("secure-server.example.com", 6380)
// Password-based authentication with TLS.
var passwordConfig = new StandaloneClientConfigurationBuilder()
.WithAddress(host, port)
.WithAuthentication("username", "password")
.WithTls()
.Build();

using var client = await GlideClient.CreateClient(config);
using var passwordClient = await GlideClient.CreateClient(passwordConfig);

// IAM authentication with TLS.
var iamAuthConfig = new IamAuthConfig("my-cluster", ServiceType.ElastiCache, "us-east-1");
var iamConfig = new ClusterClientConfigurationBuilder()
.WithAddress(host, port)
.WithAuthentication("username", iamAuthConfig)
.WithTls(true)
.Build();

using var iamClient = await GlideClient.CreateClient(iamConfig);
```

## Core API Examples
Expand Down
59 changes: 49 additions & 10 deletions rust/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use std::{

use glide_core::{
client::{
AuthenticationInfo, ConnectionRequest, ConnectionRetryStrategy, NodeAddress,
ReadFrom as coreReadFrom, TlsMode,
ConnectionRequest, ConnectionRetryStrategy, NodeAddress, ReadFrom as coreReadFrom, TlsMode,
},
request_type::RequestType,
};
Expand Down Expand Up @@ -65,7 +64,7 @@ pub struct ConnectionConfig {
pub has_connection_retry_strategy: bool,
pub connection_retry_strategy: ConnectionRetryStrategy,
pub has_authentication_info: bool,
pub authentication_info: Credentials,
pub authentication_info: AuthenticationInfo,
pub database_id: u32,
pub has_protocol: bool,
pub protocol: redis::ProtocolVersion,
Expand Down Expand Up @@ -114,10 +113,32 @@ pub(crate) unsafe fn create_connection_request(
client_name: unsafe { ptr_to_opt_str(config.client_name) },
lib_name: option_env!("GLIDE_NAME").map(|s| s.to_string()),
authentication_info: if config.has_authentication_info {
Some(AuthenticationInfo {
username: unsafe { ptr_to_opt_str(config.authentication_info.username) },
password: unsafe { ptr_to_opt_str(config.authentication_info.password) },
iam_config: None,
let auth_info = config.authentication_info;
let iam_config = if auth_info.has_iam_credentials {
Some(glide_core::client::IamAuthenticationConfig {
cluster_name: unsafe { ptr_to_str(auth_info.iam_credentials.cluster_name) },
region: unsafe { ptr_to_str(auth_info.iam_credentials.region) },
service_type: match auth_info.iam_credentials.service_type {
ServiceType::ElastiCache => glide_core::iam::ServiceType::ElastiCache,
ServiceType::MemoryDB => glide_core::iam::ServiceType::MemoryDB,
},
refresh_interval_seconds: if auth_info
.iam_credentials
.has_refresh_interval_seconds
{
Some(auth_info.iam_credentials.refresh_interval_seconds)
} else {
None
},
})
} else {
None
};

Some(glide_core::client::AuthenticationInfo {
username: unsafe { ptr_to_opt_str(auth_info.username) },
password: unsafe { ptr_to_opt_str(auth_info.password) },
iam_config,
})
} else {
None
Expand Down Expand Up @@ -210,11 +231,29 @@ pub enum ReadFromStrategy {
/// A mirror of [`AuthenticationInfo`] adopted for FFI.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Credentials {
/// zero pointer is valid, means no username is given (`None`)
pub struct AuthenticationInfo {
pub username: *const c_char,
/// zero pointer is valid, means no password is given (`None`)
pub password: *const c_char,
pub has_iam_credentials: bool,
pub iam_credentials: IamCredentials,
}

/// A mirror of [`IamCredentials`] adopted for FFI.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct IamCredentials {
pub cluster_name: *const c_char,
pub region: *const c_char,
pub service_type: ServiceType,
pub has_refresh_interval_seconds: bool,
pub refresh_interval_seconds: u32,
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum ServiceType {
ElastiCache = 0,
MemoryDB = 1,
}

#[repr(C)]
Expand Down
63 changes: 63 additions & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,66 @@ unsafe fn convert_string_pointer_array_to_vector<'a>(

result
}

/// Manually refresh the IAM authentication token.
///
/// This function triggers an immediate refresh of the IAM token and updates the connection.
/// It is only available if the client was created with IAM authentication.
///
/// # Arguments
///
/// * `client_ptr` - A pointer to a valid client returned from [`create_client`].
/// * `callback_index` - A unique identifier for the callback to be called when the command completes.
///
/// # Safety
///
/// * `client_ptr` must not be `null`.
/// * `client_ptr` must be able to be safely casted to a valid [`Arc<Client>`] via [`Arc::from_raw`]. See the safety documentation of [`Arc::from_raw`].
/// * This function should only be called with a `client_ptr` created by [`create_client`], before [`close_client`] was called with the pointer.
#[unsafe(no_mangle)]
pub unsafe extern "C-unwind" fn refresh_iam_token(
client_ptr: *const c_void,
callback_index: usize,
) {
let client = unsafe {
Arc::increment_strong_count(client_ptr);
Arc::from_raw(client_ptr as *mut Client)
};
let core = client.core.clone();

let mut panic_guard = PanicGuard {
panicked: true,
failure_callback: core.failure_callback,
callback_index,
};

client.runtime.spawn(async move {
let mut async_panic_guard = PanicGuard {
panicked: true,
failure_callback: core.failure_callback,
callback_index,
};

let result = core.client.clone().refresh_iam_token().await;
match result {
Ok(()) => {
let response = ResponseValue::from_value(redis::Value::Okay);
let ptr = Box::into_raw(Box::new(response));
unsafe { (core.success_callback)(callback_index, ptr) };
}
Err(err) => unsafe {
report_error(
core.failure_callback,
callback_index,
error_message(&err),
error_type(&err),
);
},
};
async_panic_guard.panicked = false;
drop(async_panic_guard);
});

panic_guard.panicked = false;
drop(panic_guard);
}
2 changes: 1 addition & 1 deletion sources/Valkey.Glide/Abstract/ConnectionMultiplexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ internal static T CreateClientConfigBuilder<T>(ConfigurationOptions configuratio
config.UseTls = configuration.Ssl;
_ = configuration.ConnectTimeout.HasValue ? config.ConnectionTimeout = TimeSpan.FromMilliseconds(configuration.ConnectTimeout.Value) : new();
_ = configuration.ResponseTimeout.HasValue ? config.RequestTimeout = TimeSpan.FromMilliseconds(configuration.ResponseTimeout.Value) : new();
_ = (configuration.User ?? configuration.Password) is not null ? config.Authentication = (configuration.User, configuration.Password!) : new();
_ = (configuration.User ?? configuration.Password) is not null ? config.WithAuthentication(configuration.User, configuration.Password!) : new();
_ = configuration.ClientName is not null ? config.ClientName = configuration.ClientName : "";
if (configuration.Protocol is not null)
{
Expand Down
20 changes: 20 additions & 0 deletions sources/Valkey.Glide/BaseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ public void Dispose()

public override int GetHashCode() => (int)ClientPointer;

/// <summary>
/// Manually refresh the IAM authentication token.
/// This method is only available when the client is configured with IAM authentication.
/// </summary>
/// <returns>A task that completes when the refresh attempt finishes.</returns>
public async Task RefreshIamTokenAsync()
{
Message message = MessageContainer.GetMessageForCall();
RefreshIamTokenFfi(ClientPointer, (ulong)message.Index);
IntPtr response = await message;
try
{
HandleResponse(response);
}
finally
{
FreeResponse(response);
}
}

#endregion public methods

#region protected methods
Expand Down
Loading
Loading