I am calling a non-WCF service from a WCF client. The WCF client includes the "MustUnderstand" header attribute set to "1". Here's a typical SOAP request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2010-08-23T20:48:52.680Z</u:Created>
<u:Expires>2010-08-23T20:53:52.680Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-72ea0c0a-43aa-43b2-bed7-c2da13624105-1">
<o:Username>blablabla</o:Username>
<o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">blablabla</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<HeartbeatRequest xmlns="http://removed">
<DateTime xmlns="">8/23/2010 4:48:51 PM</DateTime>
<Message xmlns="">123</Message>
</HeartbeatRequest>
</s:Body>
Now, here's the response I get back for this.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<Misunderstood qname="o:Security" xmlns="http://www.w3.org/2002/06/soap-faults" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" />
</soapenv:Header>
<soapenv:Body>
<soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode>soapenv:MustUnderstand</faultcode>
<faultstring>WSWS3173E: Error: Did not understand "MustUnderstand" header(s):{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security</faultstring>
</soapenv:Fault>
</soapenv:Body>
Note the part about MustUnderstand not being understood.
The owner of this service has indicated that they allow elements that have the WSSE namespace prefix but aren't actually in the XSD, and do some other processing that would prevent them from accepting MustUnderstand="1" so I have to find a way to send messages with MustUnderstand="0".
I tried to change this in the MessageContract for the proxy client using a MessageHeader attribute, but that didn't help.
Next, I implemented a custom client message inspector. I created classes per MSDN for a custom Behavior Extension Element and an IEndpointBehavior, these are trivial but here for completeness:
public class ExClientBehavior : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// no op
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExInspector inspector = new ExInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// no op
}
public void Validate(ServiceEndpoint endpoint)
{
// no op
}
#endregion
}
public class ExClientBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(ExClientBehavior); }
}
protected override object CreateBehavior()
{
return new ExClientBehavior();
}
}
and now the actual Inspector:
public class ExInspector : IClientMessageInspector
{
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// no op
return;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);
Message newMessage = buffer.CreateMessage();
newMessage.Headers.RemoveAt(0);
newMessage.Headers.Add(MessageHeader.CreateHeader
(
request.Headers[0].Name,
request.Headers[0].Namespace,
string.Empty,
false,
string.Empty,
request.Headers[0].Relay
)
);
request = newMessage;
return null;
}
#endregion
}
As you see, I am creating a new request via buffered copy, then deleting the security header (there is only one header) and adding a new one with MustUnderstand set to false (why am I doing this? MessageHeader.MustUnderstand is read-only). I set a breakpoint in this method and indeed, the new header is added, the newMessage is written back to the request, and both newMessage.Headers[0].MustUnderstand as well as request.Headers[0].MustUnderstand are false at the end of this method.
However, the message that is sent to the service still includes MustUnderstand="1" on the header!!!!!
Here's the app.config that includes the behavior above:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="WebServiceSOAP" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="https://removed"
behaviorConfiguration="ovrExClientBehavior"
binding="basicHttpBinding"
bindingConfiguration="WebServiceSOAP"
contract="EWebService.EWebService"
name="WebServiceSOAP" />
</client>
<extensions>
<behaviorExtensions>
<add name="exClientBehavior" type="ExMessageInspector.ExClientBehaviorExtensionElement, ExMessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="ovrExClientBehavior">
<exClientBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
So my question: is it possible to change MustUnderstand on an outgoing message like above, or in a similar manner? Or is it forcibly being changed back to true later in the pipeline, after the inspector replaces the security header?
Note: the service owner says that they are only aware of one other organization consuming this service in .NET, and that that consumer had to essentially throw out WCF and WSE and create SOAP messages - and handle the replies - from scratch, probably using POX POSTs or some such. We would really prefer to avoid this since we need to call a number of operations on the service.
Also, we need to preserve the body and properties of the message intact.
Any help would be really appreciated!!