[System.ServiceModel] Fix timeout defaulting to 0 seconds
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientRuntimeChannel.cs
index d0aeb8e78bea9efd658270de9be4137c5453d7c5..2e2b0d60b0860a432a7a5229b0f1e409a0ab9af2 100644 (file)
@@ -46,7 +46,7 @@ namespace System.ServiceModel.MonoInternal
        {
                ContractDescription Contract { get; }
 
-               object Process (MethodBase method, string operationName, object [] parameters);
+               object Process (MethodBase method, string operationName, object [] parameters, OperationContext context);
 
                IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
 
@@ -67,12 +67,13 @@ namespace System.ServiceModel.MonoInternal
                TimeSpan default_open_timeout, default_close_timeout;
                IChannel channel;
                IChannelFactory factory;
-               OperationContext context;
+               TimeSpan? operation_timeout = null;
+
 
                #region delegates
                readonly ProcessDelegate _processDelegate;
 
-               delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
+               delegate object ProcessDelegate (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context);
 
                readonly RequestDelegate requestDelegate;
 
@@ -93,6 +94,8 @@ namespace System.ServiceModel.MonoInternal
                {
                        if (runtime == null)
                                throw new ArgumentNullException ("runtime");
+                       if (messageVersion == null)
+                               throw new ArgumentNullException ("messageVersion");
                        this.runtime = runtime;
                        this.remote_address = remoteAddress;
                        if (runtime.Via == null)
@@ -107,7 +110,6 @@ namespace System.ServiceModel.MonoInternal
 
                        // default values
                        AllowInitializationUI = true;
-                       OperationTimeout = TimeSpan.FromMinutes (1);
 
                        if (contextChannel != null)
                                channel = contextChannel;
@@ -196,7 +198,6 @@ namespace System.ServiceModel.MonoInternal
                                }
                        }
 
-#if !MOONLIGHT
                        public override bool WaitOne (int millisecondsTimeout, bool exitContext)
                        {
                                return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
@@ -206,7 +207,6 @@ namespace System.ServiceModel.MonoInternal
                        {
                                return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
                        }
-#endif
                }
 
                class DisplayUIAsyncResult : IAsyncResult
@@ -323,8 +323,17 @@ namespace System.ServiceModel.MonoInternal
                        }
                }
 
-               [MonoTODO]
-               public TimeSpan OperationTimeout { get; set; }
+               public TimeSpan OperationTimeout {
+                       get {
+                               if (!this.operation_timeout.HasValue) {
+                                       this.operation_timeout = DefaultCommunicationTimeouts.Instance.ReceiveTimeout;
+                               }
+                               return this.operation_timeout.Value;
+                       }
+                       set {
+                               this.operation_timeout = value;
+                       }
+               }
 
                public IOutputSession OutputSession {
                        get {
@@ -418,6 +427,8 @@ namespace System.ServiceModel.MonoInternal
 
                public T GetProperty<T> () where T : class
                {
+                       if (typeof (T) == typeof (MessageVersion))
+                               return (T) (object) message_version;
                        return OperationChannel.GetProperty<T> ();
                }
 
@@ -435,77 +446,59 @@ namespace System.ServiceModel.MonoInternal
 
                #region Request/Output processing
 
-               class TempAsyncResult : IAsyncResult
-               {
-                       public TempAsyncResult (object returnValue, object state)
-                       {
-                               ReturnValue = returnValue;
-                               AsyncState = state;
-                               CompletedSynchronously = true;
-                               IsCompleted = true;
-                               AsyncWaitHandle = new ManualResetEvent (true);
-                       }
-                       
-                       public object ReturnValue { get; set; }
-                       public object AsyncState { get; set; }
-                       public bool CompletedSynchronously { get; set; }
-                       public bool IsCompleted { get; set; }
-                       public WaitHandle AsyncWaitHandle { get; set; }
-               }
-
                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;
-
-                       // FIXME: this is a workaround for bug #633945
-                       switch (Environment.OSVersion.Platform) {
-                       case PlatformID.Unix:
-                       case PlatformID.MacOSX:
-                               return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
-                       default:
-                               var result = Process (method, operationName, parameters);
-                               var ret = new TempAsyncResult (result, asyncState);
-                               if (callback != null)
-                                       callback (ret);
-                               return ret;
-                       }
+                       var p = parameters;
+                       var retval = _processDelegate.BeginInvoke (method, operationName, true, ref p, OperationContext.Current, callback, asyncState);
+                       if (p != parameters)
+                               throw new InvalidOperationException ();
+                       return retval;
                }
 
                public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
                {
-                       context = null;
                        if (result == null)
                                throw new ArgumentNullException ("result");
                        if (parameters == null)
                                throw new ArgumentNullException ("parameters");
-                       // FIXME: the method arguments should be verified to be 
-                       // identical to the arguments in the corresponding begin method.
-                       // FIXME: this is a workaround for bug #633945
-                       switch (Environment.OSVersion.Platform) {
-                       case PlatformID.Unix:
-                       case PlatformID.MacOSX:
-                               return _processDelegate.EndInvoke (result);
-                       default:
-                               return ((TempAsyncResult) result).ReturnValue;
-                       }
+
+                       object[] p = parameters;
+                       var retval = _processDelegate.EndInvoke (ref p, result);
+                       if (p == parameters)
+                               return retval;
+
+                       if (p.Length != parameters.Length)
+                               throw new InvalidOperationException ();
+                       Array.Copy (p, parameters, p.Length);
+                       return retval;
                }
 
-               public object Process (MethodBase method, string operationName, object [] parameters)
+               public object Process (MethodBase method, string operationName, object [] parameters, OperationContext context)
                {
+                       var p = parameters;
+                       var retval = Process (method, operationName, false, ref p, context);
+                       if (p != parameters)
+                               throw new InvalidOperationException ();
+                       return retval;
+               }
+
+               object Process (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
+               {
+                       var previousContext = OperationContext.Current;
                        try {
-                               return DoProcess (method, operationName, parameters);
+                               // Inherit the context from the calling thread
+                               OperationContext.Current = context;
+
+                               return DoProcess (method, operationName, isAsync, ref parameters, context);
                        } catch (Exception ex) {
-#if MOONLIGHT // just for debugging
-                               Console.Write ("Exception in async operation: ");
-                               Console.WriteLine (ex);
-#endif
                                throw;
+                       } finally {
+                               // Reset the context before the thread goes back into the pool
+                               OperationContext.Current = previousContext;
                        }
                }
 
-               object DoProcess (MethodBase method, string operationName, object [] parameters)
+               object DoProcess (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
                {
                        if (AllowInitializationUI)
                                DisplayInitializationUI ();
@@ -515,9 +508,9 @@ namespace System.ServiceModel.MonoInternal
                                Open ();
 
                        if (!od.IsOneWay)
-                               return Request (od, parameters);
+                               return Request (od, isAsync, ref parameters, context);
                        else {
-                               Output (od, parameters);
+                               Output (od, parameters, context);
                                return null;
                        }
                }
@@ -535,17 +528,17 @@ namespace System.ServiceModel.MonoInternal
                        return od;
                }
 
-               void Output (OperationDescription od, object [] parameters)
+               void Output (OperationDescription od, object [] parameters, OperationContext context)
                {
                        ClientOperation op = runtime.Operations [od.Name];
-                       Send (CreateRequest (op, parameters), OperationTimeout);
+                       Send (CreateRequest (op, parameters, context), OperationTimeout);
                }
 
-               object Request (OperationDescription od, object [] parameters)
+               object Request (OperationDescription od, bool isAsync, ref object [] parameters, OperationContext context)
                {
                        ClientOperation op = runtime.Operations [od.Name];
                        object [] inspections = new object [runtime.MessageInspectors.Count];
-                       Message req = CreateRequest (op, parameters);
+                       Message req = CreateRequest (op, parameters, context);
 
                        for (int i = 0; i < inspections.Length; i++)
                                inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
@@ -561,14 +554,12 @@ namespace System.ServiceModel.MonoInternal
                                                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);
@@ -584,10 +575,15 @@ namespace System.ServiceModel.MonoInternal
                        for (int i = 0; i < inspections.Length; i++)
                                runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
 
-                       if (op.DeserializeReply)
-                               return op.Formatter.DeserializeReply (res, parameters);
-                       else
+                       if (!op.DeserializeReply)
                                return res;
+
+                       if (isAsync && od.EndMethod != null) {
+                               var endParams = od.EndMethod.GetParameters ();
+                               parameters = new object [endParams.Length - 1];
+                       }
+
+                       return op.Formatter.DeserializeReply (res, parameters);
                }
 
                #region Message-based Request() and Send()
@@ -602,6 +598,9 @@ namespace System.ServiceModel.MonoInternal
 
                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));
@@ -636,7 +635,7 @@ namespace System.ServiceModel.MonoInternal
                }
                #endregion
 
-               Message CreateRequest (ClientOperation op, object [] parameters)
+               Message CreateRequest (ClientOperation op, object [] parameters, OperationContext context)
                {
                        MessageVersion version = message_version;
                        if (version == null)