Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is WCF reading input stream to EOF on Close()?

Tags:

wcf

streaming

We're using WCF to build a simple web service which our product uses to upload large files over a WAN link. It's supposed to be a simple HTTP PUT, and it's working fine for the most part.

Here's a simplified version of the service contract:

[ServiceContract, XmlSerializerFormat]
public interface IReplicationWebService
{
    [OperationContract]
    [WebInvoke(Method = "PUT", UriTemplate = "agents/{sourceName}/epoch/{guid}/{number}/{type}")]
    ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream stream);
}

In the implementation of this contract, we read data from stream and write it out to a file. This works great, so we added some error handling for cases when there's not enough disk space to store the file. Here's roughly what it looks like:

    public ReplayResult PutEpochFile(string sourceName, string guid, string number, string type, Stream inStream)
    {
        //Stuff snipped
        try
        {
            //Read from the stream and write to the file
        }
        catch (IOException ioe)
        {
            //IOException may mean no disk space
            try
            {
                inStream.Close();
            }
            // if instream caused the IOException, close may throw
            catch
            {
            }
            _logger.Debug(ioe.ToString());
            throw new FaultException<IOException>(ioe, new FaultReason(ioe.Message), new FaultCode("IO"));
        }
    }

To test this, I'm sending a 100GB file to a server that doesn't have enough space for the file. As expected this throws an exception, but the call to inStream.Close() appeared to hang. I checked into it, and what's actually happening is that the call to Close() made its way through the WCF plumbing until it reached System.ServiceModel.Channels.DrainOnCloseStream.Close(), which according to Reflector allocates a Byte[] buffer and keeps reading from the stream until it's at EOF.

In other words, the Close call is reading the entire 100GB of test data from the stream before returning!

Now it may be that I don't need to call Close() on this stream. If that's the case I'd like an explanation as to why. But more importantly, I'd appreciate it if anyone could explain to me why Close() is behaving this way, why it's not considered a bug, and how to reconfigure WCF so that doesn't happen.

like image 421
anelson Avatar asked Oct 15 '22 12:10

anelson


1 Answers

.Close() is intended to be a "safe" and "friendly" way of stopping your operation - and it will indeed complete the currently running requests before shutting down - by design.

If you want to throw down the sledgehammer, use .Abort() on your client proxy (or service host) instead. That just shuts down everything without checking and without being nice about waiting for operations to complete.

like image 100
marc_s Avatar answered Oct 28 '22 22:10

marc_s