Last active
December 3, 2022 11:14
-
-
Save AngelMunoz/01f9bccbf338c1be18470ec684e91898 to your computer and use it in GitHub Desktop.
Implement SSE with F# and Saturn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Document</title> | |
</head> | |
<body> | |
<script> | |
const source = new EventSource("/sse"); | |
source.onopen = function() { | |
console.log("Connection Opened") | |
} | |
source.addEventListener('reload', (event) => { | |
console.log("Reloading, file changed: ", event.data) | |
}); | |
source.addEventListener('start', (event) => { | |
console.log(event.data) | |
}) | |
source.addEventListener('message', function(event) { | |
console.log(event) | |
console.log(event.data) | |
}); | |
source.addEventListener('error', function(err) { | |
console.error(err); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open FSharp.Control.Tasks | |
open Giraffe | |
open Saturn | |
open Saturn.Endpoint.Router | |
open Microsoft.AspNetCore.Http | |
open System | |
open System.IO | |
open Microsoft.AspNetCore.Http.Features | |
open FSharp.Control.Reactive | |
type INotifierService = | |
inherit IDisposable | |
abstract OnFileChanged : IObservable<string> | |
let getNotifier (path: string) : INotifierService = | |
let fsw = new FileSystemWatcher(path) | |
fsw.NotifyFilter <- NotifyFilters.FileName ||| NotifyFilters.Size | |
fsw.EnableRaisingEvents <- true | |
let changed = | |
fsw.Changed | |
|> Observable.map (fun args -> args.Name) | |
let deleted = | |
fsw.Deleted | |
|> Observable.map (fun args -> args.Name) | |
let renamed = | |
fsw.Renamed | |
|> Observable.map (fun args -> args.Name) | |
let created = | |
fsw.Created | |
|> Observable.map (fun args -> args.Name) | |
let obs = | |
Observable.mergeSeq [ changed | |
deleted | |
renamed | |
created ] | |
{ new INotifierService with | |
override _.Dispose() : unit = fsw.Dispose() | |
override _.OnFileChanged: IObservable<string> = obs } | |
let sse next (ctx: HttpContext) = | |
task { | |
let res = ctx.Response | |
ctx.SetStatusCode 200 | |
ctx.SetHttpHeader("Content-Type", "text/event-stream") | |
ctx.SetHttpHeader("Cache-Control", "no-cache") | |
let notifier = getNotifier @"C:\Users\scyth\Desktop" | |
let onFileChanged = | |
notifier.OnFileChanged | |
|> Observable.subscribe | |
(fun filename -> | |
task { | |
do! res.WriteAsync $"event:reload\ndata:{filename}\n\n" | |
do! res.Body.FlushAsync() | |
} | |
|> Async.AwaitTask | |
|> Async.Start) | |
do! res.WriteAsync($"event:start\ndata:{DateTime.Now}\n\n") | |
do! res.Body.FlushAsync() | |
ctx.RequestAborted.Register | |
(fun _ -> | |
notifier.Dispose() | |
onFileChanged.Dispose()) | |
|> ignore | |
while true do | |
do! Async.Sleep(TimeSpan.FromSeconds 1.) | |
return! text "" next ctx | |
} | |
let appRouter = router { get "/sse" sse } | |
[<EntryPoint>] | |
let main args = | |
let app = | |
application { | |
use_endpoint_router appRouter | |
use_static "wwwroot" | |
} | |
run app | |
0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open FSharp.Control.Tasks | |
open Giraffe | |
open Saturn | |
open Saturn.Endpoint.Router | |
open Microsoft.AspNetCore.Http | |
open System | |
open System.Text.Json | |
let sse next (ctx: HttpContext) = | |
task { | |
let res = ctx.Response | |
ctx.SetStatusCode 200 | |
ctx.SetHttpHeader("Content-Type", "text/event-stream") | |
ctx.SetHttpHeader("Cache-Control", "no-cache") | |
let data = JsonSerializer.Serialize({| event = "reload" |}) | |
let mutable count = 0 | |
while true do | |
do! res.WriteAsync($"data: {data}\nid:{count}\n\n") | |
do! res.Body.FlushAsync() | |
do! Async.Sleep (TimeSpan.FromSeconds 1.) | |
count <- count + 1 | |
return! text "" next ctx | |
} | |
let appRouter = | |
router { | |
get "/sse" sse | |
} | |
[<EntryPoint>] | |
let main args = | |
let app = | |
application { | |
use_endpoint_router appRouter | |
use_static "wwwroot" | |
} | |
run app | |
0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment