diff --git a/Directory.Packages.props b/Directory.Packages.props
index 00a88526627..d9841011f47 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -40,7 +40,6 @@
-
@@ -147,7 +146,6 @@
-
diff --git a/Orleans.slnx b/Orleans.slnx
index b15f18cb065..e4047b5f194 100644
--- a/Orleans.slnx
+++ b/Orleans.slnx
@@ -70,7 +70,6 @@
-
diff --git a/docs/site/src/content/docs/host/connection-middleware.md b/docs/site/src/content/docs/host/connection-middleware.md
index d92f8b39eca..9caff701cd6 100644
--- a/docs/site/src/content/docs/host/connection-middleware.md
+++ b/docs/site/src/content/docs/host/connection-middleware.md
@@ -1,55 +1,55 @@
---
-title: Connection middleware
-description: Plug custom logic, such as authentication or custom framing, into Orleans silo-to-silo and client-to-silo connections.
-ms.date: 08/11/2026
+title: Message transport middleware
+description: Decorate Orleans message transport connectors and listeners with cross-cutting connection behavior.
+ms.date: 08/21/2026
ms.topic: how-to
---
-# Connection middleware
+# Message transport middleware
-Orleans silo-to-silo and client-to-gateway connections are built from a chain of middleware, similar to an ASP.NET Core request pipeline. lets you insert custom logic, such as authentication, custom framing, or connection-level diagnostics, into that pipeline without reimplementing connection setup.
+Orleans establishes silo-to-silo and client-to-gateway connections through message transport connectors and listeners. Transport middleware decorates those components before they create or accept a connection.
-Orleans itself uses this abstraction for TLS. See [Secure Orleans connections with TLS](transport-layer-security.md) for the built-in TLS middleware.
+ applies to outbound connectors. applies to inbound listeners. Orleans applies every registered middleware instance in dependency-injection registration order.
-## The middleware interface
-
-The interface defines `OnConnectionAsync(ConnectionContext, ConnectionDelegate)`. A middleware instance can be invoked concurrently for multiple connections, so store per-connection state in local variables or on the , not on the middleware instance. Implementations should call `next(context)` to continue the pipeline after performing their work; not calling `next` terminates the connection.
+Orleans uses transport middleware to apply TLS. See [Secure Orleans connections with TLS](transport-layer-security.md) for the built-in security configuration.
## Register middleware
-Use on an to add middleware to a connection pipeline:
+Register connector and listener middleware as singleton services:
:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="RegisterMiddleware":::
-Connection pipelines are configured through (silo) and (client). A silo has three distinct pipelines:
+The middleware instances can be invoked for multiple connectors or listeners, so keep per-connection state in the returned decorator or the resulting .
+
+## Decorate outbound connectors
-:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="SiloPipelines":::
+A connector middleware receives the next and returns the connector Orleans should use. The decorator can observe connection attempts, select another transport, or wrap the returned message transport:
-An Orleans client has a single outbound pipeline, configured with :
+:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="ConnectorDecorator":::
-:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="ClientPipeline":::
+Delegate , `IsValid`, and disposal unless the middleware deliberately changes those guarantees.
-Middleware added first runs first, wrapping every middleware added after it, matching the order `UseMiddleware` is called. Register the middleware type itself (for example `MyClientSideMiddleware`, `MyServerSideMiddleware`) as a singleton service when using the generic `UseMiddleware()` overload.
+Orleans owns the connector decorators returned by middleware. During host disposal, the connection factory closes connection-establishment admission, awaits admitted attempts, and disposes the decorated connector chain. Each decorator disposes its own resources and delegates disposal to its inner connector. The service provider owns the registered transport connector and disposes it separately.
-> [!NOTE]
-> Silo-to-silo and client-to-silo connections perform an Orleans-internal handshake after the connection pipeline runs. Middleware that terminates or replaces the transport (such as TLS) must leave a working `ConnectionContext` in place before calling `next`, because Orleans handshake and message framing execute afterward.
+## Decorate inbound listeners
-## Read and write framed data
+A listener middleware receives the next and returns the listener Orleans should bind and accept from:
-Custom middleware that exchanges its own protocol data before calling `next` (for example, a handshake) can read and write directly from `context.Transport.Input`/`Output` (`PipeReader`/`PipeWriter`), or use for structured, length-prefixed frames. `ConnectionFrameHelper` is optional; it exists to save middleware authors from re-implementing length-prefixed framing.
+:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="ListenerDecorator":::
-The wire format per frame is `[4-byte little-endian length][1-byte frame type][payload]`, where the length equals `1 + payload.Length`.
+Preserve the listener name so Orleans can select the silo and gateway listeners by role. Return `null` from `AcceptAsync` when the inner listener has stopped, matching the listener contract.
-:::code language="csharp" source="./snippets/connection-middleware/ConnectionMiddlewareExamples.cs" id="ServerMiddleware":::
+## Migrate byte-stream middleware
-`ConnectionFrameHelper` also provides `WriteLengthPrefixedString`/`ReadLengthPrefixedString` helpers for encoding UTF-8 strings inside a frame payload, and a zero-copy `WriteFrameAsync` overload that writes the payload directly into the transport pipe buffer via an `Action>` delegate, avoiding an intermediate `byte[]` allocation.
+Customizations previously registered through `ClientConnectionOptions`, `SiloConnectionOptions`, or `IConnectionMiddleware` migrate to the connector and listener decorators shown above. Register outbound behavior as connector middleware and inbound behavior as listener middleware. Listener decorators can use the listener name to configure the `silo` and `gateway` roles independently.
-`ReadFrameAsync` throws `InvalidOperationException` if the connection is closed mid-frame or if the declared frame length exceeds `maxFrameLength` (`ConnectionFrameHelper.DefaultMaxFrameLength`, 1 MB, by default). Pass a smaller `maxFrameLength` if your protocol's frames are bounded more tightly, to fail fast on malformed or hostile input.
+The message transport request contracts own framing, completion, and buffer lifetimes. Decorators preserve those contracts while applying per-connection behavior.
## See also
--
--
+-
+-
+-
- [Secure Orleans connections with TLS](transport-layer-security.md)
- [Server configuration](configuration-guide/server-configuration.md)
- [Client configuration](configuration-guide/client-configuration.md)
diff --git a/docs/site/src/content/docs/host/snippets/connection-middleware/ConnectionMiddlewareExamples.cs b/docs/site/src/content/docs/host/snippets/connection-middleware/ConnectionMiddlewareExamples.cs
index 0b7f0d5f57b..a0f505a26ec 100644
--- a/docs/site/src/content/docs/host/snippets/connection-middleware/ConnectionMiddlewareExamples.cs
+++ b/docs/site/src/content/docs/host/snippets/connection-middleware/ConnectionMiddlewareExamples.cs
@@ -1,128 +1,84 @@
-using System.Text;
-using Microsoft.AspNetCore.Connections;
+using System.Net;
using Microsoft.Extensions.DependencyInjection;
-using Orleans;
-using Orleans.Configuration;
-using Orleans.Hosting;
-using Orleans.Runtime.Messaging;
+using Microsoft.Extensions.Logging;
+using Orleans.Connections.Transport;
namespace ConnectionMiddlewareSnippets;
internal static class ConnectionMiddlewareExamples
{
//
- public static void RegisterFromDependencyInjection(
- IServiceCollection services,
- IConnectionBuilder connectionBuilder)
+ public static void RegisterTransportMiddleware(IServiceCollection services)
{
- services.AddSingleton();
- connectionBuilder.UseMiddleware();
- }
-
- public static void RegisterInstance(IConnectionBuilder connectionBuilder)
- {
- // The caller owns this shared instance and is responsible for disposing it.
- connectionBuilder.UseMiddleware(new MyClientSideMiddleware());
+ services.AddSingleton();
+ services.AddSingleton();
}
//
+}
- //
- public static void ConfigureSilo(ISiloBuilder siloBuilder)
- {
- siloBuilder.Services.AddSingleton();
- siloBuilder.Services.AddSingleton();
-
- siloBuilder.Configure(options =>
- {
- // Connections this silo makes to other silos.
- options.ConfigureSiloOutboundConnection(connectionBuilder =>
- {
- connectionBuilder.UseMiddleware();
- });
+internal sealed class LoggingConnectorMiddleware(ILoggerFactory loggerFactory)
+ : IMessageTransportConnectorMiddleware
+{
+ public MessageTransportConnector Apply(MessageTransportConnector transport) =>
+ new LoggingConnector(transport, loggerFactory.CreateLogger());
+}
- // Connections this silo accepts from other silos.
- options.ConfigureSiloInboundConnection(connectionBuilder =>
- {
- connectionBuilder.UseMiddleware();
- });
+internal sealed class LoggingListenerMiddleware(ILoggerFactory loggerFactory)
+ : IMessageTransportListenerMiddleware
+{
+ public MessageTransportListener Apply(MessageTransportListener listener) =>
+ new LoggingListener(listener, loggerFactory.CreateLogger());
+}
- // Connections this silo accepts from Orleans clients through the gateway.
- options.ConfigureGatewayInboundConnection(connectionBuilder =>
- {
- connectionBuilder.UseMiddleware();
- });
- });
- }
- //
+//
+internal sealed class LoggingConnector(
+ MessageTransportConnector inner,
+ ILogger logger) : MessageTransportConnector
+{
+ public override IFeatureCollection Features => inner.Features;
+ public override bool IsValid => inner.IsValid;
- //
- public static void ConfigureClient(IClientBuilder clientBuilder)
+ public override async ValueTask CreateAsync(
+ EndPoint endpoint,
+ CancellationToken cancellationToken = default)
{
- clientBuilder.Services.AddSingleton();
-
- clientBuilder.Configure(options =>
- {
- options.ConfigureConnection(connectionBuilder =>
- {
- connectionBuilder.UseMiddleware();
- });
- });
+ logger.LogInformation("Connecting Orleans transport to {Endpoint}", endpoint);
+ return await inner.CreateAsync(endpoint, cancellationToken);
}
- //
+
+ public override ValueTask DisposeAsync() => inner.DisposeAsync();
}
+//
-internal sealed class MyClientSideMiddleware : IConnectionMiddleware
+//
+internal sealed class LoggingListener(
+ MessageTransportListener inner,
+ ILogger logger) : MessageTransportListener
{
- public async Task OnConnectionAsync(
- ConnectionContext context,
- ConnectionDelegate next)
- {
- var requestPayload = Encoding.UTF8.GetBytes("hello");
- await ConnectionFrameHelper.WriteFrameAsync(
- context, frameType: 0x01, requestPayload, context.ConnectionClosed);
+ public override bool IsValid => inner.IsValid;
+ public override string ListenerName => inner.ListenerName;
+ public override IFeatureCollection Features => inner.Features;
- var (frameType, payload) = await ConnectionFrameHelper.ReadFrameAsync(
- context, context.ConnectionClosed);
+ public override ValueTask BindAsync(CancellationToken cancellationToken = default) =>
+ inner.BindAsync(cancellationToken);
- if (frameType != 0x02
- || !string.Equals(
- Encoding.UTF8.GetString(payload),
- "ok",
- StringComparison.Ordinal))
+ public override async ValueTask AcceptAsync(
+ CancellationToken cancellationToken = default)
+ {
+ var transport = await inner.AcceptAsync(cancellationToken);
+ if (transport is not null)
{
- throw new InvalidOperationException("Unexpected handshake response.");
+ logger.LogInformation(
+ "Accepted Orleans transport on listener {ListenerName}",
+ ListenerName);
}
- await next(context);
+ return transport;
}
-}
-//
-internal sealed class MyServerSideMiddleware : IConnectionMiddleware
-{
- public async Task OnConnectionAsync(
- ConnectionContext context,
- ConnectionDelegate next)
- {
- // Read one frame of the custom handshake protocol.
- var (frameType, payload) = await ConnectionFrameHelper.ReadFrameAsync(
- context, context.ConnectionClosed);
-
- if (frameType != 0x01
- || !string.Equals(
- Encoding.UTF8.GetString(payload),
- "hello",
- StringComparison.Ordinal))
- {
- throw new InvalidOperationException("Unexpected handshake request.");
- }
-
- var responsePayload = Encoding.UTF8.GetBytes("ok");
- await ConnectionFrameHelper.WriteFrameAsync(
- context, frameType: 0x02, responsePayload, context.ConnectionClosed);
+ public override ValueTask UnbindAsync(CancellationToken cancellationToken = default) =>
+ inner.UnbindAsync(cancellationToken);
- // Continue the pipeline; Orleans's own handshake and framing run after this.
- await next(context);
- }
+ public override ValueTask DisposeAsync() => inner.DisposeAsync();
}
-//
+//
diff --git a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/ClientExample.csproj b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/ClientExample.csproj
index f66a2d1fa99..9e46c5973a5 100644
--- a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/ClientExample.csproj
+++ b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/ClientExample.csproj
@@ -5,11 +5,11 @@
net10.0enableenable
+ $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\..\..\..\..\..\..\..\'))
-
-
+
diff --git a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/Program.cs b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/Program.cs
index cb51b4d150a..d7fd2ab714e 100644
--- a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/Program.cs
+++ b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/ClientExample/Program.cs
@@ -1,6 +1,6 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Hosting;
-using Orleans.Connections.Security;
+using Orleans.Connections.Transport.Security;
using Orleans.Hosting;
Console.WriteLine("TLS configuration examples");
diff --git a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/Program.cs b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/Program.cs
index 96c75624662..3339b506e73 100644
--- a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/Program.cs
+++ b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/Program.cs
@@ -1,6 +1,6 @@
using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Hosting;
-using Orleans.Connections.Security;
+using Orleans.Connections.Transport.Security;
using Orleans.Hosting;
Console.WriteLine("TLS configuration examples");
diff --git a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/SiloExample.csproj b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/SiloExample.csproj
index 002ac7c5eb1..db0e7ef53db 100644
--- a/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/SiloExample.csproj
+++ b/docs/site/src/content/docs/host/snippets/transport-layer-security/csharp/SiloExample/SiloExample.csproj
@@ -5,11 +5,11 @@
net10.0enableenable
+ $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\..\..\..\..\..\..\..\'))
-
-
+
diff --git a/docs/site/src/content/docs/host/transport-layer-security.md b/docs/site/src/content/docs/host/transport-layer-security.md
index fe8fb9cb0a7..daec67a665e 100644
--- a/docs/site/src/content/docs/host/transport-layer-security.md
+++ b/docs/site/src/content/docs/host/transport-layer-security.md
@@ -10,9 +10,22 @@ ms.topic: how-to
Orleans can protect client-to-silo and silo-to-silo connections with Transport Layer Security (TLS). TLS encrypts traffic and authenticates the endpoint acting as the TLS server. Mutual TLS (mTLS) additionally requires and authenticates the endpoint acting as the TLS client.
> [!IMPORTANT]
-> defaults to `RequireCertificate`. On a silo's inbound connections, this default requires the connecting silo or Orleans client to present a certificate. To configure server-authenticated TLS without client certificates, explicitly set it to `NoCertificate` on every silo.
+> defaults to `RequireCertificate`. On a silo's inbound connections, this default requires the connecting silo or Orleans client to present a certificate. To configure server-authenticated TLS without client certificates, explicitly set it to `NoCertificate` on every silo.
-Install [Microsoft.Orleans.Connections.Security](https://www.nuget.org/packages/Microsoft.Orleans.Connections.Security) in every silo and client process.
+The TLS APIs are included in the Orleans server and client packages.
+
+## Migrate TLS configuration
+
+TLS configuration and certificate loading now reside in the Orleans Core and Runtime packages. Applications migrating from `Microsoft.Orleans.Connections.Security` update their references as follows:
+
+| Previous API | Current API |
+|---|---|
+| `Orleans.Hosting.OrleansConnectionSecurityHostingExtensions` client overloads | |
+| `Orleans.Hosting.OrleansConnectionSecurityHostingExtensions` silo overloads | |
+| `Orleans.Connections.Security.CertificateLoader` | |
+| `Orleans.Connections.Security` TLS options and features | `Orleans.Connections.Transport.Security` |
+
+The `builder.UseTls(...)` call syntax is preserved. Explicit static calls use the client or silo hosting type listed above. This package and assembly consolidation is a binary API change: rebuild applications and libraries against the message transport APIs and deploy those rebuilt binaries together.
## Choose an authentication model
@@ -23,8 +36,8 @@ Install [Microsoft.Orleans.Connections.Security](https://www.nuget.org/packages/
The two similarly named options apply at different stages:
-- controls whether the remote endpoint must present a certificate. In server middleware, `RequireCertificate` requires a certificate from an inbound Orleans client or silo, `AllowCertificate` requests one but permits none, and `NoCertificate` doesn't request one. Silo configuration uses this same value for outbound middleware; the TLS server still presents a certificate and platform validation authenticates it when no custom callback is installed.
-- controls selection of the local client certificate in client middleware. On a silo, it applies when the silo initiates a silo-to-silo connection. On an Orleans client, it applies when the client initiates a gateway connection. It doesn't control inbound silo behavior.
+- controls peer certificate requirements. On inbound silo connections, `RequireCertificate` requires a certificate from the connecting Orleans client or silo, `AllowCertificate` requests one and accepts its absence, and `NoCertificate` omits client-certificate requests. On outbound connections, this option governs validation of the TLS server's certificate.
+- controls selection of the local client certificate whenever Orleans initiates a TLS connection: silo-to-silo connections for a silo and gateway connections for an Orleans client.
`ClientCertificateMode` defaults to `AllowCertificate`: a configured local certificate is sent when it's valid for client authentication, but a missing or unsuitable local certificate is tolerated. Setting it to `RequireCertificate` makes the outbound requirement explicit and fails configuration or connection setup when an appropriate local certificate isn't available.
@@ -64,7 +77,7 @@ Configure an Orleans client without a local certificate:
:::code language="csharp" source="./snippets/transport-layer-security/csharp/ClientExample/Program.cs" id="ServerAuthenticatedTls":::
- must match a DNS Subject Alternative Name (SAN) on the server certificate. Use the stable service name clients use to reach the silos, not an arbitrary certificate subject.
+ must match a DNS Subject Alternative Name (SAN) on the server certificate. Use the stable service name clients use to reach the silos, not an arbitrary certificate subject.
## Configure mutual TLS
@@ -74,10 +87,10 @@ For mTLS, silos require a certificate from every inbound Orleans client or silo
:::code language="csharp" source="./snippets/transport-layer-security/csharp/ClientExample/Program.cs" id="MutualTls":::
-The default platform validation applies when isn't set. If you provide that callback, keep normal chain and name checks and add only the deployment-specific policy you require. A callback which returns `true` unconditionally defeats peer authentication.
+The default platform validation applies when isn't set. If you provide that callback, keep normal chain and name checks and add only the deployment-specific policy you require. A callback which returns `true` unconditionally defeats peer authentication.
> [!WARNING]
-> is suitable only for isolated local development. It accepts any remote certificate and therefore doesn't protect against impersonation or an active man-in-the-middle.
+> is suitable only for isolated local development. It accepts any remote certificate and therefore doesn't protect against impersonation or an active man-in-the-middle.
## Establish certificate trust
@@ -91,9 +104,11 @@ Keep trust stores narrow. Don't place unrelated public or corporate roots in a w
## Protocols and revocation
- defaults to TLS 1.2 and TLS 1.3. Retain those defaults unless an interoperability or policy requirement calls for a narrower set. Orleans doesn't enable TLS 1.0 or TLS 1.1 by default.
+The TLS transports negotiate the `Orleans1` application-layer protocol identifier, preserving protocol negotiation with existing Orleans endpoints. Authentication callbacks can access the through the message transport's feature collection.
+
+ defaults to TLS 1.2 and TLS 1.3. Retain those defaults unless an interoperability or policy requirement calls for a narrower set. Orleans doesn't enable TLS 1.0 or TLS 1.1 by default.
-Set to check remote certificates on inbound silo connections. For outbound connections, set in . Before enabling revocation checks, verify that every workload can reach the certificate revocation list (CRL) or Online Certificate Status Protocol (OCSP) service and decide how outages should affect availability.
+Set to check remote certificates on inbound silo connections. For outbound connections, set in . Before enabling revocation checks, verify that every workload can reach the certificate revocation list (CRL) or Online Certificate Status Protocol (OCSP) service and decide how outages should affect availability.
## Rotate certificates
@@ -102,7 +117,7 @@ Plan rotation before deployment:
1. Issue the replacement certificate with the same required names and EKUs.
2. Distribute the new issuing chain to trust stores before any endpoint presents the new certificate.
3. Make both old and new chains valid during an overlap window.
-4. Restart processes with the replacement certificate, or use and to select certificates dynamically.
+4. Restart processes with the replacement certificate, or use and to select certificates dynamically.
5. Confirm new connections use the replacement, then remove the old certificate and obsolete trust roots.
Certificate selectors are called during authentication, but certificate loading, caching, disposal, and refresh are application responsibilities. Test rotation under normal reconnect and silo restart behavior. Alert on certificate expiration well before the overlap window closes.
@@ -123,8 +138,9 @@ Certificate selectors are called during authentication, but certificate loading,
- [Orleans security](../security/index.md)
- [Network hardening](../security/networking.md)
--
--
+-
+-
+-
- [Client configuration](configuration-guide/client-configuration.md)
- [Server configuration](configuration-guide/server-configuration.md)
- [.NET TLS/SSL best practices](https://learn.microsoft.com/dotnet/core/extensions/sslstream-best-practices)
diff --git a/docs/site/src/content/docs/implementation/messaging-networking.md b/docs/site/src/content/docs/implementation/messaging-networking.md
index 079429df77c..d0f6690adca 100644
--- a/docs/site/src/content/docs/implementation/messaging-networking.md
+++ b/docs/site/src/content/docs/implementation/messaging-networking.md
@@ -7,7 +7,7 @@ ms.topic: concept-article
# Transport and networking internals
-Orleans separates message routing from transport management. `MessageCenter` decides where a message should go; `ConnectionManager` obtains a usable connection to the target silo; the connection implementation frames, queues, and writes messages to a socket. This separation lets routing repair activation addresses while transport focuses on connection management.
+Orleans separates message routing from transport management. `MessageCenter` decides where a message should go; `ConnectionManager` obtains a usable connection to the target silo; the connection implementation batches and frames messages into requests for a message transport. This separation lets routing repair activation addresses while the transport manages reads, writes, and completion callbacks.
## Connection lifecycle
@@ -17,23 +17,25 @@ Connection establishment has a bounded `OpenConnectionTimeout`. A failed attempt
Source: [`ConnectionManager`](https://github.com/dotnet/orleans/blob/main/src/Orleans.Core/Networking/ConnectionManager.cs), [`Connection`](https://github.com/dotnet/orleans/blob/main/src/Orleans.Core/Networking/Connection.cs), and [`SiloConnection`](https://github.com/dotnet/orleans/blob/main/src/Orleans.Runtime/Networking/SiloConnection.cs).
+The TCP transport enables keep-alive on outbound and accepted sockets. By default, the operating system sends the first probe after 90 seconds of idle time, then probes every 30 seconds and allows 10 unanswered probes before terminating the connection. Accepted sockets use the current options for their named listener, so each silo or gateway listener applies its configured keep-alive policy to new connections. When the platform reports an unsupported keep-alive option, the transport logs that option and retains supported settings; tuning continues for the other supported options.
+
## Message path and framing
When `MessageCenter.SendMessage` has a target silo, it reuses an existing connection or obtains one from `ConnectionManager`. A local target uses receive processing directly. A known-dead target causes a transient rejection for request and one-way messages, and expired messages are dropped before consuming transport work.
-The connection pipeline performs the protocol preamble and then exchanges framed payloads. `MessageSerializer` encodes the message header and body using Orleans serialization; the frame helper validates lengths and rejects malformed input before dispatch. TLS wraps the same Orleans framing and message protocol as connection middleware.
+The connection pipeline performs the protocol preamble and then exchanges framed payloads. `MessageSerializer` encodes the message header and body using Orleans serialization. `MessageReadRequest` validates frame lengths before consuming the header and body. The body remains buffered until deserialization, forwarding, or disposal releases it. TLS decorates the message transport while preserving Orleans framing and the message protocol.
-The connection accepts outgoing messages through an unbounded channel. Its socket writer observes transport flow control while concurrent senders can continue adding messages to that channel, so a slow connection can accumulate queued messages until it recovers or closes. A successful socket write means the frame was handed to the transport; the grain result confirms request completion. Disconnects remove the connection from the endpoint entry and cause later sends to establish a replacement.
+The connection accepts outgoing messages through an unbounded queue. Its send worker batches messages into write requests, and the transport writer observes flow control while concurrent senders continue adding messages to the connection queue. A slow connection can therefore accumulate queued messages until it recovers or closes. A successful write completes the request and releases its buffered message bodies; the grain result confirms application request completion. Disconnects remove the connection from the endpoint entry and cause later sends to establish a replacement.
## Failure and shutdown
-When a connection terminates, the base connection sends each in-flight message to the connection-specific `OnSendMessageFailure` implementation and calls `RetryMessage` for messages which remain queued. Silo and gateway inbound connections fail in-flight messages; client outbound connections return them to `MessageCenter` for routing. Application-level response timeouts remain ambiguous because a request can have reached the target before the connection failed.
+When a connection terminates, failed write requests and queued send work reroute their messages through the connection-specific `RetryMessage` implementation. That implementation returns messages to `MessageCenter` for routing up to the retry limit, then rejects requests and drops other messages. A partially written request can have reached its target before failure, so applications reconcile ambiguous outcomes using their delivery and idempotency requirements.
-Shutdown first blocks new application traffic while allowing responses and membership traffic needed to complete the stop protocol. `MessageCenter` rejects or drops blocked messages, stops accepting client messages, and then closes connections. Inbound requests arriving at a stopping silo are rejected or dropped according to message direction, so callers must still handle a rejection or timeout.
+Shutdown first blocks new application traffic while allowing responses and membership traffic needed to complete the stop protocol. `MessageCenter` rejects or drops blocked messages, stops accepting client messages, and then closes connections. Inbound application requests arriving at a stopping silo receive transient rejections with targeted cache invalidation, allowing senders to remove routes to that silo. Other inbound application messages are dropped, and their buffered bodies are released.
`ConnectionManager` closes establishment admission before canceling pending attempts and signaling existing connections to close. Each admitted producer remains owned until it publishes its connection or completes failure cleanup, including its connection runner. Shutdown then closes any late publications and drains the remaining outbound runners through middleware cleanup. The shutdown cancellation source stays valid until those operations finish; a canceled host stop preserves it for operations still unwinding. Inbound listeners stop accepting connections and close their tracked connections before awaiting the manager's closed signal and disposing their transport listener.
-Connection closure aborts the underlying transport to release pending TLS handshakes and Orleans preamble exchange. Middleware then completes its cleanup and restores the original transport before the connection context is disposed. A connection closed before its queued runner starts completes this same transport shutdown while preventing middleware startup.
+Connection closure stops send-work admission and closes the message transport to release pending TLS handshakes, preamble exchange, and transport I/O. After the connection runner and send worker quiesce, transport disposal releases the decorated transport chain. A connection closed before its queued runner starts completes transport shutdown before protocol initialization.
## Design trade-offs
diff --git a/docs/site/src/content/docs/resources/nuget-packages.md b/docs/site/src/content/docs/resources/nuget-packages.md
index 3691222a92c..6858d4ef0c9 100644
--- a/docs/site/src/content/docs/resources/nuget-packages.md
+++ b/docs/site/src/content/docs/resources/nuget-packages.md
@@ -28,7 +28,6 @@ For installation guidance, see [`dotnet package add`](https://learn.microsoft.co
| [Microsoft.Orleans.Hosting.Kubernetes](https://www.nuget.org/packages/Microsoft.Orleans.Hosting.Kubernetes) | Kubernetes hosting integration. |
| [Microsoft.Orleans.Dashboard](https://www.nuget.org/packages/Microsoft.Orleans.Dashboard) | Built-in Orleans Dashboard server and UI. |
| [Microsoft.Orleans.Dashboard.Abstractions](https://www.nuget.org/packages/Microsoft.Orleans.Dashboard.Abstractions) | Dashboard contracts for components which don't host the UI. |
-| [Microsoft.Orleans.Connections.Security](https://www.nuget.org/packages/Microsoft.Orleans.Connections.Security) | TLS support for Orleans connections. |
`Microsoft.Orleans.Runtime`, `Microsoft.Orleans.Core`, and the abstractions packages are lower-level dependencies of the metapackages. Reference them directly only when building a library with a narrower dependency requirement.
diff --git a/samples/TransportLayerSecurity/Directory.Packages.props b/samples/TransportLayerSecurity/Directory.Packages.props
index e5c05d42ae3..b03e68a9c35 100644
--- a/samples/TransportLayerSecurity/Directory.Packages.props
+++ b/samples/TransportLayerSecurity/Directory.Packages.props
@@ -8,7 +8,6 @@
-
diff --git a/samples/TransportLayerSecurity/TLS.Client/TLS.Client.csproj b/samples/TransportLayerSecurity/TLS.Client/TLS.Client.csproj
index c2401ca2d0e..3cb21dfbe6a 100644
--- a/samples/TransportLayerSecurity/TLS.Client/TLS.Client.csproj
+++ b/samples/TransportLayerSecurity/TLS.Client/TLS.Client.csproj
@@ -10,7 +10,6 @@
-
diff --git a/samples/TransportLayerSecurity/TLS.Server/TLS.Server.csproj b/samples/TransportLayerSecurity/TLS.Server/TLS.Server.csproj
index e0bf91b02d7..7fde2dd0f93 100644
--- a/samples/TransportLayerSecurity/TLS.Server/TLS.Server.csproj
+++ b/samples/TransportLayerSecurity/TLS.Server/TLS.Server.csproj
@@ -9,7 +9,6 @@
-
diff --git a/src/Orleans.Connections.Security/Hosting/HostingExtensions.cs b/src/Orleans.Connections.Security/Hosting/HostingExtensions.cs
deleted file mode 100644
index 8e163842297..00000000000
--- a/src/Orleans.Connections.Security/Hosting/HostingExtensions.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Security.Cryptography.X509Certificates;
-using Microsoft.AspNetCore.Connections;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-using Orleans.Connections.Security;
-
-namespace Orleans
-{
- ///
- /// Provides extension methods for adding TLS middleware to Orleans connections.
- ///
- public static class TlsConnectionBuilderExtensions
- {
- ///
- /// Adds TLS server authentication and encryption to the connection pipeline.
- ///
- /// The connection pipeline builder.
- /// The TLS configuration.
- public static void UseServerTls(
- this IConnectionBuilder builder,
- TlsOptions options)
- {
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- var loggerFactory = builder.ApplicationServices.GetService(typeof(ILoggerFactory)) as ILoggerFactory ?? NullLoggerFactory.Instance;
- builder.UseMiddleware(new TlsServerConnectionMiddleware(options, loggerFactory));
- }
-
- ///
- /// Adds TLS client authentication and encryption to the connection pipeline.
- ///
- /// The connection pipeline builder.
- /// The TLS configuration.
- public static void UseClientTls(
- this IConnectionBuilder builder,
- TlsOptions options)
- {
- if (options is null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- var loggerFactory = builder.ApplicationServices.GetService(typeof(ILoggerFactory)) as ILoggerFactory ?? NullLoggerFactory.Instance;
- builder.UseMiddleware(new TlsClientConnectionMiddleware(options, loggerFactory));
- }
-
- internal static void ThrowNoPrivateKey(X509Certificate2 certificate, string parameterName)
- {
- throw new ArgumentException($"Certificate {certificate.ToString(verbose: true)} does not contain a private key", parameterName);
- }
- }
-}
diff --git a/src/Orleans.Connections.Security/Orleans.Connections.Security.csproj b/src/Orleans.Connections.Security/Orleans.Connections.Security.csproj
deleted file mode 100644
index 8a54b288b39..00000000000
--- a/src/Orleans.Connections.Security/Orleans.Connections.Security.csproj
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- Microsoft.Orleans.Connections.Security
- Microsoft Orleans TLS support
- Support for security communication using TLS in Microsoft Orleans.
- $(PackageTags) TLS SSL
- $(DefaultTargetFrameworks)
- true
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Orleans.Connections.Security/Security/CertificateLoader.cs b/src/Orleans.Connections.Security/Security/CertificateLoader.cs
deleted file mode 100644
index 35b3572f44e..00000000000
--- a/src/Orleans.Connections.Security/Security/CertificateLoader.cs
+++ /dev/null
@@ -1,124 +0,0 @@
-using System;
-using System.Linq;
-using System.Security.Cryptography.X509Certificates;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// Provides methods for loading TLS certificates from an operating system certificate store.
- ///
- public static class CertificateLoader
- {
- // See http://oid-info.com/get/1.3.6.1.5.5.7.3.1
- // Indicates that a certificate can be used as a TLS server certificate
- private const string ServerAuthenticationOid = "1.3.6.1.5.5.7.3.1";
-
- // See http://oid-info.com/get/1.3.6.1.5.5.7.3.2
- // Indicates that a certificate can be used as a TLS client certificate
- private const string ClientAuthenticationOid = "1.3.6.1.5.5.7.3.2";
-
- ///
- /// Loads the newest matching certificate which has an accessible private key and is valid for the requested TLS role.
- ///
- /// The subject name to search for.
- /// The name of the certificate store.
- /// The location of the certificate store.
- ///
- /// to include certificates which do not pass certificate-chain validation;
- /// otherwise, .
- ///
- ///
- /// to select a certificate valid for server authentication;
- /// to select one valid for client authentication.
- ///
- /// The selected certificate.
- /// No suitable certificate was found.
- public static X509Certificate2 LoadFromStoreCert(string subject, string storeName, StoreLocation storeLocation, bool allowInvalid, bool server)
- {
- using (var store = new X509Store(storeName, storeLocation))
- {
- X509Certificate2Collection? storeCertificates = null;
- X509Certificate2? foundCertificate = null;
-
- try
- {
- store.Open(OpenFlags.ReadOnly);
- storeCertificates = store.Certificates;
- var foundCertificates = storeCertificates.Find(X509FindType.FindBySubjectName, subject, !allowInvalid);
- foundCertificate = foundCertificates
- .OfType()
- .Where(c => server ? IsCertificateAllowedForServerAuth(c) : IsCertificateAllowedForClientAuth(c))
- .Where(DoesCertificateHaveAnAccessiblePrivateKey)
- .OrderByDescending(certificate => certificate.NotAfter)
- .FirstOrDefault();
-
- if (foundCertificate == null)
- {
- throw new InvalidOperationException($"Certificate {subject} not found in store {storeLocation} / {storeName}. AllowInvalid: {allowInvalid}");
- }
-
- return foundCertificate;
- }
- finally
- {
- DisposeCertificates(storeCertificates, except: foundCertificate);
- }
- }
- }
-
- internal static bool IsCertificateAllowedForServerAuth(X509Certificate2 certificate) => IsCertificateAllowedForKeyUsage(certificate, ServerAuthenticationOid);
-
- internal static bool IsCertificateAllowedForClientAuth(X509Certificate2 certificate) => IsCertificateAllowedForKeyUsage(certificate, ClientAuthenticationOid);
-
- private static bool IsCertificateAllowedForKeyUsage(X509Certificate2 certificate, string purposeOid)
- {
- /* If the Extended Key Usage extension is included, then we check that the serverAuth usage is included. (http://oid-info.com/get/1.3.6.1.5.5.7.3.1)
- * If the Extended Key Usage extension is not included, then we assume the certificate is allowed for all usages.
- *
- * See also https://blogs.msdn.microsoft.com/kaushal/2012/02/17/client-certificates-vs-server-certificates/
- *
- * From https://tools.ietf.org/html/rfc3280#section-4.2.1.13 "Certificate Extensions: Extended Key Usage"
- *
- * If the (Extended Key Usage) extension is present, then the certificate MUST only be used
- * for one of the purposes indicated. If multiple purposes are
- * indicated the application need not recognize all purposes indicated,
- * as long as the intended purpose is present. Certificate using
- * applications MAY require that a particular purpose be indicated in
- * order for the certificate to be acceptable to that application.
- */
-
- var hasEkuExtension = false;
-
- foreach (var extension in certificate.Extensions.OfType())
- {
- hasEkuExtension = true;
- foreach (var oid in extension.EnhancedKeyUsages)
- {
- if (oid.Value!.Equals(purposeOid, StringComparison.Ordinal))
- {
- return true;
- }
- }
- }
-
- return !hasEkuExtension;
- }
-
- internal static bool DoesCertificateHaveAnAccessiblePrivateKey(X509Certificate2 certificate)
- => certificate.HasPrivateKey;
-
- private static void DisposeCertificates(X509Certificate2Collection? certificates, X509Certificate2? except)
- {
- if (certificates != null)
- {
- foreach (var certificate in certificates)
- {
- if (!certificate.Equals(except))
- {
- certificate.Dispose();
- }
- }
- }
- }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/DuplexPipeStream.cs b/src/Orleans.Connections.Security/Security/DuplexPipeStream.cs
deleted file mode 100644
index 3f371d216c6..00000000000
--- a/src/Orleans.Connections.Security/Security/DuplexPipeStream.cs
+++ /dev/null
@@ -1,280 +0,0 @@
-using System;
-using System.Buffers;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.IO.Pipelines;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Orleans.Connections.Security
-{
- internal class DuplexPipeStream : Stream
- {
- private readonly PipeReader _reader;
- private readonly PipeWriter _writer;
-
- public override bool CanRead => true;
- public override bool CanSeek => false;
- public override bool CanWrite => true;
- public override long Length => throw new NotSupportedException();
- public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
-
- public DuplexPipeStream(IDuplexPipe pipe)
- {
- _reader = pipe.Input;
- _writer = pipe.Output;
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- _reader.Complete();
- _writer.Complete();
- }
- base.Dispose(disposing);
- }
-
- public override async ValueTask DisposeAsync()
- {
- await _reader.CompleteAsync().ConfigureAwait(false);
- await _writer.CompleteAsync().ConfigureAwait(false);
- }
-
- public override void Flush()
- {
- FlushAsync().GetAwaiter().GetResult();
- }
-
- public override async Task FlushAsync(CancellationToken cancellationToken)
- {
- FlushResult r = await _writer.FlushAsync(cancellationToken).ConfigureAwait(false);
- if (r.IsCanceled) throw new OperationCanceledException(cancellationToken);
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- ValidateBufferArguments(buffer, offset, count);
-
- ValueTask t = ReadAsync(buffer.AsMemory(offset, count));
- return
- t.IsCompleted ? t.GetAwaiter().GetResult() :
- t.AsTask().GetAwaiter().GetResult();
- }
-
- public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- ValidateBufferArguments(buffer, offset, count);
-
- return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
- }
-
- public override async ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
- {
- ReadResult result = await _reader.ReadAsync(cancellationToken).ConfigureAwait(false);
-
- if (result.IsCanceled)
- {
- throw new OperationCanceledException();
- }
-
- ReadOnlySequence sequence = result.Buffer;
- long bufferLength = sequence.Length;
- SequencePosition consumed = sequence.Start;
-
- try
- {
- if (bufferLength != 0)
- {
- int actual = (int)Math.Min(bufferLength, buffer.Length);
-
- ReadOnlySequence slice = actual == bufferLength ? sequence : sequence.Slice(0, actual);
- consumed = slice.End;
- slice.CopyTo(buffer.Span);
-
- return actual;
- }
-
- if (result.IsCompleted)
- {
- return 0;
- }
- }
- finally
- {
- _reader.AdvanceTo(consumed);
- }
-
- // This is a buggy PipeReader implementation that returns 0 byte reads even though the PipeReader
- // isn't completed or canceled.
- throw new InvalidOperationException("Read zero bytes unexpectedly");
- }
-
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
- {
- return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state);
- }
-
- public override int EndRead(IAsyncResult asyncResult)
- {
- return TaskToApm.End(asyncResult);
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
-
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- ValidateBufferArguments(buffer, offset, count);
-
- return WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
- }
-
- public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
- {
- FlushResult r = await _writer.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
- if (r.IsCanceled) throw new OperationCanceledException(cancellationToken);
- }
-
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
- {
- return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state);
- }
-
- public override void EndWrite(IAsyncResult asyncResult)
- {
- TaskToApm.End(asyncResult);
- }
-
- public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
- {
- return _reader.CopyToAsync(destination, cancellationToken);
- }
-
- ///
- /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern.
- ///
- internal static class TaskToApm
- {
- ///
- /// Marshals the Task as an IAsyncResult, using the supplied callback and state
- /// to implement the APM pattern.
- ///
- /// The Task to be marshaled.
- /// The callback to be invoked upon completion.
- /// The state to be stored in the IAsyncResult.
- /// An IAsyncResult to represent the task's asynchronous operation.
- public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) =>
- new TaskAsyncResult(task, state, callback);
-
- /// Processes an IAsyncResult returned by Begin.
- /// The IAsyncResult to unwrap.
- public static void End(IAsyncResult asyncResult)
- {
- if (GetTask(asyncResult) is Task t)
- {
- t.GetAwaiter().GetResult();
- return;
- }
-
- ThrowArgumentException(asyncResult);
- }
-
- /// Processes an IAsyncResult returned by Begin.
- /// The IAsyncResult to unwrap.
- public static TResult End(IAsyncResult asyncResult)
- {
- if (GetTask(asyncResult) is Task task)
- {
- return task.GetAwaiter().GetResult();
- }
-
- ThrowArgumentException(asyncResult);
- return default!; // Unreachable because ThrowArgumentException always throws.
- }
-
- /// Gets the task represented by the IAsyncResult.
- public static Task? GetTask(IAsyncResult asyncResult) => (asyncResult as TaskAsyncResult)?._task;
-
- /// Throws an argument exception for the invalid .
- [DoesNotReturn]
- private static void ThrowArgumentException(IAsyncResult asyncResult) =>
- throw (asyncResult is null ?
- new ArgumentNullException(nameof(asyncResult)) :
- new ArgumentException(null, nameof(asyncResult)));
-
- /// Provides a simple IAsyncResult that wraps a Task.
- ///
- /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state,
- /// but that's very rare, in particular in a situation where someone cares about allocation, and always
- /// using TaskAsyncResult simplifies things and enables additional optimizations.
- ///
- internal sealed class TaskAsyncResult : IAsyncResult
- {
- /// The wrapped Task.
- internal readonly Task _task;
- /// Callback to invoke when the wrapped task completes.
- private readonly AsyncCallback? _callback;
-
- /// Initializes the IAsyncResult with the Task to wrap and the associated object state.
- /// The Task to wrap.
- /// The new AsyncState value.
- /// Callback to invoke when the wrapped task completes.
- internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
- {
- Debug.Assert(task != null);
- _task = task;
- AsyncState = state;
-
- if (task.IsCompleted)
- {
- // Synchronous completion. Invoke the callback. No need to store it.
- CompletedSynchronously = true;
- callback?.Invoke(this);
- }
- else if (callback != null)
- {
- // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in
- // order to avoid running synchronously if the task has already completed by the time we get here but still run
- // synchronously as part of the task's completion if the task completes after (the more common case).
- _callback = callback;
- _task.ConfigureAwait(continueOnCapturedContext: false)
- .GetAwaiter()
- .OnCompleted(InvokeCallback); // allocates a delegate, but avoids a closure
- }
- }
-
- /// Invokes the callback.
- private void InvokeCallback()
- {
- Debug.Assert(!CompletedSynchronously);
- Debug.Assert(_callback != null);
- _callback.Invoke(this);
- }
-
- /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
- public object? AsyncState { get; }
- /// Gets a value that indicates whether the asynchronous operation completed synchronously.
- /// This is set lazily based on whether the has completed by the time this object is created.
- public bool CompletedSynchronously { get; }
- /// Gets a value that indicates whether the asynchronous operation has completed.
- public bool IsCompleted => _task.IsCompleted;
- /// Gets a that is used to wait for an asynchronous operation to complete.
- public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;
- }
- }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/DuplexPipeStreamAdapter.cs b/src/Orleans.Connections.Security/Security/DuplexPipeStreamAdapter.cs
deleted file mode 100644
index cdb6985eba5..00000000000
--- a/src/Orleans.Connections.Security/Security/DuplexPipeStreamAdapter.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.IO;
-using System.IO.Pipelines;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// A helper for wrapping a Stream decorator from an .
- ///
- ///
- internal class DuplexPipeStreamAdapter : DuplexPipeStream, IDuplexPipe where TStream : Stream
- {
- private bool _disposed;
-#if NET9_0_OR_GREATER
- private readonly Lock _disposeLock = new();
-#else
- private readonly object _disposeLock = new();
-#endif
-
- public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, Func createStream) :
- this(duplexPipe, new StreamPipeReaderOptions(leaveOpen: true), new StreamPipeWriterOptions(leaveOpen: true), createStream)
- {
- }
-
- public DuplexPipeStreamAdapter(IDuplexPipe duplexPipe, StreamPipeReaderOptions readerOptions, StreamPipeWriterOptions writerOptions, Func createStream) :
- base(duplexPipe)
- {
- var stream = createStream(this);
- Stream = stream;
- Input = PipeReader.Create(stream, readerOptions);
- Output = PipeWriter.Create(stream, writerOptions);
- }
-
- public TStream Stream { get; }
-
- public PipeReader Input { get; }
-
- public PipeWriter Output { get; }
-
- public override async ValueTask DisposeAsync()
- {
- lock (_disposeLock)
- {
- if (_disposed)
- {
- return;
- }
- _disposed = true;
- }
-
- await Input.CompleteAsync();
- await Output.CompleteAsync();
- }
-
- protected override void Dispose(bool disposing)
- {
- lock (_disposeLock)
- {
- if (_disposed)
- {
- return;
- }
- _disposed = true;
- }
-
- if (disposing)
- {
- Input.Complete();
- Output.Complete();
- }
- }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/ITlsApplicationProtocolFeature.cs b/src/Orleans.Connections.Security/Security/ITlsApplicationProtocolFeature.cs
deleted file mode 100644
index b4c8bdf025e..00000000000
--- a/src/Orleans.Connections.Security/Security/ITlsApplicationProtocolFeature.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// Provides access to the application protocol negotiated during the TLS handshake.
- ///
- public interface ITlsApplicationProtocolFeature
- {
- ///
- /// Gets the negotiated application protocol bytes.
- ///
- ReadOnlyMemory ApplicationProtocol { get; }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/ITlsConnectionFeature.cs b/src/Orleans.Connections.Security/Security/ITlsConnectionFeature.cs
deleted file mode 100644
index ed44fa6fc1a..00000000000
--- a/src/Orleans.Connections.Security/Security/ITlsConnectionFeature.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Security.Cryptography.X509Certificates;
-using System.Threading.Tasks;
-using System.Threading;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// Provides access to certificate information for an authenticated TLS connection.
- ///
- public interface ITlsConnectionFeature
- {
- ///
- /// Synchronously retrieves the remote endpoint's certificate, if any.
- ///
- X509Certificate2? RemoteCertificate { get; set; }
-
- ///
- /// Asynchronously retrieves the remote endpoint's certificate, if any.
- ///
- /// The cancellation token associated with the request.
- /// A task which returns the remote endpoint's certificate, or if none is available.
- Task GetRemoteCertificateAsync(CancellationToken cancellationToken);
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/ITlsHandshakeFeature.cs b/src/Orleans.Connections.Security/Security/ITlsHandshakeFeature.cs
deleted file mode 100644
index f1386656fe7..00000000000
--- a/src/Orleans.Connections.Security/Security/ITlsHandshakeFeature.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.Net.Security;
-using System.Security.Authentication;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// Provides information about the negotiated TLS handshake.
- ///
- public interface ITlsHandshakeFeature
- {
- ///
- /// Gets the negotiated TLS protocol version.
- ///
- SslProtocols Protocol { get; }
-
- ///
- /// Gets the .
- ///
- TlsCipherSuite? NegotiatedCipherSuite => null;
-
- ///
- /// Gets the host name from the "server_name" (SNI) extension of the client hello if present.
- ///
- string HostName => string.Empty;
-
- ///
- /// Gets the negotiated bulk encryption algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- CipherAlgorithmType CipherAlgorithm { get; }
-
- ///
- /// Gets the strength, in bits, of the negotiated bulk encryption algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- int CipherStrength { get; }
-
- ///
- /// Gets the negotiated message authentication algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- HashAlgorithmType HashAlgorithm { get; }
-
- ///
- /// Gets the strength, in bits, of the negotiated message authentication algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- int HashStrength { get; }
-
- ///
- /// Gets the negotiated key exchange algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- ExchangeAlgorithmType KeyExchangeAlgorithm { get; }
-
- ///
- /// Gets the strength, in bits, of the negotiated key exchange algorithm.
- ///
-#if NET10_0_OR_GREATER
- [Obsolete("KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties are obsolete. Use NegotiatedCipherSuite instead.", DiagnosticId = "SYSLIB0058", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
-#endif
- int KeyExchangeStrength { get; }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/MemoryPoolExtensions.cs b/src/Orleans.Connections.Security/Security/MemoryPoolExtensions.cs
deleted file mode 100644
index 65d1dc3b985..00000000000
--- a/src/Orleans.Connections.Security/Security/MemoryPoolExtensions.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Buffers;
-
-namespace Orleans.Connections.Security
-{
- internal static class MemoryPoolExtensions
- {
- ///
- /// Computes a minimum segment size
- ///
- ///
- ///
- public static int GetMinimumSegmentSize(this MemoryPool? pool)
- {
- if (pool == null)
- {
- return 4096;
- }
-
- return Math.Min(4096, pool.MaxBufferSize);
- }
-
- public static int GetMinimumAllocSize(this MemoryPool? pool)
- {
- // 1/2 of a segment
- return pool.GetMinimumSegmentSize() / 2;
- }
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/OrleansApplicationProtocol.cs b/src/Orleans.Connections.Security/Security/OrleansApplicationProtocol.cs
deleted file mode 100644
index 14ab24ceeee..00000000000
--- a/src/Orleans.Connections.Security/Security/OrleansApplicationProtocol.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Net.Security;
-
-namespace Orleans.Connections.Security
-{
- internal static class OrleansApplicationProtocol
- {
- public static readonly SslApplicationProtocol Orleans1 = new SslApplicationProtocol("Orleans1");
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/RemoteCertificateMode.cs b/src/Orleans.Connections.Security/Security/RemoteCertificateMode.cs
deleted file mode 100644
index 923ad969f2d..00000000000
--- a/src/Orleans.Connections.Security/Security/RemoteCertificateMode.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Orleans.Connections.Security
-{
- ///
- /// Describes the remote certificate requirements for a TLS connection.
- ///
- public enum RemoteCertificateMode
- {
- ///
- /// A remote certificate is not required and will not be requested from remote endpoints.
- ///
- NoCertificate,
-
- ///
- /// A remote certificate will be requested; however, authentication will not fail if a certificate is not provided by the remote endpoint.
- ///
- AllowCertificate,
-
- ///
- /// A remote certificate will be requested, and the remote endpoint must provide a valid certificate for authentication.
- ///
- RequireCertificate
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/TlsClientAuthenticationOptions.cs b/src/Orleans.Connections.Security/Security/TlsClientAuthenticationOptions.cs
deleted file mode 100644
index 262ac014f1f..00000000000
--- a/src/Orleans.Connections.Security/Security/TlsClientAuthenticationOptions.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using System.Collections.Generic;
-using System.Net.Security;
-using System.Security.Authentication;
-using System.Security.Cryptography.X509Certificates;
-
-namespace Orleans.Connections.Security
-{
- ///
- /// Selects a local certificate for client authentication.
- ///
- /// The object requesting certificate selection.
- /// The name of the target host.
- /// The available local certificates.
- /// The remote certificate, if available.
- /// The certificate issuer names accepted by the remote endpoint.
- /// The certificate to use for client authentication, or to omit a client certificate.
- public delegate X509Certificate? ClientCertificateSelectionCallback(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate? remoteCertificate, string[] acceptableIssuers);
-
- ///
- /// Configures client authentication for an Orleans TLS connection.
- ///
- public class TlsClientAuthenticationOptions
- {
- internal SslClientAuthenticationOptions Value { get; } = new SslClientAuthenticationOptions
- {
- ApplicationProtocols = new List
- {
- OrleansApplicationProtocol.Orleans1
- }
- };
-
- ///
- /// Gets or sets the callback which selects a local client certificate.
- ///
- public ClientCertificateSelectionCallback? LocalCertificateSelectionCallback
- {
- get => Value.LocalCertificateSelectionCallback is null ? null : new ClientCertificateSelectionCallback(Value.LocalCertificateSelectionCallback);
- set
- {
-#if NET10_0_OR_GREATER
- Value.LocalCertificateSelectionCallback = value is null ? null : new System.Net.Security.LocalCertificateSelectionCallback(value);
-#else
- Value.LocalCertificateSelectionCallback = value is null
- ? null
- : (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
- value(sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers)!;
-#endif
- }
- }
-
- ///
- /// Gets or sets the certificates available for client authentication.
- ///
- public X509CertificateCollection? ClientCertificates
- {
- get => this.Value.ClientCertificates;
- set => this.Value.ClientCertificates = value;
- }
-
- ///
- /// Gets or sets the TLS protocol versions permitted for authentication.
- ///
- public SslProtocols EnabledSslProtocols
- {
- get => this.Value.EnabledSslProtocols;
- set => this.Value.EnabledSslProtocols = value;
- }
-
- ///
- /// Gets or sets the certificate revocation checking mode used during authentication.
- ///
- public X509RevocationMode CertificateRevocationCheckMode
- {
- get => this.Value.CertificateRevocationCheckMode;
- set => this.Value.CertificateRevocationCheckMode = value;
- }
-
- ///
- /// Gets or sets the target host name used for server certificate validation and Server Name Indication (SNI).
- ///
- public string? TargetHost
- {
- get => this.Value.TargetHost;
- set => this.Value.TargetHost = value;
- }
-
- ///
- /// Gets the underlying instance.
- ///
- public object SslClientAuthenticationOptions => this.Value;
- }
-}
diff --git a/src/Orleans.Connections.Security/Security/TlsClientConnectionMiddleware.cs b/src/Orleans.Connections.Security/Security/TlsClientConnectionMiddleware.cs
deleted file mode 100644
index e74868e7cf3..00000000000
--- a/src/Orleans.Connections.Security/Security/TlsClientConnectionMiddleware.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.IO.Pipelines;
-using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Connections;
-using Microsoft.AspNetCore.Connections.Features;
-using Microsoft.Extensions.Logging;
-using Orleans.Runtime.Messaging;
-
-namespace Orleans.Connections.Security
-{
- internal partial class TlsClientConnectionMiddleware : IConnectionMiddleware
- {
- private readonly TlsOptions _options;
- private readonly ILogger? _logger;
- private readonly X509Certificate2? _certificate;
- private readonly Func