unosquare / embedio

A tiny, cross-platform, module based web server for .NET
http://unosquare.github.io/embedio
Other
1.47k stars 176 forks source link

Response intended as chunked if ContentLength is set using Response.Headers[HttpHeaderNames.ContentLength] #355

Closed AbeniMatteo closed 5 years ago

AbeniMatteo commented 5 years ago

To Reproduce Run the following snippet:

namespace Repro
{
    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    using EmbedIO;

    class Program
    {
        static void Main()
        {
            Test().Wait();
        }

        async static Task Test()
        {
            var data = Encoding.UTF8.GetBytes("content");

            var server = new WebServer(12345);
            server.WithAction("/correct", HttpVerbs.Get, async context =>
            {
                context.Response.ContentLength64 = data.Length;
                await context.Response.OutputStream.WriteAsync(data, 0, data.Length);
            });
            server.WithAction("/wrong", HttpVerbs.Get, async context =>
            {
                context.Response.Headers[HttpHeaderNames.ContentLength] = data.Length.ToString();
                await context.Response.OutputStream.WriteAsync(data, 0, data.Length);
            });

            server.Start();

            var correct = await GetByteArrayAsync(new Uri("http://localhost:12345/correct"));

            var wrong = await GetByteArrayAsync(new Uri("http://localhost:12345/wrong"));
            // wrong[0] = '7' char (data length)
            // wrong[1] = cr
            // wrong[2] = lf
            // wrong[3] = data[0] = correct[0]
            // wrong[4] = data[1] = correct[1]
            // ...
        }

        static async Task<byte[]> GetByteArrayAsync(Uri url)
        {
            var httpClient = new HttpClient();
            var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, url));
            return await response.Content.ReadAsByteArrayAsync();
        }
    }
}

Expected behavior /correct and /wrong responses should be the same.

Cause Using Response.Headers[HttpHeaderNames.ContentLength] doesn't set _clSet to true so the response is handled here as chunked. This will cause the various clients to wait for additional bytes or to truncate the response (like the repo case).

Version 3.0.4

geoperez commented 5 years ago

I created a unit test of your issue, but at least with TestWebServer this is not happening.

geoperez commented 5 years ago

Forget my last comment, TestWebServer is not using HttpListenerRequest.

geoperez commented 5 years ago

@AbeniMatteo check my PR, I think I resolved the issue.

As side-note, this problem might arise for ContentType too.

rdeago commented 5 years ago

Sorry to be the annoying old uncle here, but do we really want to fix all issues with the obsolete HTTP implementation we more or less inherited from an old version of Mono?

I think it's about time we start a serious and productive conversation on which HTTP implementation we want for EmbedIO. Which means HttpListener has to go. Otherwise we're wasting time and effort on old, intrinsecally obsolete, and quite messed-up code, instead of solving problems once and for all by replacing it. Just my 2 cents.

geoperez commented 5 years ago

I did the merge, today or tomorrow I will release a new nuget. Probably with the new version of SWAN.

@rdeago I'm open to kill the Http Listener, how would you like to start?

geoperez commented 5 years ago

The new nuget was released as version 3.0.5!

AbeniMatteo commented 5 years ago

Works fine now, thank you.

I'm leaving this issue open if you want to continue the HttpListener discussion. Feel free to close it at any time.

rdeago commented 5 years ago

@rdeago I'm open to kill the Http Listener, how would you like to start?

I think this matter deserves its own issue, so I am going to close this one.

We could also have a preliminary discussion on Slack, to examine our options at a somewhat quicker rate.

rdeago commented 5 years ago

@AbeniMatteo your valuable insights are of course also appreciated on EmbedIO's Slack workspace, to which I hereby renew my invitation for you to join. We now have a channel to specifically discuss the HTTP protocol implementation change.