Bug 58641 - Using HttpResponseMessage after HttpClient.Dispose()
Summary: Using HttpResponseMessage after HttpClient.Dispose()
Status: CONFIRMED
Alias: None
Product: Class Libraries
Classification: Mono
Component: System.Net.Http (show other bugs)
Version: 5.4 (2017-06)
Hardware: Other Linux
: --- normal
Target Milestone: Future Release
Assignee: Martin Baulig
URL:
Depends on:
Blocks:
 
Reported: 2017-08-08 12:06 UTC by Andy
Modified: 2018-01-19 13:57 UTC (History)
5 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 58641 on GitHub or Developer Community if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: GitHub Markdown or Developer Community HTML
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
CONFIRMED

Description Andy 2017-08-08 12:06:14 UTC
Running Mono 5.4.0.36

When using a HttpClient to download and you set the HttpCompletionOption to ResponseHeadersRead, if you then read the HttpResponseMessage content and pass it to a file stream to copy to you will get a "Cannot access a disposed object."

var httpClient = new HttpClient();
var response = httpClient.GetAsync("SomeURL", HttpCompletionOption.ResponseHeadersRead).Result;

using (var streamToReadFrom = response.Content.ReadAsStreamAsync().Result)
{
    using (var streamToWriteTo = File.Open("C:\DownlodedFile.dat", FileMode.Create))
    {
        streamToReadFrom.CopyTo(streamToWriteTo);
    }
}


Object name: 'System.Net.Sockets.NetworkStream'. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.NetworkStream'.
  at System.Net.WebConnection.BeginRead (System.Net.HttpWebRequest request, System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.AsyncCallback cb, System.Object state) [0x0002b] in <630c0f79573e4170a5c7a506b24194ce>:0 
  at System.Net.WebConnectionStream.BeginRead (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.AsyncCallback cb, System.Object state) [0x0017d] in <630c0f79573e4170a5c7a506b24194ce>:0 
  at System.Net.WebConnectionStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) [0x00007] in <630c0f79573e4170a5c7a506b24194ce>:0 
  at System.IO.Stream.InternalCopyTo (System.IO.Stream destination, System.Int32 bufferSize) [0x00012] in <5d6981f842764a0d98ada564ac3cd92e>:0 
  at System.IO.Stream.CopyTo (System.IO.Stream destination) [0x00084] in <5d6981f842764a0d98ada564ac3cd92e>:0 
  at (wrapper remoting-invoke-with-check) System.IO.Stream:CopyTo (System.IO.Stream)
Comment 1 Marek Safar 2017-09-08 22:59:42 UTC
I cannot reproduce the issue locally using the following repro.


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

namespace Test1234
{
	class MainClass
	{
		public static void Main(string[] args)
		{
			var httpClient = new HttpClient ();
			var response = httpClient.GetAsync ("https://en.wikipedia.org/wiki/Main_Page", HttpCompletionOption.ResponseHeadersRead).Result;

			using (var streamToReadFrom = response.Content.ReadAsStreamAsync ().Result) {
				using (var streamToWriteTo = new MemoryStream ()) {
					streamToReadFrom.CopyTo (streamToWriteTo);
				}
			}
		}
	}
}


In order to investigate the issue further can you please attach a reproduction project and steps to reproduce this issue? For help on writing a bug report, please see our guide on this topic:

https://bugzilla.xamarin.com/page.cgi?id=bug-writing.html

Some of your next steps would be:

1. Including a sample project or steps to reproduce this problem
2. Your Version Information
3. Your expected results and actual results

Thank you!
Comment 2 Andy 2017-09-13 15:00:20 UTC
I've found the problem. In my application there is a call to the HttpClient and the returned HttpResponseMessage is passed to another method. This leaves the HttpClient object ready for disposing. However the second call would try and read the steam when the HttpClient had been disposed. 

The example below runs fine under Windows and I will change my code so that the HttpClient is not immediately disposed. I'm happy for this to be closed but any thoughts?


using System.IO;
using System.Net.Http;

namespace MonoTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var response = GetMedia();

            StoreResponseData(response);
        }

        private static HttpResponseMessage GetMedia()
        {
            using (var httpClient = new HttpClient())
            {
               return httpClient.GetAsync("https://en.wikipedia.org/wiki/Main_Page", HttpCompletionOption.ResponseHeadersRead).Result;
            }
        }

        private static void StoreResponseData(HttpResponseMessage response)
        {
            using (var streamToReadFrom = response.Content.ReadAsStreamAsync().Result)
            {
                using (var streamToWriteTo = new MemoryStream())
                {
                    streamToReadFrom.CopyTo(streamToWriteTo);
                }
            }
        }
    }
}
Comment 3 Marek Safar 2017-09-14 10:14:05 UTC
I can reproduce it now
Comment 4 Martin Baulig 2017-12-11 21:53:42 UTC
Ok, I spent some time playing around with this and it looks like .NET only cancels _pending_ requests when HttpClient.Dispose() is called.

It will not actually close the underlying connection as Mono's implementation currently does.

Since there is an easy workaround, I would suggest that we spend some time improving our test coverage for HttpClient before we address this issue.  I would especially like to have tests for timeouts, aborts and all that.
Comment 5 Marek Safar 2018-01-19 13:57:12 UTC
Martin, considering this has very simple repro could you look into addressing the issue first before exploring all other possible problems.