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 
Process(MethodBase method, string operationName, object [] parameters, OperationContext context)49 		object Process (MethodBase method, string operationName, object [] parameters, OperationContext context);
50 
BeginProcess(MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)51 		IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
52 
EndProcess(MethodBase method, string operationName, object [] parameters, IAsyncResult result)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 		TimeSpan? operation_timeout = null;
71 		ChannelFactory channel_factory;
72 
73 		#region delegates
74 		readonly ProcessDelegate _processDelegate;
75 
ProcessDelegate(MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)76 		delegate object ProcessDelegate (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context);
77 
78 		readonly RequestDelegate requestDelegate;
79 
RequestDelegate(Message msg, TimeSpan timeout)80 		delegate Message RequestDelegate (Message msg, TimeSpan timeout);
81 
82 		readonly SendDelegate sendDelegate;
83 
SendDelegate(Message msg, TimeSpan timeout)84 		delegate void SendDelegate (Message msg, TimeSpan timeout);
85 		#endregion
86 
ClientRuntimeChannel(ServiceEndpoint endpoint, ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)87 		public ClientRuntimeChannel (ServiceEndpoint endpoint,
88 			ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
89 			: this (endpoint.CreateClientRuntime (null), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
90 		{
91 			channel_factory = channelFactory;
92 		}
93 
ClientRuntimeChannel(ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)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 
115 			if (contextChannel != null)
116 				channel = contextChannel;
117 			else {
118 				var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
119 				try {
120 					channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
121 					this.factory = factory;
122 				} catch (TargetInvocationException ex) {
123 					if (ex.InnerException != null)
124 						throw ex.InnerException;
125 					else
126 						throw;
127 				}
128 			}
129 		}
130 
131 		public ContractDescription Contract {
132 			get { return contract; }
133 		}
134 
135 		public ClientRuntime Runtime {
136 			get { return runtime; }
137 		}
138 
139 		IRequestChannel RequestChannel {
140 			get { return channel as IRequestChannel; }
141 		}
142 
143 		IOutputChannel OutputChannel {
144 			get { return channel as IOutputChannel; }
145 		}
146 
147 		internal IDuplexChannel DuplexChannel {
148 			get { return channel as IDuplexChannel; }
149 		}
150 
151 		#region IClientChannel
152 
153 		bool did_interactive_initialization;
154 
155 		public bool AllowInitializationUI { get; set; }
156 
157 		public bool DidInteractiveInitialization {
158 			get { return did_interactive_initialization; }
159 		}
160 
161 		public Uri Via {
162 			get { return runtime.Via; }
163 		}
164 
165 		class DelegatingWaitHandle : WaitHandle
166 		{
DelegatingWaitHandle(IAsyncResult [] results)167 			public DelegatingWaitHandle (IAsyncResult [] results)
168 			{
169 				this.results = results;
170 			}
171 
172 			IAsyncResult [] results;
173 
Dispose(bool disposing)174 			protected override void Dispose (bool disposing)
175 			{
176 				if (disposing)
177 					foreach (var r in results)
178 						r.AsyncWaitHandle.Close ();
179 			}
180 
WaitOne()181 			public override bool WaitOne ()
182 			{
183 				foreach (var r in results)
184 					r.AsyncWaitHandle.WaitOne ();
185 				return true;
186 			}
187 
WaitOne(int millisecondsTimeout)188 			public override bool WaitOne (int millisecondsTimeout)
189 			{
190 				return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout);
191 			}
192 
193 			WaitHandle [] ResultWaitHandles {
194 				get {
195 					var arr = new WaitHandle [results.Length];
196 					for (int i = 0; i < arr.Length; i++)
197 						arr [i] = results [i].AsyncWaitHandle;
198 					return arr;
199 				}
200 			}
201 
WaitOne(int millisecondsTimeout, bool exitContext)202 			public override bool WaitOne (int millisecondsTimeout, bool exitContext)
203 			{
204 				return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
205 			}
206 
WaitOne(TimeSpan timeout, bool exitContext)207 			public override bool WaitOne (TimeSpan timeout, bool exitContext)
208 			{
209 				return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
210 			}
211 		}
212 
213 		class DisplayUIAsyncResult : IAsyncResult
214 		{
DisplayUIAsyncResult(IAsyncResult [] results)215 			public DisplayUIAsyncResult (IAsyncResult [] results)
216 			{
217 				this.results = results;
218 			}
219 
220 			IAsyncResult [] results;
221 
222 			internal IAsyncResult [] Results {
223 				get { return results; }
224 			}
225 
226 			public object AsyncState {
227 				get { return null; }
228 			}
229 
230 			WaitHandle wait_handle;
231 
232 			public WaitHandle AsyncWaitHandle {
233 				get {
234 					if (wait_handle == null)
235 						wait_handle = new DelegatingWaitHandle (results);
236 					return wait_handle;
237 				}
238 			}
239 
240 			public bool CompletedSynchronously {
241 				get {
242 					foreach (var r in results)
243 						if (!r.CompletedSynchronously)
244 							return false;
245 					return true;
246 				}
247 			}
248 			public bool IsCompleted {
249 				get {
250 					foreach (var r in results)
251 						if (!r.IsCompleted)
252 							return false;
253 					return true;
254 				}
255 			}
256 		}
257 
BeginDisplayInitializationUI( AsyncCallback callback, object state)258 		public IAsyncResult BeginDisplayInitializationUI (
259 			AsyncCallback callback, object state)
260 		{
261 			OnInitializationUI ();
262 			IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
263 			int i = 0;
264 			foreach (var init in runtime.InteractiveChannelInitializers)
265 				arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
266 			return new DisplayUIAsyncResult (arr);
267 		}
268 
EndDisplayInitializationUI( IAsyncResult result)269 		public void EndDisplayInitializationUI (
270 			IAsyncResult result)
271 		{
272 			DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
273 			int i = 0;
274 			foreach (var init in runtime.InteractiveChannelInitializers)
275 				init.EndDisplayInitializationUI (r.Results [i++]);
276 
277 			did_interactive_initialization = true;
278 		}
279 
DisplayInitializationUI()280 		public void DisplayInitializationUI ()
281 		{
282 			OnInitializationUI ();
283 			foreach (var init in runtime.InteractiveChannelInitializers)
284 				init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
285 
286 			did_interactive_initialization = true;
287 		}
288 
OnInitializationUI()289 		void OnInitializationUI ()
290 		{
291 			if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
292 				throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
293 		}
294 
Dispose()295 		public void Dispose ()
296 		{
297 			Close ();
298 		}
299 
300 		public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
301 
302 		#endregion
303 
304 		#region IContextChannel
305 
306 		[MonoTODO]
307 		public bool AllowOutputBatching { get; set; }
308 
309 		public IInputSession InputSession {
310 			get {
311 				ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
312 				ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
313 				if (ch != null)
314 					return ch.Session;
315 				var dch = OutputChannel as ISessionChannel<IDuplexSession>;
316 				return dch != null ? dch.Session : null;
317 			}
318 		}
319 
320 		public EndpointAddress LocalAddress {
321 			get {
322 				var dc = OperationChannel as IDuplexChannel;
323 				return dc != null ? dc.LocalAddress : null;
324 			}
325 		}
326 
327 		public TimeSpan OperationTimeout {
328 			get { return this.operation_timeout ?? (channel_factory != null ? channel_factory.Endpoint.Binding.SendTimeout : DefaultCommunicationTimeouts.Instance.SendTimeout); }
329 			set { this.operation_timeout = value; }
330 		}
331 
332 		public IOutputSession OutputSession {
333 			get {
334 				ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
335 				ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
336 				if (ch != null)
337 					return ch.Session;
338 				var dch = OutputChannel as ISessionChannel<IDuplexSession>;
339 				return dch != null ? dch.Session : null;
340 			}
341 		}
342 
343 		public EndpointAddress RemoteAddress {
344 			get { return RequestChannel != null ? RequestChannel.RemoteAddress : OutputChannel.RemoteAddress; }
345 		}
346 
347 		public string SessionId {
348 			get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
349 		}
350 
351 		#endregion
352 
353 		// CommunicationObject
354 		protected internal override TimeSpan DefaultOpenTimeout {
355 			get { return default_open_timeout; }
356 		}
357 
358 		protected internal override TimeSpan DefaultCloseTimeout {
359 			get { return default_close_timeout; }
360 		}
361 
OnAbort()362 		protected override void OnAbort ()
363 		{
364 			channel.Abort ();
365 			if (factory != null) // ... is it valid?
366 				factory.Abort ();
367 		}
368 
369 		Action<TimeSpan> close_delegate;
370 
OnBeginClose( TimeSpan timeout, AsyncCallback callback, object state)371 		protected override IAsyncResult OnBeginClose (
372 			TimeSpan timeout, AsyncCallback callback, object state)
373 		{
374 			if (close_delegate == null)
375 				close_delegate = new Action<TimeSpan> (OnClose);
376 			return close_delegate.BeginInvoke (timeout, callback, state);
377 		}
378 
OnEndClose(IAsyncResult result)379 		protected override void OnEndClose (IAsyncResult result)
380 		{
381 			close_delegate.EndInvoke (result);
382 		}
383 
OnClose(TimeSpan timeout)384 		protected override void OnClose (TimeSpan timeout)
385 		{
386 			if (channel.State == CommunicationState.Opened)
387 				channel.Close (timeout);
388 		}
389 
390 		Action<TimeSpan> open_callback;
391 
OnBeginOpen( TimeSpan timeout, AsyncCallback callback, object state)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 
OnEndOpen(IAsyncResult result)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 
OnOpen(TimeSpan timeout)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 
BeginProcess(MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)442 		public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
443 		{
444 			var p = parameters;
445 			var retval = _processDelegate.BeginInvoke (method, operationName, true, ref p, OperationContext.Current, callback, asyncState);
446 			if (p != parameters)
447 				throw new InvalidOperationException ();
448 			return retval;
449 		}
450 
EndProcess(MethodBase method, string operationName, object [] parameters, IAsyncResult result)451 		public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
452 		{
453 			if (result == null)
454 				throw new ArgumentNullException ("result");
455 			if (parameters == null)
456 				throw new ArgumentNullException ("parameters");
457 
458 			object[] p = parameters;
459 			var retval = _processDelegate.EndInvoke (ref p, result);
460 			if (p == parameters)
461 				return retval;
462 
463 			Array.Copy (p, parameters, p.Length);
464 			return retval;
465 		}
466 
Process(MethodBase method, string operationName, object [] parameters, OperationContext context)467 		public object Process (MethodBase method, string operationName, object [] parameters, OperationContext context)
468 		{
469 			var p = parameters;
470 			var retval = Process (method, operationName, false, ref p, context);
471 			if (p != parameters)
472 				throw new InvalidOperationException ();
473 			return retval;
474 		}
475 
Process(MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)476 		object Process (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
477 		{
478 			var previousContext = OperationContext.Current;
479 			try {
480 				// Inherit the context from the calling thread
481 				OperationContext.Current = context;
482 
483 				return DoProcess (method, operationName, isAsync, ref parameters, context);
484 			} catch (Exception ex) {
485 				throw;
486 			} finally {
487 				// Reset the context before the thread goes back into the pool
488 				OperationContext.Current = previousContext;
489 			}
490 		}
491 
DoProcess(MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)492 		object DoProcess (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
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, isAsync, ref parameters, context);
503 			else {
504 				Output (od, parameters, context);
505 				return null;
506 			}
507 		}
508 
SelectOperation(MethodBase method, string operationName, object [] parameters)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 
Output(OperationDescription od, object [] parameters, OperationContext context)522 		void Output (OperationDescription od, object [] parameters, OperationContext context)
523 		{
524 			ClientOperation op = runtime.Operations [od.Name];
525 			Send (CreateRequest (op, parameters, context), OperationTimeout);
526 		}
527 
Request(OperationDescription od, bool isAsync, ref object [] parameters, OperationContext context)528 		object Request (OperationDescription od, bool isAsync, ref object [] parameters, OperationContext context)
529 		{
530 			ClientOperation op = runtime.Operations [od.Name];
531 			object [] inspections = new object [runtime.MessageInspectors.Count];
532 			Message req = CreateRequest (op, parameters, context);
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 res;
571 
572 			if (isAsync && od.EndMethod != null) {
573 				var endParams = od.EndMethod.GetParameters ();
574 				parameters = new object [endParams.Length - 1];
575 			}
576 
577 			return op.Formatter.DeserializeReply (res, parameters);
578 		}
579 
580 		#region Message-based Request() and Send()
581 		// They are internal for ClientBase<T>.ChannelBase use.
Request(Message msg, TimeSpan timeout)582 		internal Message Request (Message msg, TimeSpan timeout)
583 		{
584 			if (RequestChannel != null)
585 				return RequestChannel.Request (msg, timeout);
586 			else
587 				return RequestCorrelated (msg, timeout, OutputChannel);
588 		}
589 
RequestCorrelated(Message msg, TimeSpan timeout, IOutputChannel channel)590 		internal virtual Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
591 		{
592 			// FIXME: implement ConcurrencyMode check:
593 			// if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
594 
595 			DateTime startTime = DateTime.UtcNow;
596 			OutputChannel.Send (msg, timeout);
597 			return ((IDuplexChannel) channel).Receive (timeout - (DateTime.UtcNow - startTime));
598 		}
599 
BeginRequest(Message msg, TimeSpan timeout, AsyncCallback callback, object state)600 		internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
601 		{
602 			return requestDelegate.BeginInvoke (msg, timeout, callback, state);
603 		}
604 
EndRequest(IAsyncResult result)605 		internal Message EndRequest (IAsyncResult result)
606 		{
607 			return requestDelegate.EndInvoke (result);
608 		}
609 
Send(Message msg, TimeSpan timeout)610 		internal void Send (Message msg, TimeSpan timeout)
611 		{
612 			if (OutputChannel != null)
613 				OutputChannel.Send (msg, timeout);
614 			else
615 				RequestChannel.Request (msg, timeout); // and ignore returned message.
616 		}
617 
BeginSend(Message msg, TimeSpan timeout, AsyncCallback callback, object state)618 		internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
619 		{
620 			return sendDelegate.BeginInvoke (msg, timeout, callback, state);
621 		}
622 
EndSend(IAsyncResult result)623 		internal void EndSend (IAsyncResult result)
624 		{
625 			sendDelegate.EndInvoke (result);
626 		}
627 		#endregion
628 
CreateRequest(ClientOperation op, object [] parameters, OperationContext context)629 		Message CreateRequest (ClientOperation op, object [] parameters, OperationContext context)
630 		{
631 			MessageVersion version = message_version;
632 			if (version == null)
633 				version = MessageVersion.Default;
634 
635 			Message msg;
636 			if (op.SerializeRequest)
637 				msg = op.Formatter.SerializeRequest (
638 					version, parameters);
639 			else {
640 				if (parameters.Length != 1)
641 					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));
642 				if (!(parameters [0] is Message))
643 					throw new ArgumentException (String.Format ("Argument should be only a Message, but has {0}", parameters [0] != null ? parameters [0].GetType ().FullName : "null"));
644 				msg = (Message) parameters [0];
645 			}
646 
647 			context = context ?? OperationContext.Current;
648 			if (context != null) {
649 				// CopyHeadersFrom does not work here (brings duplicates -> error)
650 				foreach (var mh in context.OutgoingMessageHeaders) {
651 					int x = msg.Headers.FindHeader (mh.Name, mh.Namespace, mh.Actor);
652 					if (x >= 0)
653 						msg.Headers.RemoveAt (x);
654 					msg.Headers.Add ((MessageHeader) mh);
655 				}
656 				msg.Properties.CopyProperties (context.OutgoingMessageProperties);
657 			}
658 
659 			// FIXME: disabling MessageId as it's not seen for bug #567672 case. But might be required for PeerDuplexChannel. Check it later.
660 			//if (OutputSession != null)
661 			//	msg.Headers.MessageId = new UniqueId (OutputSession.Id);
662 			msg.Properties.AllowOutputBatching = AllowOutputBatching;
663 
664 			if (msg.Version.Addressing.Equals (AddressingVersion.WSAddressing10)) {
665 				if (msg.Headers.MessageId == null)
666 					msg.Headers.MessageId = new UniqueId ();
667 				if (msg.Headers.ReplyTo == null)
668 					msg.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
669 				if (msg.Headers.To == null && RemoteAddress != null)
670 					msg.Headers.To = RemoteAddress.Uri;
671 			}
672 
673 			return msg;
674 		}
675 
676 		#endregion
677 	}
678 }
679