Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Libraries/Opc.Ua.Server/Session/ISessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public interface ISessionManager : IDisposable
/// </summary>
event SessionEventHandler SessionClosing;

/// <summary>
/// Raised after diagnostics of an existing session were changed.
/// </summary>
event SessionEventHandler SessionDiagnosticsChanged;

/// <summary>
/// Raised to signal a channel that the session is still alive.
/// </summary>
Expand Down Expand Up @@ -141,6 +146,11 @@ ValueTask<CreateSessionResult> CreateSessionAsync(
/// and that the sequence number is not out of order (update requests only).
/// </remarks>
OperationContext ValidateRequest(RequestHeader requestHeader, SecureChannelContext secureChannelContext, RequestType requestType);

/// <summary>
/// Triggers the <see cref="ISessionManager.SessionDiagnosticsChanged"/> event so subscribers can react.
/// </summary>
void RaiseSessionDiagnosticsChangedEvent(ISession session);
}

/// <summary>
Expand Down Expand Up @@ -194,6 +204,11 @@ public enum SessionEventReason
/// </summary>
Activated,

/// <summary>
/// The diagnostics of an existing session were changed.
/// </summary>
DiagnosticsChanged,

/// <summary>
/// A session is about to be closed.
/// </summary>
Expand Down
10 changes: 7 additions & 3 deletions Libraries/Opc.Ua.Server/Session/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ public virtual void ValidateRequest(RequestHeader requestHeader, SecureChannelCo

lock (m_lock)
{

if (secureChannelContext == null || !IsSecureChannelValid(secureChannelContext.SecureChannelId))
{
UpdateDiagnosticCounters(requestType, true, true);
Expand Down Expand Up @@ -1141,6 +1140,8 @@ private void UpdateDiagnosticCounters(
bool error,
bool authorizationError)
{
ServiceCounterDataType counter = null;

lock (DiagnosticsLock)
{
if (!error)
Expand All @@ -1160,8 +1161,6 @@ private void UpdateDiagnosticCounters(
}
}

ServiceCounterDataType counter = null;

switch (requestType)
{
case RequestType.Read:
Expand Down Expand Up @@ -1271,6 +1270,11 @@ private void UpdateDiagnosticCounters(
}
}
}

if (counter != null)
{
m_server.SessionManager.RaiseSessionDiagnosticsChangedEvent(this);
}
}

private readonly Lock m_lock = new();
Expand Down
34 changes: 34 additions & 0 deletions Libraries/Opc.Ua.Server/Session/SessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,17 @@ protected virtual ISession CreateSession(
m_maxHistoryContinuationPoints);
}

/// <inheritdoc />
public virtual void RaiseSessionDiagnosticsChangedEvent(ISession session)
{
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}

RaiseSessionEvent(session, SessionEventReason.DiagnosticsChanged);
}

/// <summary>
/// Raises an event related to a session.
/// </summary>
Expand All @@ -592,6 +603,9 @@ protected virtual void RaiseSessionEvent(ISession session, SessionEventReason re
case SessionEventReason.Closing:
handler = m_SessionClosing;
break;
case SessionEventReason.DiagnosticsChanged:
handler = m_SessionDiagnosticsChanged;
break;
case SessionEventReason.ChannelKeepAlive:
handler = m_SessionChannelKeepAlive;
break;
Expand Down Expand Up @@ -688,6 +702,7 @@ await m_server.CloseSessionAsync(null, session.Id, false)
private event SessionEventHandler m_SessionCreated;
private event SessionEventHandler m_SessionActivated;
private event SessionEventHandler m_SessionClosing;
private event SessionEventHandler m_SessionDiagnosticsChanged;
private event SessionEventHandler m_SessionChannelKeepAlive;
private event ImpersonateEventHandler m_ImpersonateUser;
private event EventHandler<ValidateSessionLessRequestEventArgs> m_ValidateSessionLessRequest;
Expand Down Expand Up @@ -730,6 +745,25 @@ public event SessionEventHandler SessionActivated
}
}

/// <inheritdoc/>
public event SessionEventHandler SessionDiagnosticsChanged
{
add
{
lock (m_eventLock)
{
m_SessionDiagnosticsChanged += value;
}
}
remove
{
lock (m_eventLock)
{
m_SessionDiagnosticsChanged -= value;
}
}
}

/// <inheritdoc/>
public event SessionEventHandler SessionClosing
{
Expand Down
95 changes: 95 additions & 0 deletions Tests/Opc.Ua.Server.Tests/SessionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System.Threading.Tasks;
using NUnit.Framework;

using Assert = NUnit.Framework.Legacy.ClassicAssert;

namespace Opc.Ua.Server.Tests
{
[TestFixture]
[Category("Event")]
public class SessionTests
{
[Test]
public async Task UpdateDiagnosticCounters_RaisesEvent_WhenPerRequestCounterChanged()
{
var fixture = new ServerFixture<StandardServer>();
await fixture.StartAsync().ConfigureAwait(false);

try
{
StandardServer server = fixture.Server;

(RequestHeader requestHeader, SecureChannelContext secureChannelContext) =
await ServerFixtureUtils.CreateAndActivateSessionAsync(server, "UpdateDiagnosticCountersTest").ConfigureAwait(false);

ISession session = server.CurrentInstance.SessionManager.GetSession(requestHeader.AuthenticationToken);
Assert.NotNull(session, "Session should exist after Create/Activate.");

bool eventRaised = false;

server.CurrentInstance.SessionManager.SessionDiagnosticsChanged += (s, reason)
=> eventRaised = true;

uint before = session.SessionDiagnostics.ReadCount.TotalCount;

// Call ValidateRequest for a request type that maps to a counter (Read).
session.ValidateRequest(requestHeader, secureChannelContext, RequestType.Read);

Assert.IsTrue(eventRaised, "SessionDiagnosticsChanged event should be raised when a per-request counter changes.");
Assert.Greater(session.SessionDiagnostics.ReadCount.TotalCount, before, "ReadCount.TotalCount should have incremented.");
}
finally
{
await fixture.StopAsync().ConfigureAwait(false);
}
}

[Test]
[Category("Event")]
[TestCase(RequestType.Unknown)]
[TestCase(RequestType.FindServers)]
[TestCase(RequestType.GetEndpoints)]
[TestCase(RequestType.CreateSession)]
[TestCase(RequestType.ActivateSession)]
[TestCase(RequestType.CloseSession)]
[TestCase(RequestType.Cancel)]
public async Task UpdateDiagnosticCounters_DoesNotRaiseEvent_ForIgnoredRequestTypes(RequestType requestType)
{
var fixture = new ServerFixture<StandardServer>();
await fixture.StartAsync().ConfigureAwait(false);

try
{
StandardServer server = fixture.Server;

(RequestHeader requestHeader, SecureChannelContext secureChannelContext) =
await ServerFixtureUtils.CreateAndActivateSessionAsync(server, "UpdateDiagnosticCountersIgnoredTest").ConfigureAwait(false);

ISession session = server.CurrentInstance.SessionManager.GetSession(requestHeader.AuthenticationToken);
Assert.NotNull(session, "Session should exist after Create/Activate.");

bool eventRaised = false;

server.CurrentInstance.SessionManager.SessionDiagnosticsChanged += (s, reason)
=> eventRaised = true;

// Capture total requests before; UpdateDiagnosticCounters always increments TotalRequestCount.
uint totalBefore = session.SessionDiagnostics.TotalRequestCount.TotalCount;

// Call ValidateRequest with one of the ignored request types.
session.ValidateRequest(requestHeader, secureChannelContext, requestType);

Assert.AreEqual(
totalBefore + 1,
session.SessionDiagnostics.TotalRequestCount.TotalCount,
"TotalRequestCount should increment for all request types.");

Assert.IsFalse(eventRaised, $"SessionDiagnosticsChanged event must NOT be raised for request type {requestType}.");
}
finally
{
await fixture.StopAsync().ConfigureAwait(false);
}
}
}
}
Loading