ROT 128 Stream Upgrade Sample, Part 4

The final pieces needed for the ROT 128 sample are a stream upgrade initiator and a stream upgrade acceptor. The initiator starts the upgrade process by providing an upgrade type string from GetNextUpgrade. I've coded this so that the initiator and acceptor share a type string that is stored as a static member back on the binding element. You can produce or store your upgrade type string however you want. This is an opaque string to the runtime.

 using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Microsoft.ServiceModel.Samples
{
   class InitiateAsyncResult : TypedCompletedAsyncResult<Stream>
   {
      internal InitiateAsyncResult(Stream stream, AsyncCallback callback, object state)
         : base(stream, callback, state)
      {
      }
   }

   class ROT128StreamUpgradeInitiator : StreamUpgradeInitiator
   {
      ROT128StreamUpgradeProvider provider;
      string nextUpgrade = ROT128StreamUpgradeBindingElement.ROT128UpgradeType;

      internal ROT128StreamUpgradeInitiator(ROT128StreamUpgradeProvider provider, EndpointAddress remoteAddress, Uri via)
         : base()
      {
         this.provider = provider;
      }

      public override IAsyncResult BeginInitiateUpgrade(Stream stream, AsyncCallback callback, object state)
      {
         return new InitiateAsyncResult(new ROT128Stream(stream), callback, state);
      }

      public override Stream EndInitiateUpgrade(IAsyncResult result)
      {
         return ((InitiateAsyncResult)result).Data;
      }

      public override string GetNextUpgrade()
      {
         string result = nextUpgrade;
         nextUpgrade = null;
         return result;
      }

      public override Stream InitiateUpgrade(Stream stream)
      {
         return new ROT128Stream(stream);
      }
   }
}

Each time that GetNextUpgrade is called, you're expected to provide a different upgrade type string if you support multiple upgrades. Once you're out of upgrade types to suggest, GetNextUpgrade should return null forever afterwards. This sample only supports a single upgrade type. At some point in the future, after the upgrade is accepted, you'll get a call on InitiateUpgrade to actually perform the Stream transformation. There's no type string given to InitiateUpgrade so, implicitly, calling InitiateUpgrade means to perform the upgrade for the last upgrade type that you passed out of GetNextUpgrade.

To get to the point where you're transforming Streams, you first need to make it through the upgrade acceptor. The upgrade acceptor takes an upgrade type string argument to the CanUpgrade method and returns whether it recognizes this upgrade type. If CanUpgrade returns false, then the upgrade fails and the connection cannot continue. If CanUpgrade returns true, then at some point in the future you'll get a call on AcceptUpgrade to transform the Stream on this end of the connection. Again, there's no type string given to AcceptUpgrade, so you need to know the upgrade type that you've just accepted.

 using System;
using System.IO;
using System.ServiceModel.Channels;

namespace Microsoft.ServiceModel.Samples
{
   class AcceptAsyncResult : TypedCompletedAsyncResult<Stream>
   {
      internal AcceptAsyncResult(Stream stream, AsyncCallback callback, object state)
         : base(stream, callback, state)
      {
      }
   }

   class ROT128StreamUpgradeAcceptor : StreamUpgradeAcceptor
   {
      internal ROT128StreamUpgradeAcceptor(ROT128StreamUpgradeProvider provider)
         : base()
      {
      }

      public override Stream AcceptUpgrade(Stream stream)
      {
         return new ROT128Stream(stream);
      }

      public override IAsyncResult BeginAcceptUpgrade(Stream stream, AsyncCallback callback, object state)
      {
         return new AcceptAsyncResult(new ROT128Stream(stream), callback, state);
      }

      public override bool CanUpgrade(string contentType)
      {
         return contentType == ROT128StreamUpgradeBindingElement.ROT128UpgradeType;
      }

      public override Stream EndAcceptUpgrade(IAsyncResult result)
      {
         return ((AcceptAsyncResult)result).Data;
      }
   }
}

That's all there is to performing a stream upgrade. Next time, we'll look at what those debugging statements I embedded in the upgrade Stream class print during a service call.

Next time: ROT 128 Stream Upgrade Sample, Part 5