WSE 2.0 and Via for Physical and Logical Endpoints

I am ashamed.  I admit that there is a function of WSE that I didn't fully comprehend.  OK, I openly admit that there are many functions of WSE I don't comprehend, but this is a pretty basic one that I read, digested, then embarassingly dismissed.

WSE has the capability to separate the physical address endpoint from the logical address endpoint, using the Microsoft.Web.Services2.Referral.Via class.  The question was born out of a co-worker's email where a webservice is called and an intermidiary firewall router munged the URL.  The WSE exception thrown was something to the effect of "the To: address does not match the destination."  Kirill was kind enough to explain how Via could be used in this scenario today in the TechEd 2005 cabanas.  Notice that the https://foo destination is fictitious, there is nothing listening at that endpoint.  This is a logical endpoint, only existing in our minds, but the physical endpoint is specified in the Via.  The only WSE requirement is that the protocol scheme is one of the registered schemes for WSE 2.0 (will have more information later this week on WSE schemes and transports).

Here is a C# Console application that serves as the service endpoint.  Notice that it defines the physical endpoint that it is listening on as well as the logical endpoint that it is listening for.

using System;
using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Addressing;
using Microsoft.Web.Services2.Messaging;
using Microsoft.Web.Services2.Referral;

namespace WSEViaService
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
NewWSEService1 s = new NewWSEService1();
string uriString = string.Format("https://foo");
System.Uri endpoint = new Uri(uriString);
string viaString = string.Format("soap.tcp://{0}:8080/WSEViaService",System.Net.Dns.GetHostName());
Microsoft.Web.Services2.Messaging.SoapReceivers.Add(new EndpointReference(endpoint, new Uri(viaString)),s);
Console.WriteLine("WSEViaService is running. Press ENTER to continue...");

Console.ReadLine();
Microsoft.Web.Services2.Messaging.SoapReceivers.Remove(endpoint);
}
}

 public class NewWSEService1 : SoapService
{
[SoapMethod("urn:contoso:com/MyMethod")]
public string HelloWorld()
{
return "Hello World";
}

  protected override void Receive(SoapEnvelope envelope)
{
Console.WriteLine(envelope.OuterXml);
base.Receive(envelope);
}
}
}

Putting a Via on the service is kind of expected, as the service is saying "here is my logical address as well as my physical address". So, how does the client use these two bits of information? The really cool part is the client service. The client happens to know about the physical endpoint URI for this example, expressed in the Via as well. Here we see a separation of the physical from the logical endpoint binding. Again, nothing is listening at https://foo, this is a completly fictitious naming convention with the sole requirement of being a registered Uri scheme that WSE is aware of (like http, soap.tcp, or soap.inproc as the out-of-the-box implementations).

using System;
using System.Web.Services.Protocols;
using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Messaging;
using Microsoft.Web.Services2.Addressing;
using Microsoft.Web.Services2.Referral;

namespace WSEViaClient
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
string uriString = string.Format("https://foo");

System.Uri endpoint = new Uri(uriString);
NewWSEClient1 c = new NewWSEClient1(endpoint);

   string viaString = string.Format("soap.tcp://{0}:8080/WSEViaService",System.Net.Dns.GetHostName());
//c.Destination.Via = new Via(new Uri(viaString));
c.Destination = new EndpointReference(endpoint, new Uri(viaString));

Console.WriteLine(c.HelloWorld());
Console.WriteLine("Press ENTER to continue ... ");
Console.ReadLine();
}
}

 public class NewWSEClient1 : SoapClient
{
public NewWSEClient1(EndpointReference destination) : base(destination)
{
}

  [SoapMethod("urn:contoso:com/MyMethod")]
public string HelloWorld()
{
try
{
SoapEnvelope env = base.SendRequestResponse("HelloWorld",new object());
return env.Body.InnerText;
}
catch(Exception oops)
{
Console.WriteLine(oops.ToString());
throw new ApplicationException("Exception occurred",oops);
}

}
}
}