// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
+using System.Collections.Generic;
using System.Reflection;
+using System.Runtime.Serialization;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading;
using System.Xml;
-namespace System.ServiceModel
+namespace System.ServiceModel.MonoInternal
{
-#if NET_2_1
- internal class DuplexClientRuntimeChannel
- {
- }
-#else
- internal class DuplexClientRuntimeChannel
- : ClientRuntimeChannel, IDuplexContextChannel
+#if DISABLE_REAL_PROXY
+ // FIXME: This is a quick workaround for bug #571907
+ public
+#endif
+ interface IInternalContextChannel
{
- public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
- ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
- : base (endpoint, factory, remoteAddress, via)
- {
- }
+ ContractDescription Contract { get; }
- public bool AutomaticInputSessionShutdown {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public InstanceContext CallbackInstance { get; set; }
-
- Action<TimeSpan> session_shutdown_delegate;
+ object Process (MethodBase method, string operationName, object [] parameters);
- public void CloseOutputSession (TimeSpan timeout)
- {
- throw new NotImplementedException ();
- }
+ IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
- public IAsyncResult BeginCloseOutputSession (TimeSpan timeout, AsyncCallback callback, object state)
- {
- if (session_shutdown_delegate == null)
- session_shutdown_delegate = new Action<TimeSpan> (CloseOutputSession);
- return session_shutdown_delegate.BeginInvoke (timeout, callback, state);
- }
-
- public void EndCloseOutputSession (IAsyncResult result)
- {
- session_shutdown_delegate.EndInvoke (result);
- }
+ object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result);
}
-#endif
- internal class ClientRuntimeChannel
- : CommunicationObject, IClientChannel
+#if DISABLE_REAL_PROXY
+ // FIXME: This is a quick workaround for bug #571907
+ public
+#endif
+ class ClientRuntimeChannel
+ : CommunicationObject, IClientChannel, IInternalContextChannel
{
ClientRuntime runtime;
EndpointAddress remote_address;
ContractDescription contract;
MessageVersion message_version;
- IChannelFactory factory;
TimeSpan default_open_timeout, default_close_timeout;
- IRequestChannel request_channel;
- IOutputChannel output_channel; // could also be IDuplexChannel instance.
+ IChannel channel;
+ IChannelFactory factory;
+ OperationContext context;
#region delegates
readonly ProcessDelegate _processDelegate;
public ClientRuntimeChannel (ServiceEndpoint endpoint,
ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
+ : this (endpoint.CreateClientRuntime (null), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
{
- this.runtime = endpoint.CreateRuntime ();
- this.remote_address = remoteAddress ?? endpoint.Address;
- runtime.Via = via;
- this.contract = endpoint.Contract;
- this.message_version = endpoint.Binding.MessageVersion;
- this.factory = channelFactory.OpenedChannelFactory;
- default_open_timeout = channelFactory.DefaultOpenTimeout;
- default_close_timeout = channelFactory.DefaultCloseTimeout;
+ }
+
+ public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
+ {
+ if (runtime == null)
+ throw new ArgumentNullException ("runtime");
+ this.runtime = runtime;
+ this.remote_address = remoteAddress;
+ if (runtime.Via == null)
+ runtime.Via = via ?? (remote_address != null ?remote_address.Uri : null);
+ this.contract = contract;
+ this.message_version = messageVersion;
+ default_open_timeout = openTimeout;
+ default_close_timeout = closeTimeout;
_processDelegate = new ProcessDelegate (Process);
requestDelegate = new RequestDelegate (Request);
sendDelegate = new SendDelegate (Send);
AllowInitializationUI = true;
OperationTimeout = TimeSpan.FromMinutes (1);
- // determine operation channel to create.
- if (factory is IChannelFactory<IRequestChannel> ||
- factory is IChannelFactory<IRequestSessionChannel>)
- SetupRequestChannel ();
- else
- SetupOutputChannel ();
+ if (contextChannel != null)
+ channel = contextChannel;
+ else {
+ var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
+ try {
+ channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
+ this.factory = factory;
+ } catch (TargetInvocationException ex) {
+ if (ex.InnerException != null)
+ throw ex.InnerException;
+ else
+ throw;
+ }
+ }
+ }
+
+ public ContractDescription Contract {
+ get { return contract; }
}
public ClientRuntime Runtime {
get { return runtime; }
}
+ IRequestChannel RequestChannel {
+ get { return channel as IRequestChannel; }
+ }
+
+ IOutputChannel OutputChannel {
+ get { return channel as IOutputChannel; }
+ }
+
+ internal IDuplexChannel DuplexChannel {
+ get { return channel as IDuplexChannel; }
+ }
+
#region IClientChannel
bool did_interactive_initialization;
public override bool WaitOne (int millisecondsTimeout)
{
- return WaitOne (millisecondsTimeout, false);
+ return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout);
}
WaitHandle [] ResultWaitHandles {
}
}
+#if !MOONLIGHT
public override bool WaitOne (int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
{
return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
}
+#endif
}
class DisplayUIAsyncResult : IAsyncResult
public IInputSession InputSession {
get {
- ISessionChannel<IInputSession> ch = request_channel as ISessionChannel<IInputSession>;
- ch = ch ?? output_channel as ISessionChannel<IInputSession>;
+ ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
+ ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
if (ch != null)
return ch.Session;
- var dch = output_channel as ISessionChannel<IDuplexSession>;
+ var dch = OutputChannel as ISessionChannel<IDuplexSession>;
return dch != null ? dch.Session : null;
}
}
public IOutputSession OutputSession {
get {
- ISessionChannel<IOutputSession> ch = request_channel as ISessionChannel<IOutputSession>;
- ch = ch ?? output_channel as ISessionChannel<IOutputSession>;
+ ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
+ ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
if (ch != null)
return ch.Session;
- var dch = output_channel as ISessionChannel<IDuplexSession>;
+ var dch = OutputChannel as ISessionChannel<IDuplexSession>;
return dch != null ? dch.Session : null;
}
}
public EndpointAddress RemoteAddress {
- get { return request_channel != null ? request_channel.RemoteAddress : output_channel.RemoteAddress; }
+ get { return RequestChannel != null ? RequestChannel.RemoteAddress : OutputChannel.RemoteAddress; }
}
public string SessionId {
protected override void OnAbort ()
{
- factory.Abort ();
+ channel.Abort ();
+ if (factory != null) // ... is it valid?
+ factory.Abort ();
}
+ Action<TimeSpan> close_delegate;
+
protected override IAsyncResult OnBeginClose (
TimeSpan timeout, AsyncCallback callback, object state)
{
- return factory.BeginClose (timeout, callback, state);
+ if (close_delegate == null)
+ close_delegate = new Action<TimeSpan> (OnClose);
+ return close_delegate.BeginInvoke (timeout, callback, state);
}
protected override void OnEndClose (IAsyncResult result)
{
- factory.EndClose (result);
+ close_delegate.EndInvoke (result);
}
protected override void OnClose (TimeSpan timeout)
{
- factory.Close (timeout);
+ DateTime start = DateTime.Now;
+ if (channel.State == CommunicationState.Opened)
+ channel.Close (timeout);
}
+ Action<TimeSpan> open_callback;
+
protected override IAsyncResult OnBeginOpen (
TimeSpan timeout, AsyncCallback callback, object state)
{
- throw new SystemException ("INTERNAL ERROR: this should not be called (or not supported yet)");
+ if (open_callback == null)
+ open_callback = new Action<TimeSpan> (OnOpen);
+ return open_callback.BeginInvoke (timeout, callback, state);
}
protected override void OnEndOpen (IAsyncResult result)
{
+ if (open_callback == null)
+ throw new InvalidOperationException ("Async open operation has not started");
+ open_callback.EndInvoke (result);
}
protected override void OnOpen (TimeSpan timeout)
{
if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
+ if (channel.State == CommunicationState.Created)
+ channel.Open (timeout);
}
// IChannel
IChannel OperationChannel {
- get { return (IChannel) request_channel ?? output_channel; }
+ get { return channel; }
}
public T GetProperty<T> () where T : class
}
// IExtensibleObject<IContextChannel>
- [MonoTODO]
+
+ IExtensionCollection<IContextChannel> extensions;
+
public IExtensionCollection<IContextChannel> Extensions {
- get { throw new NotImplementedException (); }
+ get {
+ if (extensions == null)
+ extensions = new ExtensionCollection<IContextChannel> (this);
+ return extensions;
+ }
}
#region Request/Output processing
public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
{
+ if (context != null)
+ throw new InvalidOperationException ("another operation is in progress");
+ context = OperationContext.Current;
+
return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
}
public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
{
+ context = null;
if (result == null)
throw new ArgumentNullException ("result");
if (parameters == null)
try {
return DoProcess (method, operationName, parameters);
} catch (Exception ex) {
+#if MOONLIGHT // just for debugging
Console.Write ("Exception in async operation: ");
Console.WriteLine (ex);
+#endif
throw;
}
}
if (AllowInitializationUI)
DisplayInitializationUI ();
OperationDescription od = SelectOperation (method, operationName, parameters);
+
+ if (State != CommunicationState.Opened)
+ Open ();
+
if (!od.IsOneWay)
return Request (od, parameters);
else {
return od;
}
- // This handles IDuplexChannel, IOutputChannel, and those for session channels.
- void SetupOutputChannel ()
- {
- if (output_channel != null)
- return;
-
- var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
- output_channel = (IOutputChannel) method.Invoke (factory, new object [] {remote_address, Via});
- }
-
- // This handles both IRequestChannel and IRequestSessionChannel.
- void SetupRequestChannel ()
- {
- if (request_channel != null)
- return;
-
- var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
- request_channel = (IRequestChannel) method.Invoke (factory, new object [] {remote_address, Via});
- }
-
void Output (OperationDescription od, object [] parameters)
{
- if (output_channel.State != CommunicationState.Opened)
- output_channel.Open ();
-
ClientOperation op = runtime.Operations [od.Name];
Send (CreateRequest (op, parameters), OperationTimeout);
}
object Request (OperationDescription od, object [] parameters)
{
- if (OperationChannel.State != CommunicationState.Opened)
- OperationChannel.Open ();
-
ClientOperation op = runtime.Operations [od.Name];
object [] inspections = new object [runtime.MessageInspectors.Count];
Message req = CreateRequest (op, parameters);
Message res = Request (req, OperationTimeout);
if (res.IsFault) {
- MessageFault fault = MessageFault.CreateFault (res, runtime.MaxFaultSize);
- if (fault.HasDetail && fault is MessageFault.SimpleMessageFault) {
- MessageFault.SimpleMessageFault simpleFault = fault as MessageFault.SimpleMessageFault;
- object detail = simpleFault.Detail;
- Type t = detail.GetType ();
- Type faultType = typeof (FaultException<>).MakeGenericType (t);
- object [] constructorParams = new object [] { detail, fault.Reason, fault.Code, fault.Actor };
- FaultException fe = (FaultException) Activator.CreateInstance (faultType, constructorParams);
- throw fe;
- }
- else {
- // given a MessageFault, it is hard to figure out the type of the embedded detail
- throw new FaultException(fault);
+ var resb = res.CreateBufferedCopy (runtime.MaxFaultSize);
+ MessageFault fault = MessageFault.CreateFault (resb.CreateMessage (), runtime.MaxFaultSize);
+ var conv = OperationChannel.GetProperty<FaultConverter> () ?? FaultConverter.GetDefaultFaultConverter (res.Version);
+ Exception ex;
+ if (!conv.TryCreateException (resb.CreateMessage (), fault, out ex)) {
+ if (fault.HasDetail) {
+ Type detailType = typeof (ExceptionDetail);
+ var freader = fault.GetReaderAtDetailContents ();
+ DataContractSerializer ds = null;
+#if !NET_2_1
+ foreach (var fci in op.FaultContractInfos)
+ if (res.Headers.Action == fci.Action || fci.Serializer.IsStartObject (freader)) {
+ detailType = fci.Detail;
+ ds = fci.Serializer;
+ break;
+ }
+#endif
+ if (ds == null)
+ ds = new DataContractSerializer (detailType);
+ var detail = ds.ReadObject (freader);
+ ex = (Exception) Activator.CreateInstance (typeof (FaultException<>).MakeGenericType (detailType), new object [] {detail, fault.Reason, fault.Code, res.Headers.Action});
+ }
+
+ if (ex == null)
+ ex = new FaultException (fault);
}
+ throw ex;
}
for (int i = 0; i < inspections.Length; i++)
runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
if (op.DeserializeReply)
- return op.GetFormatter ().DeserializeReply (res, parameters);
+ return op.Formatter.DeserializeReply (res, parameters);
else
return res;
}
// They are internal for ClientBase<T>.ChannelBase use.
internal Message Request (Message msg, TimeSpan timeout)
{
- if (request_channel != null)
- return request_channel.Request (msg, timeout);
- else {
- DateTime startTime = DateTime.Now;
- output_channel.Send (msg, timeout);
- return ((IDuplexChannel) output_channel).Receive (timeout - (DateTime.Now - startTime));
- }
+ if (RequestChannel != null)
+ return RequestChannel.Request (msg, timeout);
+ else
+ return RequestCorrelated (msg, timeout, OutputChannel);
+ }
+
+ internal virtual Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
+ {
+ // FIXME: implement ConcurrencyMode check:
+ // if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
+
+ DateTime startTime = DateTime.Now;
+ OutputChannel.Send (msg, timeout);
+ return ((IDuplexChannel) channel).Receive (timeout - (DateTime.Now - startTime));
}
internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
internal void Send (Message msg, TimeSpan timeout)
{
- output_channel.Send (msg, timeout);
+ if (OutputChannel != null)
+ OutputChannel.Send (msg, timeout);
+ else
+ RequestChannel.Request (msg, timeout); // and ignore returned message.
}
internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
Message msg;
if (op.SerializeRequest)
- msg = op.GetFormatter ().SerializeRequest (
+ msg = op.Formatter.SerializeRequest (
version, parameters);
- else
+ else {
+ if (parameters.Length != 1)
+ throw new ArgumentException (String.Format ("Argument parameters does not match the expected input. It should contain only a Message, but has {0} parameters", parameters.Length));
+ if (!(parameters [0] is Message))
+ throw new ArgumentException (String.Format ("Argument should be only a Message, but has {0}", parameters [0] != null ? parameters [0].GetType ().FullName : "null"));
msg = (Message) parameters [0];
+ }
- if (OutputSession != null)
- msg.Headers.MessageId = new UniqueId (OutputSession.Id);
+ context = context ?? OperationContext.Current;
+ if (context != null) {
+ // CopyHeadersFrom does not work here (brings duplicates -> error)
+ foreach (var mh in context.OutgoingMessageHeaders) {
+ int x = msg.Headers.FindHeader (mh.Name, mh.Namespace, mh.Actor);
+ if (x >= 0)
+ msg.Headers.RemoveAt (x);
+ msg.Headers.Add ((MessageHeader) mh);
+ }
+ msg.Properties.CopyProperties (context.OutgoingMessageProperties);
+ }
+
+ // FIXME: disabling MessageId as it's not seen for bug #567672 case. But might be required for PeerDuplexChannel. Check it later.
+ //if (OutputSession != null)
+ // msg.Headers.MessageId = new UniqueId (OutputSession.Id);
msg.Properties.AllowOutputBatching = AllowOutputBatching;
+ if (msg.Version.Addressing.Equals (AddressingVersion.WSAddressing10)) {
+ if (msg.Headers.MessageId == null)
+ msg.Headers.MessageId = new UniqueId ();
+ if (msg.Headers.ReplyTo == null)
+ msg.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
+ if (msg.Headers.To == null && RemoteAddress != null)
+ msg.Headers.To = RemoteAddress.Uri;
+ }
+
return msg;
}