Merge pull request #5198 from BrzVlad/fix-binprot-stats
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / DuplexClientRuntimeChannel.cs
index ef8f80047254dea5fab91582d96182b2b4d16ecf..08e6a6e38bae049056f0299c43bf761a007f89cc 100644 (file)
@@ -26,6 +26,8 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using System.Reflection;
 using System.ServiceModel.Channels;
 using System.ServiceModel.Description;
@@ -34,19 +36,21 @@ using System.ServiceModel.Security;
 using System.Threading;
 using System.Xml;
 
-namespace System.ServiceModel
+namespace System.ServiceModel.MonoInternal
 {
-       internal class DuplexClientRuntimeChannel
+#if DISABLE_REAL_PROXY
+       // FIXME: This is a quick workaround for bug #571907
+       public
+#endif
+       class DuplexClientRuntimeChannel
                : ClientRuntimeChannel, IDuplexContextChannel
        {
                public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
                        ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
                        : base (endpoint, factory, remoteAddress, via)
                {
-                       var cd = ContractDescription.GetContract (endpoint.Contract.CallbackContractType);
-                       var se = new ServiceEndpoint (cd, factory.Endpoint.Binding, remoteAddress);
-                       var ed = new EndpointDispatcher (remoteAddress, cd.Name, cd.Namespace);
-                       ed.InitializeServiceEndpoint (true, null, se);
+                       var ed = new EndpointDispatcher (remoteAddress, endpoint.Contract.Name, endpoint.Contract.Namespace);
+                       ed.InitializeServiceEndpoint (true, null, endpoint);
                        Runtime.CallbackDispatchRuntime = ed.DispatchRuntime;
                }
 
@@ -94,6 +98,7 @@ namespace System.ServiceModel
                IAsyncResult loop_result;
                AutoResetEvent loop_handle = new AutoResetEvent (false);
                AutoResetEvent finish_handle = new AutoResetEvent (false);
+               AutoResetEvent receive_reply_handle = new AutoResetEvent (false);
 
                protected override void OnOpen (TimeSpan timeout)
                {
@@ -110,12 +115,12 @@ namespace System.ServiceModel
 
                protected override void OnClose (TimeSpan timeout)
                {
-                       DateTime start = DateTime.Now;
+                       DateTime start = DateTime.UtcNow;
                        base.OnClose (timeout);
                        loop = false;
-                       if (!loop_handle.WaitOne (timeout - (DateTime.Now - start)))
+                       if (!loop_handle.WaitOne (timeout - (DateTime.UtcNow - start)))
                                throw new TimeoutException ();
-                       if (!finish_handle.WaitOne (timeout - (DateTime.Now - start)))
+                       if (!finish_handle.WaitOne (timeout - (DateTime.UtcNow - start)))
                                throw new TimeoutException ();
                }
 
@@ -153,19 +158,40 @@ namespace System.ServiceModel
                        }
                }
 
-               void ProcessInput (IInputChannel input, Message message)
+               void ProcessInputCore (IInputChannel input, Message message)
                {
-                       try {
+                               bool isReply = message != null && Contract.Operations.Any (od => (od.DeclaringContract.CallbackContractType == od.DeclaringContract.ContractType || !od.InCallbackContract) && od.Messages.Any (md => md.Action == message.Headers.Action));
+                               if (isReply) {
+                                       if (ReplyHandlerQueue.Count > 0) {
+                                               if (isReply) {
+                                                       var h = ReplyHandlerQueue.Dequeue ();
+                                                       h (message);
+                                                       return;
+                                               }
+                                       }
+                               }
+                               
+                               if (message.IsFault) {
+                                       Exception ex;
+                                       var mf = MessageFault.CreateFault (message, 0x10000);
+                                       if (FaultConverter.GetDefaultFaultConverter (message.Version).TryCreateException (message, mf, out ex)) // FIXME: get maxMessageSize somehow
+                                               throw ex;
+                                       else
+                                               throw new FaultException (mf);
+                               }
+                               
                                if (!MessageMatchesEndpointDispatcher (message, Runtime.CallbackDispatchRuntime.EndpointDispatcher))
                                        throw new EndpointNotFoundException (String.Format ("The request message has the target '{0}' with action '{1}' which is not reachable in this service contract", message.Headers.To, message.Headers.Action));
                                new InputOrReplyRequestProcessor (Runtime.CallbackDispatchRuntime, input).ProcessInput (message);
+               }
+
+               void ProcessInput (IInputChannel input, Message message)
+               {
+                       try {
+                               ProcessInputCore (input, message);
                        } catch (Exception ex) {
                                // FIXME: log it.
                                Console.WriteLine (ex);
-                       } finally {
-                               // unless it is closed by session/call manager, move it back to the loop to receive the next message.
-                               if (input.State != CommunicationState.Closed)
-                                       ProcessRequestOrInput (input);
                        }
                }
 
@@ -175,5 +201,23 @@ namespace System.ServiceModel
 
                        return endpoint.ContractFilter.Match (req);
                }
+               
+               internal override Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
+               {
+                       DateTime startTime = DateTime.UtcNow;
+                       Message ret = null;
+                       ManualResetEvent wait = new ManualResetEvent (false);
+                       Action<Message> handler = delegate (Message reply) {
+                               ret = reply;
+                               wait.Set ();
+                       };
+                       ReplyHandlerQueue.Enqueue (handler);
+                       channel.Send (msg, timeout);
+                       if (ret == null && !wait.WaitOne (timeout - (DateTime.UtcNow - startTime)))
+                               throw new TimeoutException ();
+                       return ret;
+               }
+               
+               internal Queue<Action<Message>> ReplyHandlerQueue = new Queue<Action<Message>> ();
        }
 }