How to modify the RST/SCT request from WCF client to add a cookie in the header.
Issue:
You have a WCF service using the security mode as “Message” and hosted behind a load balancer on 2 different servers. During a call from a client, it sets up the security context token with 1st server during the “RST/Issue” and receives an affinity cookie.
However, the “RST/SCT” call goes to a different server as the header does not contain the affinity cookie in it. So, the server sets a new affinity cookie and the request gets terminated as the security context is invalid on this server.
Resolution:
The requirement is to modify the RST/SCT and add the affinity cookie in the header that was received in the previous response.
Steps:
To do this, we need to create a CustomEndpointBehavior on the client application. This will add a message inspector which will modify the request to add the affinity cookie before it is sent over the channel. The affinity cookie will need to be read from the first response.
The Class CustomEndpointBehavior is as follows:
class CustomEndpointBehavior : System.ServiceModel.Description.IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(System.ServiceModel.Description.ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
return;
}
public void public void ApplyClientBehavior(System.ServiceModel.Description.ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
//The message inspector of Type SetHttpHeaderMessageInspector
clientRuntime.MessageInspectors.Add(new SetHttpHeaderMessageInspector());
}
public void ApplyDispatchBehavior(System.ServiceModel.Description.ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
return;
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
#endregion
}
The class SetHttpHeaderMessageInspector is as follows:
class SetHttpHeaderMessageInspector : System.ServiceModel.Dispatcher.IClientMessageInspector
{
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
System.Net.WebHeaderCollection whc = channel.GetProperty();
if (whc != null)
{
//Adding the affinity cookie to the header
whc.Add(System.Net.HttpRequestHeader.Cookie, Common.AffinityCookie);
}
if (whc != null && whc.Count > 0)
{
object prop = null;
System.ServiceModel.Channels.HttpRequestMessageProperty rmp = null;
if (request.Properties.TryGetValue(System.ServiceModel.Channels.HttpRequestMessageProperty.Name, out prop))
rmp = prop as System.ServiceModel.Channels.HttpRequestMessageProperty;
else
{
rmp = new HttpRequestMessageProperty();
request.Properties.Add(HttpRequestMessageProperty.Name, rmp);
}
rmp.Headers.Add(whc);
}
return null;
}
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
return;
}
}
Finally add the endpoint behavior to the proxy object.
EndpointAddress endpoint = new EndpointAddress(uri);
this.WCFProxy = new ServiceReference1.Service1Client(binding, endpoint);
//Adding the Custom Endpoint Behavior object
this.WCFProxy.Endpoint.Behaviors.Add(new CustomEndpointBehavior());
Now all the requests including the RST/SCT will have the affinity cookie and will be redirected to the same server with which the security context was set up!
Hope this help!
Saurav Dey (MSFT)