You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Despite the fixes in #201 and #219, a SOAP proxy call over WcfSoapWithOpenIdConnect can still fault on its first use after some as-yet-unconfirmed trigger:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
The existing recovery mechanism (from #219) only kicks in on the next call to the same channel: WCF flips the faulted channel's CommunicationState, and Get*25Channel()''s null/faulted check rebuilds it - but only when that method is entered again. So the user sees one hard failure, then has to re-run the same cmdlet for it to succeed. Confirmed this is single-channel behavior, not a cross-channel token issue - Find-IshBaseline and Get-IshPublicationOutputContent both go through the same cached Baseline25 channel, and the working call happened first.
Proposal
Wrap each returned channel in a thin retry proxy (System.Reflection.DispatchProxy) that catches CommunicationException/FaultException on the actual call, forces a rebuild via the existing Get*25Channel() rebuild logic, and retries exactly once - so a single cmdlet invocation self-heals instead of requiring a second call. Scope: InfoShareWcfSoapWithOpenIdConnectConnection.cs only (WcfSoapWithWsTrust stays untouched, per precedent in #201/#219).
internalsealedclassRetryOnFaultProxy<T>:DispatchProxywhereT:class{privateT_target;privateFunc<T>_rebuild;publicstaticTWrap(Ttarget,Func<T>rebuild){varproxy=Create<T,RetryOnFaultProxy<T>>()asRetryOnFaultProxy<T>;proxy._target=target;proxy._rebuild=rebuild;returnproxyasT;}protectedoverrideobjectInvoke(MethodInfotargetMethod,object[]args){try{returntargetMethod.Invoke(_target,args);}catch(TargetInvocationExceptiontie)when(tie.InnerExceptionisCommunicationException||tie.InnerExceptionisFaultException){_target=_rebuild();// re-enters the existing, unchanged rebuild logicreturntargetMethod.Invoke(_target,args);// one retry only, then propagate}}}
Repeated for all ~19 Get*25Channel() methods (Annotation, Application, DocumentObj, Folder, User, UserRole, UserGroup, ListOfValues, PublicationOutput, OutputFormat, Settings, EDT, EventMonitor, Baseline, MetadataBinding, Search, TranslationJob, TranslationTemplate, BackgroundTask) - one-line change per method, both #if NET48/#else arms.
Positive side effects
No cmdlet files touched; change fully contained in Connection/, matching the layer''s own "keep changes inside this folder" rule.
Cmdlet scripts/automation no longer need their own retry-on-transient-fault handling for this specific error - one less thing external callers have to work around.
Faulted-channel rebuild logic itself is untouched/not duplicated - the retry proxy just re-enters the existing method.
Risks / open questions
New net48-only NuGet dependency: System.Reflection.DispatchProxy (built into BCL on net6.0/net10.0, not referenced yet on net48).
Retried call happens transparently - if the underlying fault is not transient (e.g. genuinely invalid credentials, revoked ClientSecret), the cmdlet now takes ~2x as long to fail instead of failing fast. Acceptable trade-off but worth calling out.
The proxy wraps only the plain service-contract interface (e.g. Application), not ICommunicationObject/IDisposable - confirmed no code outside Connection/ casts to those on the returned channel, so this is safe, but any future code that tries to cast the returned proxy to ICommunicationObject would break; worth a code comment warning against that.
Only handles the synchronous contract methods actually used by cmdlets; the generated *Async() methods on the same interfaces are not exercised by ISHRemote today and are not specifically designed for in this change.
Problem
Despite the fixes in #201 and #219, a SOAP proxy call over
WcfSoapWithOpenIdConnectcan still fault on its first use after some as-yet-unconfirmed trigger:The existing recovery mechanism (from #219) only kicks in on the next call to the same channel: WCF flips the faulted channel's
CommunicationState, andGet*25Channel()''s null/faulted check rebuilds it - but only when that method is entered again. So the user sees one hard failure, then has to re-run the same cmdlet for it to succeed. Confirmed this is single-channel behavior, not a cross-channel token issue -Find-IshBaselineandGet-IshPublicationOutputContentboth go through the same cachedBaseline25channel, and the working call happened first.Proposal
Wrap each returned channel in a thin retry proxy (
System.Reflection.DispatchProxy) that catchesCommunicationException/FaultExceptionon the actual call, forces a rebuild via the existingGet*25Channel()rebuild logic, and retries exactly once - so a single cmdlet invocation self-heals instead of requiring a second call. Scope:InfoShareWcfSoapWithOpenIdConnectConnection.csonly (WcfSoapWithWsTruststays untouched, per precedent in #201/#219).Before
After
New shared class (single-sourced, added once):
Repeated for all ~19
Get*25Channel()methods (Annotation, Application, DocumentObj, Folder, User, UserRole, UserGroup, ListOfValues, PublicationOutput, OutputFormat, Settings, EDT, EventMonitor, Baseline, MetadataBinding, Search, TranslationJob, TranslationTemplate, BackgroundTask) - one-line change per method, both#if NET48/#elsearms.Positive side effects
Connection/, matching the layer''s own "keep changes inside this folder" rule.Risks / open questions
net48-only NuGet dependency:System.Reflection.DispatchProxy(built into BCL onnet6.0/net10.0, not referenced yet onnet48).Application), notICommunicationObject/IDisposable- confirmed no code outsideConnection/casts to those on the returned channel, so this is safe, but any future code that tries to cast the returned proxy toICommunicationObjectwould break; worth a code comment warning against that.*Async()methods on the same interfaces are not exercised by ISHRemote today and are not specifically designed for in this change.