dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.29k stars 4.74k forks source link

reuse httpclient failed on windows. #22516

Closed jiabiao closed 4 years ago

jiabiao commented 7 years ago

The following code works fine under full framework and Linux, but failed on windows .net core.

csproj:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

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

</Project>

Program.cs

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

namespace HttpClientIssue
{
    class Program
    {
        static void Main(string[] args)
        {
            Run().Wait();
            Console.Read();
        }

        static async Task Run()
        {
            HttpClient client = new HttpClient()
            {
                BaseAddress = new Uri("http://www.github.com")
            };
            await client.PostAsXmlAsync("", new object());
            await client.PostAsXmlAsync("", new object());
        }
    }   
}

Exception: Unhandled Exception: System.AggregateException: One or more errors occurred. (Error while copying content to a stream.) ---> System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: The write operation failed, see inner exception. ---> System.Net.Http.WinHttpException: The connection with the server was terminated abnormally

image

please see the screenshot of fiddler, it seems there are some bytes left in the request stream buffer that cause the second request header parsing failed.

System.Net.Http.Formatting.dll include many useful extension methods of HttpClient which has not been include in corefx. The aspnetwebstack project is not maintained. But i think corefx should be able to fix this issue as these code works well on Linux.

davidsh commented 7 years ago

Thanks for the detailed bug report.

Based on what you describe, would you say this is a bug in the System.Net.Http.Formatting.dll and specifically the PostAsXmlAsync() extension method? If so, we need to move this bug to the ASP.NET repo.

Or do you think this is a bug in the CoreFx HTTP stack when using PostAsXmlAsync() ?

jiabiao commented 7 years ago

I didn't go deep into this issue, and don't know the root cause of it. Because these code works on Linux , so I guess it may be a bug in the CoreFx HTTP stack on windows.

ism commented 7 years ago

I could not reproduce it.

github2

jiabiao commented 7 years ago

This should be a bug on corefx ,but it have been fixed in netcore2.0 preview2 Steps to reproduce the issue under preview1: (no reference to System.Net.Http.Formatting.dll )

  1. open fiddler
  2. checked Tools->Options->Connections->Reuse client connections
  3. run the codes

csproj:

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp2.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>  <!--force downgrade to netcore2.0 preview1-->
        <PackageReference Update="Microsoft.NETCore.App" Version="2.0.0-preview1-002111-00" />
    </ItemGroup>
</Project>

program.cs:

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

public class MyContent : HttpContent
{
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        using (XmlWriter writer = XmlWriter.Create(stream))
        {
            var serializer = new XmlSerializer(typeof(object));
            serializer.Serialize(writer, new object());
        }
        return Task.CompletedTask;
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Run().Wait();
        Console.Read();
    }

    static async Task Run()
    {
        HttpClient client = new HttpClient()
        {
            BaseAddress = new Uri("https://mws.amazonservices.com")
        };

        await client.PostAsync("", new MyContent());
        await client.PostAsync("", new MyContent());
        Console.WriteLine("ok");
    }
}
davidsh commented 7 years ago

Since you are no observing the bug anymore with the .NET Core 2.0 pre-release binaries, it is possible that your issue was related to dotnet/corefx#19082 change where we no longer dispose the request body content after doing a PostAsync or SendAsync. However, that change only affected scenarios where the same HttpContent object was being re-used. In your repro above, you are creating new content objects with each PostAsync:

await client.PostAsync("", new MyContent());
await client.PostAsync("", new MyContent());

So, this is interesting that you observed that behavior with separate content objects.

I am going to re-open this issue so that we have time to investigate this further even though you are not seeing the behavior anymore in .NET Core 2.0.

davidsh commented 6 years ago

No longer can repro in current .NET Core.