diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs index 542d85e2bd7..7e802c5966a 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs @@ -361,7 +361,13 @@ Task ConnectAsync (HttpURLConnection httpConnection, CancellationToken ct) protected virtual async Task WriteRequestContentToOutput (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) { using (var stream = await request.Content.ReadAsStreamAsync ().ConfigureAwait (false)) { - await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested (); + try { + await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false); + } catch (ObjectDisposedException ex) { + Logger.Log (LogLevel.Error, LOG_APP, $"Stream disposed while copying the content stream to output: {ex}"); + cancellationToken.ThrowIfCancellationRequested (); + } // // Rewind the stream to beginning in case the HttpContent implementation @@ -381,8 +387,15 @@ protected virtual async Task WriteRequestContentToOutput (HttpRequestMessage req // // See https://bugzilla.xamarin.com/show_bug.cgi?id=55477 // - if (stream.CanSeek) - stream.Seek (0, SeekOrigin.Begin); + if (stream.CanSeek) { + cancellationToken.ThrowIfCancellationRequested (); + try { + stream.Seek (0, SeekOrigin.Begin); + } catch (ObjectDisposedException ex) { + Logger.Log (LogLevel.Error, LOG_APP, $"Stream disposed while seeking to the beginning of the content stream: {ex}"); + cancellationToken.ThrowIfCancellationRequested (); + } + } } } diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs index e5218687ae7..7f0caae20b8 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs @@ -569,29 +569,43 @@ protected virtual async Task WriteRequestContentToOutput (HttpRequestMessage req if (request.Content is null) return; - var stream = await request.Content.ReadAsStreamAsync ().ConfigureAwait (false); - await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false); - - // - // Rewind the stream to beginning in case the HttpContent implementation - // will be accessed again (e.g. after redirect) and it keeps its stream - // open behind the scenes instead of recreating it on the next call to - // ReadAsStreamAsync. If we don't rewind it, the ReadAsStreamAsync - // call above will throw an exception as we'd be attempting to read an - // already "closed" stream (that is one whose Position is set to its - // end). - // - // This is not a perfect solution since the HttpContent may do weird - // things in its implementation, but it's better than copying the - // content into a buffer since we have no way of knowing how the data is - // read or generated and also we don't want to keep potentially large - // amounts of data in memory (which would happen if we read the content - // into a byte[] buffer and kept it cached for re-use on redirect). - // - // See https://bugzilla.xamarin.com/show_bug.cgi?id=55477 - // - if (stream.CanSeek) - stream.Seek (0, SeekOrigin.Begin); + using (var stream = await request.Content.ReadAsStreamAsync ().ConfigureAwait (false)) { + cancellationToken.ThrowIfCancellationRequested (); + try { + await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false); + } catch (ObjectDisposedException ex) { + Logger.Log (LogLevel.Error, LOG_APP, $"Stream disposed while copying the content stream to output: {ex}"); + cancellationToken.ThrowIfCancellationRequested (); + } + + // + // Rewind the stream to beginning in case the HttpContent implementation + // will be accessed again (e.g. after redirect) and it keeps its stream + // open behind the scenes instead of recreating it on the next call to + // ReadAsStreamAsync. If we don't rewind it, the ReadAsStreamAsync + // call above will throw an exception as we'd be attempting to read an + // already "closed" stream (that is one whose Position is set to its + // end). + // + // This is not a perfect solution since the HttpContent may do weird + // things in its implementation, but it's better than copying the + // content into a buffer since we have no way of knowing how the data is + // read or generated and also we don't want to keep potentially large + // amounts of data in memory (which would happen if we read the content + // into a byte[] buffer and kept it cached for re-use on redirect). + // + // See https://bugzilla.xamarin.com/show_bug.cgi?id=55477 + // + if (stream.CanSeek) { + cancellationToken.ThrowIfCancellationRequested (); + try { + stream.Seek (0, SeekOrigin.Begin); + } catch (ObjectDisposedException ex) { + Logger.Log (LogLevel.Error, LOG_APP, $"Stream disposed while seeking to the beginning of the content stream: {ex}"); + cancellationToken.ThrowIfCancellationRequested (); + } + } + } } internal Task WriteRequestContentToOutputInternal (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)