ROT 128 Stream Upgrade Sample, Part 3

Last time, we built the binding element for the stream upgrade sample. The job of the binding element was to stash itself away in the binding context so that the transport could later pull out the stream upgrade and build the provider. This time we'll look at the implementation of the stream upgrade provider.

The stream upgrade provider needs to do three things:

  1. Build the upgrade initiator for the client side of the connection. The initiator gets the remote address and via of the connection so that we can differentiate behavior based on where the connection is actually going.
  2. Build the upgrade acceptor for the server side of the connection. The acceptor and initiator pieces are the first time we have asymmetry in this simple example. Fundamentally, the initiator and acceptor use a request-reply exchange regardless of the shape of the contract or connection. This means that you're always going to get asymmetry at least at this level.
  3. Run any part of the protocol that happens independently from making the individual connections. These actions go in the Open, Close, and Abort methods of the provider. I've given all of these empty implementations because this sample doesn't need to do anything here. If you had resources allocated at pre-connect time, such as a socket connection, you would do the allocation and deallocation in these methods.
 using System;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace Microsoft.ServiceModel.Samples
{
   class OpenAsyncResult : CompletedAsyncResult
   {
      internal OpenAsyncResult(AsyncCallback callback, object state)
         : base(callback, state)
      {
      }
   }

   class CloseAsyncResult : CompletedAsyncResult
   {
      internal CloseAsyncResult(AsyncCallback callback, object state)
         : base(callback, state)
      {
      }
   }

   class ROT128StreamUpgradeProvider : StreamUpgradeProvider
   {
      public ROT128StreamUpgradeProvider(ROT128StreamUpgradeBindingElement element, BindingContext context)
         : base(context.Binding)
      {
      }

      public override StreamUpgradeAcceptor CreateUpgradeAcceptor()
      {
         return new ROT128StreamUpgradeAcceptor(this);
      }

      public override StreamUpgradeInitiator CreateUpgradeInitiator(EndpointAddress remoteAddress, Uri via)
      {
         return new ROT128StreamUpgradeInitiator(this, remoteAddress, via);
      }

      protected override void OnAbort()
      {
      }

      protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
      {
         return new CloseAsyncResult(callback, state);
      }

      protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
      {
         return new OpenAsyncResult(callback, state);
      }

      protected override void OnClose(TimeSpan timeout)
      {
      }

      protected override void OnEndClose(IAsyncResult result)
      {
      }

      protected override void OnEndOpen(IAsyncResult result)
      {
      }

      protected override void OnOpen(TimeSpan timeout)
      {
      }
   }
}

I've used the AsyncResult class from our SDK samples so that I don't have to keep implementing these myself. I'm going to be using a contract-based client and server in this example, which calls the asynchronous code paths by default. I've also added the helper class as an attachment to this post.

Next time: ROT 128 Stream Upgrade Sample, Part 4

AsyncResult.cs