Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching HttpClient timeouts within an async workflow

I'm calling HttpClient via Async.AwaitTask, being called from within an agent (MailboxProcessor). I was wanting to catch errors during the HTTP call so used a try...with in the async workflow, but it completely misses catching client-side timeout exceptions which then cause the agent to crash.

Minimal reproduction:

#r "System.Net.Http"
open System
open System.Net.Http

let client = new HttpClient()
client.Timeout <- TimeSpan.FromSeconds(1.)
async {
    try
        let! content = Async.AwaitTask <| client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
        return content
    with ex ->
        // Does not catch client-side timeout exception
        return "Caught it!"
}
|> Async.RunSynchronously
// Throws System.OperationCanceledException: The operation was canceled

I can fix it by making it completely syncronous, but would prefer to keep the whole stack async as might be running a lot of these in parallel:

#r "System.Net.Http"
open System
open System.Net.Http

let client = new HttpClient()
client.Timeout <- TimeSpan.FromSeconds(1.)
try
    Async.AwaitTask <| client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
    |> Async.RunSynchronously
with ex ->
    "Caught it!"
// Returns "Caught it!"

Is there an effective way of catching the OperationCanceledException within an async context?

like image 687
Daniel Bradley Avatar asked May 09 '16 09:05

Daniel Bradley


1 Answers

This happens because the HttpClient.GetStringAsync task will be cancelled, instead of failing with a TimeoutException, thus prompting the async mechanism to fire its cancellation continuations, which cannot be handled. A simple way to solve this problem is the following:

async {
    try
        let! content = 
            client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
                  .ContinueWith(fun (t:Task<string>) -> t.Result)
            |> Async.AwaitTask
        return content
    with ex ->
        // Does not catch client-side timeout exception
        return "Caught it!"
}
like image 113
eirik Avatar answered Nov 15 '22 08:11

eirik