Merge pull request #138 from eisnerd/bug-winforms-datagridview-resize-490247
[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                         if (messageVersion == null)
97                                 throw new ArgumentNullException ("messageVersion");
98                         this.runtime = runtime;
99                         this.remote_address = remoteAddress;
100                         if (runtime.Via == null)
101                                 runtime.Via = via ?? (remote_address != null ?remote_address.Uri : null);
102                         this.contract = contract;
103                         this.message_version = messageVersion;
104                         default_open_timeout = openTimeout;
105                         default_close_timeout = closeTimeout;
106                         _processDelegate = new ProcessDelegate (Process);
107                         requestDelegate = new RequestDelegate (Request);
108                         sendDelegate = new SendDelegate (Send);
109
110                         // default values
111                         AllowInitializationUI = true;
112                         OperationTimeout = TimeSpan.FromMinutes (1);
113
114                         if (contextChannel != null)
115                                 channel = contextChannel;
116                         else {
117                                 var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
118                                 try {
119                                         channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
120                                         this.factory = factory;
121                                 } catch (TargetInvocationException ex) {
122                                         if (ex.InnerException != null)
123                                                 throw ex.InnerException;
124                                         else
125                                                 throw;
126                                 }
127                         }
128                 }
129
130                 public ContractDescription Contract {
131                         get { return contract; }
132                 }
133
134                 public ClientRuntime Runtime {
135                         get { return runtime; }
136                 }
137
138                 IRequestChannel RequestChannel {
139                         get { return channel as IRequestChannel; }
140                 }
141
142                 IOutputChannel OutputChannel {
143                         get { return channel as IOutputChannel; }
144                 }
145
146                 internal IDuplexChannel DuplexChannel {
147                         get { return channel as IDuplexChannel; }
148                 }
149
150                 #region IClientChannel
151
152                 bool did_interactive_initialization;
153
154                 public bool AllowInitializationUI { get; set; }
155
156                 public bool DidInteractiveInitialization {
157                         get { return did_interactive_initialization; }
158                 }
159
160                 public Uri Via {
161                         get { return runtime.Via; }
162                 }
163
164                 class DelegatingWaitHandle : WaitHandle
165                 {
166                         public DelegatingWaitHandle (IAsyncResult [] results)
167                         {
168                                 this.results = results;
169                         }
170
171                         IAsyncResult [] results;
172
173                         protected override void Dispose (bool disposing)
174                         {
175                                 if (disposing)
176                                         foreach (var r in results)
177                                                 r.AsyncWaitHandle.Close ();
178                         }
179
180                         public override bool WaitOne ()
181                         {
182                                 foreach (var r in results)
183                                         r.AsyncWaitHandle.WaitOne ();
184                                 return true;
185                         }
186
187                         public override bool WaitOne (int millisecondsTimeout)
188                         {
189                                 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout);
190                         }
191
192                         WaitHandle [] ResultWaitHandles {
193                                 get {
194                                         var arr = new WaitHandle [results.Length];
195                                         for (int i = 0; i < arr.Length; i++)
196                                                 arr [i] = results [i].AsyncWaitHandle;
197                                         return arr;
198                                 }
199                         }
200
201 #if !MOONLIGHT
202                         public override bool WaitOne (int millisecondsTimeout, bool exitContext)
203                         {
204                                 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
205                         }
206
207                         public override bool WaitOne (TimeSpan timeout, bool exitContext)
208                         {
209                                 return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
210                         }
211 #endif
212                 }
213
214                 class DisplayUIAsyncResult : IAsyncResult
215                 {
216                         public DisplayUIAsyncResult (IAsyncResult [] results)
217                         {
218                                 this.results = results;
219                         }
220
221                         IAsyncResult [] results;
222
223                         internal IAsyncResult [] Results {
224                                 get { return results; }
225                         }
226
227                         public object AsyncState {
228                                 get { return null; }
229                         }
230
231                         WaitHandle wait_handle;
232
233                         public WaitHandle AsyncWaitHandle {
234                                 get {
235                                         if (wait_handle == null)
236                                                 wait_handle = new DelegatingWaitHandle (results);
237                                         return wait_handle;
238                                 }
239                         }
240
241                         public bool CompletedSynchronously {
242                                 get {
243                                         foreach (var r in results)
244                                                 if (!r.CompletedSynchronously)
245                                                         return false;
246                                         return true;
247                                 }
248                         }
249                         public bool IsCompleted {
250                                 get {
251                                         foreach (var r in results)
252                                                 if (!r.IsCompleted)
253                                                         return false;
254                                         return true;
255                                 }
256                         }
257                 }
258
259                 public IAsyncResult BeginDisplayInitializationUI (
260                         AsyncCallback callback, object state)
261                 {
262                         OnInitializationUI ();
263                         IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
264                         int i = 0;
265                         foreach (var init in runtime.InteractiveChannelInitializers)
266                                 arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
267                         return new DisplayUIAsyncResult (arr);
268                 }
269
270                 public void EndDisplayInitializationUI (
271                         IAsyncResult result)
272                 {
273                         DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
274                         int i = 0;
275                         foreach (var init in runtime.InteractiveChannelInitializers)
276                                 init.EndDisplayInitializationUI (r.Results [i++]);
277
278                         did_interactive_initialization = true;
279                 }
280
281                 public void DisplayInitializationUI ()
282                 {
283                         OnInitializationUI ();
284                         foreach (var init in runtime.InteractiveChannelInitializers)
285                                 init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
286
287                         did_interactive_initialization = true;
288                 }
289
290                 void OnInitializationUI ()
291                 {
292                         if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
293                                 throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
294                 }
295
296                 public void Dispose ()
297                 {
298                         Close ();
299                 }
300
301                 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
302
303                 #endregion
304
305                 #region IContextChannel
306
307                 [MonoTODO]
308                 public bool AllowOutputBatching { get; set; }
309
310                 public IInputSession InputSession {
311                         get {
312                                 ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
313                                 ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
314                                 if (ch != null)
315                                         return ch.Session;
316                                 var dch = OutputChannel as ISessionChannel<IDuplexSession>;
317                                 return dch != null ? dch.Session : null;
318                         }
319                 }
320
321                 public EndpointAddress LocalAddress {
322                         get {
323                                 var dc = OperationChannel as IDuplexChannel;
324                                 return dc != null ? dc.LocalAddress : null;
325                         }
326                 }
327
328                 [MonoTODO]
329                 public TimeSpan OperationTimeout { get; set; }
330
331                 public IOutputSession OutputSession {
332                         get {
333                                 ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
334                                 ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
335                                 if (ch != null)
336                                         return ch.Session;
337                                 var dch = OutputChannel as ISessionChannel<IDuplexSession>;
338                                 return dch != null ? dch.Session : null;
339                         }
340                 }
341
342                 public EndpointAddress RemoteAddress {
343                         get { return RequestChannel != null ? RequestChannel.RemoteAddress : OutputChannel.RemoteAddress; }
344                 }
345
346                 public string SessionId {
347                         get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
348                 }
349
350                 #endregion
351
352                 // CommunicationObject
353                 protected internal override TimeSpan DefaultOpenTimeout {
354                         get { return default_open_timeout; }
355                 }
356
357                 protected internal override TimeSpan DefaultCloseTimeout {
358                         get { return default_close_timeout; }
359                 }
360
361                 protected override void OnAbort ()
362                 {
363                         channel.Abort ();
364                         if (factory != null) // ... is it valid?
365                                 factory.Abort ();
366                 }
367
368                 Action<TimeSpan> close_delegate;
369
370                 protected override IAsyncResult OnBeginClose (
371                         TimeSpan timeout, AsyncCallback callback, object state)
372                 {
373                         if (close_delegate == null)
374                                 close_delegate = new Action<TimeSpan> (OnClose);
375                         return close_delegate.BeginInvoke (timeout, callback, state);
376                 }
377
378                 protected override void OnEndClose (IAsyncResult result)
379                 {
380                         close_delegate.EndInvoke (result);
381                 }
382
383                 protected override void OnClose (TimeSpan timeout)
384                 {
385                         DateTime start = DateTime.Now;
386                         if (channel.State == CommunicationState.Opened)
387                                 channel.Close (timeout);
388                 }
389
390                 Action<TimeSpan> open_callback;
391
392                 protected override IAsyncResult OnBeginOpen (
393                         TimeSpan timeout, AsyncCallback callback, object state)
394                 {
395                         if (open_callback == null)
396                                 open_callback = new Action<TimeSpan> (OnOpen);
397                         return open_callback.BeginInvoke (timeout, callback, state);
398                 }
399
400                 protected override void OnEndOpen (IAsyncResult result)
401                 {
402                         if (open_callback == null)
403                                 throw new InvalidOperationException ("Async open operation has not started");
404                         open_callback.EndInvoke (result);
405                 }
406
407                 protected override void OnOpen (TimeSpan timeout)
408                 {
409                         if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
410                                 throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
411                         if (channel.State == CommunicationState.Created)
412                                 channel.Open (timeout);
413                 }
414
415                 // IChannel
416
417                 IChannel OperationChannel {
418                         get { return channel; }
419                 }
420
421                 public T GetProperty<T> () where T : class
422                 {
423                         if (typeof (T) == typeof (MessageVersion))
424                                 return (T) (object) message_version;
425                         return OperationChannel.GetProperty<T> ();
426                 }
427
428                 // IExtensibleObject<IContextChannel>
429
430                 IExtensionCollection<IContextChannel> extensions;
431
432                 public IExtensionCollection<IContextChannel> Extensions {
433                         get {
434                                 if (extensions == null)
435                                         extensions = new ExtensionCollection<IContextChannel> (this);
436                                 return extensions;
437                         }
438                 }
439
440                 #region Request/Output processing
441
442                 public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
443                 {
444                         if (context != null)
445                                 throw new InvalidOperationException ("another operation is in progress");
446                         context = OperationContext.Current;
447
448                         return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
449                 }
450
451                 public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
452                 {
453                         context = null;
454                         if (result == null)
455                                 throw new ArgumentNullException ("result");
456                         if (parameters == null)
457                                 throw new ArgumentNullException ("parameters");
458                         // FIXME: the method arguments should be verified to be 
459                         // identical to the arguments in the corresponding begin method.
460                         return _processDelegate.EndInvoke (result);
461                 }
462
463                 public object Process (MethodBase method, string operationName, object [] parameters)
464                 {
465                         try {
466                                 return DoProcess (method, operationName, parameters);
467                         } catch (Exception ex) {
468 #if MOONLIGHT // just for debugging
469                                 Console.Write ("Exception in async operation: ");
470                                 Console.WriteLine (ex);
471 #endif
472                                 throw;
473                         }
474                 }
475
476                 object DoProcess (MethodBase method, string operationName, object [] parameters)
477                 {
478                         if (AllowInitializationUI)
479                                 DisplayInitializationUI ();
480                         OperationDescription od = SelectOperation (method, operationName, parameters);
481
482                         if (State != CommunicationState.Opened)
483                                 Open ();
484
485                         if (!od.IsOneWay)
486                                 return Request (od, parameters);
487                         else {
488                                 Output (od, parameters);
489                                 return null;
490                         }
491                 }
492
493                 OperationDescription SelectOperation (MethodBase method, string operationName, object [] parameters)
494                 {
495                         string operation;
496                         if (Runtime.OperationSelector != null)
497                                 operation = Runtime.OperationSelector.SelectOperation (method, parameters);
498                         else
499                                 operation = operationName;
500                         OperationDescription od = contract.Operations.Find (operation);
501                         if (od == null)
502                                 throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
503                         return od;
504                 }
505
506                 void Output (OperationDescription od, object [] parameters)
507                 {
508                         ClientOperation op = runtime.Operations [od.Name];
509                         Send (CreateRequest (op, parameters), OperationTimeout);
510                 }
511
512                 object Request (OperationDescription od, object [] parameters)
513                 {
514                         ClientOperation op = runtime.Operations [od.Name];
515                         object [] inspections = new object [runtime.MessageInspectors.Count];
516                         Message req = CreateRequest (op, parameters);
517
518                         for (int i = 0; i < inspections.Length; i++)
519                                 inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
520
521                         Message res = Request (req, OperationTimeout);
522                         if (res.IsFault) {
523                                 var resb = res.CreateBufferedCopy (runtime.MaxFaultSize);
524                                 MessageFault fault = MessageFault.CreateFault (resb.CreateMessage (), runtime.MaxFaultSize);
525                                 var conv = OperationChannel.GetProperty<FaultConverter> () ?? FaultConverter.GetDefaultFaultConverter (res.Version);
526                                 Exception ex;
527                                 if (!conv.TryCreateException (resb.CreateMessage (), fault, out ex)) {
528                                         if (fault.HasDetail) {
529                                                 Type detailType = typeof (ExceptionDetail);
530                                                 var freader = fault.GetReaderAtDetailContents ();
531                                                 DataContractSerializer ds = null;
532 #if !NET_2_1
533                                                 foreach (var fci in op.FaultContractInfos)
534                                                         if (res.Headers.Action == fci.Action || fci.Serializer.IsStartObject (freader)) {
535                                                                 detailType = fci.Detail;
536                                                                 ds = fci.Serializer;
537                                                                 break;
538                                                         }
539 #endif
540                                                 if (ds == null)
541                                                         ds = new DataContractSerializer (detailType);
542                                                 var detail = ds.ReadObject (freader);
543                                                 ex = (Exception) Activator.CreateInstance (typeof (FaultException<>).MakeGenericType (detailType), new object [] {detail, fault.Reason, fault.Code, res.Headers.Action});
544                                         }
545
546                                         if (ex == null)
547                                                 ex = new FaultException (fault);
548                                 }
549                                 throw ex;
550                         }
551
552                         for (int i = 0; i < inspections.Length; i++)
553                                 runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
554
555                         if (op.DeserializeReply)
556                                 return op.Formatter.DeserializeReply (res, parameters);
557                         else
558                                 return res;
559                 }
560
561                 #region Message-based Request() and Send()
562                 // They are internal for ClientBase<T>.ChannelBase use.
563                 internal Message Request (Message msg, TimeSpan timeout)
564                 {
565                         if (RequestChannel != null)
566                                 return RequestChannel.Request (msg, timeout);
567                         else
568                                 return RequestCorrelated (msg, timeout, OutputChannel);
569                 }
570
571                 internal virtual Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
572                 {
573                         // FIXME: implement ConcurrencyMode check:
574                         // if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
575
576                         DateTime startTime = DateTime.Now;
577                         OutputChannel.Send (msg, timeout);
578                         return ((IDuplexChannel) channel).Receive (timeout - (DateTime.Now - startTime));
579                 }
580
581                 internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
582                 {
583                         return requestDelegate.BeginInvoke (msg, timeout, callback, state);
584                 }
585
586                 internal Message EndRequest (IAsyncResult result)
587                 {
588                         return requestDelegate.EndInvoke (result);
589                 }
590
591                 internal void Send (Message msg, TimeSpan timeout)
592                 {
593                         if (OutputChannel != null)
594                                 OutputChannel.Send (msg, timeout);
595                         else
596                                 RequestChannel.Request (msg, timeout); // and ignore returned message.
597                 }
598
599                 internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
600                 {
601                         return sendDelegate.BeginInvoke (msg, timeout, callback, state);
602                 }
603
604                 internal void EndSend (IAsyncResult result)
605                 {
606                         sendDelegate.EndInvoke (result);
607                 }
608                 #endregion
609
610                 Message CreateRequest (ClientOperation op, object [] parameters)
611                 {
612                         MessageVersion version = message_version;
613                         if (version == null)
614                                 version = MessageVersion.Default;
615
616                         Message msg;
617                         if (op.SerializeRequest)
618                                 msg = op.Formatter.SerializeRequest (
619                                         version, parameters);
620                         else {
621                                 if (parameters.Length != 1)
622                                         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));
623                                 if (!(parameters [0] is Message))
624                                         throw new ArgumentException (String.Format ("Argument should be only a Message, but has {0}", parameters [0] != null ? parameters [0].GetType ().FullName : "null"));
625                                 msg = (Message) parameters [0];
626                         }
627
628                         context = context ?? OperationContext.Current;
629                         if (context != null) {
630                                 // CopyHeadersFrom does not work here (brings duplicates -> error)
631                                 foreach (var mh in context.OutgoingMessageHeaders) {
632                                         int x = msg.Headers.FindHeader (mh.Name, mh.Namespace, mh.Actor);
633                                         if (x >= 0)
634                                                 msg.Headers.RemoveAt (x);
635                                         msg.Headers.Add ((MessageHeader) mh);
636                                 }
637                                 msg.Properties.CopyProperties (context.OutgoingMessageProperties);
638                         }
639
640                         // FIXME: disabling MessageId as it's not seen for bug #567672 case. But might be required for PeerDuplexChannel. Check it later.
641                         //if (OutputSession != null)
642                         //      msg.Headers.MessageId = new UniqueId (OutputSession.Id);
643                         msg.Properties.AllowOutputBatching = AllowOutputBatching;
644
645                         if (msg.Version.Addressing.Equals (AddressingVersion.WSAddressing10)) {
646                                 if (msg.Headers.MessageId == null)
647                                         msg.Headers.MessageId = new UniqueId ();
648                                 if (msg.Headers.ReplyTo == null)
649                                         msg.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
650                                 if (msg.Headers.To == null && RemoteAddress != null)
651                                         msg.Headers.To = RemoteAddress.Uri;
652                         }
653
654                         return msg;
655                 }
656
657                 #endregion
658         }
659 }