Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mtom Encoding .Net6 vs .Net Framework #4831

Closed
hdanau opened this issue May 23, 2022 · 5 comments · Fixed by #5069
Closed

Mtom Encoding .Net6 vs .Net Framework #4831

hdanau opened this issue May 23, 2022 · 5 comments · Fixed by #5069
Assignees

Comments

@hdanau
Copy link

hdanau commented May 23, 2022

Describe the bug
Currently, I'm trying to update my project from .Net Framework to .Net 6. In this project there is a wcf service that I speak to.
Unfortunately, It is just not working. the service requires mtom encoding & https binding element.

Fiddler Request Payload
I have the fiddler load of both the requests.

.Net framework working payload
POST ... HTTP/1.1
MIME-Version: 1.0
Content-Type: multipart/related; type="application/xop+xml";start="http://tempuri.org/0";boundary="uuid:c8b08b14-f349-4777-962c-cdcd656baa89+id=1";start-info="text/xml"
SOAPAction: "..."
Accept-Encoding: gzip, deflate
Authorization: Basic ...
Host: ...
Content-Length: 1388
Expect: 100-continue

--uuid:c8b08b14-f349-4777-962c-cdcd656baa89+id=1
Content-ID: http://tempuri.org/0
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"

Request Body: ...

.Net 6 not working payload
POST ... HTTP/1.1
Host: ...
Cache-Control: no-cache, max-age=0
SOAPAction: "..."
Expect: 100-continue
Accept-Encoding: gzip, deflate
traceparent: 00-847edbb9b5965db5832afb65156fe0e7-e9bd0532d6964841-00
Authorization: Basic ...
Content-Type: multipart/related; type="application/xop+xml"; start="http://tempuri.org/0"; boundary="uuid:ece3cb8d-07f9-4014-9b7e-82c617f8e75b+id=1"; start-info="text/xml"
Content-Length: 1578

MIME-Version: 1.0
Content-Type: multipart/related;type="application/xop+xml";boundary="uuid:ece3cb8d-07f9-4014-9b7e-82c617f8e75b+id=1";start="http://tempuri.org/0";start-info="text/xml"

--uuid:ece3cb8d-07f9-4014-9b7e-82c617f8e75b+id=1
Content-ID: http://tempuri.org/0
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"

Request Body: ...

Response
I expect to have the same responses. But the Dotnet 6 version response gives me a "Error while parsing an XML stream: 'BOM / charset detection failed'."

Questions
Why is it so different?

@felix-ri
Copy link

I have the same problem.

It seems that the block with the MIME-Version and the second Content-Type is written into the preamble of the MIME Message instead of being sent in the HTTP Header (and merged with the existing Content-Type in the Header).

Also the XMLMtomWriter writes the header into what will become the Content.

if (_initialContentTypeForMimeMessage != null)
{
_mimeWriter.StartPreface();
_mimeWriter.WriteHeader(MimeGlobals.MimeVersionHeader, MimeGlobals.DefaultVersion);
_mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, _initialContentTypeForMimeMessage);
_initialContentTypeForMimeMessage = null;
}

Another possible related thing: The call that encodes the message has a weird callstack; the ValidateAndNormalizeRequest() checks the ContentLength of the Header and the getter of the ContentLength triggers the encoding way before the HttpConnection will encode/copy it.

>	System.Private.ServiceModel.dll!System.Xml.MimeWriter.WriteHeader(string name, string value) Line 1335	C#
 	System.Private.ServiceModel.dll!System.Xml.XmlMtomWriter.Initialize() Line 136	C#
 	System.Private.ServiceModel.dll!System.Xml.XmlMtomWriter.Writer.get() Line 86	C#
 	System.Private.ServiceModel.dll!System.Xml.XmlMtomWriter.WriteStartElement(string prefix, System.Xml.XmlDictionaryString localName, System.Xml.XmlDictionaryString ns) Line 226	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.Message.OnWriteStartEnvelope(System.Xml.XmlDictionaryWriter writer) Line 503	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.Message.WriteMessagePreamble(System.Xml.XmlDictionaryWriter writer) Line 789	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.Message.OnWriteMessage(System.Xml.XmlDictionaryWriter writer) Line 779	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.Message.WriteMessage(System.Xml.XmlDictionaryWriter writer) Line 712	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(System.ServiceModel.Channels.Message message, System.ServiceModel.Channels.BufferManager bufferManager, int initialOffset, int maxSizeQuota) Line 61	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.MtomMessageEncoder.WriteMessageInternal(System.ServiceModel.Channels.Message message, int maxMessageSize, System.ServiceModel.Channels.BufferManager bufferManager, int messageOffset, string startInfo, string boundary, string startUri, bool writeMessageHeaders) Line 476	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.MtomMessageEncoder.WriteMessage(System.ServiceModel.Channels.Message message, int maxMessageSize, System.ServiceModel.Channels.BufferManager bufferManager, int messageOffset) Line 424	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.MessageEncoder.WriteMessage(System.ServiceModel.Channels.Message message, int maxMessageSize, System.ServiceModel.Channels.BufferManager bufferManager) Line 120	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.BufferedMessageContent.EnsureMessageEncoded() Line 249	C#
 	System.Private.ServiceModel.dll!System.ServiceModel.Channels.BufferedMessageContent.TryComputeLength(out long length) Line 271	C#
 	System.Net.Http.dll!System.Net.Http.HttpContent.GetComputedOrBufferLength() Line 599	C#
 	System.Net.Http.dll!System.Net.Http.Headers.HttpContentHeaders.ContentLength.get() Line 95	C#
 	System.Net.Http.dll!System.Net.Http.SocketsHttpHandler.ValidateAndNormalizeRequest(System.Net.Http.HttpRequestMessage request) Line 642	C#
 	System.Net.Http.dll!System.Net.Http.SocketsHttpHandler.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 582	C#
 	System.Net.Http.dll!System.Net.Http.HttpClientHandler.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 317	C#
 	System.Net.Http.dll!System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Line 82	C#
 	System.Net.Http.dll!System.Net.Http.HttpClient.SendAsync.__Core|83_0(System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationTokenSource cts, bool disposeCts, System.Threading.CancellationTokenSource pendingRequestsCts, System.Threading.CancellationToken originalCancellationToken) Line 527	C#
 	System.Net.Http.dll!System.Net.Http.HttpClient.SendAsync(System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) Line 515	C#


@moritzhub
Copy link

Same issue here. Is there any work around?

@LouisSimon73
Copy link

I do agree with @felix-ri on his first assumption. Skipping the "if" gives us a good message but we have no way of doing it. Could you provide us a workaround ? We would like to migrate to .NET Core but we are currently stuck in .Net 4.8 because of that.

@mconnew
Copy link
Member

mconnew commented Mar 27, 2023

It looks like there's two mistakes done when porting the mtom feature.

The Mime-Version header is added on .NET Framework here and that's missing on .NET. You can temporarily work around that by adding the Mime-Version header using an HttpRequestMessageProperty. Where to add this is going to be interesting as on .NET Framework, the Http transport code knows about the MtomMessageEncoder class. When I ported the implementation, I removed knowledge of MtomMessageEncoder from the Http code make so we weren't special casing any behavior. This was because we've had multiple issues we weren't able to resolve over the years because you couldn't replace the MtomEncoder implementation to change behavior due to this internal knowledge of the type. I think this can be solved by making the Http transport aware of the content type of Mtom without knowing about the encoder. The content type with the MtomEncoder will always start with
multipart/related; type="application/xop+xml". We could get the content type and if it starts with this string, add the Mime header.

The second problem is writeMessageHeaders is passed as true here. That's a simple fix, just change the true to false. This bug was also introduced when making Http unaware of MtomEncoder. The .NET Framework code calls a different internal WriteMessage method on MessageEncoder which includes the boundary value as a parameter. This triggers this value being passed as false. If you call the regular MessageEncoder.WriteMessage method without the boundary, it would pass true as the message encoder didn't know what boundary value to use. Now that the message encoder handles the boundary value itself, it can pass false.

@LouisSimon73
Copy link

Thank you @mconnew , I've migrated a small test project that uses mtom messages (and normal messages) and that works like a charm !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants