mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-21 00:56:28 +08:00
052576115d
# Description When the **initial** connect of a `ManagedSession` failed, `CreateAsync` did not throw. It returned a session with no inner `Session` behind it, so every subsequent member threw `BadNotConnected` and the real error — `BadCertificateUntrusted` in the reported case — was swallowed. **Mechanism.** A failed initial connect moves the state machine `Connecting` → `Reconnecting`, which calls `ManagedSession.HandleReconnectAsync`. That method skipped its entire body when `m_session` was `null` — exactly the state after a failed initial connect — and fell through to `return ServiceResult.Good`. The state machine read that as a successful reconnect, transitioned to `Connected`, released the `CreateAsync` waiter, and handed back a session that was never connected. The logs show it plainly: a "Reconnected" line immediately after "Reconnecting" with no service call in between. The terminal path was broken as well: once the reconnect policy **and** the failover were exhausted the machine settled in `Disconnected` and nothing ever set the connected event, so `WaitForConnectedAsync` — and with it `CreateAsync` — waited forever rather than surfacing the failure. **Fix.** - `ManagedSession.HandleReconnectAsync` reads the inner session once under the service lock and, when there is none, runs a full connect (`HandleConnectAsync`) instead of reporting success. The reconnect policy now genuinely retries the initial connect. - `ConnectionStateMachine` tracks the error of the last failed connect/reconnect attempt and sets a new *settled* event when the connect cycle ends — either by connecting or by giving up (`Disconnected` after a failed failover, or `Closing`/`Closed`). `WaitForConnectedAsync` waits on that and throws a `ServiceResultException` carrying the last attempt's error when the connection was abandoned. A failover failure only supplies that error if no attempt recorded one, so the `BadNotSupported` returned by a failover with no redundant server configured never masks the actual cause. `CreateAsync` can no longer hand back a session that reports connected with `m_session == null`. ## Behavior change worth reviewing A permanently failing initial connect no longer returns instantly: `CreateAsync` retries under the configured policy and throws only once `MaxRetries` / `MaxTotalReconnectTime` and failover are exhausted (default budget 5 minutes). Callers wanting fail-fast cap the policy or pass a cancellation token — documented in `docs/Sessions.md`. No status code is treated as non-retryable. Classifying e.g. `BadCertificateUntrusted` as fatal would break the very scenario in the issue, where the connect can start succeeding once an operator trusts the certificate. ## Tests - `ConnectionStateMachineTests.WaitForConnectedAsyncThrowsOriginalErrorWhenConnectGivesUp` — the original error survives the failover's `BadNotSupported`. - `ConnectionStateMachineTests.WaitForConnectedAsyncThrowsWhenClosedWhileConnecting` — a close while connecting releases the waiter instead of hanging. - `ManagedSessionTests.CreateAsyncThrowsInitialConnectErrorInsteadOfReturningUnconnectedSession` — asserts the original status propagates out of `CreateAsync` and that the connect was attempted more than once. - `WssOpenApiIntegrationTests.ManagedSessionOverWssOpenApiBearerRejectedWhenNoBearerAuthRegistered` relied on the old behavior (it asserted `Connected == false` on the session returned by a failed connect). Rewritten to bound the retry policy and assert that `ConnectAsync` throws; the fail-closed HTTP 401 property it guards is unchanged. Suites run locally on net10.0, all green: `Opc.Ua.Client.Tests` (2155), `Opc.Ua.Sessions.Tests` (775), `Opc.Ua.Gds.Tests` (1096), `Opc.Ua.Client.ComplexTypes.Tests` (2773), `Opc.Ua.Redundancy.Client.Tests` (122). No new build or analyzer warnings. ## Related Issues - Fixes #4347 ## Checklist - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>