2009-08-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientRuntimeChannel.cs
index 5cc13b8b4fe5396e447b7e887f3aeb7d5b47332c..9bd49501185f70b917164ec6e3b1b715942b492e 100644 (file)
@@ -32,22 +32,62 @@ using System.ServiceModel.Description;
 using System.ServiceModel.Dispatcher;
 using System.ServiceModel.Security;
 using System.Threading;
+using System.Xml;
 
 namespace System.ServiceModel
 {
-#if TARGET_DOTNET
-       [MonoTODO]
-       public
+#if NET_2_1
+       internal class DuplexClientRuntimeChannel
+       {
+       }
 #else
-       internal
+       internal class DuplexClientRuntimeChannel
+               : ClientRuntimeChannel, IDuplexContextChannel
+       {
+               public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
+                       ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
+                       : base (endpoint, factory, remoteAddress, via)
+               {
+               }
+
+               public bool AutomaticInputSessionShutdown {
+                       get { throw new NotImplementedException (); }
+                       set { throw new NotImplementedException (); }
+               }
+
+               public InstanceContext CallbackInstance { get; set; }
+
+               Action<TimeSpan> session_shutdown_delegate;
+
+               public void CloseOutputSession (TimeSpan timeout)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               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);
+               }
+       }
 #endif
-       class ClientRuntimeChannel
+
+       internal class ClientRuntimeChannel
                : CommunicationObject, IClientChannel
        {
                ClientRuntime runtime;
-               ChannelFactory factory;
-               IRequestChannel request_channel;
-               IOutputChannel output_channel; // could also be IDuplexChannel instance.
+               EndpointAddress remote_address;
+               ContractDescription contract;
+               MessageVersion message_version;
+               TimeSpan default_open_timeout, default_close_timeout;
+               IChannel channel;
+               IChannelFactory factory;
 
                #region delegates
                readonly ProcessDelegate _processDelegate;
@@ -64,10 +104,20 @@ namespace System.ServiceModel
                #endregion
 
                public ClientRuntimeChannel (ServiceEndpoint endpoint,
-                       ChannelFactory factory)
+                       ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
+                       : this (endpoint.CreateRuntime (), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
+               {
+               }
+
+               public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
                {
-                       this.runtime = endpoint.CreateRuntime ();
-                       this.factory = factory;
+                       this.runtime = runtime;
+                       this.remote_address = remoteAddress;
+                       runtime.Via = via;
+                       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);
@@ -76,18 +126,27 @@ namespace System.ServiceModel
                        AllowInitializationUI = true;
                        OperationTimeout = TimeSpan.FromMinutes (1);
 
-                       // determine operation channel to create.
-                       if (factory.OpenedChannelFactory is IChannelFactory<IRequestChannel> ||
-                           factory.OpenedChannelFactory is IChannelFactory<IRequestSessionChannel>)
-                               SetupRequestChannel ();
-                       else
-                               SetupOutputChannel ();
+                       if (contextChannel != null)
+                               channel = contextChannel;
+                       else {
+                               var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
+                               channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
+                               this.factory = factory;
+                       }
                }
 
                public ClientRuntime Runtime {
                        get { return runtime; }
                }
 
+               IRequestChannel RequestChannel {
+                       get { return channel as IRequestChannel; }
+               }
+
+               IOutputChannel OutputChannel {
+                       get { return channel as IOutputChannel; }
+               }
+
                #region IClientChannel
 
                bool did_interactive_initialization;
@@ -248,9 +307,12 @@ namespace System.ServiceModel
 
                public IInputSession InputSession {
                        get {
-                               ISessionChannel<IInputSession> ch = request_channel as ISessionChannel<IInputSession>;
-                               ch = ch ?? output_channel as ISessionChannel<IInputSession>;
-                               return ch != null ? ch.Session : null;
+                               ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
+                               ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
+                               if (ch != null)
+                                       return ch.Session;
+                               var dch = OutputChannel as ISessionChannel<IDuplexSession>;
+                               return dch != null ? dch.Session : null;
                        }
                }
 
@@ -266,14 +328,17 @@ namespace System.ServiceModel
 
                public IOutputSession OutputSession {
                        get {
-                               ISessionChannel<IOutputSession> ch = request_channel as ISessionChannel<IOutputSession>;
-                               ch = ch ?? output_channel as ISessionChannel<IOutputSession>;
-                               return ch != null ? ch.Session : null;
+                               ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
+                               ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
+                               if (ch != null)
+                                       return ch.Session;
+                               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 {
@@ -284,32 +349,41 @@ namespace System.ServiceModel
 
                // CommunicationObject
                protected internal override TimeSpan DefaultOpenTimeout {
-                       get { return factory.DefaultOpenTimeout; }
+                       get { return default_open_timeout; }
                }
 
                protected internal override TimeSpan DefaultCloseTimeout {
-                       get { return factory.DefaultCloseTimeout; }
+                       get { return default_close_timeout; }
                }
 
                protected override void OnAbort ()
                {
-                       factory.Abort ();
+                       channel.Abort ();
+                       if (factory != null)
+                               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;
+                       channel.Close (timeout);
+                       if (factory != null)
+                               factory.Close (timeout - (DateTime.Now - start));
                }
 
                protected override IAsyncResult OnBeginOpen (
@@ -331,7 +405,7 @@ namespace System.ServiceModel
                // IChannel
 
                IChannel OperationChannel {
-                       get { return (IChannel) request_channel ?? output_channel; }
+                       get { return channel; }
                }
 
                public T GetProperty<T> () where T : class
@@ -394,58 +468,16 @@ namespace System.ServiceModel
                                operation = Runtime.OperationSelector.SelectOperation (method, parameters);
                        else
                                operation = operationName;
-                       OperationDescription od = factory.Endpoint.Contract.Operations.Find (operation);
+                       OperationDescription od = contract.Operations.Find (operation);
                        if (od == null)
                                throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
                        return od;
                }
 
-               BindingParameterCollection CreateBindingParameters ()
-               {
-                       BindingParameterCollection pl =
-                               new BindingParameterCollection ();
-
-                       ContractDescription cd = factory.Endpoint.Contract;
-#if !NET_2_1
-                       pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
-
-                       foreach (IEndpointBehavior behavior in factory.Endpoint.Behaviors)
-                               behavior.AddBindingParameters (factory.Endpoint, pl);
-#endif
-
-                       return pl;
-               }
-
-               // This handles IDuplexChannel, IOutputChannel, and those for session channels.
-               void SetupOutputChannel ()
-               {
-                       if (output_channel != null)
-                               return;
-
-                       EndpointAddress address = factory.Endpoint.Address;
-                       Uri via = Runtime.Via;
-
-                       var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
-                       output_channel = (IOutputChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
-               }
-
-               // This handles both IRequestChannel and IRequestSessionChannel.
-               void SetupRequestChannel ()
-               {
-                       if (request_channel != null)
-                               return;
-
-                       EndpointAddress address = factory.Endpoint.Address;
-                       Uri via = Runtime.Via;
-
-                       var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
-                       request_channel = (IRequestChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
-               }
-
                void Output (OperationDescription od, object [] parameters)
                {
-                       if (output_channel.State != CommunicationState.Opened)
-                               output_channel.Open ();
+                       if (OutputChannel.State != CommunicationState.Opened)
+                               OutputChannel.Open ();
 
                        ClientOperation op = runtime.Operations [od.Name];
                        Send (CreateRequest (op, parameters), OperationTimeout);
@@ -494,12 +526,12 @@ namespace System.ServiceModel
                // 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);
+                       if (RequestChannel != null)
+                               return RequestChannel.Request (msg, timeout);
                        else {
                                DateTime startTime = DateTime.Now;
-                               output_channel.Send (msg, timeout);
-                               return ((IDuplexChannel) output_channel).Receive (timeout - (DateTime.Now - startTime));
+                               OutputChannel.Send (msg, timeout);
+                               return ((IDuplexChannel) OutputChannel).Receive (timeout - (DateTime.Now - startTime));
                        }
                }
 
@@ -515,7 +547,7 @@ namespace System.ServiceModel
 
                internal void Send (Message msg, TimeSpan timeout)
                {
-                       output_channel.Send (msg, timeout);
+                       OutputChannel.Send (msg, timeout);
                }
 
                internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
@@ -531,7 +563,7 @@ namespace System.ServiceModel
 
                Message CreateRequest (ClientOperation op, object [] parameters)
                {
-                       MessageVersion version = factory.Endpoint.Binding.MessageVersion;
+                       MessageVersion version = message_version;
                        if (version == null)
                                version = MessageVersion.Default;
 
@@ -541,7 +573,11 @@ namespace System.ServiceModel
                                        version, parameters);
                        else
                                msg = (Message) parameters [0];
+
+                       if (OutputSession != null)
+                               msg.Headers.MessageId = new UniqueId (OutputSession.Id);
                        msg.Properties.AllowOutputBatching = AllowOutputBatching;
+
                        return msg;
                }