Header menu logo IcedTasks

How to use CancellableTask in Falco

See Falco Docs

To use a cancellableTask with Falco, we'll need to get the RequestAborted property off the HttpContext.

We'll start off with a slightly longer version but then make a helper for this.

// This just the dance to get Falco to compile for this example.
// Really you'd set up these references in an fsproj
#load "../../runtime-scripts/Microsoft.AspNetCore.App-latest-7.fsx"
#r "nuget: Falco"
#r "nuget: IcedTasks"

open Microsoft.AspNetCore.Http
open System.Threading
open System.Threading.Tasks
open IcedTasks
open Falco
open Falco.Routing


// This is a stand in for some real database call like Npgsql where it would take a CancellationToken
type Database =
    static member Get(query, queryParams, cancellationToken: CancellationToken) =
        task { do! Task.Delay(10) }


module ExampleVerbose =


    // Some function that's doing the real handler's work
    let myRealWork ctx =
        cancellableTask {
            // use a lamdbda to get the cancellableTask's current CancellationToken
            let! result =
                fun ct -> Database.Get("SELECT foo FROM bar where baz = @0", [ "@0", "buzz" ], ct)

            return! Response.ofJson result ctx
        }

    // A helper to get the context's RequestAborted CancellationToken which will give the cancellableTask
    // the context to pass long.
    let myCustomHandler (ctx: HttpContext) =
        task {
            let cancellationToken = ctx.RequestAborted
            return! myRealWork ctx cancellationToken
        }
        :> Task

    let endpoints = [ get "/" myCustomHandler ]

We'll end up creating our own Routing Handler helpers to make it easier when adding to a webapp.

module RoutingC =
    type CancellableHandler = HttpContext -> CancellationToken -> Task<unit>

    // A helper to get the context's RequestAborted CancellationToken which will give the cancellableTask
    // the context to pass long.
    let inline toHandler
        (cancellableHandler: HttpContext -> CancellationToken -> Task<unit>)
        (ctx: HttpContext)
        =
        task { return! cancellableHandler ctx ctx.RequestAborted } :> Task

    /// HttpEndpoint constructor that matches any HttpVerb.
    let any (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route ANY pattern (toHandler handler)

    /// GET HttpEndpoint constructor.
    let get (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route GET pattern (toHandler handler)

    /// HEAD HttpEndpoint constructor.
    let head (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route HEAD pattern (toHandler handler)

    /// POST HttpEndpoint constructor.
    let post (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route POST pattern (toHandler handler)

    /// PUT HttpEndpoint constructor.
    let put (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route PUT pattern (toHandler handler)

    /// PATCH HttpEndpoint constructor.
    let patch (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route PATCH pattern (toHandler handler)

    /// DELETE HttpEndpoint constructor.
    let delete (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route DELETE pattern (toHandler handler)

    /// OPTIONS HttpEndpoint constructor.
    let options (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route OPTIONS pattern (toHandler handler)

    /// TRACE HttpEndpoint construct.
    let trace (pattern: string) (handler: CancellableHandler) : HttpEndpoint =
        route TRACE pattern (toHandler handler)

module ExampleRefactor1 =

    // Some function that's doing the real handler's work
    let myRealWork ctx =
        cancellableTask {
            // use a lamdbda to get the cancellableTask's current CancellationToken
            let! result =
                fun ct -> Database.Get("SELECT foo FROM bar where baz = @0", [ "@0", "buzz" ], ct)

            return! Response.ofJson result ctx
        }

    let endpoints = [ RoutingC.get "/" myRealWork ]
namespace Microsoft
namespace Microsoft.AspNetCore
namespace Microsoft.AspNetCore.Http
namespace System
namespace System.Threading
namespace System.Threading.Tasks
namespace IcedTasks
namespace Falco
module Routing from Falco
val query: 'a
val queryParams: 'b
val cancellationToken: CancellationToken
Multiple items
[<Struct>] type CancellationToken = new: canceled: bool -> unit member Equals: other: obj -> bool + 1 overload member GetHashCode: unit -> int member Register: callback: Action -> CancellationTokenRegistration + 4 overloads member ThrowIfCancellationRequested: unit -> unit member UnsafeRegister: callback: Action<obj,CancellationToken> * state: obj -> CancellationTokenRegistration + 1 overload static member (<>) : left: CancellationToken * right: CancellationToken -> bool static member (=) : left: CancellationToken * right: CancellationToken -> bool member CanBeCanceled: bool member IsCancellationRequested: bool ...
<summary>Propagates notification that operations should be canceled.</summary>

--------------------
CancellationToken ()
CancellationToken(canceled: bool) : CancellationToken
val task: TaskBuilder
Multiple items
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable + 1 overload member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 overloads ...
<summary>Represents an asynchronous operation.</summary>

--------------------
type Task<'TResult> = inherit Task new: ``function`` : Func<obj,'TResult> * state: obj -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult> + 1 overload member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 4 overloads member Result: 'TResult static member Factory: TaskFactory<'TResult>
<summary>Represents an asynchronous operation that can return a value.</summary>
<typeparam name="TResult">The type of the result produced by this <see cref="T:System.Threading.Tasks.Task`1" />.</typeparam>


--------------------
Task(action: System.Action) : Task
Task(action: System.Action, cancellationToken: CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task

--------------------
Task(``function`` : System.Func<'TResult>) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task.Delay(delay: System.TimeSpan) : Task
Task.Delay(millisecondsDelay: int) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider) : Task
Task.Delay(delay: System.TimeSpan, cancellationToken: CancellationToken) : Task
Task.Delay(millisecondsDelay: int, cancellationToken: CancellationToken) : Task
Task.Delay(delay: System.TimeSpan, timeProvider: System.TimeProvider, cancellationToken: CancellationToken) : Task
val myRealWork: ctx: HttpContext -> CancellableTask<unit>
val ctx: HttpContext
val cancellableTask: CancellableTaskBuilder
<summary> Builds a cancellableTask using computation expression syntax. </summary>
val result: unit
val ct: CancellationToken
type Database = static member Get: query: 'a * queryParams: 'b * cancellationToken: CancellationToken -> Task<unit>
static member Database.Get: query: 'a * queryParams: 'b * cancellationToken: CancellationToken -> Task<unit>
module Response from Falco
val ofJson: obj: 'T -> HttpHandler
<summary> Returns a "application/json; charset=utf-8" response with the serialized object provided to the client. </summary>
val myCustomHandler: ctx: HttpContext -> Task
type HttpContext = override Abort: unit -> unit member Connection: ConnectionInfo member Features: IFeatureCollection member Items: IDictionary<obj,obj> member Request: HttpRequest member RequestAborted: CancellationToken member RequestServices: IServiceProvider member Response: HttpResponse member Session: ISession member TraceIdentifier: string ...
property HttpContext.RequestAborted: CancellationToken with get, set
val endpoints: HttpEndpoint list
val get: pattern: string -> handler: HttpHandler -> HttpEndpoint
<summary> GET HttpEndpoint constructor. </summary>
type CancellableHandler = HttpContext -> CancellationToken -> Task<unit>
type unit = Unit
val toHandler: cancellableHandler: (HttpContext -> CancellationToken -> Task<unit>) -> ctx: HttpContext -> Task
val cancellableHandler: (HttpContext -> CancellationToken -> Task<unit>)
val any: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 HttpEndpoint constructor that matches any HttpVerb.
val pattern: string
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val handler: CancellableHandler
type HttpEndpoint = { Pattern: string Handlers: (HttpVerb * HttpHandler) list }
<summary> Specifies an association of a route pattern to a collection of HttpEndpointHandler. </summary>
val route: verb: HttpVerb -> pattern: string -> handler: HttpHandler -> HttpEndpoint
<summary> Constructor for a singular HttpEndpoint. </summary>
union case HttpVerb.ANY: HttpVerb
val get: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 GET HttpEndpoint constructor.
union case HttpVerb.GET: HttpVerb
val head: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 HEAD HttpEndpoint constructor.
union case HttpVerb.HEAD: HttpVerb
val post: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 POST HttpEndpoint constructor.
union case HttpVerb.POST: HttpVerb
val put: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 PUT HttpEndpoint constructor.
union case HttpVerb.PUT: HttpVerb
val patch: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 PATCH HttpEndpoint constructor.
union case HttpVerb.PATCH: HttpVerb
val delete: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 DELETE HttpEndpoint constructor.
union case HttpVerb.DELETE: HttpVerb
val options: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 OPTIONS HttpEndpoint constructor.
union case HttpVerb.OPTIONS: HttpVerb
val trace: pattern: string -> handler: CancellableHandler -> HttpEndpoint
 TRACE HttpEndpoint construct.
union case HttpVerb.TRACE: HttpVerb
module ExampleRefactor1 from Cancellable-Task-In-Falco
module RoutingC from Cancellable-Task-In-Falco
val get: pattern: string -> handler: RoutingC.CancellableHandler -> HttpEndpoint
 GET HttpEndpoint constructor.

Type something to start searching.