Ref parameter was not covered by ParameterInfo.IsOut. Fixed bug #696784.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientRuntimeChannel.cs
1 //
2 // ClientRuntimeChannel.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Runtime.Serialization;
32 using System.ServiceModel.Channels;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using System.ServiceModel.Security;
36 using System.Threading;
37 using System.Xml;
38
39 namespace System.ServiceModel.MonoInternal
40 {
41 #if DISABLE_REAL_PROXY
42         // FIXME: This is a quick workaround for bug #571907
43         public
44 #endif
45         interface IInternalContextChannel
46         {
47                 ContractDescription Contract { get; }
48
49                 object Process (MethodBase method, string operationName, object [] parameters);
50
51                 IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
52
53                 object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result);
54         }
55
56 #if DISABLE_REAL_PROXY
57         // FIXME: This is a quick workaround for bug #571907
58         public
59 #endif
60         class ClientRuntimeChannel
61                 : CommunicationObject, IClientChannel, IInternalContextChannel
62         {
63                 ClientRuntime runtime;
64                 EndpointAddress remote_address;
65                 ContractDescription contract;
66                 MessageVersion message_version;
67                 TimeSpan default_open_timeout, default_close_timeout;
68                 IChannel channel;
69                 IChannelFactory factory;
70                 OperationContext context;
71
72                 #region delegates
73                 readonly ProcessDelegate _processDelegate;
74
75                 delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
76
77                 readonly RequestDelegate requestDelegate;
78
79                 delegate Message RequestDelegate (Message msg, TimeSpan timeout);
80
81                 readonly SendDelegate sendDelegate;
82
83                 delegate void SendDelegate (Message msg, TimeSpan timeout);
84                 #endregion
85
86                 public ClientRuntimeChannel (ServiceEndpoint endpoint,
87                         ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
88                         : this (endpoint.CreateClientRuntime (null), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
89                 {
90                 }
91
92                 public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
93                 {
94                         if (runtime == null)
95                                 throw new ArgumentNullException ("runtime");
96                         this.runtime = runtime;
97                         this.remote_address = remoteAddress;
98                         if (runtime.Via == null)
99                                 runtime.Via = via ?? (remote_address != null ?remote_address.Uri : null);
100                         this.contract = contract;
101                         this.message_version = messageVersion;
102                         default_open_timeout = openTimeout;
103                         default_close_timeout = closeTimeout;
104                         _processDelegate = new ProcessDelegate (Process);
105                         requestDelegate = new RequestDelegate (Request);
106                         sendDelegate = new SendDelegate (Send);
107
108                         // default values
109                         AllowInitializationUI = true;
110                         OperationTimeout = TimeSpan.FromMinutes (1);
111
112                         if (contextChannel != null)
113                                 channel = contextChannel;
114                         else {
115                                 var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
116                                 try {
117                                         channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
118                                         this.factory = factory;
119                                 } catch (TargetInvocationException ex) {
120                                         if (ex.InnerException != null)
121                                                 throw ex.InnerException;
122                                         else
123                                                 throw;
124                                 }
125                         }
126                 }
127
128                 public ContractDescription Contract {
129                         get { return contract; }
130                 }
131
132                 public ClientRuntime Runtime {
133                         get { return runtime; }
134                 }
135
136                 IRequestChannel RequestChannel {
137                         get { return channel as IRequestChannel; }
138                 }
139
140                 IOutputChannel OutputChannel {
141                         get { return channel as IOutputChannel; }
142                 }
143
144                 internal IDuplexChannel DuplexChannel {
145                         get { return channel as IDuplexChannel; }
146                 }
147
148                 #region IClientChannel
149
150                 bool did_interactive_initialization;
151
152                 public bool AllowInitializationUI { get; set; }
153
154                 public bool DidInteractiveInitialization {
155                         get { return did_interactive_initialization; }
156                 }
157
158                 public Uri Via {
159                         get { return runtime.Via; }
160                 }
161
162                 class DelegatingWaitHandle : WaitHandle
163                 {
164                         public DelegatingWaitHandle (IAsyncResult [] results)
165                         {
166                                 this.results = results;
167                         }
168
169                         IAsyncResult [] results;
170
171                         protected override void Dispose (bool disposing)
172                         {
173                                 if (disposing)
174                                         foreach (var r in results)
175                                                 r.AsyncWaitHandle.Close ();
176                         }
177
178                         public override bool WaitOne ()
179                         {
180                                 foreach (var r in results)
181                                         r.AsyncWaitHandle.WaitOne ();
182                                 return true;
183                         }
184
185                         public override bool WaitOne (int millisecondsTimeout)
186                         {
187                                 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout);
188                         }
189
190                         WaitHandle [] ResultWaitHandles {
191                                 get {
192                                         var arr = new WaitHandle [results.Length];
193                                         for (int i = 0; i < arr.Length; i++)
194                                                 arr [i] = results [i].AsyncWaitHandle;
195                                         return arr;
196                                 }
197                         }
198
199 #if !MOONLIGHT
200                         public override bool WaitOne (int millisecondsTimeout, bool exitContext)
201                         {
202                                 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
203                         }
204
205                         public override bool WaitOne (TimeSpan timeout, bool exitContext)
206                         {
207                                 return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
208                         }
209 #endif
210                 }
211
212                 class DisplayUIAsyncResult : IAsyncResult
213                 {
214                         public DisplayUIAsyncResult (IAsyncResult [] results)
215                         {
216                                 this.results = results;
217                         }
218
219                         IAsyncResult [] results;
220
221                         internal IAsyncResult [] Results {
222                                 get { return results; }
223                         }
224
225                         public object AsyncState {
226                                 get { return null; }
227                         }
228
229                         WaitHandle wait_handle;
230
231                         public WaitHandle AsyncWaitHandle {
232                                 get {
233                                         if (wait_handle == null)
234                                                 wait_handle = new DelegatingWaitHandle (results);
235                                         return wait_handle;
236                                 }
237                         }
238
239                         public bool CompletedSynchronously {
240                                 get {
241                                         foreach (var r in results)
242                                                 if (!r.CompletedSynchronously)
243                                                         return false;
244                                         return true;
245                                 }
246                         }
247                         public bool IsCompleted {
248                                 get {
249                                         foreach (var r in results)
250                                                 if (!r.IsCompleted)
251                                                         return false;
252                                         return true;
253                                 }
254                         }
255                 }
256
257                 public IAsyncResult BeginDisplayInitializationUI (
258                         AsyncCallback callback, object state)
259                 {
260                         OnInitializationUI ();
261                         IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
262                         int i = 0;
263                         foreach (var init in runtime.InteractiveChannelInitializers)
264                                 arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
265                         return new DisplayUIAsyncResult (arr);
266                 }
267
268                 public void EndDisplayInitializationUI (
269                         IAsyncResult result)
270                 {
271                         DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
272                         int i = 0;
273                         foreach (var init in runtime.InteractiveChannelInitializers)
274                                 init.EndDisplayInitializationUI (r.Results [i++]);
275
276                         did_interactive_initialization = true;
277                 }
278
279                 public void DisplayInitializationUI ()
280                 {
281                         OnInitializationUI ();
282                         foreach (var init in runtime.InteractiveChannelInitializers)
283                                 init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
284
285                         did_interactive_initialization = true;
286                 }
287
288                 void OnInitializationUI ()
289                 {
290                         if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
291                                 throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
292                 }
293
294                 public void Dispose ()
295                 {
296                         Close ();
297                 }
298
299                 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
300
301                 #endregion
302
303                 #region IContextChannel
304
305                 [MonoTODO]
306                 public bool AllowOutputBatching { get; set; }
307
308                 public IInputSession InputSession {
309                         get {
310                                 ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
311                                 ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
312                                 if (ch != null)
313                                         return ch.Session;
314                                 var dch = OutputChannel as ISessionChannel<IDuplexSession>;
315                                 return dch != null ? dch.Session : null;
316                         }
317                 }
318
319                 public EndpointAddress LocalAddress {
320                         get {
321                                 var dc = OperationChannel as IDuplexChannel;
322                                 return dc != null ? dc.LocalAddress : null;
323                         }
324                 }
325
326                 [MonoTODO]
327                 public TimeSpan OperationTimeout { get; set; }
328
329                 public IOutputSession OutputSession {
330                         get {
331                                 ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
332                                 ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
333                                 if (ch != null)
334                                         return ch.Session;
335                                 var dch = OutputChannel as ISessionChannel<IDuplexSession>;
336                                 return dch != null ? dch.Session : null;
337                         }
338                 }
339
340                 public EndpointAddress RemoteAddress {
341                         get { return RequestChannel != null ? RequestChannel.RemoteAddress : OutputChannel.RemoteAddress; }
342                 }
343
344                 public string SessionId {
345                         get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
346                 }
347
348                 #endregion
349
350                 // CommunicationObject
351                 protected internal override TimeSpan DefaultOpenTimeout {
352                         get { return default_open_timeout; }
353                 }
354
355                 protected internal override TimeSpan DefaultCloseTimeout {
356                         get { return default_close_timeout; }
357                 }
358
359                 protected override void OnAbort ()
360                 {
361                         channel.Abort ();
362                         if (factory != null) // ... is it valid?
363                                 factory.Abort ();
364                 }
365
366                 Action<TimeSpan> close_delegate;
367
368                 protected override IAsyncResult OnBeginClose (
369                         TimeSpan timeout, AsyncCallback callback, object state)
370                 {
371                         if (close_delegate == null)
372                                 close_delegate = new Action<TimeSpan> (OnClose);
373                         return close_delegate.BeginInvoke (timeout, callback, state);
374                 }
375
376                 protected override void OnEndClose (IAsyncResult result)
377                 {
378                         close_delegate.EndInvoke (result);
379                 }
380
381                 protected override void OnClose (TimeSpan timeout)
382                 {
383                         DateTime start = DateTime.Now;
384                         if (channel.State == CommunicationState.Opened)
385                                 channel.Close (timeout);
386                 }
387
388                 Action<TimeSpan> open_callback;
389
390                 protected override IAsyncResult OnBeginOpen (
391                         TimeSpan timeout, AsyncCallback callback, object state)
392                 {
393                         if (open_callback == null)
394                                 open_callback = new Action<TimeSpan> (OnOpen);
395                         return open_callback.BeginInvoke (timeout, callback, state);
396                 }
397
398                 protected override void OnEndOpen (IAsyncResult result)
399                 {
400                         if (open_callback == null)
401                                 throw new InvalidOperationException ("Async open operation has not started");
402                         open_callback.EndInvoke (result);
403                 }
404
405                 protected override void OnOpen (TimeSpan timeout)
406                 {
407                         if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
408                                 throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
409                         if (channel.State == CommunicationState.Created)
410                                 channel.Open (timeout);
411                 }
412
413                 // IChannel
414
415                 IChannel OperationChannel {
416                         get { return channel; }
417                 }
418
419                 public T GetProperty<T> () where T : class
420                 {
421                         return OperationChannel.GetProperty<T> ();
422                 }
423
424                 // IExtensibleObject<IContextChannel>
425
426                 IExtensionCollection<IContextChannel> extensions;
427
428                 public IExtensionCollection<IContextChannel> Extensions {
429                         get {
430                                 if (extensions == null)
431                                         extensions = new ExtensionCollection<IContextChannel> (this);
432                                 return extensions;
433                         }
434                 }
435
436                 #region Request/Output processing
437
438                 public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
439                 {
440                         if (context != null)
441                                 throw new InvalidOperationException ("another operation is in progress");
442                         context = OperationContext.Current;
443
444                         return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
445                 }
446
447                 public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
448                 {
449                         context = null;
450                         if (result == null)
451                                 throw new ArgumentNullException ("result");
452                         if (parameters == null)
453                                 throw new ArgumentNullException ("parameters");
454                         // FIXME: the method arguments should be verified to be 
455                         // identical to the arguments in the corresponding begin method.
456                         return _processDelegate.EndInvoke (result);
457                 }
458
459                 public object Process (MethodBase method, string operationName, object [] parameters)
460                 {
461                         try {
462                                 return DoProcess (method, operationName, parameters);
463                         } catch (Exception ex) {
464 #if MOONLIGHT // just for debugging
465                                 Console.Write ("Exception in async operation: ");
466                                 Console.WriteLine (ex);
467 #endif
468                                 throw;
469                         }
470                 }
471
472                 object DoProcess (MethodBase method, string operationName, object [] parameters)
473                 {
474                         if (AllowInitializationUI)
475                                 DisplayInitializationUI ();
476                         OperationDescription od = SelectOperation (method, operationName, parameters);
477
478                         if (State != CommunicationState.Opened)
479                                 Open ();
480
481                         if (!od.IsOneWay)
482                                 return Request (od, parameters);
483                         else {
484                                 Output (od, parameters);
485                                 return null;
486                         }
487                 }
488
489                 OperationDescription SelectOperation (MethodBase method, string operationName, object [] parameters)
490                 {
491                         string operation;
492                         if (Runtime.OperationSelector != null)
493                                 operation = Runtime.OperationSelector.SelectOperation (method, parameters);
494                         else
495                                 operation = operationName;
496                         OperationDescription od = contract.Operations.Find (operation);
497                         if (od == null)
498                                 throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
499                         return od;
500                 }
501
502                 void Output (OperationDescription od, object [] parameters)
503                 {
504                         ClientOperation op = runtime.Operations [od.Name];
505                         Send (CreateRequest (op, parameters), OperationTimeout);
506                 }
507
508                 object Request (OperationDescription od, object [] parameters)
509                 {
510                         ClientOperation op = runtime.Operations [od.Name];
511                         object [] inspections = new object [runtime.MessageInspectors.Count];
512                         Message req = CreateRequest (op, parameters);
513
514                         for (int i = 0; i < inspections.Length; i++)
515                                 inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
516
517                         Message res = Request (req, OperationTimeout);
518                         if (res.IsFault) {
519                                 var resb = res.CreateBufferedCopy (runtime.MaxFaultSize);
520                                 MessageFault fault = MessageFault.CreateFault (resb.CreateMessage (), runtime.MaxFaultSize);
521                                 var conv = OperationChannel.GetProperty<FaultConverter> () ?? FaultConverter.GetDefaultFaultConverter (res.Version);
522                                 Exception ex;
523                                 if (!conv.TryCreateException (resb.CreateMessage (), fault, out ex)) {
524                                         if (fault.HasDetail) {
525                                                 Type detailType = typeof (ExceptionDetail);
526                                                 var freader = fault.GetReaderAtDetailContents ();
527                                                 DataContractSerializer ds = null;
528 #if !NET_2_1
529                                                 foreach (var fci in op.FaultContractInfos)
530                                                         if (res.Headers.Action == fci.Action || fci.Serializer.IsStartObject (freader)) {
531                                                                 detailType = fci.Detail;
532                                                                 ds = fci.Serializer;
533                                                                 break;
534                                                         }
535 #endif
536                                                 if (ds == null)
537                                                         ds = new DataContractSerializer (detailType);
538                                                 var detail = ds.ReadObject (freader);
539                                                 ex = (Exception) Activator.CreateInstance (typeof (FaultException<>).MakeGenericType (detailType), new object [] {detail, fault.Reason, fault.Code, res.Headers.Action});
540                                         }
541
542                                         if (ex == null)
543                                                 ex = new FaultException (fault);
544                                 }
545                                 throw ex;
546                         }
547
548                         for (int i = 0; i < inspections.Length; i++)
549                                 runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
550
551                         if (op.DeserializeReply)
552                                 return op.Formatter.DeserializeReply (res, parameters);
553                         else
554                                 return res;
555                 }
556
557                 #region Message-based Request() and Send()
558                 // They are internal for ClientBase<T>.ChannelBase use.
559                 internal Message Request (Message msg, TimeSpan timeout)
560                 {
561                         if (RequestChannel != null)
562                                 return RequestChannel.Request (msg, timeout);
563                         else
564                                 return RequestCorrelated (msg, timeout, OutputChannel);
565                 }
566
567                 internal virtual Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
568                 {
569                         // FIXME: implement ConcurrencyMode check:
570                         // if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
571
572                         DateTime startTime = DateTime.Now;
573                         OutputChannel.Send (msg, timeout);
574                         return ((IDuplexChannel) channel).Receive (timeout - (DateTime.Now - startTime));
575                 }
576
577                 internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
578                 {
579                         return requestDelegate.BeginInvoke (msg, timeout, callback, state);
580                 }
581
582                 internal Message EndRequest (IAsyncResult result)
583                 {
584                         return requestDelegate.EndInvoke (result);
585                 }
586
587                 internal void Send (Message msg, TimeSpan timeout)
588                 {
589                         if (OutputChannel != null)
590                                 OutputChannel.Send (msg, timeout);
591                         else
592                                 RequestChannel.Request (msg, timeout); // and ignore returned message.
593                 }
594
595                 internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
596                 {
597                         return sendDelegate.BeginInvoke (msg, timeout, callback, state);
598                 }
599
600                 internal void EndSend (IAsyncResult result)
601                 {
602                         sendDelegate.EndInvoke (result);
603                 }
604                 #endregion
605
606                 Message CreateRequest (ClientOperation op, object [] parameters)
607                 {
608                         MessageVersion version = message_version;
609                         if (version == null)
610                                 version = MessageVersion.Default;
611
612                         Message msg;
613                         if (op.SerializeRequest)
614                                 msg = op.Formatter.SerializeRequest (
615                                         version, parameters);
616                         else {
617                                 if (parameters.Length != 1)
618                                         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));
619                                 if (!(parameters [0] is Message))
620                                         throw new ArgumentException (String.Format ("Argument should be only a Message, but has {0}", parameters [0] != null ? parameters [0].GetType ().FullName : "null"));
621                                 msg = (Message) parameters [0];
622                         }
623
624                         context = context ?? OperationContext.Current;
625                         if (context != null) {
626                                 // CopyHeadersFrom does not work here (brings duplicates -> error)
627                                 foreach (var mh in context.OutgoingMessageHeaders) {
628                                         int x = msg.Headers.FindHeader (mh.Name, mh.Namespace, mh.Actor);
629                                         if (x >= 0)
630                                                 msg.Headers.RemoveAt (x);
631                                         msg.Headers.Add ((MessageHeader) mh);
632                                 }
633                                 msg.Properties.CopyProperties (context.OutgoingMessageProperties);
634                         }
635
636                         // FIXME: disabling MessageId as it's not seen for bug #567672 case. But might be required for PeerDuplexChannel. Check it later.
637                         //if (OutputSession != null)
638                         //      msg.Headers.MessageId = new UniqueId (OutputSession.Id);
639                         msg.Properties.AllowOutputBatching = AllowOutputBatching;
640
641                         if (msg.Version.Addressing.Equals (AddressingVersion.WSAddressing10)) {
642                                 if (msg.Headers.MessageId == null)
643                                         msg.Headers.MessageId = new UniqueId ();
644                                 if (msg.Headers.ReplyTo == null)
645                                         msg.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
646                                 if (msg.Headers.To == null && RemoteAddress != null)
647                                         msg.Headers.To = RemoteAddress.Uri;
648                         }
649
650                         return msg;
651                 }
652
653                 #endregion
654         }
655 }