// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
+using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel.Channels;
namespace System.ServiceModel.MonoInternal
{
+#if DISABLE_REAL_PROXY
// FIXME: This is a quick workaround for bug #571907
- public class ClientRuntimeChannel
- : CommunicationObject, IClientChannel
+ public
+#endif
+ interface IInternalContextChannel
+ {
+ ContractDescription Contract { get; }
+
+ object Process (MethodBase method, string operationName, object [] parameters, OperationContext context);
+
+ IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState);
+
+ object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result);
+ }
+
+#if DISABLE_REAL_PROXY
+ // FIXME: This is a quick workaround for bug #571907
+ public
+#endif
+ class ClientRuntimeChannel
+ : CommunicationObject, IClientChannel, IInternalContextChannel
{
ClientRuntime runtime;
EndpointAddress remote_address;
TimeSpan default_open_timeout, default_close_timeout;
IChannel channel;
IChannelFactory factory;
- OperationContext context;
+ TimeSpan? operation_timeout = null;
+
#region delegates
readonly ProcessDelegate _processDelegate;
- delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
+ delegate object ProcessDelegate (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context);
readonly RequestDelegate requestDelegate;
public ClientRuntimeChannel (ServiceEndpoint endpoint,
ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
- : this (endpoint.CreateRuntime (), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
+ : this (endpoint.CreateClientRuntime (null), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
{
}
public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
{
+ if (runtime == null)
+ throw new ArgumentNullException ("runtime");
+ if (messageVersion == null)
+ throw new ArgumentNullException ("messageVersion");
this.runtime = runtime;
this.remote_address = remoteAddress;
- runtime.Via = via;
+ if (runtime.Via == null)
+ runtime.Via = via ?? (remote_address != null ?remote_address.Uri : null);
this.contract = contract;
this.message_version = messageVersion;
default_open_timeout = openTimeout;
// default values
AllowInitializationUI = true;
- OperationTimeout = TimeSpan.FromMinutes (1);
if (contextChannel != null)
channel = contextChannel;
else {
var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
- channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
- this.factory = factory;
+ try {
+ channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
+ this.factory = factory;
+ } catch (TargetInvocationException ex) {
+ if (ex.InnerException != null)
+ throw ex.InnerException;
+ else
+ throw;
+ }
}
}
}
}
-#if !MOONLIGHT
public override bool WaitOne (int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
{
return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
}
-#endif
}
class DisplayUIAsyncResult : IAsyncResult
}
}
- [MonoTODO]
- public TimeSpan OperationTimeout { get; set; }
+ public TimeSpan OperationTimeout {
+ get {
+ if (!this.operation_timeout.HasValue) {
+ this.operation_timeout = DefaultCommunicationTimeouts.Instance.ReceiveTimeout;
+ }
+ return this.operation_timeout.Value;
+ }
+ set {
+ this.operation_timeout = value;
+ }
+ }
public IOutputSession OutputSession {
get {
protected override void OnClose (TimeSpan timeout)
{
DateTime start = DateTime.Now;
- channel.Close (timeout);
+ if (channel.State == CommunicationState.Opened)
+ channel.Close (timeout);
}
Action<TimeSpan> open_callback;
public T GetProperty<T> () where T : class
{
+ if (typeof (T) == typeof (MessageVersion))
+ return (T) (object) message_version;
return OperationChannel.GetProperty<T> ();
}
public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
{
- if (context != null)
- throw new InvalidOperationException ("another operation is in progress");
- context = OperationContext.Current;
- return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
+ var p = parameters;
+ var retval = _processDelegate.BeginInvoke (method, operationName, true, ref p, OperationContext.Current, callback, asyncState);
+ if (p != parameters)
+ throw new InvalidOperationException ();
+ return retval;
}
public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
{
- context = null;
if (result == null)
throw new ArgumentNullException ("result");
if (parameters == null)
throw new ArgumentNullException ("parameters");
- // FIXME: the method arguments should be verified to be
- // identical to the arguments in the corresponding begin method.
- return _processDelegate.EndInvoke (result);
+
+ object[] p = parameters;
+ var retval = _processDelegate.EndInvoke (ref p, result);
+ if (p == parameters)
+ return retval;
+
+ if (p.Length != parameters.Length)
+ throw new InvalidOperationException ();
+ Array.Copy (p, parameters, p.Length);
+ return retval;
+ }
+
+ public object Process (MethodBase method, string operationName, object [] parameters, OperationContext context)
+ {
+ var p = parameters;
+ var retval = Process (method, operationName, false, ref p, context);
+ if (p != parameters)
+ throw new InvalidOperationException ();
+ return retval;
}
- public object Process (MethodBase method, string operationName, object [] parameters)
+ object Process (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
{
+ var previousContext = OperationContext.Current;
try {
- return DoProcess (method, operationName, parameters);
+ // Inherit the context from the calling thread
+ OperationContext.Current = context;
+
+ return DoProcess (method, operationName, isAsync, ref parameters, context);
} catch (Exception ex) {
-#if MOONLIGHT // just for debugging
- Console.Write ("Exception in async operation: ");
- Console.WriteLine (ex);
-#endif
throw;
+ } finally {
+ // Reset the context before the thread goes back into the pool
+ OperationContext.Current = previousContext;
}
}
- object DoProcess (MethodBase method, string operationName, object [] parameters)
+ object DoProcess (MethodBase method, string operationName, bool isAsync, ref object [] parameters, OperationContext context)
{
if (AllowInitializationUI)
DisplayInitializationUI ();
Open ();
if (!od.IsOneWay)
- return Request (od, parameters);
+ return Request (od, isAsync, ref parameters, context);
else {
- Output (od, parameters);
+ Output (od, parameters, context);
return null;
}
}
return od;
}
- void Output (OperationDescription od, object [] parameters)
+ void Output (OperationDescription od, object [] parameters, OperationContext context)
{
ClientOperation op = runtime.Operations [od.Name];
- Send (CreateRequest (op, parameters), OperationTimeout);
+ Send (CreateRequest (op, parameters, context), OperationTimeout);
}
- object Request (OperationDescription od, object [] parameters)
+ object Request (OperationDescription od, bool isAsync, ref object [] parameters, OperationContext context)
{
ClientOperation op = runtime.Operations [od.Name];
object [] inspections = new object [runtime.MessageInspectors.Count];
- Message req = CreateRequest (op, parameters);
+ Message req = CreateRequest (op, parameters, context);
for (int i = 0; i < inspections.Length; i++)
inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
Type detailType = typeof (ExceptionDetail);
var freader = fault.GetReaderAtDetailContents ();
DataContractSerializer ds = null;
-#if !NET_2_1
foreach (var fci in op.FaultContractInfos)
if (res.Headers.Action == fci.Action || fci.Serializer.IsStartObject (freader)) {
detailType = fci.Detail;
ds = fci.Serializer;
break;
}
-#endif
if (ds == null)
ds = new DataContractSerializer (detailType);
var detail = ds.ReadObject (freader);
for (int i = 0; i < inspections.Length; i++)
runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
- if (op.DeserializeReply)
- return op.GetFormatter ().DeserializeReply (res, parameters);
- else
+ if (!op.DeserializeReply)
return res;
+
+ if (isAsync && od.EndMethod != null) {
+ var endParams = od.EndMethod.GetParameters ();
+ parameters = new object [endParams.Length - 1];
+ }
+
+ return op.Formatter.DeserializeReply (res, parameters);
}
#region Message-based Request() and Send()
{
if (RequestChannel != null)
return RequestChannel.Request (msg, timeout);
- else {
- DateTime startTime = DateTime.Now;
- OutputChannel.Send (msg, timeout);
- return ((IDuplexChannel) OutputChannel).Receive (timeout - (DateTime.Now - startTime));
- }
+ else
+ return RequestCorrelated (msg, timeout, OutputChannel);
+ }
+
+ internal virtual Message RequestCorrelated (Message msg, TimeSpan timeout, IOutputChannel channel)
+ {
+ // FIXME: implement ConcurrencyMode check:
+ // if it is .Single && this instance for a callback channel && the operation is invoked inside service operation, then error.
+
+ DateTime startTime = DateTime.Now;
+ OutputChannel.Send (msg, timeout);
+ return ((IDuplexChannel) channel).Receive (timeout - (DateTime.Now - startTime));
}
internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
internal void Send (Message msg, TimeSpan timeout)
{
- OutputChannel.Send (msg, timeout);
+ if (OutputChannel != null)
+ OutputChannel.Send (msg, timeout);
+ else
+ RequestChannel.Request (msg, timeout); // and ignore returned message.
}
internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
}
#endregion
- Message CreateRequest (ClientOperation op, object [] parameters)
+ Message CreateRequest (ClientOperation op, object [] parameters, OperationContext context)
{
MessageVersion version = message_version;
if (version == null)
Message msg;
if (op.SerializeRequest)
- msg = op.GetFormatter ().SerializeRequest (
+ msg = op.Formatter.SerializeRequest (
version, parameters);
else {
if (parameters.Length != 1)
msg.Properties.CopyProperties (context.OutgoingMessageProperties);
}
- if (OutputSession != null)
- msg.Headers.MessageId = new UniqueId (OutputSession.Id);
+ // FIXME: disabling MessageId as it's not seen for bug #567672 case. But might be required for PeerDuplexChannel. Check it later.
+ //if (OutputSession != null)
+ // msg.Headers.MessageId = new UniqueId (OutputSession.Id);
msg.Properties.AllowOutputBatching = AllowOutputBatching;
if (msg.Version.Addressing.Equals (AddressingVersion.WSAddressing10)) {
msg.Headers.MessageId = new UniqueId ();
if (msg.Headers.ReplyTo == null)
msg.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
- if (msg.Headers.To == null)
+ if (msg.Headers.To == null && RemoteAddress != null)
msg.Headers.To = RemoteAddress.Uri;
}