4

I am trying to hit a SOAP-based, MTOM service which accepts multipart attachment. Here is how I create the request from C#:

var request = (HttpWebRequest)WebRequest.Create("...skipping URL...");
request.ContentType = @"multipart/related;charset=""UTF-8"";type=""application/xop+xml"";start=""<http://tempuri.org/0>"";boundary=""uuid:f276e990-75d0-4b5d-a0fd-e00a096e30ce+id=1"";start-info=""application/soap+xml""";
request.Method = "POST";
request.Timeout = 30000;
request.Headers.Add("MIME-Version", "1.0");
request.Headers.Add("Accept-Encoding", "gzip, deflate");

var postData = @"--uuid:f276e990-75d0-4b5d-a0fd-e00a096e30ce+id=1
Content-ID: <http://tempuri.org/0>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type=""text/xml""

[Skipping the SOAP envelope that goes here...]

--uuid:f276e990-75d0-4b5d-a0fd-e00a096e30ce+id=1
Content-ID: <http://tempuri.org/1/636875796573424479>
Content-Transfer-Encoding: binary
Content-Type: application/octet-stream

" + Encoding.UTF8.GetString(File.ReadAllBytes(@"C:\Users\User\Desktop\Sample.pdf")) + @"

--uuid:f276e990-75d0-4b5d-a0fd-e00a096e30ce+id=1--";

using (var stream = request.GetRequestStream())
using (var writer = new StreamWriter(stream))
    writer.Write(postData);

My question is this: Since I know the content type must be application/octet-stream and the transfer encoding must be binary, am I correctly reading the binary data of my PDF file for submission to this service? Is it correct to use Encoding.UTF8.GetString(File.ReadAllBytes(@"C:\Users\User\Desktop\Sample.pdf")) here or is it incorrect? How should I create the request to send the attachment?

Edit: Would it be more correct to do something like this?

using (var stream = request.GetRequestStream())
using (var writer = new StreamWriter(stream))
using (var attachment = new FileStream(@"C:\Users\User\Desktop\Sample.pdf", FileMode.Open, FileAccess.Read))
{
    writer.Write(postDataPrefix); // Add everything up to the start of the attachment binary. 
    writer.Flush();
    stream.Flush();
    attachment.CopyTo(stream); // Add the actual attachment binary.
    writer.Flush();
    stream.Flush();
    writer.Write(postDataSuffix); // Add everything after the attachment binary.
    writer.Flush();
    stream.Flush();
}
Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • You are using http so you should be using a Base 64 string not UTF8. – jdweng Mar 11 '19 at 15:55
  • @jdweng I can't assume the server supports base 64, the only thing I know is that it supports binary octet streams. Its kind of like a black box to me. – Alexandru Mar 11 '19 at 16:22
  • http attachments are always base 64 strings according to the specification. The server must support base 64. It is really the same as GZIP. – jdweng Mar 11 '19 at 16:35
  • 1
    @jdweng This is a SOAP + MTOM server. It works differently, like this -> https://www.crcind.com/csp/docbook/DocBook.UI.Page.cls?KEY=GSOAP_mtom Binary data can be placed into separate MIME parts without base-64 encoding – Alexandru Mar 11 '19 at 16:42
  • It is only supported with chunking mode which is http 1.1 (not stream http 1.0). See https://en.wikipedia.org/wiki/MIME – jdweng Mar 11 '19 at 16:54
  • @jdweng By the way, I want to say thanks for your help so far. So, right now I am wondering how to craft such a request. I am wondering if toggling SendChunked on would be enough: https://learn.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest.sendchunked?view=netframework-4.7.2 – Alexandru Mar 11 '19 at 18:23
  • Not sure. I usually use : request.ProtocolVersion = HttpVersion.Version11; – jdweng Mar 11 '19 at 19:42
  • @jdweng It looks like it defaults to version 1.1 but I have enforced Version11 like you suggested. So, it looks like I am able to write to the POST stream with the contents of the binary file like I have in my edit. I am wondering if this is correctly RFC compliant as its a straight up binary octet stream as far as I can see. – Alexandru Mar 11 '19 at 20:06
  • The client and server negotiate operating mode by using comparing header in the webpage and the headers in the request to get a common operating mode. With the server set to 1.1 it defaults to 1.1 unless the client requests 1.0 to be backwards compatible. A server that is set to 1.0 normally cannot be set to 1.1. So the server may not allow Binary Mode if it is older and is only 1.0 compatible. I assume you mean the client defaults to 1.0. You either have to look at the response to see if server support 1.1 or look at the server source html data. – jdweng Mar 12 '19 at 03:10
  • @jdweng I don't see anywhere that it says you need chunking for this, do you? I'm pretty sure MTOM does not require chunking at all. – Alexandru Mar 14 '19 at 18:40
  • Look again at your link that shows http 1.1 : https://www.crcind.com/csp/docbook/DocBook.UI.Page.cls?KEY=GSOAP_mtom – jdweng Mar 14 '19 at 20:42
  • @jdweng Its not required, though. You can chunk your request if you want, but its up to you. For example, check these payloads: https://docs.axway.com/bundle/APIGateway_762_PolicyDevFilterReference_allOS_en_HTML5/page/Content/PolicyDevTopics/conversion_insert_mtom.htm – Alexandru Mar 14 '19 at 22:06
  • So have you actually tested your code? Does it work properly? And how can we test soap mtom server, is there any test server online? – Alexander Mar 16 '19 at 00:04
  • Have you considered using WCF? That way you would be able to do this in a couple of lines of code. Just import the WSDL and shoot. See [here](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/large-data-and-streaming). – Jesse de Wit Mar 20 '19 at 15:31

0 Answers0