Expose per-attempt connection outcomes with the selected TLS client certificate
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
The issue is about modifying the StackExchange.Redis client's connection logic to expose per-attempt outcomes. Start by examining the PhysicalBridge class and its OnConnectionFailed method to understand the current suppression logic. Look for where LocalCertificateSelectionCallback is invoked and how to wrap it to capture the selected certificate. The goal is to design and implement a new callback, ConnectionAttemptCompleted, with the proposed EventArgs, ensuring it fires for all physical connection attempts.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
Please add supported Tunnel hooks for the outcome of every physical connection attempt.
The hooks should:
- observe initial connections and reconnects;
- report successful establishment after the Redis handshake completes;
- report every failed attempt, including reconnect failures suppressed by the public
ConnectionFailedevent; - include an identifier for the client certificate selected for that physical attempt;
- include the exception and failure stage when unsuccessful.
Motivation
We maintain a managed client-certificate integration for StackExchange.Redis. It selects a client certificate for each physical TLS connection and reports whether that exact certificate succeeded or failed, allowing fallback to a previously known-good certificate.
In short:
I want to provide a client certificate and know whether the physical connection using that certificate succeeded.
Tunnel is already a configuration-time extension point and is available before ConnectionMultiplexer.Connect returns, so it can observe initial connections as well as later reconnects. Adding physical-attempt outcome hooks there would avoid introducing another independent configuration callback.
Current limitations
Reconnect failures can be suppressed
PhysicalBridge.OnConnectionFailed raises the public ConnectionFailed event only while reportNextFailure is true.
After the first failure, the flag remains false until that bridge establishes a connection. Subsequent reconnect failures are therefore not reported through ConnectionFailed.
This creates the following sequence:
- Redis is connected successfully.
- The established connection closes with
SocketClosed. ConnectionFailedis raised, consuming the bridge's notification.- A newly rotated client certificate is selected for reconnect.
- The Redis server rejects that certificate.
- The reconnect fails, but no new
ConnectionFailedevent is raised because connectivity was never restored. - The integration cannot report the rejected certificate or activate its known-good fallback.
- Subsequent reconnects continue using the rejected certificate.
A custom failure classifier cannot fix a notification that is never delivered.
Certificate selection and outcomes are separate
LocalCertificateSelectionCallback reports which certificate was selected, while ConnectionFailed and ConnectionRestored report outcomes independently.
The outcome does not identify the selected certificate. Maintaining a global FIFO queue is unsafe because interactive and subscription connections can attempt reconnection concurrently and complete out of order.
For example:
- Certificate A is selected for one physical connection.
- Certificate B is selected for another.
- The attempts complete in the opposite order.
- Certificate B's rejection can be incorrectly attributed to A.
This can produce incorrect fallback decisions, incorrect telemetry, and retained certificate references.
Proposed Tunnel hooks
Names and argument organization are illustrative:
public abstract class Tunnel
{
public virtual ValueTask OnConnectionEstablishedAsync(
ConnectionAttemptEventArgs args,
CancellationToken cancellationToken) => default;
public virtual ValueTask OnConnectionAttemptFailedAsync(
ConnectionAttemptFailedEventArgs args,
CancellationToken cancellationToken) => default;
}
Suggested arguments:
public class ConnectionAttemptEventArgs : EventArgs
{
public EndPoint EndPoint { get; }
public ConnectionType ConnectionType { get; }
public string? PhysicalName { get; }
public string? ClientCertificateThumbprint { get; }
}
public sealed class ConnectionAttemptFailedEventArgs
: ConnectionAttemptEventArgs
{
public ConnectionAttemptStage Stage { get; }
public ConnectionFailureType FailureType { get; }
public Exception Exception { get; }
public bool? ServerCertificateAccepted { get; }
public SslPolicyErrors? ServerCertificatePolicyErrors { get; }
}
Suggested stages:
public enum ConnectionAttemptStage
{
SocketConnect,
TlsAuthentication,
RedisAuthentication,
RedisHandshake,
}
An alternative is one exactly-once completion hook:
public virtual ValueTask OnConnectionAttemptCompletedAsync(
ConnectionAttemptCompletedEventArgs args,
CancellationToken cancellationToken) => default;
where the arguments include success/failure, stage, failure type, and exception.
Either shape works provided that every physical attempt produces exactly one terminal outcome.
ClientCertificateThumbprint can be either a sha1 or sha256 thumbprint, with the end user determining which based on length.
Alternatively, a second property ClientCertificateThumbprintHashAlgorithm can be added which states the algorithm used (using the .net HashAlgorithmName).
The stage and server-validation result help consumers exclude failures unrelated to the client certificate. Consumers may still need platform-specific classification unless StackExchange.Redis can expose a structured positive signal such as ClientCertificateRejected or a peer TLS alert.
Example usage
internal sealed class ManagedCertificateTunnel : Tunnel
{
public override ValueTask OnConnectionEstablishedAsync(
ConnectionAttemptEventArgs args,
CancellationToken cancellationToken)
{
if (args.ClientCertificateThumbprint is not null)
{
// Report that the used certificate was healthy.
// This can be used for telemetry or to mark a last-known-good certificate.
}
return default;
}
public override ValueTask OnConnectionAttemptFailedAsync(
ConnectionAttemptFailedEventArgs args,
CancellationToken cancellationToken)
{
if (args.ClientCertificateThumbprint is not null
&& IsClientCertificateRejection(args))
{
// Report that the used certificate was unhealthy.
// This can be used for telemetry or to trigger fallback to a LKG certificate.
}
return default;
}
}
Existing alternative
Tunnel.BeforeAuthenticateAsync makes a workaround possible today:
- The integration creates and authenticates its own
SslStream. - It disables StackExchange.Redis's normal TLS layer.
- It returns a custom stream that tracks the first Redis read and all pre-establishment I/O
failures.
This provides deterministic certificate correlation, including delayed TLS 1.3 alerts, but it requires the integration to take ownership of substantial transport behavior:
- TLS configuration and authentication;
- stream and socket lifetime;
- framework-specific synchronous and asynchronous stream members;
- cloning
SslClientAuthenticationOptions; - preserving existing custom tunnels;
- introducing a replacement for the write-only
ConfigurationOptions.CertificateValidation
event; - tracking StackExchange.Redis I/O behavior across versions.
Physical-attempt hooks would allow StackExchange.Redis to continue owning TLS and transport details while providing the small amount of lifecycle information required by certificate integrations.
Logging is not a suitable alternative application-control-flow contract. It does not provide the selected certificate, structured stage information, or a stable guarantee that every physical attempt will continue to be logged.
Compatibility
The proposed methods are additive virtual members with default no-op implementations.
The existing ConnectionFailed and ConnectionRestored events can retain their current suppression and notification behavior.
- Lenguaje dominante
- C#
- Estrellas
- 6.2k
- Forks
- 1.6k
- Merge medio
- 1 d 15 min
- PR fusionados (30 d)
- 33
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de StackExchange/StackExchange.Redis
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
StackExchange/StackExchange.Redis#3269 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 78/100
StackExchange/StackExchange.Redis#3249 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
HotKeysClusterTests.CanUseClusterFilter skips intermittently, hiding the assertions it exists forAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
StackExchange/StackExchange.Redis#3239 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
StackExchange/StackExchange.Redis#3240 ·
Los mantenedores suelen responder en 1 día
-
RedisValue equality treats "1,000", "(5)" and " 5 " as numbers, via NumberStyles.AnyPosiblemente ocupada @banlor la tomó hace 16 días. Abierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
StackExchange/StackExchange.Redis#3233 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de StackExchange/StackExchange.Redis
Issues similares
-
VideoViewer: rotated (portrait phone) videos shown sideways when system decimal separator is a commaAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Volodymyr-Petrunin/Bankomaten#45 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
AvaloniaUI/Avalonia#22420 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
dotnet/SqlClient#4823 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
type/automation type/tech-debt
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día