Skip to content

Instantly share code, notes, and snippets.

@Szer
Created July 11, 2022 13:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Szer/880fae06e7dedea012a4dd8a36e3b514 to your computer and use it in GitHub Desktop.
Save Szer/880fae06e7dedea012a4dd8a36e3b514 to your computer and use it in GitHub Desktop.
Running Kestrel from scripts is simple, you only need to...
#r "nuget: Microsoft.AspNetCore.Server.Kestrel, 2.2.0"
#r "nuget: Microsoft.AspNetCore.Server.Kestrel.Core, 2.2.0"
#r "nuget: Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv, 2.2.0"
#r "nuget: Microsoft.AspNetCore.WebSockets, 2.2.0"
#r "nuget: Ply"
open System
open System.Net.WebSockets
open System.Text
open System.Threading
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Hosting.Server
open Microsoft.AspNetCore.Http
open Microsoft.AspNetCore.Http.Features
open Microsoft.AspNetCore.Server.Kestrel.Core
open Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
open Microsoft.AspNetCore.WebSockets
open Microsoft.Extensions.Logging.Abstractions
open Microsoft.Extensions.Options
open FSharp.Control.Tasks.Affine
open Microsoft.Extensions.Primitives
type Context = { Features: IFeatureCollection }
type App() =
let message = Encoding.ASCII.GetBytes "hello world"
let wsOptions = WebSocketOptions()
let continueRequest = RequestDelegate(fun ctx ->
if ctx.WebSockets.IsWebSocketRequest then
unitTask {
let! socket = ctx.WebSockets.AcceptWebSocketAsync()
do! socket.SendAsync(ArraySegment message, WebSocketMessageType.Text, true, CancellationToken.None)
let buffer = Array.zeroCreate<byte> 4096
let! _ = socket.ReceiveAsync(buffer.AsMemory(), CancellationToken.None) in ()
}
else
let buffer = ReadOnlyMemory message
ctx.Response.Headers.Add("Content-Type", StringValues "text/plain")
(ctx.Response.Body.WriteAsync buffer).AsTask()
)
let wsMiddleware = WebSocketMiddleware(continueRequest, OptionsWrapper wsOptions)
interface IHttpApplication<Context> with
member this.CreateContext ctxFeatures = { Features = ctxFeatures }
member this.DisposeContext(_,_) = ()
member this.ProcessRequestAsync ctx =
let httpContext = new DefaultHttpContext(ctx.Features);
wsMiddleware.Invoke(httpContext);
type AppLifetime() =
let startedSource = new CancellationTokenSource()
let stoppingSource = new CancellationTokenSource()
let stoppedSource = new CancellationTokenSource()
interface IApplicationLifetime with
member this.ApplicationStarted = startedSource.Token
member this.ApplicationStopped = stoppedSource.Token
member this.ApplicationStopping = stoppingSource.Token
member this.StopApplication() = lock stoppingSource (fun _ ->
if not(stoppingSource.Token.IsCancellationRequested) then
stoppingSource.Cancel(throwOnFirstException = true)
)
let serverOptions = new KestrelServerOptions()
serverOptions.ListenAnyIP 8080
let transportOptions = SocketTransportOptions()
let loggerFactory = new NullLoggerFactory()
let applicationLifetime = AppLifetime()
let transportFactory = SocketTransportFactory(OptionsWrapper(transportOptions), applicationLifetime, loggerFactory)
let server = new KestrelServer(OptionsWrapper<KestrelServerOptions>(serverOptions), transportFactory, loggerFactory)
server.StartAsync(App(), CancellationToken.None).GetAwaiter().GetResult()
Console.ReadLine() |> ignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment