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)

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


Attachments

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.

Note You need to log in before you can comment on or make changes to this bug.