From e08e0d2ba5b68c188681ad728a8c3abb2158ce39 Mon Sep 17 00:00:00 2001 From: MrAlaskan <1922345259@qq.com> Date: Sun, 19 Jul 2026 17:33:48 +0800 Subject: [PATCH 1/2] fix(server): enforce browse continuation point limit at capacity --- .../Session/SessionContinuationPoints.cs | 2 +- .../SessionContinuationPointsTests.cs | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Opc.Ua.Server/Session/SessionContinuationPoints.cs b/src/Opc.Ua.Server/Session/SessionContinuationPoints.cs index c90c740280..c3d6e98506 100644 --- a/src/Opc.Ua.Server/Session/SessionContinuationPoints.cs +++ b/src/Opc.Ua.Server/Session/SessionContinuationPoints.cs @@ -85,7 +85,7 @@ public void SaveBrowse(ContinuationPoint continuationPoint) m_browse ??= []; // remove the first continuation point if too many points. - while (m_browse.Count > MaxBrowse) + while (m_browse.Count >= MaxBrowse) { ContinuationPoint cp = m_browse[0]; m_browse.RemoveAt(0); diff --git a/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs b/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs index 362f4c8880..235774b1e0 100644 --- a/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs +++ b/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs @@ -118,6 +118,30 @@ public void SaveBrowseEvictsOldestAndNotifiesStore() Times.Once); } + [Test] + public void SaveBrowseEvictsWhenCountReachesConfiguredLimit() + { + var store = new Mock(MockBehavior.Loose); + SessionContinuationPoints holder = NewHolder(maxBrowse: 2, store: store.Object); + + var evicted = new TrackingDisposable(); + ContinuationPoint cp1 = NewBrowsePoint(data: evicted); + ContinuationPoint cp2 = NewBrowsePoint(); + ContinuationPoint cp3 = NewBrowsePoint(); + + holder.SaveBrowse(cp1); + holder.SaveBrowse(cp2); + holder.SaveBrowse(cp3); + + Assert.That(evicted.Disposed, Is.True); + Assert.That(holder.RestoreBrowse(ToByteString(cp1.Id)), Is.Null); + Assert.That(holder.RestoreBrowse(ToByteString(cp2.Id)), Is.SameAs(cp2)); + Assert.That(holder.RestoreBrowse(ToByteString(cp3.Id)), Is.SameAs(cp3)); + store.Verify( + s => s.RemoveContinuationPoint(s_sessionId, ContinuationPointKind.Browse, cp1.Id), + Times.Once); + } + [Test] public void SaveBrowseStoresEnvelopeWithNormalizedNodeIds() { From 83388493ef2285155996ef44dd2cd2a9de323d16 Mon Sep 17 00:00:00 2001 From: MrAlaskan <1922345259@qq.com> Date: Mon, 20 Jul 2026 11:58:36 +0800 Subject: [PATCH 2/2] Fix browse continuation point eviction limit semantics --- tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs b/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs index 235774b1e0..d1e5176d7c 100644 --- a/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs +++ b/tests/Opc.Ua.Server.Tests/SessionContinuationPointsTests.cs @@ -111,11 +111,14 @@ public void SaveBrowseEvictsOldestAndNotifiesStore() Assert.That(evicted.Disposed, Is.True); Assert.That(holder.RestoreBrowse(ToByteString(cp1.Id)), Is.Null); - Assert.That(holder.RestoreBrowse(ToByteString(cp2.Id)), Is.SameAs(cp2)); + Assert.That(holder.RestoreBrowse(ToByteString(cp2.Id)), Is.Null); Assert.That(holder.RestoreBrowse(ToByteString(cp3.Id)), Is.SameAs(cp3)); store.Verify( s => s.RemoveContinuationPoint(s_sessionId, ContinuationPointKind.Browse, cp1.Id), Times.Once); + store.Verify( + s => s.RemoveContinuationPoint(s_sessionId, ContinuationPointKind.Browse, cp2.Id), + Times.Once); } [Test]