Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to copy HttpContent async and cancelable?

I'm using HttpClient.PostAsync() and the response is an HttpResponseMessage. Its Content property is of type HttpContent which has a CopyToAsync() method. Unfortunately, this is not cancelable. Is there a way to get the response copied into a Stream and pass a CancellationToken?

I am not stuck with CopyToAsync()! If there is a workaround, that would be fine. Like read a couple of bytes, check if canceled, continue reading and so on.

The HttpContent.CreateContentReadStreamAsync() methods looks like it would be a candidate. Unfortunately, it's not available with my selected profile. Also unclear if it would read all data in one go and waste a lot of memory.

Note: I'm using this inside a PCL targeting WP8, Windows Store 8, .NET 4.5, Xamarin.iOS and Xamarin.Android

like image 272
Krumelur Avatar asked Jan 03 '14 11:01

Krumelur


2 Answers

I believe this should work:

public static async Task DownloadToStreamAsync(string uri, HttpContent data, Stream target, CancellationToken token)
{
    using (var client = new HttpClient())
    using (var response = await client.PostAsync(uri, data, token))
    using (var stream = await response.Content.ReadAsStreamAsync())
    {
        await stream.CopyToAsync(target, 4096, token);
    }
}

Note that ReadAsStreamAsync calls CreateContentReadStreamAsync, which for stream responses just returns the underlying content stream without buffering it into memory.

like image 111
Stephen Cleary Avatar answered Oct 11 '22 18:10

Stephen Cleary


You can't cancel a non cancellable operation. See How do I cancel non-cancelable async operations?.

You can however allow your code flow to behave as if the underlying operation was canceled, with WithCancellation.

public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
    return task.IsCompleted
        ? task
        : task.ContinueWith(
            completedTask => completedTask.GetAwaiter().GetResult(),
            cancellationToken,
            TaskContinuationOptions.ExecuteSynchronously,
            TaskScheduler.Default);
}

Usage:

await httpContent.PostAsync(stream).WithCancellation(new CancellationTokenSource(1000).Token);
like image 22
i3arnon Avatar answered Oct 11 '22 19:10

i3arnon