Share via


ReplyMangler Channel

To finish up the series on one-way HTTP requests, I promised to supply a custom channel that fixes the scenario of using the POX message encoder together with one-way requests. This is primarily a code post since most of the interesting discussion is already taken care of. I'm supplying a binding element, channel factory, and request channel. There isn't a channel listener or reply channel because there's no conflict on the server-side between one-way and POX. It's only the client side that requires this patch. Stick the binding element in your channel stack between the transport and the OneWay channel.

 CustomBinding binding = new CustomBinding(
   new OneWayBindingElement(),
   new ReplyMangler(),
   new TextMessageEncodingBindingElement(),
   new HttpTransportBindingElement()
);

The channel code is mostly plumbing to make the reply run through the FilterMessage method that I posted last time. I spent less than 15 seconds testing this so it would be unwise to drop the code directly into your production system. It does appear however to do the proper job of swallowing messages that come back from the POX message encoder.

 class ReplyManglerChannel : ChannelBase, IRequestChannel
{
   IRequestChannel innerChannel;

   public ReplyManglerChannel(ChannelManagerBase channelManager, IRequestChannel innerChannel)
      : base(channelManager)
   {
      this.innerChannel = innerChannel;
   }

   public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginRequest(message, timeout, callback, state);
   }

   public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
   {
      return BeginRequest(message, DefaultSendTimeout, callback, state);
   }

   public Message EndRequest(IAsyncResult result)
   {
      return FilterMessage(innerChannel.EndRequest(result));
   }

   Message FilterMessage(Message reply)
   {
      if (reply == null)
      {
         return null;
      }
      HttpResponseMessageProperty properties =
         (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
      if (properties != null && properties.StatusCode == HttpStatusCode.Accepted)
      {
         return null;
      }
      return reply;
   }

   protected override void OnAbort()
   {
      innerChannel.Abort();
   }

   protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginClose(timeout, callback, state);
   }

   protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannel.BeginOpen(timeout, callback, state);
   }

   protected override void OnClose(TimeSpan timeout)
   {
      innerChannel.Close(timeout);
   }

   protected override void OnEndClose(IAsyncResult result)
   {
      innerChannel.EndClose(result);
   }

   protected override void OnEndOpen(IAsyncResult result)
   {
      innerChannel.EndOpen(result);
   }

   protected override void OnOpen(TimeSpan timeout)
   {
      innerChannel.Open(timeout);
   }

   public EndpointAddress RemoteAddress
   {
      get { return innerChannel.RemoteAddress; }
   }

   public Message Request(Message message, TimeSpan timeout)
   {
      return FilterMessage(innerChannel.Request(message, timeout));
   }

   public Message Request(Message message)
   {
      return Request(message, DefaultSendTimeout);
   }

   public Uri Via
   {
      get { return innerChannel.Via; }
   }
}

class ReplyManglerChannelFactory : ChannelFactoryBase<IRequestChannel>
{
   IChannelFactory<IRequestChannel> innerChannelFactory;

   public ReplyManglerChannelFactory(IChannelFactory<IRequestChannel> innerChannelFactory)
   {
      this.innerChannelFactory = innerChannelFactory;
   }

   protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
   {
      return innerChannelFactory.BeginOpen(timeout, callback, state);
   }

   protected override IRequestChannel OnCreateChannel(EndpointAddress address, Uri via)
   {
      return new ReplyManglerChannel(this, innerChannelFactory.CreateChannel(address, via));
   }

   protected override void OnEndOpen(IAsyncResult result)
   {
      innerChannelFactory.EndOpen(result);
   }

   protected override void OnOpen(TimeSpan timeout)
   {
      innerChannelFactory.Open(timeout);
   }
}

public class ReplyMangler : BindingElement
{
   public ReplyMangler()
      : base()
   {
   }

   protected ReplyMangler(ReplyMangler copyFrom)
      : base(copyFrom)
   {
   }

   public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
   {
      if (!CanBuildChannelFactory<TChannel>(context))
      {
         throw new ArgumentException();
      }
      IChannelFactory<IRequestChannel> innerChannelFactory =
         (IChannelFactory<IRequestChannel>)base.BuildChannelFactory<TChannel>(context);
      return (IChannelFactory<TChannel>)new ReplyManglerChannelFactory(innerChannelFactory);
   }

   public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
   {
      if (typeof(TChannel) != typeof(IRequestChannel))
      {
         return false;
      }
      return base.CanBuildChannelFactory<TChannel>(context);
   }

   public override BindingElement Clone()
   {
      return new ReplyMangler(this);
   }

   public override T GetProperty<T>(BindingContext context)
   {
      return context.GetInnerProperty<T>();
   }
}

Next time: Manually Adding MEX Endpoints

Comments