c3e5cd605e1b460a3096c9f01d6c6bb723ee675f
[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.Reflection;
30 using System.ServiceModel.Channels;
31 using System.ServiceModel.Description;
32 using System.ServiceModel.Dispatcher;
33 using System.ServiceModel.Security;
34
35 namespace System.ServiceModel
36 {
37 #if TARGET_DOTNET
38         [MonoTODO]
39         public
40 #else
41         internal
42 #endif
43         class ClientRuntimeChannel
44                 : CommunicationObject, IClientChannel
45         {
46                 ClientRuntime runtime;
47                 ChannelFactory factory;
48                 IRequestChannel request_channel;
49                 IOutputChannel output_channel;
50                 readonly ProcessDelegate _processDelegate;
51
52                 delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
53
54                 public ClientRuntimeChannel (ClientRuntime runtime,
55                         ChannelFactory factory)
56                 {
57                         this.runtime = runtime;
58                         this.factory = factory;
59                         _processDelegate = new ProcessDelegate (Process);
60                 }
61
62                 public ClientRuntime Runtime {
63                         get { return runtime; }
64                 }
65
66                 #region IClientChannel
67
68                 [MonoTODO]
69                 public bool AllowInitializationUI {
70                         get { throw new NotImplementedException (); }
71                         set { throw new NotImplementedException (); }
72                 }
73
74                 [MonoTODO]
75                 public bool DidInteractiveInitialization {
76                         get { throw new NotImplementedException (); }
77                 }
78
79                 public Uri Via {
80                         get { return runtime.Via; }
81                 }
82
83                 [MonoTODO]
84                 public IAsyncResult BeginDisplayInitializationUI (
85                         AsyncCallback callback, object state)
86                 {
87                         throw new NotImplementedException ();
88                 }
89
90                 [MonoTODO]
91                 public void EndDisplayInitializationUI (
92                         IAsyncResult result)
93                 {
94                         throw new NotImplementedException ();
95                 }
96
97                 [MonoTODO]
98                 public void DisplayInitializationUI ()
99                 {
100                         throw new NotImplementedException ();
101                 }
102
103                 public void Dispose ()
104                 {
105                         Close ();
106                 }
107
108                 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
109
110                 #endregion
111
112                 #region IContextChannel
113
114                 [MonoTODO]
115                 public bool AllowOutputBatching {
116                         get { throw new NotImplementedException (); }
117                         set { throw new NotImplementedException (); }
118                 }
119
120                 [MonoTODO]
121                 public IInputSession InputSession {
122                         get { throw new NotImplementedException (); }
123                 }
124
125                 [MonoTODO]
126                 public EndpointAddress LocalAddress {
127                         get { throw new NotImplementedException (); }
128                 }
129
130                 [MonoTODO]
131                 public TimeSpan OperationTimeout {
132                         get { throw new NotImplementedException (); }
133                         set { throw new NotImplementedException (); }
134                 }
135
136                 [MonoTODO]
137                 public IOutputSession OutputSession {
138                         get { throw new NotImplementedException (); }
139                 }
140
141                 [MonoTODO]
142                 public EndpointAddress RemoteAddress {
143                         get { throw new NotImplementedException (); }
144                 }
145
146                 [MonoTODO]
147                 public string SessionId {
148                         get { throw new NotImplementedException (); }
149                 }
150
151                 #endregion
152
153                 // CommunicationObject
154                 protected internal override TimeSpan DefaultOpenTimeout {
155                         get { return factory.DefaultOpenTimeout; }
156                 }
157
158                 protected internal override TimeSpan DefaultCloseTimeout {
159                         get { return factory.DefaultCloseTimeout; }
160                 }
161
162                 protected override void OnAbort ()
163                 {
164                         factory.Abort ();
165                 }
166
167                 protected override IAsyncResult OnBeginClose (
168                         TimeSpan timeout, AsyncCallback callback, object state)
169                 {
170                         return factory.BeginClose (timeout, callback, state);
171                 }
172
173                 protected override void OnEndClose (IAsyncResult result)
174                 {
175                         factory.EndClose (result);
176                 }
177
178                 protected override void OnClose (TimeSpan timeout)
179                 {
180                         factory.Close (timeout);
181                 }
182
183                 protected override IAsyncResult OnBeginOpen (
184                         TimeSpan timeout, AsyncCallback callback, object state)
185                 {
186                         throw new SystemException ("INTERNAL ERROR: this should not be called (or not supported yet)");
187                 }
188
189                 protected override void OnEndOpen (IAsyncResult result)
190                 {
191                 }
192
193                 protected override void OnOpen (TimeSpan timeout)
194                 {
195                 }
196
197                 // IChannel
198                 public T GetProperty<T> () where T : class
199                 {
200                         return factory.GetProperty<T> ();
201                 }
202
203                 // IExtensibleObject<IContextChannel>
204                 [MonoTODO]
205                 public IExtensionCollection<IContextChannel> Extensions {
206                         get { throw new NotImplementedException (); }
207                 }
208
209                 #region Request/Output processing
210
211                 public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters) {
212                         object [] param = new object [parameters.Length - 2];
213                         for (int i = 0; i < param.Length; i++)
214                                 param [i] = parameters [i];
215                         return _processDelegate.BeginInvoke (method, operationName, param, (AsyncCallback) parameters [parameters.Length - 2], parameters [parameters.Length - 1]);
216                 }
217
218                 public object EndProcess (MethodBase method, string operationName, object [] parameters) {
219                         return _processDelegate.EndInvoke ((IAsyncResult) parameters [0]);
220                 }
221
222                 public object Process (MethodBase method, string operationName, object [] parameters)
223                 {
224                         OperationDescription od = SelectOperation (method, operationName, parameters);
225                         if (!od.IsOneWay)
226                                 return Request (od, parameters);
227                         else {
228                                 Output (od, parameters);
229                                 return null;
230                         }
231                 }
232
233                 OperationDescription SelectOperation (MethodBase method, string operationName, object [] parameters)
234                 {
235                         string operation;
236                         if (Runtime.OperationSelector != null)
237                                 operation = Runtime.OperationSelector.SelectOperation (method, parameters);
238                         else
239                                 operation = operationName;
240                         OperationDescription od = factory.Endpoint.Contract.Operations.Find (operation);
241                         if (od == null)
242                                 throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
243                         return od;
244                 }
245
246                 BindingParameterCollection CreateBindingParameters ()
247                 {
248                         BindingParameterCollection pl =
249                                 new BindingParameterCollection ();
250
251                         ContractDescription cd = factory.Endpoint.Contract;
252 #if !NET_2_1
253                         pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
254
255                         foreach (IEndpointBehavior behavior in factory.Endpoint.Behaviors)
256                                 behavior.AddBindingParameters (factory.Endpoint, pl);
257 #endif
258
259                         return pl;
260                 }
261
262                 void SetupOutputChannel ()
263                 {
264                         if (output_channel != null)
265                                 return;
266                         BindingParameterCollection pl =
267                                 CreateBindingParameters ();
268                         bool session = false;
269                         switch (factory.Endpoint.Contract.SessionMode) {
270                         case SessionMode.Required:
271                                 session = factory.Endpoint.Binding.CanBuildChannelFactory<IOutputSessionChannel> (pl);
272                                 if (!session)
273                                         throw new InvalidOperationException ("The contract requires session support, but the binding does not support it.");
274                                 break;
275                         case SessionMode.Allowed:
276                                 session = !factory.Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl);
277                                 break;
278                         }
279
280                         EndpointAddress address = factory.Endpoint.Address;
281                         Uri via = Runtime.Via;
282
283                         if (session) {
284                                 IChannelFactory<IOutputSessionChannel> f =
285                                         factory.Endpoint.Binding.BuildChannelFactory<IOutputSessionChannel> (pl);
286                                 f.Open ();
287                                 output_channel = f.CreateChannel (address, via);
288                         } else {
289                                 IChannelFactory<IOutputChannel> f =
290                                         factory.Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
291                                 f.Open ();
292                                 output_channel = f.CreateChannel (address, via);
293                         }
294
295                         output_channel.Open ();
296                 }
297
298                 void SetupRequestChannel ()
299                 {
300                         if (request_channel != null)
301                                 return;
302
303                         BindingParameterCollection pl =
304                                 CreateBindingParameters ();
305                         bool session = false;
306                         switch (factory.Endpoint.Contract.SessionMode) {
307                         case SessionMode.Required:
308                                 session = factory.Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl);
309                                 if (!session)
310                                         throw new InvalidOperationException ("The contract requires session support, but the binding does not support it.");
311                                 break;
312                         case SessionMode.Allowed:
313                                 session = !factory.Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl);
314                                 break;
315                         }
316
317                         EndpointAddress address = factory.Endpoint.Address;
318                         Uri via = Runtime.Via;
319
320                         if (session) {
321                                 IChannelFactory<IRequestSessionChannel> f =
322                                         factory.Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
323                                 f.Open ();
324                                 request_channel = f.CreateChannel (address, via);
325                         } else {
326                                 IChannelFactory<IRequestChannel> f =
327                                         factory.Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
328                                 f.Open ();
329                                 request_channel = f.CreateChannel (address, via);
330                         }
331
332                         request_channel.Open ();
333                 }
334
335                 void Output (OperationDescription od, object [] parameters)
336                 {
337                         SetupOutputChannel ();
338
339                         ClientOperation op = runtime.Operations [od.Name];
340                         Output (CreateRequest (op, parameters));
341                 }
342
343                 object Request (OperationDescription od, object [] parameters)
344                 {
345                         SetupRequestChannel ();
346
347                         ClientOperation op = runtime.Operations [od.Name];
348                         object [] inspections = new object [runtime.MessageInspectors.Count];
349                         Message req = CreateRequest (op, parameters);
350
351                         for (int i = 0; i < inspections.Length; i++)
352                                 inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
353
354                         Message res = Request (req);
355                         if (res.IsFault) {
356                                 MessageFault fault = MessageFault.CreateFault (res, runtime.MaxFaultSize);
357                                 if (fault.HasDetail && fault is MessageFault.SimpleMessageFault) {
358                                         MessageFault.SimpleMessageFault simpleFault = fault as MessageFault.SimpleMessageFault;
359                                         object detail = simpleFault.Detail;
360                                         Type t = detail.GetType ();
361                                         Type faultType = typeof (FaultException<>).MakeGenericType (t);
362                                         object [] constructorParams = new object [] { detail, fault.Reason, fault.Code, fault.Actor };
363                                         FaultException fe = (FaultException) Activator.CreateInstance (faultType, constructorParams);
364                                         throw fe;
365                                 }
366                                 else {
367                                         // given a MessageFault, it is hard to figure out the type of the embedded detail
368                                         throw new FaultException(fault);
369                                 }
370                         }
371
372                         for (int i = 0; i < inspections.Length; i++)
373                                 runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
374
375                         if (op.DeserializeReply)
376                                 return op.GetFormatter ().DeserializeReply (res, parameters);
377                         else
378                                 return res;
379                 }
380
381                 Message Request (Message msg)
382                 {
383                         return request_channel.Request (msg, factory.Endpoint.Binding.SendTimeout);
384                 }
385
386                 void Output (Message msg)
387                 {
388                         output_channel.Send (msg, factory.Endpoint.Binding.SendTimeout);
389                 }
390
391                 Message CreateRequest (ClientOperation op, object [] parameters)
392                 {
393                         MessageVersion version = factory.Endpoint.Binding.MessageVersion;
394                         if (version == null)
395                                 version = MessageVersion.Default;
396
397                         if (op.SerializeRequest)
398                                 return op.GetFormatter ().SerializeRequest (
399                                         version, parameters);
400                         else
401                                 return (Message) parameters [0];
402                 }
403
404                 #endregion
405         }
406 }