diff --git a/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs b/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs index b51e776745..a31fd53998 100644 --- a/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs +++ b/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs @@ -114,297 +114,7 @@ protected override void Dispose(bool disposing) } }; - public class ServerSessionForTest : Opc.Ua.Server.Session - { - public ServerSessionForTest( - OperationContext context, - IServerInternal server, - X509Certificate2 serverCertificate, - NodeId authenticationToken, - byte[] clientNonce, - byte[] serverNonce, - string sessionName, - ApplicationDescription - clientDescription, - string endpointUrl, - X509Certificate2 clientCertificate, - double sessionTimeout, - uint maxResponseMessageSize, - double maxRequestAge, - int maxBrowseContinuationPoints, - int maxHistoryContinuationPoints - ) : base( - context, - server, - serverCertificate, - authenticationToken, - clientNonce, - serverNonce, - sessionName, - clientDescription, - endpointUrl, - clientCertificate, - sessionTimeout, - maxResponseMessageSize, - maxRequestAge, - maxBrowseContinuationPoints, - maxHistoryContinuationPoints - ) - { - } - - public void SetMaxNumberOfContinuationPoints(uint maxNumberOfContinuationPoints) - { - MaxBrowseContinuationPoints = (int)maxNumberOfContinuationPoints; - } - } - public class SessionManagerForTest : Opc.Ua.Server.SessionManager - { - private IServerInternal m_4TestServer; - private int m_4TestMaxRequestAge; - private int m_4TestMaxBrowseContinuationPoints; - private int m_4TestMaxHistoryContinuationPoints; - public SessionManagerForTest(IServerInternal server, ApplicationConfiguration configuration) : base(server, configuration) - { - m_4TestServer = server; - m_4TestMaxRequestAge = configuration.ServerConfiguration.MaxRequestAge; - m_4TestMaxBrowseContinuationPoints = configuration.ServerConfiguration.MaxBrowseContinuationPoints; - m_4TestMaxHistoryContinuationPoints = configuration.ServerConfiguration.MaxHistoryContinuationPoints; - } - - protected override Opc.Ua.Server.Session CreateSession( - OperationContext context, - IServerInternal server, - X509Certificate2 serverCertificate, - NodeId sessionCookie, - byte[] clientNonce, - byte[] serverNonce, - string sessionName, - ApplicationDescription clientDescription, - string endpointUrl, - X509Certificate2 clientCertificate, - double sessionTimeout, - uint maxResponseMessageSize, - int maxRequestAge, // TBD - Remove unused parameter. - int maxContinuationPoints) // TBD - Remove unused parameter. - { - ServerSessionForTest session = new ServerSessionForTest( - context, - m_4TestServer, - serverCertificate, - sessionCookie, - clientNonce, - serverNonce, - sessionName, - clientDescription, - endpointUrl, - clientCertificate, - sessionTimeout, - maxResponseMessageSize, - m_4TestMaxRequestAge, - m_4TestMaxBrowseContinuationPoints, - m_4TestMaxHistoryContinuationPoints); - - return (Opc.Ua.Server.Session)session; - } - } - - public class MasterNodeManagerForThisUnitTest : MasterNodeManager - { - public MasterNodeManagerForThisUnitTest(IServerInternal server, ApplicationConfiguration configuration, string dynamicNamespaceUri, params INodeManager[] additionalManagers) : base(server, configuration, dynamicNamespaceUri, additionalManagers) - { - } - - private uint m_maxContinuationPointsPerBrowseForUnitTest = 0; - public uint MaxContinuationPointsPerBrowseForUnitTest { get => m_maxContinuationPointsPerBrowseForUnitTest; set => m_maxContinuationPointsPerBrowseForUnitTest = value; } - - /// - /// Returns the set of references that meet the filter criteria. - /// - public override void Browse( - OperationContext context, - ViewDescription view, - uint maxReferencesPerNode, - BrowseDescriptionCollection nodesToBrowse, - out BrowseResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (nodesToBrowse == null) throw new ArgumentNullException(nameof(nodesToBrowse)); - - if (view != null && !NodeId.IsNull(view.ViewId)) - { - INodeManager viewManager = null; - object viewHandle = GetManagerHandle(view.ViewId, out viewManager); - - if (viewHandle == null) - { - throw new ServiceResultException(StatusCodes.BadViewIdUnknown); - } - - NodeMetadata metadata = viewManager.GetNodeMetadata(context, viewHandle, BrowseResultMask.NodeClass); - - if (metadata == null || metadata.NodeClass != NodeClass.View) - { - throw new ServiceResultException(StatusCodes.BadViewIdUnknown); - } - - // validate access rights and role permissions - ServiceResult validationResult = ValidatePermissions(context, viewManager, viewHandle, PermissionType.Browse, null, true); - if (ServiceResult.IsBad(validationResult)) - { - throw new ServiceResultException(validationResult); - } - view.Handle = viewHandle; - } - - bool diagnosticsExist = false; - results = new BrowseResultCollection(nodesToBrowse.Count); - diagnosticInfos = new DiagnosticInfoCollection(nodesToBrowse.Count); - - uint continuationPointsAssigned = 0; - - for (int ii = 0; ii < nodesToBrowse.Count; ii++) - { - // check if request has timed out or been cancelled. - if (StatusCode.IsBad(context.OperationStatus)) - { - // release all allocated continuation points. - foreach (BrowseResult current in results) - { - if (current != null && current.ContinuationPoint != null && current.ContinuationPoint.Length > 0) - { - ContinuationPoint cp = context.Session.RestoreContinuationPoint(current.ContinuationPoint); - cp.Dispose(); - } - } - - throw new ServiceResultException(context.OperationStatus); - } - - BrowseDescription nodeToBrowse = nodesToBrowse[ii]; - - // initialize result. - BrowseResult result = new BrowseResult(); - result.StatusCode = StatusCodes.Good; - results.Add(result); - - ServiceResult error = null; - - // need to trap unexpected exceptions to handle bugs in the node managers. - try - { - error = base.Browse( - context, - view, - maxReferencesPerNode, - m_maxContinuationPointsPerBrowseForUnitTest > 0 ? - continuationPointsAssigned < m_maxContinuationPointsPerBrowseForUnitTest : true, - nodeToBrowse, - result); - } - catch (Exception e) - { - error = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected error browsing node."); - } - - // check for continuation point. - if (result.ContinuationPoint != null && result.ContinuationPoint.Length > 0) - { - continuationPointsAssigned++; - } - - // check for error. - result.StatusCode = error.StatusCode; - - if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) - { - DiagnosticInfo diagnosticInfo = null; - - if (error != null && error.Code != StatusCodes.Good) - { - diagnosticInfo = ServerUtils.CreateDiagnosticInfo(Server, context, error); - diagnosticsExist = true; - } - - diagnosticInfos.Add(diagnosticInfo); - } - } - - // clear the diagnostics array if no diagnostics requested or no errors occurred. - UpdateDiagnostics(context, diagnosticsExist, ref diagnosticInfos); - } - - - } - - public class ReferenceServerForThisUnitTest : ReferenceServer - { - public uint Test_MaxBrowseReferencesPerNode { get; set; } = 10u; - private MasterNodeManager MasterNodeManagerReference { get; set; } - private SessionManagerForTest SessionManagerForTest { get; set; } - - public override ResponseHeader Browse( - RequestHeader requestHeader, - ViewDescription view, - uint requestedMaxReferencesPerNode, - BrowseDescriptionCollection nodesToBrowse, - out BrowseResultCollection results, - out DiagnosticInfoCollection diagnosticInfos) - { - return base.Browse( - requestHeader, - view, - Test_MaxBrowseReferencesPerNode, - nodesToBrowse, - out results, - out diagnosticInfos - ); - - } - - public void SetMaxNumberOfContinuationPoints(uint maxNumberOfContinuationPoints) - { - Configuration.ServerConfiguration.MaxBrowseContinuationPoints = (int)maxNumberOfContinuationPoints; - ((MasterNodeManagerForThisUnitTest)MasterNodeManagerReference).MaxContinuationPointsPerBrowseForUnitTest = maxNumberOfContinuationPoints; - List theServerSideSessions = SessionManagerForTest.GetSessions().ToList(); - foreach (Opc.Ua.Server.Session session in theServerSideSessions) - { - try - { - ((ServerSessionForTest)session).SetMaxNumberOfContinuationPoints(maxNumberOfContinuationPoints); - } - catch { } - } - - } - - protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) - { - Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Manager."); - - IList nodeManagers = new List(); - - // create the custom node manager. - nodeManagers.Add(new ReferenceNodeManager(server, configuration)); - - foreach (var nodeManagerFactory in NodeManagerFactories) - { - nodeManagers.Add(nodeManagerFactory.Create(server, configuration)); - } - //this.MasterNodeManagerReference = new MasterNodeManager(server, configuration, null, nodeManagers.ToArray()); - this.MasterNodeManagerReference = new MasterNodeManagerForThisUnitTest(server, configuration, null, nodeManagers.ToArray()); - // create master node manager. - return this.MasterNodeManagerReference; - } - - protected override SessionManager CreateSessionManager(IServerInternal server, ApplicationConfiguration configuration) - { - this.SessionManagerForTest = new SessionManagerForTest(server, configuration); - return this.SessionManagerForTest; - } - } /// /// Client tests. @@ -451,8 +161,8 @@ public IEnumerable ManagedBrowseTestDataValues() }; } - public ReferenceServerForThisUnitTest ReferenceServerForThisUnitTest { get; set; } - public ServerFixture ServerFixtureForThisUnitTest { get; set; } + public ReferenceServerWithLimits ReferenceServerWithLimits { get; set; } + public ServerFixture ServerFixtureWithLimits { get; set; } public override async Task CreateReferenceServerFixture( bool enableTracing, bool disableActivityLogging, @@ -464,7 +174,7 @@ TextWriter writer { // start Ref server - ServerFixtureForThisUnitTest = new ServerFixture(enableTracing, disableActivityLogging) { + ServerFixtureWithLimits = new ServerFixture(enableTracing, disableActivityLogging) { UriScheme = UriScheme, SecurityNone = securityNone, AutoAccept = true, @@ -475,26 +185,26 @@ TextWriter writer if (writer != null && enableTracing) { - ServerFixtureForThisUnitTest.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security; + ServerFixtureWithLimits.TraceMasks = Utils.TraceMasks.Error | Utils.TraceMasks.Security; } - await ServerFixtureForThisUnitTest.LoadConfiguration(PkiRoot).ConfigureAwait(false); - ServerFixtureForThisUnitTest.Config.TransportQuotas.MaxMessageSize = TransportQuotaMaxMessageSize; - ServerFixtureForThisUnitTest.Config.TransportQuotas.MaxByteStringLength = - ServerFixtureForThisUnitTest.Config.TransportQuotas.MaxStringLength = TransportQuotaMaxStringLength; + await ServerFixtureWithLimits.LoadConfiguration(PkiRoot).ConfigureAwait(false); + ServerFixtureWithLimits.Config.TransportQuotas.MaxMessageSize = TransportQuotaMaxMessageSize; + ServerFixtureWithLimits.Config.TransportQuotas.MaxByteStringLength = + ServerFixtureWithLimits.Config.TransportQuotas.MaxStringLength = TransportQuotaMaxStringLength; - ServerFixtureForThisUnitTest.Config.ServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.UserName)); - ServerFixtureForThisUnitTest.Config.ServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.Certificate)); - ServerFixtureForThisUnitTest.Config.ServerConfiguration.UserTokenPolicies.Add( + ServerFixtureWithLimits.Config.ServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.UserName)); + ServerFixtureWithLimits.Config.ServerConfiguration.UserTokenPolicies.Add(new UserTokenPolicy(UserTokenType.Certificate)); + ServerFixtureWithLimits.Config.ServerConfiguration.UserTokenPolicies.Add( new UserTokenPolicy(UserTokenType.IssuedToken) { IssuedTokenType = Opc.Ua.Profiles.JwtUserToken }); - ServerFixtureForThisUnitTest.Config.ServerConfiguration.MaxBrowseContinuationPoints = 2; - ServerFixtureForThisUnitTest.Config.ServerConfiguration.OperationLimits.MaxNodesPerBrowse = 5; + ServerFixtureWithLimits.Config.ServerConfiguration.MaxBrowseContinuationPoints = 2; + ServerFixtureWithLimits.Config.ServerConfiguration.OperationLimits.MaxNodesPerBrowse = 5; - ReferenceServerForThisUnitTest = await ServerFixtureForThisUnitTest.StartAsync(localWriter).ConfigureAwait(false); - ReferenceServerForThisUnitTest.TokenValidator = this.TokenValidator; - ReferenceServer = ReferenceServerForThisUnitTest; - ServerFixturePort = ServerFixtureForThisUnitTest.Port; + ReferenceServerWithLimits = await ServerFixtureWithLimits.StartAsync(localWriter).ConfigureAwait(false); + ReferenceServerWithLimits.TokenValidator = this.TokenValidator; + ReferenceServer = ReferenceServerWithLimits; + ServerFixturePort = ServerFixtureWithLimits.Port; } public ContinuationPointInBatchTest(string uriScheme = Utils.UriSchemeOpcTcp) : base(uriScheme) @@ -656,10 +366,10 @@ public void MBNodeCache_BrowseAllVariables_MultipleNodes(ManagedBrowseTestDataPr ExpectedNumberOfBadNoCPSCs = testData.ExpectedNumberOfBadNoCPSCs }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -713,7 +423,7 @@ public void MBNodeCache_BrowseAllVariables_MultipleNodes(ManagedBrowseTestDataPr /// of allowed browse continuation points the number of results with status code /// BadNoContinuationPoints is reduced, as well as the number of retry attempts needed /// to get all browse results. - /// ii) if the number of allowed browse continuatino points is equal to or greater than + /// ii) if the number of allowed browse continuation points is equal to or greater than /// the number of nodes per browse, there will be no status codes which are bad. /// /// In all cases, the browse will succeed in the end, and the test verifies that @@ -724,8 +434,7 @@ public void MBNodeCache_BrowseAllVariables_MultipleNodes(ManagedBrowseTestDataPr /// /// No return value should have the status code BadContinuationPointInvalid, since there is /// no attempt to allocate continuation points in parallel from more than one service call. - /// - /// + /// [Theory, Order(200)] public void ManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDataProvider testData) { @@ -751,10 +460,10 @@ public void ManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDataProvide ExpectedNumberOfBadNoCPSCs = new List() }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -787,10 +496,10 @@ public void ManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDataProvide // now reset the server qutas to get a browse scenario without continuation points. This allows // to verify the result from the first browse service call (with quotas in place). - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass2ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass2ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass2ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -853,9 +562,8 @@ public void ManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDataProvide /// /// The server is configured with a certain number of allowed nodes per browse service call /// - /// The ManagedBrowse method is called with the parameter - /// executeDefensively set to true, which forces the method to - /// create packages which have at most + /// The ManagedBrowse method is called with the ContinuationPointPolicy 'Balanced' + /// which forces the method to create packages which have at most /// min(maxBrowseContinuationPoints, maxNodesPerBrowse) /// nodes. /// @@ -894,10 +602,10 @@ public void BalancedManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat ExpectedNumberOfBadNoCPSCs = new List() }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -930,10 +638,10 @@ public void BalancedManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat // now reset the server qutas to get a browse scenario without continuation points. This allows // to verify the result from the first browse service call (with quotas in place). - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass2ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass2ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass2ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -992,8 +700,9 @@ public void BalancedManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat /// /// in this test the service result BadContinuationPoint invalid in (an unpredictable subset) - /// of the return values from the ManagedBrowse method call is enforced, by executing the method - /// on two sets of nodes both of which require the allocation of BrowseContinuationPoints in the server + /// of the return values from the ManagedBrowse method call is enforced, by + /// concurrently executing the method on two sets of nodes both of which + /// require the allocation of BrowseContinuationPoints in the server /// /// The following results are expected: /// on a system which supports parallel execution of threads, at least one of the parallel calls @@ -1003,7 +712,7 @@ public void BalancedManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat /// In the worst case the two calls to ManagedBrowse could end up in an endless loop (to prevent this /// an upper bound for the number of rebrowse attempts would be needed, or a session wide management /// of the continuation points the server must potentially allocate for the service calls - /// from the client. + /// from the client). /// /// The result with regards to the BadNoContinuationPoint should be similar to the one from /// the ManagedBrowseWithManyContinuationPoints test case @@ -1035,10 +744,10 @@ public void ParallelManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat ExpectedNumberOfBadNoCPSCs = new List() }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -1080,10 +789,10 @@ public void ParallelManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat // finally browse again with a simple browse service call. // reset server quotas first: - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass2ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass2ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass2ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -1146,10 +855,10 @@ public async Task MBNodeCache_BrowseAllVariablesAsync(ManagedBrowseTestDataProvi ExpectedNumberOfBadNoCPSCs = testData.ExpectedNumberOfBadNoCPSCs }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -1220,10 +929,10 @@ public async Task MBNodeCache_BrowseAllVariables_MultipleNodesAsync(ManagedBrows ExpectedNumberOfBadNoCPSCs = testData.ExpectedNumberOfBadNoCPSCs }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; @@ -1268,6 +977,13 @@ public async Task MBNodeCache_BrowseAllVariables_MultipleNodesAsync(ManagedBrows } + /// + /// same as the ManagedBrowseWithManyContinuationPoints, but + /// with the ContinuationPointPolicy set to 'Default'. Instead of calling + /// ManagedBrowse, ManagedBrowseAsync is called directly. + /// + /// + /// [Theory, Order(420)] public async Task ManagedBrowseWithManyContinuationPointsAsync(ManagedBrowseTestDataProvider testData) { @@ -1291,10 +1007,10 @@ public async Task ManagedBrowseWithManyContinuationPointsAsync(ManagedBrowseTest ExpectedNumberOfBadNoCPSCs = new List() }; - ReferenceServerForThisUnitTest.Test_MaxBrowseReferencesPerNode = + ReferenceServerWithLimits.Test_MaxBrowseReferencesPerNode = pass1ExpectedResults.InputMaxNumberOfReferencesPerNode; - ReferenceServerForThisUnitTest.SetMaxNumberOfContinuationPoints( + ReferenceServerWithLimits.SetMaxNumberOfContinuationPoints( pass1ExpectedResults.InputMaxNumberOfContinuationPoints); theSession.ServerMaxContinuationPointsPerBrowse = pass1ExpectedResults.InputMaxNumberOfContinuationPoints; diff --git a/Tests/Opc.Ua.Client.Tests/ReferenceServerWithLimits.cs b/Tests/Opc.Ua.Client.Tests/ReferenceServerWithLimits.cs new file mode 100644 index 0000000000..789f5a76dc --- /dev/null +++ b/Tests/Opc.Ua.Client.Tests/ReferenceServerWithLimits.cs @@ -0,0 +1,354 @@ +/* ======================================================================== + * Copyright (c) 2005-2024 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.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Opc.Ua.Server; +using Quickstarts.ReferenceServer; + +namespace Opc.Ua.Client.Tests +{ + /// + /// Reference Server modification which allows to change the maximum number + /// of (browse) continuation points during the execution of a test. + /// This makes it easier to compare browse results without any restriction + /// with results where the server may allocate only a limited number of + /// continuation points. + /// To make this work some other classes must be derived and modified, as + /// well (see the ServerSessionWithLimits, SessionManagerWithLimits and + /// MasterNodeManagerWithLimits classes). + /// + /// Use with care. This class and especially its dedicated functionality + /// should be used for test purposes only. + /// + public class ReferenceServerWithLimits : ReferenceServer + { + public uint Test_MaxBrowseReferencesPerNode { get; set; } = 10u; + private MasterNodeManager MasterNodeManagerReference { get; set; } + private SessionManagerWithLimits SessionManagerForTest { get; set; } + + public override ResponseHeader Browse( + RequestHeader requestHeader, + ViewDescription view, + uint requestedMaxReferencesPerNode, + BrowseDescriptionCollection nodesToBrowse, + out BrowseResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { + return base.Browse( + requestHeader, + view, + Test_MaxBrowseReferencesPerNode, + nodesToBrowse, + out results, + out diagnosticInfos + ); + + } + + public void SetMaxNumberOfContinuationPoints(uint maxNumberOfContinuationPoints) + { + Configuration.ServerConfiguration.MaxBrowseContinuationPoints = (int)maxNumberOfContinuationPoints; + ((MasterNodeManagerWithLimits)MasterNodeManagerReference).MaxContinuationPointsPerBrowseForUnitTest = maxNumberOfContinuationPoints; + List theServerSideSessions = SessionManagerForTest.GetSessions().ToList(); + foreach (Opc.Ua.Server.Session session in theServerSideSessions) + { + try + { + ((ServerSessionWithLimits)session).SetMaxNumberOfContinuationPoints(maxNumberOfContinuationPoints); + } + catch { } + } + + } + + protected override MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) + { + Utils.LogInfo(Utils.TraceMasks.StartStop, "Creating the Reference Server Node Manager."); + + IList nodeManagers = new List(); + + // create the custom node manager. + nodeManagers.Add(new ReferenceNodeManager(server, configuration)); + + foreach (var nodeManagerFactory in NodeManagerFactories) + { + nodeManagers.Add(nodeManagerFactory.Create(server, configuration)); + } + //this.MasterNodeManagerReference = new MasterNodeManager(server, configuration, null, nodeManagers.ToArray()); + this.MasterNodeManagerReference = new MasterNodeManagerWithLimits(server, configuration, null, nodeManagers.ToArray()); + // create master node manager. + return this.MasterNodeManagerReference; + } + + protected override SessionManager CreateSessionManager(IServerInternal server, ApplicationConfiguration configuration) + { + this.SessionManagerForTest = new SessionManagerWithLimits(server, configuration); + return this.SessionManagerForTest; + } + } + + /// + /// provide a means to set the maximum number of browse continuation points to + /// the (Server) session. + /// + public class ServerSessionWithLimits : Opc.Ua.Server.Session + { + public ServerSessionWithLimits( + OperationContext context, + IServerInternal server, + X509Certificate2 serverCertificate, + NodeId authenticationToken, + byte[] clientNonce, + byte[] serverNonce, + string sessionName, + ApplicationDescription + clientDescription, + string endpointUrl, + X509Certificate2 clientCertificate, + double sessionTimeout, + uint maxResponseMessageSize, + double maxRequestAge, + int maxBrowseContinuationPoints, + int maxHistoryContinuationPoints + ) : base( + context, + server, + serverCertificate, + authenticationToken, + clientNonce, + serverNonce, + sessionName, + clientDescription, + endpointUrl, + clientCertificate, + sessionTimeout, + maxResponseMessageSize, + maxRequestAge, + maxBrowseContinuationPoints, + maxHistoryContinuationPoints + ) + { + } + + public void SetMaxNumberOfContinuationPoints(uint maxNumberOfContinuationPoints) + { + MaxBrowseContinuationPoints = (int)maxNumberOfContinuationPoints; + } + } + + /// + /// ensures that the (Server) session is the derived one for the test. + /// + public class SessionManagerWithLimits : Opc.Ua.Server.SessionManager + { + private IServerInternal m_4TestServer; + private int m_4TestMaxRequestAge; + private int m_4TestMaxBrowseContinuationPoints; + private int m_4TestMaxHistoryContinuationPoints; + public SessionManagerWithLimits(IServerInternal server, ApplicationConfiguration configuration) : base(server, configuration) + { + m_4TestServer = server; + m_4TestMaxRequestAge = configuration.ServerConfiguration.MaxRequestAge; + m_4TestMaxBrowseContinuationPoints = configuration.ServerConfiguration.MaxBrowseContinuationPoints; + m_4TestMaxHistoryContinuationPoints = configuration.ServerConfiguration.MaxHistoryContinuationPoints; + } + + protected override Opc.Ua.Server.Session CreateSession( + OperationContext context, + IServerInternal server, + X509Certificate2 serverCertificate, + NodeId sessionCookie, + byte[] clientNonce, + byte[] serverNonce, + string sessionName, + ApplicationDescription clientDescription, + string endpointUrl, + X509Certificate2 clientCertificate, + double sessionTimeout, + uint maxResponseMessageSize, + int maxRequestAge, // TBD - Remove unused parameter. + int maxContinuationPoints) // TBD - Remove unused parameter. + { + ServerSessionWithLimits session = new ServerSessionWithLimits( + context, + m_4TestServer, + serverCertificate, + sessionCookie, + clientNonce, + serverNonce, + sessionName, + clientDescription, + endpointUrl, + clientCertificate, + sessionTimeout, + maxResponseMessageSize, + m_4TestMaxRequestAge, + m_4TestMaxBrowseContinuationPoints, + m_4TestMaxHistoryContinuationPoints); + + return (Opc.Ua.Server.Session)session; + } + } + + /// + /// Hack which ensures the injected maximum number of browse continuation points + /// is really used when the browse service call is executed. + /// + public class MasterNodeManagerWithLimits : MasterNodeManager + { + public MasterNodeManagerWithLimits(IServerInternal server, ApplicationConfiguration configuration, string dynamicNamespaceUri, params INodeManager[] additionalManagers) : base(server, configuration, dynamicNamespaceUri, additionalManagers) + { + } + + private uint m_maxContinuationPointsPerBrowseForUnitTest = 0; + public uint MaxContinuationPointsPerBrowseForUnitTest { get => m_maxContinuationPointsPerBrowseForUnitTest; set => m_maxContinuationPointsPerBrowseForUnitTest = value; } + + /// + /// Returns the set of references that meet the filter criteria. + /// + public override void Browse( + OperationContext context, + ViewDescription view, + uint maxReferencesPerNode, + BrowseDescriptionCollection nodesToBrowse, + out BrowseResultCollection results, + out DiagnosticInfoCollection diagnosticInfos) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + if (nodesToBrowse == null) throw new ArgumentNullException(nameof(nodesToBrowse)); + + if (view != null && !NodeId.IsNull(view.ViewId)) + { + INodeManager viewManager = null; + object viewHandle = GetManagerHandle(view.ViewId, out viewManager); + + if (viewHandle == null) + { + throw new ServiceResultException(StatusCodes.BadViewIdUnknown); + } + + NodeMetadata metadata = viewManager.GetNodeMetadata(context, viewHandle, BrowseResultMask.NodeClass); + + if (metadata == null || metadata.NodeClass != NodeClass.View) + { + throw new ServiceResultException(StatusCodes.BadViewIdUnknown); + } + + // validate access rights and role permissions + ServiceResult validationResult = ValidatePermissions(context, viewManager, viewHandle, PermissionType.Browse, null, true); + if (ServiceResult.IsBad(validationResult)) + { + throw new ServiceResultException(validationResult); + } + view.Handle = viewHandle; + } + + bool diagnosticsExist = false; + results = new BrowseResultCollection(nodesToBrowse.Count); + diagnosticInfos = new DiagnosticInfoCollection(nodesToBrowse.Count); + + uint continuationPointsAssigned = 0; + + for (int ii = 0; ii < nodesToBrowse.Count; ii++) + { + // check if request has timed out or been cancelled. + if (StatusCode.IsBad(context.OperationStatus)) + { + // release all allocated continuation points. + foreach (BrowseResult current in results) + { + if (current != null && current.ContinuationPoint != null && current.ContinuationPoint.Length > 0) + { + ContinuationPoint cp = context.Session.RestoreContinuationPoint(current.ContinuationPoint); + cp.Dispose(); + } + } + + throw new ServiceResultException(context.OperationStatus); + } + + BrowseDescription nodeToBrowse = nodesToBrowse[ii]; + + // initialize result. + BrowseResult result = new BrowseResult(); + result.StatusCode = StatusCodes.Good; + results.Add(result); + + ServiceResult error = null; + + // need to trap unexpected exceptions to handle bugs in the node managers. + try + { + error = base.Browse( + context, + view, + maxReferencesPerNode, + m_maxContinuationPointsPerBrowseForUnitTest > 0 ? + continuationPointsAssigned < m_maxContinuationPointsPerBrowseForUnitTest : true, + nodeToBrowse, + result); + } + catch (Exception e) + { + error = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected error browsing node."); + } + + // check for continuation point. + if (result.ContinuationPoint != null && result.ContinuationPoint.Length > 0) + { + continuationPointsAssigned++; + } + + // check for error. + result.StatusCode = error.StatusCode; + + if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) + { + DiagnosticInfo diagnosticInfo = null; + + if (error != null && error.Code != StatusCodes.Good) + { + diagnosticInfo = ServerUtils.CreateDiagnosticInfo(Server, context, error); + diagnosticsExist = true; + } + + diagnosticInfos.Add(diagnosticInfo); + } + } + + // clear the diagnostics array if no diagnostics requested or no errors occurred. + UpdateDiagnostics(context, diagnosticsExist, ref diagnosticInfos); + } + + + } + +}