2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//------------------------------------------------------------
|
|
|
|
|
|
|
|
namespace System.ServiceModel.Channels
|
|
|
|
{
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.IO;
|
|
|
|
using System.Runtime;
|
|
|
|
using System.ServiceModel;
|
|
|
|
using System.ServiceModel.Diagnostics;
|
|
|
|
using System.ServiceModel.Security;
|
|
|
|
using System.Threading;
|
|
|
|
using System.ServiceModel.Diagnostics.Application;
|
|
|
|
using System.Security;
|
|
|
|
using System.Security.Authentication.ExtendedProtection;
|
|
|
|
using System.Security.Principal;
|
|
|
|
using System.Runtime.Diagnostics;
|
|
|
|
|
|
|
|
abstract class FramingDuplexSessionChannel : TransportDuplexSessionChannel
|
|
|
|
{
|
|
|
|
IConnection connection;
|
|
|
|
bool exposeConnectionProperty;
|
|
|
|
|
|
|
|
FramingDuplexSessionChannel(ChannelManagerBase manager, IConnectionOrientedTransportFactorySettings settings,
|
|
|
|
EndpointAddress localAddress, Uri localVia, EndpointAddress remoteAddresss, Uri via, bool exposeConnectionProperty)
|
|
|
|
: base(manager, settings, localAddress, localVia, remoteAddresss, via)
|
|
|
|
{
|
|
|
|
this.exposeConnectionProperty = exposeConnectionProperty;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected FramingDuplexSessionChannel(ChannelManagerBase factory, IConnectionOrientedTransportFactorySettings settings,
|
|
|
|
EndpointAddress remoteAddresss, Uri via, bool exposeConnectionProperty)
|
|
|
|
: this(factory, settings, EndpointAddress.AnonymousAddress, settings.MessageVersion.Addressing.AnonymousUri,
|
|
|
|
remoteAddresss, via, exposeConnectionProperty)
|
|
|
|
{
|
|
|
|
this.Session = FramingConnectionDuplexSession.CreateSession(this, settings.Upgrade);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected FramingDuplexSessionChannel(ConnectionOrientedTransportChannelListener channelListener,
|
|
|
|
EndpointAddress localAddress, Uri localVia, bool exposeConnectionProperty)
|
|
|
|
: this(channelListener, channelListener, localAddress, localVia,
|
|
|
|
EndpointAddress.AnonymousAddress, channelListener.MessageVersion.Addressing.AnonymousUri, exposeConnectionProperty)
|
|
|
|
{
|
|
|
|
this.Session = FramingConnectionDuplexSession.CreateSession(this, channelListener.Upgrade);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected IConnection Connection
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
this.connection = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override bool IsStreamedOutput
|
|
|
|
{
|
|
|
|
get { return false; }
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void CloseOutputSessionCore(TimeSpan timeout)
|
|
|
|
{
|
|
|
|
Connection.Write(SessionEncoder.EndBytes, 0, SessionEncoder.EndBytes.Length, true, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void CompleteClose(TimeSpan timeout)
|
|
|
|
{
|
|
|
|
this.ReturnConnectionIfNecessary(false, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void PrepareMessage(Message message)
|
|
|
|
{
|
|
|
|
if (exposeConnectionProperty)
|
|
|
|
{
|
|
|
|
message.Properties[ConnectionMessageProperty.Name] = this.connection;
|
|
|
|
}
|
|
|
|
base.PrepareMessage(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnSendCore(Message message, TimeSpan timeout)
|
|
|
|
{
|
|
|
|
bool allowOutputBatching;
|
|
|
|
ArraySegment<byte> messageData;
|
|
|
|
allowOutputBatching = message.Properties.AllowOutputBatching;
|
|
|
|
messageData = this.EncodeMessage(message);
|
|
|
|
this.Connection.Write(messageData.Array, messageData.Offset, messageData.Count, !allowOutputBatching,
|
|
|
|
timeout, this.BufferManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override AsyncCompletionResult BeginCloseOutput(TimeSpan timeout, WaitCallback callback, object state)
|
|
|
|
{
|
|
|
|
return this.Connection.BeginWrite(SessionEncoder.EndBytes, 0, SessionEncoder.EndBytes.Length,
|
|
|
|
true, timeout, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void FinishWritingMessage()
|
|
|
|
{
|
|
|
|
this.Connection.EndWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override AsyncCompletionResult StartWritingBufferedMessage(Message message, ArraySegment<byte> messageData, bool allowOutputBatching, TimeSpan timeout, WaitCallback callback, object state)
|
|
|
|
{
|
|
|
|
return this.Connection.BeginWrite(messageData.Array, messageData.Offset, messageData.Count,
|
|
|
|
!allowOutputBatching, timeout, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override AsyncCompletionResult StartWritingStreamedMessage(Message message, TimeSpan timeout, WaitCallback callback, object state)
|
|
|
|
{
|
|
|
|
Fx.Assert(false, "Streamed output should never be called in this channel.");
|
|
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override ArraySegment<byte> EncodeMessage(Message message)
|
|
|
|
{
|
|
|
|
ArraySegment<byte> messageData = MessageEncoder.WriteMessage(message,
|
|
|
|
int.MaxValue, this.BufferManager, SessionEncoder.MaxMessageFrameSize);
|
|
|
|
|
|
|
|
messageData = SessionEncoder.EncodeMessageFrame(messageData);
|
|
|
|
|
|
|
|
return messageData;
|
|
|
|
}
|
|
|
|
|
|
|
|
class FramingConnectionDuplexSession : ConnectionDuplexSession
|
|
|
|
{
|
|
|
|
|
|
|
|
FramingConnectionDuplexSession(FramingDuplexSessionChannel channel)
|
|
|
|
: base(channel)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public static FramingConnectionDuplexSession CreateSession(FramingDuplexSessionChannel channel,
|
|
|
|
StreamUpgradeProvider upgrade)
|
|
|
|
{
|
|
|
|
StreamSecurityUpgradeProvider security = upgrade as StreamSecurityUpgradeProvider;
|
|
|
|
if (security == null)
|
|
|
|
{
|
|
|
|
return new FramingConnectionDuplexSession(channel);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return new SecureConnectionDuplexSession(channel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SecureConnectionDuplexSession : FramingConnectionDuplexSession, ISecuritySession
|
|
|
|
{
|
|
|
|
EndpointIdentity remoteIdentity;
|
|
|
|
|
|
|
|
public SecureConnectionDuplexSession(FramingDuplexSessionChannel channel)
|
|
|
|
: base(channel)
|
|
|
|
{
|
|
|
|
// empty
|
|
|
|
}
|
|
|
|
|
|
|
|
EndpointIdentity ISecuritySession.RemoteIdentity
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (remoteIdentity == null)
|
|
|
|
{
|
|
|
|
SecurityMessageProperty security = this.Channel.RemoteSecurity;
|
|
|
|
if (security != null && security.ServiceSecurityContext != null &&
|
|
|
|
security.ServiceSecurityContext.IdentityClaim != null &&
|
|
|
|
security.ServiceSecurityContext.PrimaryIdentity != null)
|
|
|
|
{
|
|
|
|
this.remoteIdentity = EndpointIdentity.CreateIdentity(
|
|
|
|
security.ServiceSecurityContext.IdentityClaim);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.remoteIdentity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ClientFramingDuplexSessionChannel : FramingDuplexSessionChannel
|
|
|
|
{
|
|
|
|
IConnectionOrientedTransportChannelFactorySettings settings;
|
|
|
|
ClientDuplexDecoder decoder;
|
|
|
|
StreamUpgradeProvider upgrade;
|
|
|
|
ConnectionPoolHelper connectionPoolHelper;
|
|
|
|
bool flowIdentity;
|
|
|
|
|
|
|
|
public ClientFramingDuplexSessionChannel(ChannelManagerBase factory, IConnectionOrientedTransportChannelFactorySettings settings,
|
|
|
|
EndpointAddress remoteAddresss, Uri via, IConnectionInitiator connectionInitiator, ConnectionPool connectionPool,
|
|
|
|
bool exposeConnectionProperty, bool flowIdentity)
|
|
|
|
: base(factory, settings, remoteAddresss, via, exposeConnectionProperty)
|
|
|
|
{
|
|
|
|
this.settings = settings;
|
|
|
|
this.MessageEncoder = settings.MessageEncoderFactory.CreateSessionEncoder();
|
|
|
|
this.upgrade = settings.Upgrade;
|
|
|
|
this.flowIdentity = flowIdentity;
|
|
|
|
this.connectionPoolHelper = new DuplexConnectionPoolHelper(this, connectionPool, connectionInitiator);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArraySegment<byte> CreatePreamble()
|
|
|
|
{
|
|
|
|
EncodedVia encodedVia = new EncodedVia(this.Via.AbsoluteUri);
|
|
|
|
EncodedContentType encodedContentType = EncodedContentType.Create(this.MessageEncoder.ContentType);
|
|
|
|
|
|
|
|
// calculate preamble length
|
|
|
|
int startSize = ClientDuplexEncoder.ModeBytes.Length + SessionEncoder.CalcStartSize(encodedVia, encodedContentType);
|
|
|
|
int preambleEndOffset = 0;
|
|
|
|
if (this.upgrade == null)
|
|
|
|
{
|
|
|
|
preambleEndOffset = startSize;
|
|
|
|
startSize += ClientDuplexEncoder.PreambleEndBytes.Length;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] startBytes = DiagnosticUtility.Utility.AllocateByteArray(startSize);
|
|
|
|
Buffer.BlockCopy(ClientDuplexEncoder.ModeBytes, 0, startBytes, 0, ClientDuplexEncoder.ModeBytes.Length);
|
|
|
|
SessionEncoder.EncodeStart(startBytes, ClientDuplexEncoder.ModeBytes.Length, encodedVia, encodedContentType);
|
|
|
|
if (preambleEndOffset > 0)
|
|
|
|
{
|
|
|
|
Buffer.BlockCopy(ClientDuplexEncoder.PreambleEndBytes, 0, startBytes, preambleEndOffset, ClientDuplexEncoder.PreambleEndBytes.Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new ArraySegment<byte>(startBytes, 0, startSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return new OpenAsyncResult(this, timeout, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnEndOpen(IAsyncResult result)
|
|
|
|
{
|
|
|
|
OpenAsyncResult.End(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override T GetProperty<T>()
|
|
|
|
{
|
|
|
|
T result = base.GetProperty<T>();
|
|
|
|
|
|
|
|
if (result == null && this.upgrade != null)
|
|
|
|
{
|
|
|
|
result = this.upgrade.GetProperty<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
IConnection SendPreamble(IConnection connection, ArraySegment<byte> preamble, ref TimeoutHelper timeoutHelper)
|
|
|
|
{
|
|
|
|
if (TD.ClientSendPreambleStartIsEnabled())
|
|
|
|
{
|
|
|
|
TD.ClientSendPreambleStart(this.EventTraceActivity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize a new decoder
|
|
|
|
this.decoder = new ClientDuplexDecoder(0);
|
|
|
|
byte[] ackBuffer = new byte[1];
|
|
|
|
connection.Write(preamble.Array, preamble.Offset, preamble.Count, true, timeoutHelper.RemainingTime());
|
|
|
|
|
|
|
|
if (this.upgrade != null)
|
|
|
|
{
|
|
|
|
IStreamUpgradeChannelBindingProvider channelBindingProvider = upgrade.GetProperty<IStreamUpgradeChannelBindingProvider>();
|
|
|
|
StreamUpgradeInitiator upgradeInitiator = upgrade.CreateUpgradeInitiator(this.RemoteAddress, this.Via);
|
|
|
|
|
|
|
|
upgradeInitiator.Open(timeoutHelper.RemainingTime());
|
|
|
|
if (!ConnectionUpgradeHelper.InitiateUpgrade(upgradeInitiator, ref connection, this.decoder,
|
|
|
|
this, ref timeoutHelper))
|
|
|
|
{
|
|
|
|
ConnectionUpgradeHelper.DecodeFramingFault(this.decoder, connection, Via, MessageEncoder.ContentType, ref timeoutHelper);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channelBindingProvider != null && channelBindingProvider.IsChannelBindingSupportEnabled)
|
|
|
|
{
|
|
|
|
this.SetChannelBinding(channelBindingProvider.GetChannelBinding(upgradeInitiator, ChannelBindingKind.Endpoint));
|
|
|
|
}
|
|
|
|
|
|
|
|
SetRemoteSecurity(upgradeInitiator);
|
|
|
|
upgradeInitiator.Close(timeoutHelper.RemainingTime());
|
|
|
|
connection.Write(ClientDuplexEncoder.PreambleEndBytes, 0,
|
|
|
|
ClientDuplexEncoder.PreambleEndBytes.Length, true, timeoutHelper.RemainingTime());
|
|
|
|
}
|
|
|
|
|
|
|
|
// read ACK
|
|
|
|
int ackBytesRead = connection.Read(ackBuffer, 0, ackBuffer.Length, timeoutHelper.RemainingTime());
|
|
|
|
if (!ConnectionUpgradeHelper.ValidatePreambleResponse(ackBuffer, ackBytesRead, this.decoder, Via))
|
|
|
|
{
|
|
|
|
ConnectionUpgradeHelper.DecodeFramingFault(this.decoder, connection, Via,
|
|
|
|
MessageEncoder.ContentType, ref timeoutHelper);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TD.ClientSendPreambleStopIsEnabled())
|
|
|
|
{
|
|
|
|
TD.ClientSendPreambleStop(this.EventTraceActivity);
|
|
|
|
}
|
|
|
|
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
IAsyncResult BeginSendPreamble(IConnection connection, ArraySegment<byte> preamble, ref TimeoutHelper timeoutHelper,
|
|
|
|
AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return new SendPreambleAsyncResult(this, connection, preamble, this.flowIdentity, ref timeoutHelper, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
IConnection EndSendPreamble(IAsyncResult result)
|
|
|
|
{
|
|
|
|
return SendPreambleAsyncResult.End(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnOpen(TimeSpan timeout)
|
|
|
|
{
|
|
|
|
IConnection connection;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
connection = connectionPoolHelper.EstablishConnection(timeout);
|
|
|
|
}
|
|
|
|
catch (TimeoutException exception)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
new TimeoutException(SR.GetString(SR.TimeoutOnOpen, timeout), exception));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool connectionAccepted = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
AcceptConnection(connection);
|
|
|
|
connectionAccepted = true;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
if (!connectionAccepted)
|
|
|
|
{
|
|
|
|
this.connectionPoolHelper.Abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void ReturnConnectionIfNecessary(bool abort, TimeSpan timeout)
|
|
|
|
{
|
|
|
|
lock (ThisLock)
|
|
|
|
{
|
|
|
|
if (abort)
|
|
|
|
{
|
|
|
|
this.connectionPoolHelper.Abort();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.connectionPoolHelper.Close(timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AcceptConnection(IConnection connection)
|
|
|
|
{
|
|
|
|
base.SetMessageSource(new ClientDuplexConnectionReader(this, connection, decoder, this.settings, MessageEncoder));
|
|
|
|
|
|
|
|
lock (ThisLock)
|
|
|
|
{
|
|
|
|
if (this.State != CommunicationState.Opening)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
new CommunicationObjectAbortedException(SR.GetString(SR.DuplexChannelAbortedDuringOpen, this.Via)));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.Connection = connection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetRemoteSecurity(StreamUpgradeInitiator upgradeInitiator)
|
|
|
|
{
|
|
|
|
this.RemoteSecurity = StreamSecurityUpgradeInitiator.GetRemoteSecurity(upgradeInitiator);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void PrepareMessage(Message message)
|
|
|
|
{
|
|
|
|
base.PrepareMessage(message);
|
|
|
|
|
|
|
|
if (this.RemoteSecurity != null)
|
|
|
|
{
|
|
|
|
message.Properties.Security = (SecurityMessageProperty)this.RemoteSecurity.CreateCopy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class DuplexConnectionPoolHelper : ConnectionPoolHelper
|
|
|
|
{
|
|
|
|
ClientFramingDuplexSessionChannel channel;
|
|
|
|
ArraySegment<byte> preamble;
|
|
|
|
|
|
|
|
public DuplexConnectionPoolHelper(ClientFramingDuplexSessionChannel channel,
|
|
|
|
ConnectionPool connectionPool, IConnectionInitiator connectionInitiator)
|
|
|
|
: base(connectionPool, connectionInitiator, channel.Via)
|
|
|
|
{
|
|
|
|
this.channel = channel;
|
|
|
|
this.preamble = channel.CreatePreamble();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override TimeoutException CreateNewConnectionTimeoutException(TimeSpan timeout, TimeoutException innerException)
|
|
|
|
{
|
|
|
|
return new TimeoutException(SR.GetString(SR.OpenTimedOutEstablishingTransportSession,
|
|
|
|
timeout, channel.Via.AbsoluteUri), innerException);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override IAsyncResult BeginAcceptPooledConnection(IConnection connection, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return channel.BeginSendPreamble(connection, preamble, ref timeoutHelper, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override IConnection EndAcceptPooledConnection(IAsyncResult result)
|
|
|
|
{
|
|
|
|
return channel.EndSendPreamble(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override IConnection AcceptPooledConnection(IConnection connection, ref TimeoutHelper timeoutHelper)
|
|
|
|
{
|
|
|
|
return channel.SendPreamble(connection, preamble, ref timeoutHelper);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SendPreambleAsyncResult : AsyncResult
|
|
|
|
{
|
|
|
|
ClientFramingDuplexSessionChannel channel;
|
|
|
|
IConnection connection;
|
|
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
StreamUpgradeInitiator upgradeInitiator;
|
|
|
|
IStreamUpgradeChannelBindingProvider channelBindingProvider;
|
|
|
|
static WaitCallback onReadPreambleAck = new WaitCallback(OnReadPreambleAck);
|
|
|
|
static WaitCallback onWritePreamble = Fx.ThunkCallback(new WaitCallback(OnWritePreamble));
|
|
|
|
static WaitCallback onWritePreambleEnd;
|
|
|
|
static AsyncCallback onUpgrade;
|
|
|
|
static AsyncCallback onUpgradeInitiatorOpen;
|
|
|
|
static AsyncCallback onUpgradeInitiatorClose;
|
|
|
|
WindowsIdentity identityToImpersonate;
|
|
|
|
EventTraceActivity eventTraceActivity;
|
|
|
|
|
|
|
|
public SendPreambleAsyncResult(ClientFramingDuplexSessionChannel channel,
|
|
|
|
IConnection connection, ArraySegment<byte> preamble, bool flowIdentity,
|
|
|
|
ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
|
|
|
|
: base(callback, state)
|
|
|
|
{
|
|
|
|
this.channel = channel;
|
|
|
|
this.timeoutHelper = timeoutHelper;
|
|
|
|
this.connection = connection;
|
|
|
|
|
|
|
|
if (TD.ClientSendPreambleStartIsEnabled())
|
|
|
|
{
|
|
|
|
TD.ClientSendPreambleStart(this.EventTraceActivity);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flowIdentity && !SecurityContext.IsWindowsIdentityFlowSuppressed())
|
|
|
|
{
|
|
|
|
this.identityToImpersonate = WindowsIdentity.GetCurrent(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize a new decoder
|
|
|
|
channel.decoder = new ClientDuplexDecoder(0);
|
|
|
|
AsyncCompletionResult writePreambleResult = connection.BeginWrite(
|
|
|
|
preamble.Array, preamble.Offset, preamble.Count,
|
|
|
|
true, timeoutHelper.RemainingTime(), onWritePreamble, this);
|
|
|
|
|
|
|
|
if (writePreambleResult == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HandleWritePreamble())
|
|
|
|
{
|
|
|
|
base.Complete(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EventTraceActivity EventTraceActivity
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (this.eventTraceActivity == null)
|
|
|
|
{
|
|
|
|
this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
|
|
|
|
}
|
|
|
|
return this.eventTraceActivity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IConnection End(IAsyncResult result)
|
|
|
|
{
|
|
|
|
SendPreambleAsyncResult thisPtr = AsyncResult.End<SendPreambleAsyncResult>(result);
|
|
|
|
return thisPtr.connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HandleWritePreamble()
|
|
|
|
{
|
|
|
|
this.connection.EndWrite();
|
|
|
|
|
|
|
|
if (TD.ClientSendPreambleStopIsEnabled())
|
|
|
|
{
|
|
|
|
TD.ClientSendPreambleStop(this.EventTraceActivity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now upgrade if necessary
|
|
|
|
if (channel.upgrade != null)
|
|
|
|
{
|
|
|
|
this.channelBindingProvider = this.channel.upgrade.GetProperty<IStreamUpgradeChannelBindingProvider>();
|
|
|
|
this.upgradeInitiator = channel.upgrade.CreateUpgradeInitiator(channel.RemoteAddress, channel.Via);
|
|
|
|
if (onUpgradeInitiatorOpen == null)
|
|
|
|
{
|
|
|
|
onUpgradeInitiatorOpen = Fx.ThunkCallback(new AsyncCallback(OnUpgradeInitiatorOpen));
|
|
|
|
}
|
|
|
|
|
|
|
|
IAsyncResult initiatorOpenResult =
|
|
|
|
this.upgradeInitiator.BeginOpen(timeoutHelper.RemainingTime(), onUpgradeInitiatorOpen, this);
|
|
|
|
|
|
|
|
if (!initiatorOpenResult.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HandleInitiatorOpen(initiatorOpenResult);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return ReadAck();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HandleInitiatorOpen(IAsyncResult result)
|
|
|
|
{
|
|
|
|
this.upgradeInitiator.EndOpen(result);
|
|
|
|
if (onUpgrade == null)
|
|
|
|
{
|
|
|
|
onUpgrade = Fx.ThunkCallback(new AsyncCallback(OnUpgrade));
|
|
|
|
}
|
|
|
|
|
|
|
|
IAsyncResult upgradeResult = ConnectionUpgradeHelper.BeginInitiateUpgrade(
|
|
|
|
channel, channel.RemoteAddress, this.connection, channel.decoder, this.upgradeInitiator,
|
|
|
|
channel.MessageEncoder.ContentType, this.identityToImpersonate, timeoutHelper, onUpgrade, this);
|
|
|
|
|
|
|
|
if (!upgradeResult.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HandleUpgrade(upgradeResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
// finish our upgrade and read ack
|
|
|
|
bool HandleUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
this.connection = ConnectionUpgradeHelper.EndInitiateUpgrade(result);
|
|
|
|
|
|
|
|
if (this.channelBindingProvider != null && this.channelBindingProvider.IsChannelBindingSupportEnabled)
|
|
|
|
{
|
|
|
|
this.channel.SetChannelBinding(this.channelBindingProvider.GetChannelBinding(this.upgradeInitiator, ChannelBindingKind.Endpoint));
|
|
|
|
}
|
|
|
|
|
|
|
|
channel.SetRemoteSecurity(this.upgradeInitiator);
|
|
|
|
|
|
|
|
if (onUpgradeInitiatorClose == null)
|
|
|
|
{
|
|
|
|
onUpgradeInitiatorClose = Fx.ThunkCallback(new AsyncCallback(OnUpgradeInitiatorClose));
|
|
|
|
}
|
|
|
|
|
|
|
|
IAsyncResult initiatorCloseResult =
|
|
|
|
this.upgradeInitiator.BeginClose(timeoutHelper.RemainingTime(), onUpgradeInitiatorClose, this);
|
|
|
|
|
|
|
|
if (!initiatorCloseResult.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HandleInitiatorClose(initiatorCloseResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HandleInitiatorClose(IAsyncResult result)
|
|
|
|
{
|
|
|
|
this.upgradeInitiator.EndClose(result);
|
|
|
|
this.upgradeInitiator = null; // we're done with the upgrade
|
|
|
|
|
|
|
|
// in the upgrade case, preamble end bytes aren't sent with the initial bytes; we need to send them here
|
|
|
|
if (onWritePreambleEnd == null)
|
|
|
|
{
|
|
|
|
onWritePreambleEnd = Fx.ThunkCallback(new WaitCallback(OnWritePreambleEnd));
|
|
|
|
}
|
|
|
|
|
|
|
|
AsyncCompletionResult writePreambleResult = this.connection.BeginWrite(
|
|
|
|
ClientDuplexEncoder.PreambleEndBytes, 0, ClientDuplexEncoder.PreambleEndBytes.Length, true,
|
|
|
|
timeoutHelper.RemainingTime(), onWritePreambleEnd, this);
|
|
|
|
|
|
|
|
if (writePreambleResult == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.connection.EndWrite();
|
|
|
|
return ReadAck();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReadAck()
|
|
|
|
{
|
|
|
|
AsyncCompletionResult readAckResult = this.connection.BeginRead(0, 1,
|
|
|
|
timeoutHelper.RemainingTime(), onReadPreambleAck, this);
|
|
|
|
|
|
|
|
if (readAckResult == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HandlePreambleAck();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HandlePreambleAck()
|
|
|
|
{
|
|
|
|
int ackBytesRead = connection.EndRead();
|
|
|
|
|
|
|
|
// it's possible to get a fault instead of an ack
|
|
|
|
if (!ConnectionUpgradeHelper.ValidatePreambleResponse(
|
|
|
|
connection.AsyncReadBuffer, ackBytesRead, channel.decoder, channel.Via))
|
|
|
|
{
|
|
|
|
IAsyncResult decodeFaultResult = ConnectionUpgradeHelper.BeginDecodeFramingFault(channel.decoder,
|
|
|
|
connection, channel.Via, channel.MessageEncoder.ContentType, ref timeoutHelper,
|
|
|
|
Fx.ThunkCallback(new AsyncCallback(OnFailedPreamble)), this);
|
|
|
|
|
|
|
|
if (!decodeFaultResult.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConnectionUpgradeHelper.EndDecodeFramingFault(decodeFaultResult);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnWritePreamble(object asyncState)
|
|
|
|
{
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)asyncState;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandleWritePreamble();
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnReadPreambleAck(object state)
|
|
|
|
{
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)state;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandlePreambleAck();
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnUpgradeInitiatorOpen(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
bool completeSelf = false;
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandleInitiatorOpen(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
bool completeSelf = false;
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandleUpgrade(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnUpgradeInitiatorClose(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
bool completeSelf = false;
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandleInitiatorClose(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnWritePreambleEnd(object asyncState)
|
|
|
|
{
|
|
|
|
SendPreambleAsyncResult thisPtr = (SendPreambleAsyncResult)asyncState;
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
thisPtr.connection.EndWrite();
|
|
|
|
completeSelf = thisPtr.ReadAck();
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OnFailedPreamble(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ConnectionUpgradeHelper.EndDecodeFramingFault(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
base.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class OpenAsyncResult : AsyncResult
|
|
|
|
{
|
|
|
|
static AsyncCallback onEstablishConnection = Fx.ThunkCallback(new AsyncCallback(OnEstablishConnection));
|
|
|
|
ClientFramingDuplexSessionChannel duplexChannel;
|
|
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
|
|
|
|
public OpenAsyncResult(ClientFramingDuplexSessionChannel duplexChannel, TimeSpan timeout, AsyncCallback callback, object state)
|
|
|
|
: base(callback, state)
|
|
|
|
{
|
|
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
|
|
this.duplexChannel = duplexChannel;
|
|
|
|
|
|
|
|
IAsyncResult result;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
result = duplexChannel.connectionPoolHelper.BeginEstablishConnection(
|
|
|
|
timeoutHelper.RemainingTime(), onEstablishConnection, this);
|
|
|
|
}
|
|
|
|
catch (TimeoutException exception)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
new TimeoutException(SR.GetString(SR.TimeoutOnOpen, timeout), exception));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HandleEstablishConnection(result))
|
|
|
|
{
|
|
|
|
base.Complete(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void End(IAsyncResult result)
|
|
|
|
{
|
|
|
|
AsyncResult.End<OpenAsyncResult>(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HandleEstablishConnection(IAsyncResult result)
|
|
|
|
{
|
|
|
|
IConnection connection;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
connection = duplexChannel.connectionPoolHelper.EndEstablishConnection(result);
|
|
|
|
}
|
|
|
|
catch (TimeoutException exception)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
new TimeoutException(SR.GetString(SR.TimeoutOnOpen, this.timeoutHelper.OriginalTimeout), exception));
|
|
|
|
}
|
|
|
|
|
|
|
|
duplexChannel.AcceptConnection(connection);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnEstablishConnection(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.HandleEstablishConnection(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// used by StreamedFramingRequestChannel and ClientFramingDuplexSessionChannel
|
|
|
|
class ConnectionUpgradeHelper
|
|
|
|
{
|
|
|
|
public static IAsyncResult BeginDecodeFramingFault(ClientFramingDecoder decoder, IConnection connection,
|
|
|
|
Uri via, string contentType, ref TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return new DecodeFailedUpgradeAsyncResult(decoder, connection, via, contentType, ref timeoutHelper,
|
|
|
|
callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void EndDecodeFramingFault(IAsyncResult result)
|
|
|
|
{
|
|
|
|
DecodeFailedUpgradeAsyncResult.End(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void DecodeFramingFault(ClientFramingDecoder decoder, IConnection connection,
|
|
|
|
Uri via, string contentType, ref TimeoutHelper timeoutHelper)
|
|
|
|
{
|
|
|
|
ValidateReadingFaultString(decoder);
|
|
|
|
|
|
|
|
int offset = 0;
|
|
|
|
byte[] faultBuffer = DiagnosticUtility.Utility.AllocateByteArray(FaultStringDecoder.FaultSizeQuota);
|
|
|
|
int size = connection.Read(faultBuffer, offset, faultBuffer.Length, timeoutHelper.RemainingTime());
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
int bytesDecoded = decoder.Decode(faultBuffer, offset, size);
|
|
|
|
offset += bytesDecoded;
|
|
|
|
size -= bytesDecoded;
|
|
|
|
|
|
|
|
if (decoder.CurrentState == ClientFramingDecoderState.Fault)
|
|
|
|
{
|
|
|
|
ConnectionUtilities.CloseNoThrow(connection, timeoutHelper.RemainingTime());
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
FaultStringDecoder.GetFaultException(decoder.Fault, via.ToString(), contentType));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (decoder.CurrentState != ClientFramingDecoderState.ReadingFaultString)
|
|
|
|
{
|
|
|
|
throw Fx.AssertAndThrow("invalid framing client state machine");
|
|
|
|
}
|
|
|
|
if (size == 0)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
size = connection.Read(faultBuffer, offset, faultBuffer.Length, timeoutHelper.RemainingTime());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IAsyncResult BeginInitiateUpgrade(IDefaultCommunicationTimeouts timeouts, EndpointAddress remoteAddress,
|
|
|
|
IConnection connection, ClientFramingDecoder decoder,
|
|
|
|
StreamUpgradeInitiator upgradeInitiator, string contentType, WindowsIdentity identityToImpersonate, TimeoutHelper timeoutHelper,
|
|
|
|
AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return new InitiateUpgradeAsyncResult(timeouts, remoteAddress, connection, decoder, upgradeInitiator, contentType, identityToImpersonate, timeoutHelper,
|
|
|
|
callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IConnection EndInitiateUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
return InitiateUpgradeAsyncResult.End(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, ref IConnection connection,
|
|
|
|
ClientFramingDecoder decoder, IDefaultCommunicationTimeouts defaultTimeouts, ref TimeoutHelper timeoutHelper)
|
|
|
|
{
|
|
|
|
string upgradeContentType = upgradeInitiator.GetNextUpgrade();
|
|
|
|
|
|
|
|
while (upgradeContentType != null)
|
|
|
|
{
|
|
|
|
EncodedUpgrade encodedUpgrade = new EncodedUpgrade(upgradeContentType);
|
|
|
|
// write upgrade request framing for synchronization
|
|
|
|
connection.Write(encodedUpgrade.EncodedBytes, 0, encodedUpgrade.EncodedBytes.Length, true, timeoutHelper.RemainingTime());
|
|
|
|
byte[] buffer = new byte[1];
|
|
|
|
|
|
|
|
// read upgrade response framing
|
|
|
|
int size = connection.Read(buffer, 0, buffer.Length, timeoutHelper.RemainingTime());
|
|
|
|
|
|
|
|
if (!ValidateUpgradeResponse(buffer, size, decoder)) // we have a problem
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// initiate wire upgrade
|
|
|
|
ConnectionStream connectionStream = new ConnectionStream(connection, defaultTimeouts);
|
|
|
|
Stream upgradedStream = upgradeInitiator.InitiateUpgrade(connectionStream);
|
|
|
|
|
|
|
|
// and re-wrap connection
|
|
|
|
connection = new StreamConnection(upgradedStream, connectionStream);
|
|
|
|
|
|
|
|
upgradeContentType = upgradeInitiator.GetNextUpgrade();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ValidateReadingFaultString(ClientFramingDecoder decoder)
|
|
|
|
{
|
|
|
|
if (decoder.CurrentState != ClientFramingDecoderState.ReadingFaultString)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new System.ServiceModel.Security.MessageSecurityException(
|
|
|
|
SR.GetString(SR.ServerRejectedUpgradeRequest)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool ValidatePreambleResponse(byte[] buffer, int count, ClientFramingDecoder decoder, Uri via)
|
|
|
|
{
|
|
|
|
if (count == 0)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
new ProtocolException(SR.GetString(SR.ServerRejectedSessionPreamble, via),
|
|
|
|
decoder.CreatePrematureEOFException()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode until the framing byte has been processed (it always will be)
|
|
|
|
while (decoder.Decode(buffer, 0, count) == 0)
|
|
|
|
{
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
if (decoder.CurrentState != ClientFramingDecoderState.Start) // we have a problem
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ValidateUpgradeResponse(byte[] buffer, int count, ClientFramingDecoder decoder)
|
|
|
|
{
|
|
|
|
if (count == 0)
|
|
|
|
{
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ServerRejectedUpgradeRequest), decoder.CreatePrematureEOFException()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode until the framing byte has been processed (it always will be)
|
|
|
|
while (decoder.Decode(buffer, 0, count) == 0)
|
|
|
|
{
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
if (decoder.CurrentState != ClientFramingDecoderState.UpgradeResponse) // we have a problem
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class DecodeFailedUpgradeAsyncResult : AsyncResult
|
|
|
|
{
|
|
|
|
ClientFramingDecoder decoder;
|
|
|
|
IConnection connection;
|
|
|
|
Uri via;
|
|
|
|
string contentType;
|
|
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
static WaitCallback onReadFaultData = new WaitCallback(OnReadFaultData);
|
|
|
|
|
|
|
|
public DecodeFailedUpgradeAsyncResult(ClientFramingDecoder decoder, IConnection connection,
|
|
|
|
Uri via, string contentType, ref TimeoutHelper timeoutHelper,
|
|
|
|
AsyncCallback callback, object state)
|
|
|
|
: base(callback, state)
|
|
|
|
{
|
|
|
|
ValidateReadingFaultString(decoder);
|
|
|
|
|
|
|
|
this.decoder = decoder;
|
|
|
|
this.connection = connection;
|
|
|
|
this.via = via;
|
|
|
|
this.contentType = contentType;
|
|
|
|
this.timeoutHelper = timeoutHelper;
|
|
|
|
|
|
|
|
if (connection.BeginRead(0, Math.Min(FaultStringDecoder.FaultSizeQuota, connection.AsyncReadBufferSize),
|
|
|
|
timeoutHelper.RemainingTime(), onReadFaultData, this) == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CompleteReadFaultData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompleteReadFaultData()
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
int size = connection.EndRead();
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
int bytesDecoded = decoder.Decode(connection.AsyncReadBuffer, offset, size);
|
|
|
|
offset += bytesDecoded;
|
|
|
|
size -= bytesDecoded;
|
|
|
|
|
|
|
|
if (decoder.CurrentState == ClientFramingDecoderState.Fault)
|
|
|
|
{
|
|
|
|
ConnectionUtilities.CloseNoThrow(connection, timeoutHelper.RemainingTime());
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
|
|
FaultStringDecoder.GetFaultException(decoder.Fault, via.ToString(), contentType));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (decoder.CurrentState != ClientFramingDecoderState.ReadingFaultString)
|
|
|
|
{
|
|
|
|
throw Fx.AssertAndThrow("invalid framing client state machine");
|
|
|
|
}
|
|
|
|
if (size == 0)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
if (connection.BeginRead(0, Math.Min(FaultStringDecoder.FaultSizeQuota, connection.AsyncReadBufferSize),
|
|
|
|
timeoutHelper.RemainingTime(), onReadFaultData, this) == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = connection.EndRead();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(decoder.CreatePrematureEOFException());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void End(IAsyncResult result)
|
|
|
|
{
|
|
|
|
AsyncResult.End<DecodeFailedUpgradeAsyncResult>(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnReadFaultData(object state)
|
|
|
|
{
|
|
|
|
DecodeFailedUpgradeAsyncResult thisPtr = (DecodeFailedUpgradeAsyncResult)state;
|
|
|
|
|
|
|
|
// This AsyncResult only completes with an exception.
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
thisPtr.CompleteReadFaultData();
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
if (completionException != null)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class InitiateUpgradeAsyncResult : AsyncResult
|
|
|
|
{
|
|
|
|
IDefaultCommunicationTimeouts defaultTimeouts;
|
|
|
|
IConnection connection;
|
|
|
|
ConnectionStream connectionStream;
|
|
|
|
string contentType;
|
|
|
|
ClientFramingDecoder decoder;
|
|
|
|
static AsyncCallback onInitiateUpgrade = Fx.ThunkCallback(new AsyncCallback(OnInitiateUpgrade));
|
|
|
|
static WaitCallback onReadUpgradeResponse = new WaitCallback(OnReadUpgradeResponse);
|
|
|
|
static AsyncCallback onFailedUpgrade;
|
|
|
|
static WaitCallback onWriteUpgradeBytes = Fx.ThunkCallback(new WaitCallback(OnWriteUpgradeBytes));
|
|
|
|
EndpointAddress remoteAddress;
|
|
|
|
StreamUpgradeInitiator upgradeInitiator;
|
|
|
|
TimeoutHelper timeoutHelper;
|
|
|
|
WindowsIdentity identityToImpersonate;
|
|
|
|
|
|
|
|
public InitiateUpgradeAsyncResult(IDefaultCommunicationTimeouts timeouts, EndpointAddress remoteAddress,
|
|
|
|
IConnection connection,
|
|
|
|
ClientFramingDecoder decoder, StreamUpgradeInitiator upgradeInitiator,
|
|
|
|
string contentType, WindowsIdentity identityToImpersonate, TimeoutHelper timeoutHelper,
|
|
|
|
AsyncCallback callback, object state)
|
|
|
|
: base(callback, state)
|
|
|
|
{
|
|
|
|
this.defaultTimeouts = timeouts;
|
|
|
|
this.decoder = decoder;
|
|
|
|
this.upgradeInitiator = upgradeInitiator;
|
|
|
|
this.contentType = contentType;
|
|
|
|
this.timeoutHelper = timeoutHelper;
|
|
|
|
this.connection = connection;
|
|
|
|
this.remoteAddress = remoteAddress;
|
|
|
|
this.identityToImpersonate = identityToImpersonate;
|
|
|
|
if (Begin())
|
|
|
|
{
|
|
|
|
base.Complete(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Begin()
|
|
|
|
{
|
|
|
|
string upgradeContentType = upgradeInitiator.GetNextUpgrade();
|
|
|
|
|
|
|
|
while (upgradeContentType != null)
|
|
|
|
{
|
|
|
|
EncodedUpgrade encodedUpgrade = new EncodedUpgrade(upgradeContentType);
|
|
|
|
AsyncCompletionResult writeFrameResult = connection.BeginWrite(
|
|
|
|
encodedUpgrade.EncodedBytes, 0, encodedUpgrade.EncodedBytes.Length, true, timeoutHelper.RemainingTime(),
|
|
|
|
onWriteUpgradeBytes, this);
|
|
|
|
if (writeFrameResult == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CompleteWriteUpgradeBytes())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
upgradeContentType = upgradeInitiator.GetNextUpgrade();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CompleteWriteUpgradeBytes()
|
|
|
|
{
|
|
|
|
connection.EndWrite();
|
|
|
|
|
|
|
|
if (connection.BeginRead(0, ServerSessionEncoder.UpgradeResponseBytes.Length, timeoutHelper.RemainingTime(),
|
|
|
|
onReadUpgradeResponse, this) == AsyncCompletionResult.Queued)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CompleteReadUpgradeResponse();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CompleteReadUpgradeResponse()
|
|
|
|
{
|
|
|
|
int bytesRead = connection.EndRead();
|
|
|
|
|
|
|
|
if (!ConnectionUpgradeHelper.ValidateUpgradeResponse(connection.AsyncReadBuffer, bytesRead, decoder))
|
|
|
|
{
|
|
|
|
if (onFailedUpgrade == null)
|
|
|
|
{
|
|
|
|
onFailedUpgrade = Fx.ThunkCallback(new AsyncCallback(OnFailedUpgrade));
|
|
|
|
}
|
|
|
|
|
|
|
|
IAsyncResult result = ConnectionUpgradeHelper.BeginDecodeFramingFault(decoder, connection,
|
|
|
|
remoteAddress.Uri, contentType, ref timeoutHelper, onFailedUpgrade, this);
|
|
|
|
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
ConnectionUpgradeHelper.EndDecodeFramingFault(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.CompletedSynchronously;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.connectionStream = new ConnectionStream(connection, this.defaultTimeouts);
|
|
|
|
IAsyncResult initiateUpgradeResult = null;
|
|
|
|
// ensure that any impersonated identity is available to the upgrade initiator
|
|
|
|
WindowsImpersonationContext impersonationContext = (this.identityToImpersonate == null) ? null : this.identityToImpersonate.Impersonate();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using (impersonationContext)
|
|
|
|
{
|
|
|
|
initiateUpgradeResult = upgradeInitiator.BeginInitiateUpgrade(connectionStream, onInitiateUpgrade, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
//Added to guarantee that the finally of the using statement above runs before exception filters higher in the stack
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
if (!initiateUpgradeResult.CompletedSynchronously)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CompleteUpgrade(initiateUpgradeResult);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CompleteUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
Stream stream = upgradeInitiator.EndInitiateUpgrade(result);
|
|
|
|
this.connection = new StreamConnection(stream, connectionStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IConnection End(IAsyncResult result)
|
|
|
|
{
|
|
|
|
InitiateUpgradeAsyncResult thisPtr = AsyncResult.End<InitiateUpgradeAsyncResult>(result);
|
|
|
|
return thisPtr.connection;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnReadUpgradeResponse(object state)
|
|
|
|
{
|
|
|
|
InitiateUpgradeAsyncResult thisPtr = (InitiateUpgradeAsyncResult)state;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (thisPtr.CompleteReadUpgradeResponse())
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.Begin();
|
|
|
|
}
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnFailedUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
InitiateUpgradeAsyncResult thisPtr = (InitiateUpgradeAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ConnectionUpgradeHelper.EndDecodeFramingFault(result);
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnWriteUpgradeBytes(object asyncState)
|
|
|
|
{
|
|
|
|
InitiateUpgradeAsyncResult thisPtr = (InitiateUpgradeAsyncResult)asyncState;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf = false;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (thisPtr.CompleteWriteUpgradeBytes())
|
|
|
|
{
|
|
|
|
completeSelf = thisPtr.Begin();
|
|
|
|
}
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnInitiateUpgrade(IAsyncResult result)
|
|
|
|
{
|
|
|
|
if (result.CompletedSynchronously)
|
|
|
|
return;
|
|
|
|
|
|
|
|
InitiateUpgradeAsyncResult thisPtr = (InitiateUpgradeAsyncResult)result.AsyncState;
|
|
|
|
|
|
|
|
Exception completionException = null;
|
|
|
|
bool completeSelf;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
thisPtr.CompleteUpgrade(result);
|
|
|
|
completeSelf = thisPtr.Begin();
|
|
|
|
}
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
2016-08-03 10:59:49 +00:00
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(e))
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
completeSelf = true;
|
|
|
|
completionException = e;
|
|
|
|
}
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
thisPtr.Complete(false, completionException);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|