Skip to content

HttpMessageContent breaks on dotnet core 2.1 on Mac #193

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

Closed
aliostad opened this issue Aug 24, 2018 · 12 comments
Closed

HttpMessageContent breaks on dotnet core 2.1 on Mac #193

aliostad opened this issue Aug 24, 2018 · 12 comments
Assignees
Labels
external PRI: 1 - Required Must be handled in a reasonable time

Comments

@aliostad
Copy link

aliostad commented Aug 24, 2018

[Originally created here https://github.com/dotnet/corefx/issues/31918 but was closed there]

Hi,

[NOT ENTIRELY SURE IF THIS IS ASP.NET OR COREFX]

I have an HTTP Caching library for .NET and I use HttpMessageContent class to help me serialise and deseralise the messages. This has been working throughout including .NET Core 2.0 but it seems to have been broken by .NET Core 2.1 on Mac:

System.InvalidOperationException: Error parsing HTTP message header byte 697 of message System.Byte[].
   at System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsyncCore(HttpContent content, Int32 bufferSize, Int32 maxHeaderSize, CancellationToken cancellationToken)
   at CacheCow.Client.MessageContentHttpMessageSerializer.DeserializeToResponseAsync(Stream stream)
   at CacheCowCrashRepro.TestCacheStore.AddOrUpdateAsync(CacheKey key, HttpResponseMessage response) in C:\Users\james\Documents\Projects\CacheCowCrashRepro\CacheCowCrashRepro\Program.cs:line 45
   at CacheCow.Client.CachingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
   at CacheCowCrashRepro.Program.Main(String[] args) in C:\Users\james\Documents\Projects\CacheCowCrashRepro\CacheCowCrashRepro\Program.cs:line 19}	System.Exception {System.InvalidOperationException

Here is the repro code. Works with netcoreapp2.0 but breaks with netcoreapp2.1.

Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.6" />
  </ItemGroup>
</Project>

Program.cs:

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace CacheCowCrashRepro
{
    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                var serializer = new MessageContentHttpMessageSerializer();
                var client = new HttpClient();
                var request = new HttpRequestMessage(HttpMethod.Get, new Uri("https://google.com"));
                var response = await client.SendAsync(request);
                var ms = new MemoryStream();
                await serializer.SerializeAsync(response, ms);
                ms.Position = 0;
                // var bytes = ms.ToArray();
                // File.WriteAllBytes("response.bin", bytes); // to store 
                var r2 = await serializer.DeserializeToResponseAsync(ms);
                Console.WriteLine(response);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }

    public class MessageContentHttpMessageSerializer
    {
        private bool _bufferContent;

        public MessageContentHttpMessageSerializer()
            : this(true)
        {

        }

        public MessageContentHttpMessageSerializer(bool bufferContent)
        {
            _bufferContent = bufferContent;
        }

        public async Task SerializeAsync(HttpResponseMessage response, Stream stream)
        {
            if (response.Content != null)
            {
                if (_bufferContent)
                    await response.Content.LoadIntoBufferAsync();
            }

            var httpMessageContent = new HttpMessageContent(response);
            var buffer = await httpMessageContent.ReadAsByteArrayAsync();
            stream.Write(buffer, 0, buffer.Length);
        }

        public async Task SerializeAsync(HttpRequestMessage request, Stream stream)
        {
            if (request.Content != null && _bufferContent)
            {
                await request.Content.LoadIntoBufferAsync();
            }

            var httpMessageContent = new HttpMessageContent(request);
            var buffer = await httpMessageContent.ReadAsByteArrayAsync();
            stream.Write(buffer, 0, buffer.Length);
        }

        public async Task<HttpResponseMessage> DeserializeToResponseAsync(Stream stream)
        {
            var response = new HttpResponseMessage();
            response.Content = new StreamContent(stream);
            response.Content.Headers.Add("Content-Type", "application/http;msgtype=response");
            var responseMessage = await response.Content.ReadAsHttpResponseMessageAsync();
            if (responseMessage.Content != null && _bufferContent)
                await responseMessage.Content.LoadIntoBufferAsync();
            return responseMessage;
        }

        public async Task<HttpRequestMessage> DeserializeToRequestAsync(Stream stream)
        {
            var request = new HttpRequestMessage();
            request.Content = new StreamContent(stream);
            request.Content.Headers.Add("Content-Type", "application/http;msgtype=request");
            var requestMessage = await request.Content.ReadAsHttpRequestMessageAsync();
            if (requestMessage.Content != null && _bufferContent)
                await requestMessage.Content.LoadIntoBufferAsync();
            return requestMessage;
        }
    }
}

Here is the response I get which is nothing special. The only thing I notice is that there is no ContentLength header and encoding is chunked but looking at the message, I could not see a chunked encoding, the response is all in one block - maybe I missed.

response.bin

@mkArtakMSFT
Copy link
Member

@aliostad we'll have a look and get back to you.

@danroth27
Copy link
Member

This has been working throughout including .NET Core 2.0 but it seems to have been broken by .NET Core 2.1 on Mac.

@aliostad Broken how? Are you getting some sort of error?

@dougbu
Copy link
Member

dougbu commented Sep 4, 2018

@aliostad the response.bin trace you provided seems to contain the HTML for a Google search form. We'll need more information along the lines of @danroth27's questions to investigate further.

@aliostad
Copy link
Author

aliostad commented Sep 5, 2018

@danroth27 sorry I thought I put the error message but I have not. OK updated the issue.

@aliostad
Copy link
Author

aliostad commented Sep 5, 2018

@dougbu the point of that file is that the payload is NOT chunked encoding and I have a feeling it might be somehow related.

I have given you repro code, just run it and see.

@dougbu dougbu self-assigned this Sep 5, 2018
@dougbu
Copy link
Member

dougbu commented Sep 5, 2018

@aliostad I can't get to this immediately but will test things out within a week.

@aliostad
Copy link
Author

Any news? It is more than a week.

@dougbu
Copy link
Member

dougbu commented Sep 18, 2018

My apologies @aliostad. Due to other priorities, I'm not exactly sure when I'll get back to this investigation. Will let you know as soon as I've had a chance to look at this.

@dougbu dougbu added the PRI: 1 - Required Must be handled in a reasonable time label Nov 6, 2018
@xela30
Copy link

xela30 commented Nov 9, 2018

Any updates, guys? BTW, it doesn't work on Windows either.

@aliostad
Copy link
Author

aliostad commented Nov 9, 2018

Yes, please at least tell us if you can confirm it is a bug at your side?

@dougbu
Copy link
Member

dougbu commented Nov 9, 2018

I should have some time to look at this issue next week.

@dougbu
Copy link
Member

dougbu commented Nov 15, 2018

@aliostad I'm about to reactivate dotnet/corefx#31918 because this appears to be a break between .NET Core 2.0 and 2.1. w.r.t. how they parse date headers. In 2.0, Expires: -1 is treated as valid. In 2.1, the parsers throw a FormatException on this header. I'll add more information to dotnet/corefx#31918.

Note the issue doesn't seem to have anything to do with whether the response is chunked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
external PRI: 1 - Required Must be handled in a reasonable time
Projects
None yet
Development

No branches or pull requests

5 participants