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