Delen via


StreamUpgradeProvider

The StreamUpgradeProvider in WCF is not a well-known extension point. Nicholas Allen has a great set of blog posts describing how to use this. You can start reading that with this post: https://blogs.msdn.com/b/drnick/archive/2006/09/07/stream-upgrades_2c00_-part-1.aspx

I recently spent some time trying to implement a StreamUpgradeProvider that performs compression, as Nick indicates in his blog as a potential use. Ultimately I found this to be inadequate for performing compression. Here are a few reasons why:

  • No distinction between input and output streams - To understand this, have a look at the base classes you need to inherit from:

     public abstract class StreamUpgradeInitiator {
      protected StreamUpgradeInitiator();   
      public abstract IAsyncResult BeginInitiateUpgrade(Stream stream, AsyncCallback callback, object state);
      public abstract Stream EndInitiateUpgrade(IAsyncResult result);
      public abstract string GetNextUpgrade();
      public abstract Stream InitiateUpgrade(Stream stream);
    }
    
    public abstract class StreamUpgradeAcceptor {
      protected StreamUpgradeAcceptor();
      public virtual Stream AcceptUpgrade(Stream stream);
      public abstract IAsyncResult BeginAcceptUpgrade(Stream stream, AsyncCallback callback, object state);
      public abstract bool CanUpgrade(string contentType);
      public abstract Stream EndAcceptUpgrade(IAsyncResult result);
    }
    

    The InitiateUpgrade and AcceptUpgrade methods and their async counterparts take in a Stream and return a Stream. The initiator is executed on the client side and the acceptor is executed on the service side. The problem I have with this is there is no distinction between Streams used for writing and Streams used for reading. This is very important for most compression libraries because a decompress stream is separate from a compress stream.

    One option to get around this is to create a wrapper stream that, when writing uses a compress stream and when reading uses a decompress stream. This might work but it does feel like a hack.

  • All the data goes through the stream - After the stream upgrade is setup, the client and service will exchange a byte or two to signal that everything is working correctly. If you're working with a compress stream like GZipStream you will notice that these streams don't Flush when you tell them to, and for good reason. Also, they don't handle writing single bytes very well. Compressing a single byte doesn't make sense and if you did have a compress stream that worked here, you would probably have to special case this part.

  • Only one StreamUpgradeProvider can be used at a time - If you did want to use StreamUpgradeProvider to encrypt and compress, you would have to combine those into one.

  • StreamUpgradeProvider only works on sessionful channels - Basically net TCP and named pipe bindings are all this applies to.

While I think the StreamUpgradeProvider would not be very useful for most people, it's helpful to know just in case. Obviously it does have some uses as there are currently two StreamUpgradeProvider implementations in the framework for SSL and Windows client identity.