/// </summary>
public partial class NegotiateStream : AuthenticatedStream
{
+ /// <summary>Set as the _exception when the instance is disposed.</summary>
+ private static readonly ExceptionDispatchInfo s_disposedSentinel = ExceptionDispatchInfo.Capture(new ObjectDisposedException(nameof(NegotiateStream), (string?)null));
+
private const int ERROR_TRUST_FAILURE = 1790; // Used to serialize protectionLevel or impersonationLevel mismatch error to the remote side.
private const int MaxReadFrameSize = 64 * 1024;
private const int MaxWriteDataSize = 63 * 1024; // 1k for the framing and trailer that is always less as per SSPI.
private volatile int _readInProgress;
private volatile int _authInProgress;
- private Exception? _exception;
+ private ExceptionDispatchInfo? _exception;
private StreamFramer? _framer;
private NTAuthentication? _context;
private bool _canRetryAuthentication;
{
try
{
- _exception = new ObjectDisposedException(nameof(NegotiateStream));
+ _exception = s_disposedSentinel;
_context?.CloseContext();
}
finally
{
try
{
- _exception = new ObjectDisposedException(nameof(NegotiateStream));
+ _exception = s_disposedSentinel;
_context?.CloseContext();
}
finally
public override void EndWrite(IAsyncResult asyncResult) =>
TaskToApm.End(asyncResult);
+ private void ThrowIfExceptional()
+ {
+ ExceptionDispatchInfo? e = _exception;
+ if (e != null)
+ {
+ ThrowExceptional(e);
+ }
+
+ // Local function to make the check method more inline friendly.
+ static void ThrowExceptional(ExceptionDispatchInfo e)
+ {
+ // If the stored exception just indicates disposal, throw a new ODE rather than the stored one,
+ // so as to not continually build onto the shared exception's stack.
+ if (ReferenceEquals(e, s_disposedSentinel))
+ {
+ throw new ObjectDisposedException(nameof(NegotiateStream));
+ }
+
+ // Throw the stored exception.
+ e.Throw();
+ }
+ }
+
/// <summary>Validates user parameters for all Read/Write methods.</summary>
private static void ValidateParameters(byte[] buffer, int offset, int count)
{
ProtectionLevel protectionLevel,
TokenImpersonationLevel impersonationLevel)
{
- if (_exception != null && !_canRetryAuthentication)
+ if (!_canRetryAuthentication)
{
- ExceptionDispatchInfo.Throw(_exception);
+ ThrowIfExceptional();
}
if (_context != null && _context.IsValidContext)
private void SetFailed(Exception e)
{
- if (!(_exception is ObjectDisposedException))
+ if (_exception == null || !(_exception.SourceException is ObjectDisposedException))
{
- _exception = e;
+ _exception = ExceptionDispatchInfo.Capture(e);
}
_context?.CloseContext();
private void ThrowIfFailed(bool authSuccessCheck)
{
- if (_exception != null)
- {
- ExceptionDispatchInfo.Throw(_exception);
- }
+ ThrowIfExceptional();
if (authSuccessCheck && !IsAuthenticatedCore)
{