Realtime Messages With WebSockets
It's easy to set up WebSockets with Suave.
First, define a function that takes WebSocket
and HttpContext
typed parameters, and returns a socket computation expression:
open Suave.Sockets open Suave.Sockets.Control open Suave.WebSocket let ws (webSocket : WebSocket) (context: HttpContext) = socket { ... }
Next, use the read
and send
function to receive and send messages to the clients:
socket { let mutable loop = true while loop do let! msg = webSocket.read() match msg with | (Text, data, true) -> let str = UTF8.toString data let response = sprintf "response to %s" str let byteResponse = response |> System.Text.Encoding.ASCII.GetBytes |> ByteSegment do! webSocket.send Text byteResponse true | (Close, _, _) -> let emptyResponse = [||] |> ByteSegment do! webSocket.send Close emptyResponse true loop <- false | _ -> () }
Then, use the handShake
function to fit it in your web server:
let app : WebPart = choose [ path "/websocket" >=> handShake ws GET >=> choose [ path "/" >=> file "index.html"; browseHome ] NOT_FOUND "Found no handlers." ]
The complete example can be found here.
Handling connection errors
By default the socket computation expression handles any errors transparently on both writing and reading from the websocket shutting down the connection.
You may want to add your own additional error handling logic to catch and handle any errors raised when reading and writing from a websocket. Some scenarios where this may be useful:
- Alerting the rest of the application that a connection is closed.
- Unsubscribing and/or shutting down processes used by the websocket connection.
- Custom logging of the error.
Example code can be found here.
namespace Suave
namespace Suave.Sockets
namespace Suave.Sockets.Control
module WebSocket
from Suave
from Suave
val ws : webSocket:WebSocket -> context:HttpContext -> 'a
Full name: temp.ws
Full name: temp.ws
val webSocket : WebSocket
Multiple items
module WebSocket
from Suave
--------------------
type WebSocket =
new : connection:Connection -> WebSocket
member read : unit -> Async<Choice<(Opcode * byte [] * bool),Error>>
member readIntoByteSegment : byteSegmentForLengthFunc:(int -> ByteSegment) -> Async<Choice<(Opcode * ByteSegment * bool),Error>>
member send : opcode:Opcode -> bs:ByteSegment -> fin:bool -> Async<Choice<unit,Error>>
Full name: Suave.WebSocket.WebSocket
--------------------
new : connection:Connection -> WebSocket
module WebSocket
from Suave
--------------------
type WebSocket =
new : connection:Connection -> WebSocket
member read : unit -> Async<Choice<(Opcode * byte [] * bool),Error>>
member readIntoByteSegment : byteSegmentForLengthFunc:(int -> ByteSegment) -> Async<Choice<(Opcode * ByteSegment * bool),Error>>
member send : opcode:Opcode -> bs:ByteSegment -> fin:bool -> Async<Choice<unit,Error>>
Full name: Suave.WebSocket.WebSocket
--------------------
new : connection:Connection -> WebSocket
val context : HttpContext
Multiple items
module HttpContext
from Suave.Http
--------------------
type HttpContext =
{request: HttpRequest;
runtime: HttpRuntime;
connection: Connection;
userState: Map<string,obj>;
response: HttpResult;}
member clientIp : trustProxy:bool -> sources:string list -> IPAddress
member clientPort : trustProxy:bool -> sources:string list -> Port
member clientProto : trustProxy:bool -> sources:string list -> string
member clientIpTrustProxy : IPAddress
member clientPortTrustProxy : Port
member clientProtoTrustProxy : string
member isLocal : bool
static member clientIp_ : Property<HttpContext,IPAddress>
static member clientPort_ : Property<HttpContext,Port>
static member clientProto_ : Property<HttpContext,string>
static member connection_ : Property<HttpContext,Connection>
static member isLocal_ : Property<HttpContext,bool>
static member request_ : Property<HttpContext,HttpRequest>
static member response_ : Property<HttpContext,HttpResult>
static member runtime_ : Property<HttpContext,HttpRuntime>
static member userState_ : Property<HttpContext,Map<string,obj>>
Full name: Suave.Http.HttpContext
module HttpContext
from Suave.Http
--------------------
type HttpContext =
{request: HttpRequest;
runtime: HttpRuntime;
connection: Connection;
userState: Map<string,obj>;
response: HttpResult;}
member clientIp : trustProxy:bool -> sources:string list -> IPAddress
member clientPort : trustProxy:bool -> sources:string list -> Port
member clientProto : trustProxy:bool -> sources:string list -> string
member clientIpTrustProxy : IPAddress
member clientPortTrustProxy : Port
member clientProtoTrustProxy : string
member isLocal : bool
static member clientIp_ : Property<HttpContext,IPAddress>
static member clientPort_ : Property<HttpContext,Port>
static member clientProto_ : Property<HttpContext,string>
static member connection_ : Property<HttpContext,Connection>
static member isLocal_ : Property<HttpContext,bool>
static member request_ : Property<HttpContext,HttpRequest>
static member response_ : Property<HttpContext,HttpResult>
static member runtime_ : Property<HttpContext,HttpRuntime>
static member userState_ : Property<HttpContext,Map<string,obj>>
Full name: Suave.Http.HttpContext
val socket : SocketMonad
Full name: Suave.Sockets.Control.SocketMonad.socket
Full name: Suave.Sockets.Control.SocketMonad.socket
val mutable loop : bool
val msg : Opcode * byte [] * bool
union case Opcode.Text: Opcode
val data : byte []
val str : string
module UTF8
from YoLo
from YoLo
val toString : bs:byte [] -> string
Full name: YoLo.UTF8.toString
Full name: YoLo.UTF8.toString
val response : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val byteResponse : System.ArraySegment<byte>
namespace System
namespace System.Text
type Encoding =
member BodyName : string
member Clone : unit -> obj
member CodePage : int
member DecoderFallback : DecoderFallback with get, set
member EncoderFallback : EncoderFallback with get, set
member EncodingName : string
member Equals : value:obj -> bool
member GetByteCount : chars:char[] -> int + 3 overloads
member GetBytes : chars:char[] -> byte[] + 5 overloads
member GetCharCount : bytes:byte[] -> int + 2 overloads
...
Full name: System.Text.Encoding
member BodyName : string
member Clone : unit -> obj
member CodePage : int
member DecoderFallback : DecoderFallback with get, set
member EncoderFallback : EncoderFallback with get, set
member EncodingName : string
member Equals : value:obj -> bool
member GetByteCount : chars:char[] -> int + 3 overloads
member GetBytes : chars:char[] -> byte[] + 5 overloads
member GetCharCount : bytes:byte[] -> int + 2 overloads
...
Full name: System.Text.Encoding
property System.Text.Encoding.ASCII: System.Text.Encoding
System.Text.Encoding.GetBytes(s: string) : byte []
System.Text.Encoding.GetBytes(chars: char []) : byte []
System.Text.Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
System.Text.Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
System.Text.Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
System.Text.Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
System.Text.Encoding.GetBytes(chars: char []) : byte []
System.Text.Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
System.Text.Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
System.Text.Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
System.Text.Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
type ByteSegment = System.ArraySegment<byte>
Full name: Suave.Sockets.ByteSegment
Full name: Suave.Sockets.ByteSegment
union case Opcode.Close: Opcode
val emptyResponse : System.ArraySegment<byte>
val app : WebPart
Full name: temp.app
Full name: temp.app
Multiple items
module WebPart
from Suave
--------------------
type WebPart = WebPart<HttpContext>
Full name: Suave.Http.WebPart
--------------------
type WebPart<'a> = 'a -> Async<'a option>
Full name: Suave.WebPart.WebPart<_>
module WebPart
from Suave
--------------------
type WebPart = WebPart<HttpContext>
Full name: Suave.Http.WebPart
--------------------
type WebPart<'a> = 'a -> Async<'a option>
Full name: Suave.WebPart.WebPart<_>
val choose : options:WebPart<'a> list -> WebPart<'a>
Full name: Suave.WebPart.choose
Full name: Suave.WebPart.choose
val path : pathAfterDomain:string -> WebPart
Full name: Suave.Filters.path
Full name: Suave.Filters.path
val handShake : continuation:(WebSocket -> HttpContext -> SocketOp<unit>) -> ctx:HttpContext -> Async<HttpContext option>
Full name: Suave.WebSocket.handShake
Full name: Suave.WebSocket.handShake
val GET : WebPart
Full name: Suave.Filters.GET
Full name: Suave.Filters.GET
val file : fileName:string -> WebPart
Full name: Suave.Files.file
Full name: Suave.Files.file
val browseHome : WebPart
Full name: Suave.Files.browseHome
Full name: Suave.Files.browseHome
val NOT_FOUND : body:string -> WebPart
Full name: Suave.RequestErrors.NOT_FOUND
Full name: Suave.RequestErrors.NOT_FOUND