Repository navigation
Keep each Browse result with its own operation - #2107
Merged
Merged
Conversation
BrowseHelper resolves some operations before it browses: an invalid BrowseDirection, an unknown ReferenceTypeId, and a starting Node the AccessController denies Browse on. It browsed the rest as a filtered list, then wrote each result back by its index in that list into the unfiltered list. When an early-resolved operation came before a browsed one, the browsed result replaced the early result, the last browsed operation's slot stayed empty, and the final loop skipped it. The response had fewer results than the request, with a Good service result, so a client pairing results by position read the wrong Node's references. Write each result back to the operation it came from. An operation left without a result now throws instead of being skipped, since a skipped slot shifts every later result. SubscriptionManager.readTypeDefinitions pairs BrowseHelper results with its Nodes by position. With a Browse-denied Node ahead of an AnalogItemType Node, the AnalogItem lost its TypeDefinition and a Percent Deadband on it was rejected. The new test covers that path along with the three early-resolved cases. Fixes #2105
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2105.
Problem
The server resolves some Browse operations before it browses the rest: an invalid BrowseDirection (
Bad_BrowseDirectionInvalid), an unknown ReferenceTypeId (Bad_ReferenceTypeIdInvalid), and a starting Node the AccessController denies Browse on (Good, no references). When one of those came before a browsed operation in the same request, the response had fewer results than the request and the remaining results were shifted. The service result was still Good.For example, browsing
[RootFolder with an unknown ReferenceTypeId, ObjectsFolder]:[Good [Locations, Server, Aliases, ...]](one result, ObjectsFolder's references in RootFolder's slot)[Bad_ReferenceTypeIdInvalid [], Good [Locations, Server, Aliases, ...]]OPC 10000-4 5.9.2.2 requires the results to match the size and order of
nodesToBrowse. A client pairing results with its request by position read references for the wrong Node, and the error for the bad operation was lost.Cause and fix
BrowseHelper.browsebrowsed the unresolved operations as a filtered list, then wrote resultiof that list into slotiof the unfiltered list. The early result in that slot was overwritten, the last browsed operation's slot stayed empty, and the final loop skipped the empty slot.The fix keeps the filtered list of pending operations and writes each result back to the operation it came from:
The final loop now throws
IllegalStateExceptionfor an operation without a result instead of skipping it, because a skipped slot shifts every later result. After this fix every slot is filled, so this is an invariant check. If it ever fires, the Browse call fails with a service fault instead of returning misaligned results.Percent Deadband
SubscriptionManager.readTypeDefinitionsuses the same helper to find each Node's TypeDefinition for Percent Deadband and pairs results with Nodes by position. Its operations always pass the direction and ReferenceTypeId checks, but a Browse-denied Node shifted the results there too. Creating Percent Deadband items for[Browse-denied TestInt32, AnalogItemType TestAnalogValue]in one request rejected TestAnalogValue withBad_MonitoredItemFilterUnsupported, because its TypeDefinition was paired with TestInt32. With the fix the pairing holds and TestAnalogValue is accepted.A Browse-denied AnalogItem still cannot use Percent Deadband, because the server cannot see its TypeDefinition through that Session's Browse permissions. That behavior predates this change and is unchanged.
Tests
BrowseServiceResultOrderTest(integration-tests) runs against a server whose AccessController denies Browse on TestInt32. It covers:All 7 tests failed before the fix (results missing; TestAnalogValue rejected) and pass after it.
Verification run locally:
mvn spotless:apply(no changes) andmvn clean compilemvn -pl opc-ua-sdk/integration-tests -am test -Dtest='org/eclipse/milo/opcua/sdk/**' -Dsurefire.failIfNoSpecifiedTests=false: SDK Server 1071 tests (3 skipped), Integration Tests 1663 tests (2 skipped), no failures.1.1.x
mainhas the samebrowse()code. TheBrowseHelperchange applies cleanly there, but the test usessetAccessControllerFactoryandcreateTestServer, which 1.1.x does not have. This PR does not include a backport.