tags:

views:

34

answers:

1

I have found a situation where a WCF client can crash a WCF ServiceHost if the ServiceHost has a UserNamePasswordValidator that takes a while to process. I would like to hear any suggestions on solutions before I go throw this at Microsoft.

The steps to reproduce is:

  1. Open a channel
  2. Call an API method which takes 10 seconds to return.
  3. Close the channel before API method returns.
  4. API method returns. ServiceHost crashes.

The above only happens if UserNamePasswordValidator takes a while to process (my example, 2 seconds sleep). If UserNamePasswordValidator returns immediately, the above situation changes:

  1. Open a channel
  2. Call an API method which takes 10 seconds to return.
  3. Close the channel before API method returns.
  4. Closing channel waits on pending API method call.
  5. API method returns.
  6. Channel gets closed. Everything is good.

The client code is as follows:

public static void Main(string[] args)
{
    var binding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
    var identity = new DnsEndpointIdentity("Dummy");
    var endpointAddress = new EndpointAddress(new Uri("net.tcp://localhost:5000/"), identity);
    var channelFactory = new ChannelFactory<IService>(binding, endpointAddress);

    channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
    channelFactory.Credentials.UserName.UserName = "foo";
    channelFactory.Credentials.UserName.Password = "bar";

    channelFactory.Open();

    IService service = channelFactory.CreateChannel();

    ThreadPool.QueueUserWorkItem(CallPingOnChannel, service);

    Thread.Sleep(TimeSpan.FromSeconds(1));

    channelFactory.Close();
}

private static void CallPingOnChannel(object state)
{
    var result = ((state) as IService).Ping();
}

The ServiceHost is set up as follows:

public static void Main(string[] args)
{
    var serviceHost = new ServiceHost(typeof(Service), TcpBaseAddress);
    serviceHost.Description.Behaviors.Add(new ErrorHandler());
    var netTcpBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
    netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
    netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

    serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
    serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserValidator();

    serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
    serviceHost.Credentials.ServiceCertificate.Certificate = GetCertificate();

    serviceHost.AddServiceEndpoint(typeof(IService), netTcpBinding, "");

    serviceHost.Open();

    Console.ReadLine();
}

And the UserValidator is implemented as follows:

public class UserValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
    }
}

The unhandled exception which causes the main thread to crash is:

System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
A: 

Firstly you should be catching a System.TimeOutException in your client code.

Next you should look at possibly extending the SendTimeout and RecieveTimeout of your Client and Service.

Take a look at these two links.

SendTimeOut Property

RecieveTimeOut Property

kyndigs
There is no TimeoutException thrown in the client, and extending the SendTimeout or ReceiveTimeout has no effect on the issue.
mbp
How are you using it?
kyndigs
Are you referring to my usage of SendTimeout or ReceiveTimeout? Obviously I set it on the NetTcpBinding. Even if I set it up to 15 minutes, the problem still occurs. Nevertheless, I don't understand why a timeout setting would cause unhandled exceptions in a ServiceHost.
mbp
Ok, well can you give post a trace, set it to verbose and post the results.
kyndigs