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