diff --git a/src/Opc.Ua.Client/Fluent/ManagedSessionBuilder.cs b/src/Opc.Ua.Client/Fluent/ManagedSessionBuilder.cs
index 0c79aee134..e9430121d4 100644
--- a/src/Opc.Ua.Client/Fluent/ManagedSessionBuilder.cs
+++ b/src/Opc.Ua.Client/Fluent/ManagedSessionBuilder.cs
@@ -103,6 +103,9 @@ namespace Opc.Ua.Client
///
/// Use a discovery endpoint URL with optional security mode/policy.
+ /// The endpoint's encoding and message limits are taken from the
+ /// of the
+ /// configuration this builder was created with.
///
/// is null.
public ManagedSessionBuilder UseEndpoint(
@@ -120,7 +123,8 @@ namespace Opc.Ua.Client
SecurityMode = securityMode,
SecurityPolicyUri = securityPolicyUri
};
- return UseEndpoint(new ConfiguredEndpoint(null, endpointDescription, null));
+ return UseEndpoint(new ConfiguredEndpoint(
+ null, endpointDescription, EndpointConfiguration.Create(m_configuration)));
}
///
@@ -137,7 +141,9 @@ namespace Opc.Ua.Client
/// Certificate + IssuedToken / JWT) so the client can present
/// any identity supported by the server; the server-side
/// DefaultSessionlessIdentityProvider still enforces what
- /// it accepts.
+ /// it accepts. As with ,
+ /// the endpoint's limits come from the builder configuration's
+ /// .
///
///
/// The server's base URL, e.g. https://server:4843/.
@@ -189,7 +195,8 @@ namespace Opc.Ua.Client
};
m_webApiOptions ??= new WebApiClientOptions();
m_webApiOptions.Encoding = encoding;
- return UseEndpoint(new ConfiguredEndpoint(null, endpointDescription, null));
+ return UseEndpoint(new ConfiguredEndpoint(
+ null, endpointDescription, EndpointConfiguration.Create(m_configuration)));
}
///
@@ -198,7 +205,9 @@ namespace Opc.Ua.Client
/// surfaced via ,
/// OPC Foundation profile/2339). Same envelope as
/// but
- /// multiplexed over a WebSocket text-frame transport.
+ /// multiplexed over a WebSocket text-frame transport, including
+ /// the limits taken from
+ /// .
///
///
/// The server's WSS URL, e.g. wss://server:4843/.
@@ -250,7 +259,8 @@ namespace Opc.Ua.Client
};
m_webApiOptions ??= new WebApiClientOptions();
m_webApiOptions.Encoding = encoding;
- return UseEndpoint(new ConfiguredEndpoint(null, endpointDescription, null));
+ return UseEndpoint(new ConfiguredEndpoint(
+ null, endpointDescription, EndpointConfiguration.Create(m_configuration)));
}
///
diff --git a/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderExtendedTests.cs b/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderExtendedTests.cs
index 11d063026c..12ee3bb883 100644
--- a/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderExtendedTests.cs
+++ b/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderExtendedTests.cs
@@ -143,6 +143,51 @@ namespace Opc.Ua.Client.Tests.ClientBuilder
Assert.That(opts.Endpoint, Is.Not.Null);
}
+ // ----------------------------------------------------------------
+ // URL shortcuts and transport quotas
+ // ----------------------------------------------------------------
+
+ ///
+ /// The URL shortcuts build their endpoint from the application's
+ /// transport quotas instead of the built-in defaults (issue #4905).
+ ///
+ [TestCase("opc.tcp://server:4840")]
+ [TestCase("https://server:4843/")]
+ [TestCase("wss://server:4843/")]
+ public void UrlShortcutUsesApplicationTransportQuotas(string url)
+ {
+ ITelemetryContext telemetry = NUnitTelemetryContext.Create();
+ ApplicationConfiguration configuration = CreateConfig(telemetry);
+ configuration.TransportQuotas = new TransportQuotas
+ {
+ OperationTimeout = 60_000,
+ MaxStringLength = 1024 * 1024,
+ MaxByteStringLength = 2 * 1024 * 1024,
+ MaxArrayLength = 300_000,
+ MaxMessageSize = 8 * 1024 * 1024,
+ MaxBufferSize = 128 * 1024
+ };
+ var builder = new ManagedSessionBuilder(configuration, telemetry);
+ builder = url.StartsWith("https", StringComparison.Ordinal)
+ ? builder.UseWebApiEndpoint(url)
+ : url.StartsWith("wss", StringComparison.Ordinal)
+ ? builder.UseWssOpenApiEndpoint(url)
+ : builder.UseEndpoint(url, MessageSecurityMode.None, SecurityPolicies.None);
+
+ EndpointConfiguration? quotas = builder.Build().Endpoint?.Configuration;
+
+ Assert.That(quotas, Is.Not.Null);
+ Assert.Multiple(() =>
+ {
+ Assert.That(quotas!.OperationTimeout, Is.EqualTo(60_000));
+ Assert.That(quotas.MaxStringLength, Is.EqualTo(1024 * 1024));
+ Assert.That(quotas.MaxByteStringLength, Is.EqualTo(2 * 1024 * 1024));
+ Assert.That(quotas.MaxArrayLength, Is.EqualTo(300_000));
+ Assert.That(quotas.MaxMessageSize, Is.EqualTo(8 * 1024 * 1024));
+ Assert.That(quotas.MaxBufferSize, Is.EqualTo(128 * 1024));
+ });
+ }
+
// ----------------------------------------------------------------
// WithWebApiAuthentication
// ----------------------------------------------------------------
diff --git a/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderTransportQuotaTests.cs b/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderTransportQuotaTests.cs
new file mode 100644
index 0000000000..e70e95993d
--- /dev/null
+++ b/tests/Opc.Ua.Client.Tests/ClientBuilder/ManagedSessionBuilderTransportQuotaTests.cs
@@ -0,0 +1,108 @@
+/* ========================================================================
+ * Copyright (c) 2005-2026 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================*/
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Opc.Ua.Client.TestFramework;
+using Opc.Ua.Server.TestFramework;
+using Opc.Ua.Tests;
+using Quickstarts.ReferenceServer;
+
+namespace Opc.Ua.Client.Tests.ClientBuilder
+{
+ ///
+ /// Verifies against a running server that a session connected through
+ ///
+ /// decodes with the application's transport quotas (issue #4905).
+ ///
+ [TestFixture]
+ [NonParallelizable]
+ [Category("Client")]
+ [Category("Integration")]
+ public sealed class ManagedSessionBuilderTransportQuotaTests
+ {
+ ///
+ /// The session message context reflects the configured limits rather
+ /// than the built-in endpoint defaults.
+ ///
+ [Test]
+ public async Task UseEndpointUrlSessionUsesApplicationTransportQuotasAsync()
+ {
+ ITelemetryContext telemetry = NUnitTelemetryContext.Create();
+ string root = Path.Combine(Path.GetTempPath(), "mtq-" + Guid.NewGuid().ToString("N")[..8]);
+ var fixture = new ServerFixture(context => new ReferenceServer(context))
+ {
+ AutoAccept = true,
+ SecurityNone = true
+ };
+ try
+ {
+ await fixture.StartAsync(root).ConfigureAwait(false);
+ await using var client = new ClientFixture(telemetry);
+ await client.LoadClientConfigurationAsync(root, "TransportQuotaClient").ConfigureAwait(false);
+ ApplicationConfiguration configuration = client.Config;
+ configuration.TransportQuotas = new TransportQuotas
+ {
+ MaxStringLength = 1024 * 1024,
+ MaxByteStringLength = 2 * 1024 * 1024,
+ MaxArrayLength = 300_000,
+ MaxMessageSize = 8 * 1024 * 1024
+ };
+ using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(60));
+
+ await using Client.ManagedSession session = await new ManagedSessionBuilder(configuration, telemetry)
+ .UseEndpoint(
+ $"{Utils.UriSchemeOpcTcp}://localhost:{fixture.Port}",
+ MessageSecurityMode.None,
+ SecurityPolicies.None)
+ .ConnectAsync(timeout.Token)
+ .ConfigureAwait(false);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(session.MessageContext.MaxStringLength, Is.EqualTo(1024 * 1024));
+ Assert.That(session.MessageContext.MaxByteStringLength, Is.EqualTo(2 * 1024 * 1024));
+ Assert.That(session.MessageContext.MaxArrayLength, Is.EqualTo(300_000));
+ Assert.That(session.MessageContext.MaxMessageSize, Is.EqualTo(8 * 1024 * 1024));
+ });
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ if (Directory.Exists(root))
+ {
+ Directory.Delete(root, recursive: true);
+ }
+ }
+ }
+ }
+}