wcf
wcf copied to clipboard
Whitespace lost when using MTOM message encoding
Whitespace characters are trimmed on all XML content when calling WCF service using MTOM message encoding.
Steps to reproduce the behavior:
- Host a WCF service using MTOM message encoding.
- Call a method returning whitespaces from client app.
- No whitespace is observed on response.
Host app:
string baseAddress = "http://localhost:8080/wcfselfhost/";
ServiceHost host = new ServiceHost(typeof(HelloWorldService), new Uri(baseAddress));
BasicHttpBinding binding = new BasicHttpBinding();
binding.MessageEncoding = WSMessageEncoding.Mtom;
host.AddServiceEndpoint(typeof(IHelloWorldService), binding, "hello");
host.Open();
Console.ReadLine();
Host app service:
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract]
string SayHello(string name);
}
public class HelloWorldService : IHelloWorldService
{
public string SayHello(string name)
{
return string.Format(" whitespace hello, {0} ", name);
}
}
Test client:
var channelFactory = new ChannelFactory<IHelloWorldService>(
new BasicHttpBinding() { MessageEncoding = WSMessageEncoding.Mtom },
new EndpointAddress("http://localhost:8080/wcfselfhost/hello")
);
var channel = channelFactory.CreateChannel();
string resp = channel.SayHello("test");
Console.ReadKey();
Test client is built using .NET 6.0 and System.ServiceModel.Http version 4.9.0.
'resp' variable contents is "whitespace hello, test "
Expected behavior is to get " whitespace hello, test "
As you can see in related pull request test failed. White space is trimmed.
Error: expected response from service: ' Hello ' Actual was: 'Hello '
This should be fixed as top priority because we use response singing on client side and signature verification on server side. And because XML content is different, signature checks are failed.
Can you please verify that you don't see this issue when using a .NET Framework WCF client and that this is a regression?
When an XML element isn't being output in a separate multi-part segment, the MTOM reader/writer defers most of the logic to the regular System.Xml XmlReader and XmlWriter. If this is a regression, I suspect this is a difference in behavior in those implementations. I made very few changes to the code base from .NET Framework. Basically implemented a couple more async methods and made some internal book-keeping classes async too, but other than that, the code is identical from the MTOM reader/writer part.
I can confirm this is a regression.
When targeting .NET 4.7.2+ everything work as expected - whitespaces are not trimmed. After targeting .NET 6.0 MTOM encoded messages behavior is as I described earlier.
We use following workaround for now:
public void AfterReceiveReply(ref Message reply, object correlationState) { reply = Message.CreateMessage( reply.Version, reply.Headers.Action, reply.GetReaderAtBodyContents()); }
Applying this IClientMessageInspector to WCF call no whitespaces are lost.
Looks like an issue is deep in corefx. Putting a breakpoint at HttpResponseMessageHelper.GetStreamAsync and trying to read content as string: _httpResponseMessage.Content.ReadAsStringAsync().Result results in decoded XML content with lost whitespace.
@bogatykh, it's not an issue in corefx. I checked the result from ReadAsStringAsync
and the spaces are there. I also tested the repro from above on NetFx and the spaces are missing on NetFx too. I reduced the problem down to something which didn't require a running service and was able to repro the issue, and I get the same behavior on NetFx and Core.
var bufferManager = BufferManager.CreateBufferManager(0, 0);
var mmebe = new MtomMessageEncodingBindingElement();
mmebe.MessageVersion = MessageVersion.Soap11;
var encoderFactory = mmebe.CreateMessageEncoderFactory();
var encoder = encoderFactory.Encoder;
string testMessage = @"--uuid:a6d79f8f-bf20-4725-9016-b011f266c927+id=2
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type=""text/xml""
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/""><s:Body><SayHelloResponse xmlns=""http://tempuri.org/MyService""><SayHelloResult> whitespace hello, test </SayHelloResult></SayHelloResponse></s:Body></s:Envelope>
--uuid:a6d79f8f-bf20-4725-9016-b011f266c927+id=2--";
string contentType = @"multipart/related; type=""application/xop+xml""; start=""<http://tempuri.org/0>""; boundary=""uuid:a6d79f8f-bf20-4725-9016-b011f266c927+id=2""; start-info=""text/xml""";
var bytes = Encoding.UTF8.GetBytes(testMessage);
var readMessage = encoder.ReadMessage(new ArraySegment<byte>(bytes, 0, bytes.Length), bufferManager, contentType);
var reader = readMessage.GetReaderAtBodyContents();
reader.Read(); // Skip wrapping element
string resp = reader.ReadElementContentAsString();
Because this isn't a regression, this will need to wait to be addressed in the next release as regressions are taking priority at the moment.