Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Renewing a WCF client when SCT has expired?

I am using WCF to a soap endpoint using security mode "TransportWithMessageCredential".

The WCF client/server uses SCT (Security Context Token) to maintain a secure connection, and it is working as intended in the general case.

However, after a period of inactivity, the SCT will be expired and the next method call will cause a MessageSecurityException:

An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail

Inner exception:

The message could not be processed. This is most likely because the action 'http://tempuri.org/IMyService/MyMethod' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.

On subsequent calls, I renew the connection when I see that CommunicationState is Faulted. But I cannot find a way to preemptively check whether the SCT has expired before making my method call.

like image 805
mbp Avatar asked Jan 26 '09 12:01

mbp


2 Answers

You could resolve your issues by using delegates. This will allow to safely invoke the action and, if fails, catch the exception, construct a new service instance and perform the action again.

using System;
using System.ServiceModel;
using System.ServiceModel.Security;

public static class Service
{
    private static IService _service;

    public static void Invoke(Action<IService> action)
    {
        try
        {
            action(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateFreshInstance();

            action(_service);
        }           
    }
}

You could then call your helper class like Service.Invoke(s => s.Method()); to invoke the IService.Method().

like image 86
Troels Thomsen Avatar answered Oct 31 '22 20:10

Troels Thomsen


Building on the first answer, I came up with this solution that generically wraps the auto-generated client proxies created by svcutil.exe:

public class ProxyWrapper<T> where T : ICommunicationObject
{
    private T _service;

    public ProxyWrapper()
    {
        _service = CreateNewInstance();
    }

    public void Invoke(Action<T> action)
    {
        try
        {
            action(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            action(_service);
        }
    }

    public TResult Invoke<TResult>(Func<T, TResult> func)
    {
        try
        {
            return func(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            return func(_service);
        }
    }

    private T CreateNewInstance()
    {
        Type type = typeof(T);
        return (T)type.GetConstructor(Type.EmptyTypes).Invoke(null);
    }
}

To use this, all you need to do is:

ProxyWrapper<ServiceClient> client = new ProxyWrapper<ServiceClient>();
client.Invoke(s => s.SomeAction());
int val = client.Invoke<int>(s => s.ReturnsAnInteger());

Note: Since I'm only using the default constructor for the client proxies, that's all this supports.

like image 27
Steve Platz Avatar answered Oct 31 '22 20:10

Steve Platz