runtime: HttpMessageContent breaks on dotnet core 2.1

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.

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

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 3
  • Comments: 44 (31 by maintainers)

Most upvoted comments

Ignoring the value is not quite enough, the presence of the header is significant. If the client asks for the value of that header from the strongly typed property they shouldn’t get null, they should get DateTime.MinValue or similar.

if it is CoreFX problem, then please isolate a repro

using System.Net.Http.Headers;

class Program
{
    static void Main()
    {
        var headers = new DerivedHttpHeaders();
        headers.Add("Expires", "-1");
    }
}

internal sealed class DerivedHttpHeaders : HttpHeaders { }

Yes please, you seem to have the most context on the issue.

@davidsh Repro is exactly the same and the error is exactly the same:

System.InvalidOperationException: Error parsing HTTP message header byte 818 of message System.Byte[].
   at System.Net.Http.HttpContentMessageExtensions.ReadAsHttpResponseMessageAsyncCore(HttpContent content, Int32 bufferSize, Int32 maxHeaderSize, CancellationToken cancellationToken)
   at CacheCowCrashRepro.MessageContentHttpMessageSerializer.DeserializeToResponseAsync(Stream stream) in C:\Users\aliostad\RiderProjects\ConsoleApp1\Program.cs:line 78
   at CacheCowCrashRepro.Program.Main(String[] args) in C:\Users\aliostad\RiderProjects\ConsoleApp1\Program.cs:line 23

How do you say it is fixed? Simply put the repro code I have supplied and bang it crashes the same way in .NET 3.0 as in .NET 2.1.

I can create a new issue but not sure why.

Why do you think it is that critical? I see only 3 people commenting here and no upvotes on the top post. It does not seem to be affecting that many.

If you can submit PR, that would significantly increase chances to get it fixed in 3.0. We would definitely appreciate that!

Ignoring the value is not quite enough, the presence of the header is significant. If the client asks for the value of that header from the strongly typed property they shouldn’t get null, they should get DateTime.MinValue or similar.

Yes, that is probably a good compromise especially since the RFC implies that invalid date formats should be treated as sometime in the “past”.