maui icon indicating copy to clipboard operation
maui copied to clipboard

AndroidMessageHandler or HttpClientHandler throw System.ObjectDisposedException: Cannot access a closed Stream

Open PontiacGTX opened this issue 3 years ago • 13 comments

Description

I am trying to do a local http post request from a http client but whenever I tried overriding to check the signed certificate by giving a HttpClientHandler or AndroidMessageHandler ot the http client, it ends up throwing an exception when I call to PostAsync method

Steps to Reproduce

Create a Maui Project On Program Add a HttpClient to the DI container

#if DEBUG
	     var httpHelper = new HttpClientHandlerHelper();

	     var insecureHandler =new CustomAndroidMessageHandler();//httpHelper.GetInsecureHandler()
        builder.Services.AddSingleton<HttpClient>(x=> new HttpClient(httpHelper.GetInsecureHandler(), false));

#else
		builder.Services.AddSingleton<HttpClient>();
#endif

declare a class to get the http client handler

HttpClientHandlerHelper



  public class HttpClientHandlerHelper:IDisposable
    {
        private bool disposedValue;

        public HttpClientHandler GetInsecureHandler()
        {
            HttpClientHandler handler = new HttpClientHandler();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
            {
               
                return true;
            };
            return handler;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: eliminar el estado administrado (objetos administrados)
                }

                // TODO: liberar los recursos no administrados (objetos no administrados) y reemplazar el finalizador
                // TODO: establecer los campos grandes como NULL
                disposedValue = true;
            }
        }

      
        public void Dispose()
        {
            // No cambie este código. Coloque el código de limpieza en el método "Dispose(bool disposing)".
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }

make some service using this HttpClient by DI and then call a method using PostAsync, but in the url the base url should contain the local ip in my case is like, while calling a webapi running

http://192.168.250.3:5000/api/Account/Create

Version with bug

6.0 Release Candidate 2 or older

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

API 30 Android 11, WIndows 10 1709

Did you find any workaround?

Not yet

Relevant log output

System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.EnsureNotClosed()
   at System.IO.MemoryStream.get_Length()
   at System.Net.Http.HttpContent.GetComputedOrBufferLength()
   at System.Net.Http.Headers.HttpContentHeaders.get_ContentLength()
   at Xamarin.Android.Net.AndroidMessageHandler.SetupRequestBody(HttpURLConnection httpConnection, HttpRequestMessage request) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 1132
   at Xamarin.Android.Net.AndroidMessageHandler.SetupRequestInternal(HttpRequestMessage request, URLConnection conn) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 984
   at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 374
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at MauiChat.Common.Services.ChatServicesService.CreateUser(String phoneNumber) in C:\Users\PontiacGTX\Source\Repos\MauiChat\Common\Services\ChatServicesService.cs:line 38}

PontiacGTX avatar Sep 14 '22 13:09 PontiacGTX

Since you're registering as a singleton I'm guessing you're disposing of it and it keeps returning the disposed instance but need a sample to be sure.

PureWeen avatar Sep 14 '22 18:09 PureWeen

Since you're registering as a singleton I'm guessing you're disposing of it and it keeps returning the disposed instance but need a sample to be sure.

ok try this repo https://github.com/PontiacGTX/MauiChat/tree/PontiacGTX-BRANCH

PontiacGTX avatar Sep 14 '22 19:09 PontiacGTX

Since you're registering as a singleton I'm guessing you're disposing of it and it keeps returning the disposed instance but need a sample to be sure.

I have looking and I think if I could pass my own HttpMessageInvoker I could assign it to not be disposed https://github.com/dotnet/runtime/blob/4ff5a3a85a9b0de7ab9e9267959e668142815f4f/src/libraries/System.Net.Http/src/System/Net/Http/HttpMessageInvoker.cs#L124

https://microsoft.github.io/reverse-proxy/articles/http-client-config.html

Edit: also I tried instancing a new httpclient witout DI and it keep saying that it couldnt read on a disposed Stream

PontiacGTX avatar Sep 14 '22 22:09 PontiacGTX

@PontiacGTX If you configure the HttpClient using AddTransient instead of AddSingleton, does the problem persist?

hartez avatar Sep 15 '22 21:09 hartez

Hi @PontiacGTX. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

msftbot[bot] avatar Sep 15 '22 21:09 msftbot[bot]

@PontiacGTX If you configure the HttpClient using AddTransient instead of AddSingleton, does the problem persist?

same exception using AndroidMessageHandler, somewhere it is disposing it or not reading to the end of the stream correctly or I dont know what else to think also I see AndroidMessageHandler has 1012 lines and the repository has like 330s

is there a difference I am using this legacy file? https://github.com/xamarin/xamarin-android/blob/6290a9c63251e7bbbb3a40ab5bdd230688267b85/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs#L363

System.ObjectDisposedException: Cannot access a closed Stream. at System.IO.MemoryStream.EnsureNotClosed() at System.IO.MemoryStream.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) at Xamarin.Android.Net.AndroidMessageHandler.WriteRequestContentToOutput(HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 458 at Xamarin.Android.Net.AndroidMessageHandler.DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 533 at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 375 at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at MauiChat.Common.HelperClass.HttpClientHelper.PostStringDataAsync(HttpClient httpClient, Object data, String url) in C:\Users\PontiacGTX\source\repos\MauiChat\Common\HelperClass\HttpClientHelper.cs:line 19 at MauiChat.Common.Services.ChatServicesService.CreateUser(String phoneNumber) in C:\Users\PontiacGTX\source\repos\MauiChat\Common\Services\ChatServicesService.cs:line 39}

PontiacGTX avatar Sep 16 '22 13:09 PontiacGTX

@PontiacGTX does the endpoint you are posting to respond with a redirect?

hartez avatar Sep 16 '22 15:09 hartez

Hi @PontiacGTX. We have added the "s/needs-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

msftbot[bot] avatar Sep 16 '22 15:09 msftbot[bot]

@PontiacGTX does the endpoint you are posting to respond with a redirect?

no I have tried it with postman and returns Ok and then I see that it isnt being called with my debugger. I think IISExpress should be fine since I am doing a request from a local ip, curiously if the server has a timeout the the http client states it has time out but when reading the message it seems to have issues I will try again uninstalling my previous Android SDKs and see if that is the issue, or how can I use the latest version of AndroidClientHandler?

uninstalled all sdk except 30/31 android 11/12

and still the same exception using the default handler for android

System.ObjectDisposedException: Cannot access a closed Stream. at System.IO.MemoryStream.EnsureNotClosed() at System.IO.MemoryStream.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) at Xamarin.Android.Net.AndroidMessageHandler.WriteRequestContentToOutput(HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 458 at Xamarin.Android.Net.AndroidMessageHandler.DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 533 at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 375 at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) at MauiChat.Common.HelperClass.HttpClientHelper.PostStringDataAsync(HttpClient httpClient, Object data, String url) in C:\Users\PontiacGTX\source\repos\MauiChat\Common\HelperClass\HttpClientHelper.cs:line 19 at MauiChat.Common.Services.ChatServicesService.CreateUser(String phoneNumber) in C:\Users\PontiacGTX\source\repos\MauiChat\Common\Services\ChatServicesService.cs:line 39}

using

        {
            HttpClientHandler handler = new();
            handler.ClientCertificateOptions = ClientCertificateOption.Manual;
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
            {
                Console.WriteLine(message);
                return true;
            };
            return handler;
        }

returns

System.Net.WebException: Hostname 192.168.250.3 not verified: certificate: sha1/fVGKMV1jTGIwfF6V4Tb79pfSZ8Q= DN: CN=localhost subjectAltNames: [localhost] ---> Javax.Net.Ssl.SSLPeerUnverifiedException: Hostname 192.168.250.3 not verified: certificate: sha1/fVGKMV1jTGIwfF6V4Tb79pfSZ8Q= DN: CN=localhost subjectAltNames: [localhost] at Java.Interop.JniEnvironment.InstanceMethods.CallVoidMethod(JniObjectReference instance, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniEnvironment.g.cs:line 11884 at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeAbstractVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 17 at Javax.Net.Ssl.HttpsURLConnectionInvoker.Connect() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net6.0/android-31/mcw/Javax.Net.Ssl.HttpsURLConnection.cs:line 433 at Xamarin.Android.Net.AndroidMessageHandler.<>c__DisplayClass125_0.<ConnectAsync>b__0() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 450 at System.Threading.Tasks.Task.InnerInvoke() at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object obj) at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location --- at Xamarin.Android.Net.AndroidMessageHandler.DoProcessRequest(HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken, RequestRedirectionState redirectState) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 502 at Xamarin.Android.Net.AndroidMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs:line 375 --- End of managed Javax.Net.Ssl.SSLPeerUnverifiedException stack trace --- javax.net.ssl.SSLPeerUnverifiedException: Hostname 192.168.250.3 not verified: certificate: sha1/fVGKMV1jTGIwfF6V4Tb79pfSZ8Q= DN: CN=localhost subjectAltNames: [localhost] at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:205) at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:153) at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116) at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186) at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128) at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97) at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289) at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131) at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:90) at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:30)

--- End of managed Javax.Net.Ssl.SSLPeerUnverifiedException stack trace --- javax.net.ssl.SSLPeerUnverifiedException: Hostname 192.168.250.3 not verified: certificate: sha1/fVGKMV1jTGIwfF6V4Tb79pfSZ8Q= DN: CN=localhost subjectAltNames: [localhost] at com.android.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:205) at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:153) at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116) at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186) at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128) at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97) at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289) at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)

similar issue to https://github.com/dotnet/runtime/issues/70434

PontiacGTX avatar Sep 16 '22 16:09 PontiacGTX

Also seeing this issue. Only affects POST calls with content and the HttpClient is injected, we see this. If we use the same HttpClient for a get, or a post with no content, it works fine.

However, if we create a brand new Http client immediately before the call, it's fine.

joebeernink avatar Sep 21 '22 02:09 joebeernink

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

msftbot[bot] avatar Sep 21 '22 18:09 msftbot[bot]

Also seeing this issue. Only affects POST calls with content and the HttpClient is injected, we see this. If we use the same HttpClient for a get, or a post with no content, it works fine.

However, if we create a brand new Http client immediately before the call, it's fine.

So what should I do is instancing a new http client before making a post?as a workaround?

PontiacGTX avatar Sep 21 '22 21:09 PontiacGTX

This is what we had to do. It's not great, but at least it unblocked us.

public async Task AddAttendeeAsync(EventAttendee eventAttendee, CancellationToken cancellationToken = default) { try { var content = JsonContent.Create(eventAttendee, typeof(EventAttendee), null, SerializerOptions);

           var authorizedHttpClient = HttpClientService.CreateAuthorizedClient();
            var httpClient = new HttpClient();
            httpClient.BaseAddress = authorizedHttpClient.BaseAddress;



           using (var response = await httpClient.PostAsync(EventAttendeeApi, content, cancellationToken))
            {
                response.EnsureSuccessStatusCode();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"\tERROR {0}", ex.Message);
            throw;
        }
    }

joebeernink avatar Sep 21 '22 21:09 joebeernink