0

I have not found a way to specify the timeout to the operation - see the code snippet to see what I try to achieve.

type ResponseKind =
    | Json of string
    | Error of Exception
    | Timeout

let download (wclient : WebClient) (timeout : int option) (base_url : Uri) (sub_url : string) : Async<ResponseKind> =
    async {
        let target_uri = Uri(base_url, sub_url)
        try
            let! result = wclient.AsyncDownloadString(target_uri)
            return ResponseKind.Json result
        with
        | :? TimeoutException -> 
            return ResponseKind.Timeout
        | _ as ex ->
            return ResponseKind.Error ex
    }

Now, I am aware that there are myriads of other ways to achieve the same, e.g. using WebRequest instead. But maybe it is just a simple oversight of mine, not finding out how to set a timeout on the operation.

Ideas?

Update

Further investigation on my behalf led me to an FSSnippet which uses a timeout task in parallel to the task to be executed.

Reusing that approach in my context, yielded a modified version of my code:

type ResponseKind =
    | Json of string
    | Error of Exception
    | Timeout

let await_download_with_timeout (task : Task<string>) (timeout : int) : Async<ResponseKind>=
    async {
        use cts = new CancellationTokenSource()
        use timer = Task.Delay (timeout,cts.Token)
        let! completed =
            Task.WhenAny(task,timer)
            |> Async.AwaitTask
        if completed = (task :> Task)
        then
            cts.Cancel()
            let! result = Async.AwaitTask task
            if task.IsCompleted
            then
                return ResponseKind.Json result
            else
                return ResponseKind.Error (task.Exception)
        else
            return ResponseKind.Timeout
    }

let download (wclient : WebClient) (timeout : int option) (base_url : Uri) (sub_url : string) : Async<ResponseKind> =
    async {
        let target_uri = Uri(base_url, sub_url)
        try
            match timeout with
            | Some t ->
                let dtask = wclient.DownloadStringTaskAsync(target_uri)
                let! result = await_download_with_timeout dtask t
                return result
            | None ->
                let! result = wclient.AsyncDownloadString(target_uri)
                return ResponseKind.Json result
        with
        | :? TimeoutException -> 
            return ResponseKind.Timeout
        | _ as ex ->
            return ResponseKind.Error ex
    }

Now, the problem remaining is that I am not sure if my detection of something else going wrong is okay.

BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • http://stackoverflow.com/questions/1789627/how-to-change-the-timeout-on-a-net-webclient-object - Dang - I did not find this before writing my question - so my question is a duplicate - sorry. – BitTickler May 07 '16 at 04:07
  • That might not work on async. – Ringil May 07 '16 at 04:08

1 Answers1

0

I have this code that's been well-tested, but is oriented around HttpWebRequest rather than WebClient; I'm sure it can be trivially suited to your needs:

type System.Net.WebRequest with
  member req.AsyncGetResponseWithTimeout () =
    let impl = async {
      let iar = req.BeginGetResponse (null, null)
      let! success = Async.AwaitIAsyncResult (iar, req.Timeout)
      return if success then req.EndGetResponse iar
             else req.Abort ()
                  raise (System.Net.WebException "The operation has timed out") }
    Async.TryCancelled (impl, fun _ -> req.Abort ())
ildjarn
  • 62,044
  • 9
  • 127
  • 211