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

Multipart form-data boundary is missing from Content-type header #92

Closed
Qonstrukt opened this issue Sep 30, 2014 · 13 comments
Closed

Multipart form-data boundary is missing from Content-type header #92

Qonstrukt opened this issue Sep 30, 2014 · 13 comments

Comments

@Qonstrukt
Copy link

Something I'm noticing since the ModernHttpClient 2.x series is that the boundary is missing from the Content-type header when using Multipart form-data.

I'm using a derivate class from System.Net.Http.MultipartContent which has an optional boundary parameter in it's constructor. When this isn't set HttpClient generates one automatically. This boundary is then added between all the content parts, and added as a parameter to the Content-type like this:
"multipart\/form-data; boundary=\"379dc741-a272-4441-b777-c892aa3cd45d\""

The Content-type using ModernHttpClient looks like this:
"multipart\/form-data"

I've tried setting a boundary myself in the constructor of MultipartContent, but this doesn't make any difference.

When dumping Content.Headers.ContentType.ToString () from the request, it correctly outputs the header including the boundary, so it's kind of surprising it's missing from the actual request being sent. It's as if only the MediaType property is being sent, while the Parameters property is completely ignored.

@rogihee
Copy link
Contributor

rogihee commented Oct 6, 2014

Having the exact same issue, 2.0.1 works fine, while the latest 2.1.2 is missing this part exactly. Take this for example:

public async void PostFile(string url, string fileNameLocal, CancellationToken cancellationToken)
        {
            var fileName = Path.GetFileName(fileNameLocal);
            using (var client = new HttpClient(new NativeMessageHandler()))
            {
                var fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read);
                var streamContent = new StreamContent(fileStream);
                streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
                streamContent.Headers.ContentDisposition.Name = "\"file\"";
                streamContent.Headers.ContentDisposition.FileName = "\"" + fileName + "\"";
                streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                string boundary = "12345";
                var fContent = new MultipartFormDataContent(boundary);
                fContent.Headers.Remove("Content-Type");
                fContent.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
                fContent.Add(streamContent);
                var response = await client.PostAsync(new Uri(url), fContent, cancellationToken);
                response.EnsureSuccessStatusCode();
                streamContent.Dispose();
                fileStream.Dispose();
            }
        }

@talanta
Copy link

talanta commented Oct 14, 2014

That took me a day to try to force the boundar. I also tried to use OkhttpClient directly: I got the boundary, but I lost the Content-Length, and that Amazon S3 doesn't like.

So at the end I moved back to 2.0.1, now I works, at least I can upload images on S3. Thank you @rogihee

@anaisbetts
Copy link
Owner

Can someone create a repro project that shows the bug? http://httpbin.org might be helpful here

@rogihee
Copy link
Contributor

rogihee commented Oct 30, 2014

Hi Paul,

don’t have a complete sample, but here is some code for posting a file to an url in multi-part form that is working in 2.0, but not in the latest version:

public async void PostRequestFileImpl(string url, string localFileName, CancellationToken cancelToken, Action<string, Exception> result)

    {

        try

        {

            var fileName = Path.GetFileName(localFileName);

            using (var fClient = new HttpClient(new NativeMessageHandler()))

            {

                var fileStream = File.Open(localFileName, FileMode.Open, FileAccess.Read);

                var streamContent = new StreamContent(fileStream);

                streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");

                streamContent.Headers.ContentDisposition.Name = "\"file\"";

                streamContent.Headers.ContentDisposition.FileName = "\"" + fileName + "\"";

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

                string boundary = Guid.NewGuid().ToString();

                var content = new MultipartFormDataContent(boundary);

                content.Headers.Remove("Content-Type");

                content.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);

                content.Add(streamContent);



                var response = await fClient.PostAsync(new Uri(url), content, cancelToken);

                response.EnsureSuccessStatusCode();

                string resultStr = await response.Content.ReadAsStringAsync();

                streamContent.Dispose();

                fileStream.Dispose();

                result.Invoke(resultStr, response.IsSuccessStatusCode ? null : new Exception("Error posting: " + response.StatusCode));

            }

        }

        catch (Exception e)

        {

            result.Invoke("Could not post file", e);

        }

    }

I was already attempting in fixing it, and was trying something in the SendAsync method in the OkNetworkHttHandler:

var body = default(RequestBody);

        if (request.Content != null) {

            var bytes = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

            if (request.Content.Headers.ContentType.MediaType.Contains("multipart"))

            {

                var multiContent = request.Content as MultipartContent;

                var multiBuilder = new MultipartBuilder().Type(MultipartBuilder.Form);

                foreach (var part in multiContent.ToList())

                {

                    var partStream = part as StreamContent;

                    var partBytes = await partStream.ReadAsByteArrayAsync();

                    multiBuilder.AddPart(RequestBody.Create(MediaType.Parse(part.Headers.ContentType.MediaType), partBytes));

                }

                body = multiBuilder.Build();

            }

            else

            {

                body = RequestBody.Create(MediaType.Parse(request.Content.Headers.ContentType.MediaType), bytes);

            }

        }

I’m too unfamiliar with Ok for this to completely understand / grasp, but this was the general direction I was looking for a possible solution.

Best regards,

Rogier

From: Paul Betts [mailto:[email protected]]
Sent: woensdag 29 oktober 2014 22:59
To: paulcbetts/ModernHttpClient
Cc: Rogier van der Hee
Subject: Re: [ModernHttpClient] Multipart form-data boundary is missing from Content-type header (#92)

Can someone create a repro project that shows the bug? http://httpbin.org might be helpful here


Reply to this email directly or view it on GitHub #92 (comment) . https://github.com/notifications/beacon/AEwOppgbNO2ngFL7l_861W0ml1xDqr_mks5nIVqtgaJpZM4Co4Eq.gif

@anaisbetts
Copy link
Owner

@rogihee Cool - can you create a PR that modifies Playground.Android to show this bug, using this code above?

@rogihee
Copy link
Contributor

rogihee commented Nov 2, 2014

I'm on it

@anaisbetts
Copy link
Owner

@rogihee Any progress on this?

@rogihee
Copy link
Contributor

rogihee commented Nov 6, 2014

Was about to PM you but it's gonna be the weekend, too busy in the day job. I checked it in iny private repo though, but really needs testing before doing PR.

@anaisbetts
Copy link
Owner

@rogihee Whatever you've got is fine, you can always make more commits to continue work on a PR. You should always open PRs when you start working on something, not when you finish it!

@anaisbetts
Copy link
Owner

This is fixed! Huge thanks to @rogihee for the repro app.

@rogihee
Copy link
Contributor

rogihee commented Nov 10, 2014

Glad you found the bug! Thanx for fixing it ;-)

@Qonstrukt
Copy link
Author

Thanks for picking this up @paulcbetts and @rogihee!

@radhe12
Copy link

radhe12 commented Jul 9, 2018

@rogihee could you please guide me on this "https://stackoverflow.com/users/8875271/radhey-g"

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

No branches or pull requests

5 participants