Problems with WebApi, Multipart Content Upload and boundary quotes

Damir Dobric Posts

Next talks:

 

    

Follow me on Twitter: #ddobric



 

 

Archives

From time to time you will probably have a requirement to simply upload some file to some remote system by using HTTP. Assuming that you are .NET developer you will probably peak HttpClient library which is a client implementation of WebApi.
As long your client and WebApi are implemented on the same stack (for example Microsoft) all will work fine. But, there are in the world very old systems and new systems which are not implemented by Microsoft. In such cases the probability is high, that people who read RFC 211 specification have taken different drinks. As a result of this is that some words are differently understood by different group of people. If this happen, you will run in trouble, for sure.
This post describe one such story and provide a fix which might make you happy.

Imagine you are uploading some file and you get response code 200 (which means Success).

Here is one code sample which does a kind of strange upload to some Linux based server.

        public void UploadFile(string fileName)
        {
           
NetworkCredential myCred = new NetworkCredential(m_UserName, m_Pwd);

           
CredentialCache myCache = new CredentialCache();

            myCache.Add(
new Uri("https://..."), "Basic", myCred);

           
HttpClientHandler handler = new HttpClientHandler(){Credentials = myCred};

           
using (var client = new HttpClient(handler))
            {
                client.BaseAddress = m_Uri;

               
using (var content = new MultipartFormDataContent())
                {
                   
var stringContent = new StringContent("load:load");
                    stringContent.Headers.ContentDisposition =
new ContentDispositionHeaderValue("form-data")
                    {
                        Name =
"\"what\"",
                    };
                    stringContent.Headers.ContentType =
null;
                    content.Add(stringContent);

                   
var fileContent = new StreamContent(new StreamReader(fileName).BaseStream);
                   
FileInfo fI = new FileInfo(fileName);
                    fileContent.Headers.ContentDisposition =
new ContentDispositionHeaderValue("form-data")
                    {
                        Name =
"\"p1\"",
                        FileName =
"\"" + fI.Name + "\"",
                    };

                    fileContent.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");

                    content.Add(fileContent);

                   
var boundaryValue = content.Headers.ContentType.Parameters.FirstOrDefault(p => p.Name == "boundary");
                    boundaryValue.Value = boundaryValue.Value.Replace(
"\"", String
.Empty);

                   
var requestUri = "";
                   
var result = client.PostAsync(requestUri, content).Result;
                }
            }
        }

The code shown above sends following request, which contains multipart content:

Content-Type: multipart/form-data; boundary=”123456789”
Authorization: Basic dHdxZGJ0OlB3NFRRZGI=
Host: info.europipe.com
Content-Length: 237
Expect: 100-continue

--123456789
Content-Disposition: form-data; name="what"

load:load
--123456789
Content-Disposition: form-data; name="p1"; filename="000000016_I88_h.daenet"
Content-Type: application/octet-stream

12027
4000
63
0

--123456789--

 

The payload consists of two parts:

1.“what” (some form value)
2. “p1” (file content)

 

This is a common valid HTTP request with response code will be 200 (success). But nothing will work. :(
If you ask the vendor of the remote system, he/she will probably tell you that this is working for at least 10 years. Unfortunately, you cannot argument, that you are user of the cool WebApi, which simply must be the right one. :)
Anyhow, the problem is that some systems do not accept the quoted boundary value as WebApi will render it. This is how WebApi formats the boundary value:

Content-Type: multipart/form-data; boundary=”123456789”

And this is how it should be: (remove quotes form boundary)

Content-Type: multipart/form-data; boundary=123456789

Note that quotes are missing in the working version. Now you can ask yourself who didn’t read specification correctly? Hmmm?

Here is the answer :) http://www.ietf.org/rfc/rfc2616.txt

2) Although RFC 2046 [40] permits the boundary string to be quoted, some existing implementations handle a quoted boundary string incorrectly.

The answer is, Microsoft guys have implemented WebApi correctly.

Solution

I used following two lines in the code above to workaround this issue:

var boundaryValue = content.Headers.ContentType.Parameters.FirstOrDefault(p => p.Name == "boundary");
boundaryValue.Value = boundaryValue.Value.Replace(
"\"", String
.Empty);


After the content is created I will simply remove quotes in the boundary value.


Posted Sep 10 2013, 05:11 PM by Damir Dobric
Filed under:
developers.de is a .Net Community Blog powered by daenet GmbH.