Skip to content

Commit 5f1ae47

Browse files
author
Henrik Feldt
authored
Merge pull request #560 from lucasmreis/websockets-docs-example
Websockets docs example
2 parents 7bc5ec8 + f4b06d1 commit 5f1ae47

File tree

3 files changed

+115
-15
lines changed

3 files changed

+115
-15
lines changed

docs/_layouts/default.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ <h2 id="project_tagline">Suave is a simple web development F# library providing
9696
<li><a href="azure-app-service.html">Deploying Suave to Azure App Service</a></li>
9797
<li><a href="logs.html">Getting Hold of Suave’s Logs</a></li>
9898
<li><a href="paket.html">Suave + Paket = ♥</a></li>
99+
<li><a href="websockets.html">Realtime messages with WebSockets</a></li>
99100
<li><a href="https://www.gitbook.com/book/theimowski/suave-music-store/">Music Store Tutorial</a></li>
100101
<li><a href="http://products.tamizhvendan.in/fsharp-applied/">E-Book: Web Development In F# Using Suave</a></li>
101102
<li><a href="http://blog.2mas.xyz/fsharp-suave-app-on-dotnet-core-on-kubernetes-on-google-cloud/">Suave on dotnet core on kubernetes on google cloud</a></li>

docs/websockets.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
layout: default
3+
---
4+
5+
Realtime Messages With WebSockets
6+
---------------------------------
7+
8+
It's easy to set up WebSockets with Suave.
9+
10+
First, define a function that takes `WebSocket` and `HttpContext` typed parameters, and returns a socket computation expression:
11+
12+
{% highlight fsharp %}
13+
open Suave.Sockets
14+
open Suave.Sockets.Control
15+
open Suave.WebSocket
16+
17+
let ws (webSocket : WebSocket) (context: HttpContext) =
18+
socket {
19+
...
20+
}
21+
{% endhighlight %}
22+
23+
Next, use the `read` and `send` function to receive and send messages to the clients:
24+
25+
{% highlight fsharp %}
26+
socket {
27+
let mutable loop = true
28+
29+
while loop do
30+
let! msg = webSocket.read()
31+
32+
match msg with
33+
| (Text, data, true) ->
34+
let str = UTF8.toString data
35+
let response = sprintf "response to %s" str
36+
let byteResponse =
37+
response
38+
|> System.Text.Encoding.ASCII.GetBytes
39+
|> ByteSegment
40+
do! webSocket.send Text byteResponse true
41+
42+
| (Close, _, _) ->
43+
let emptyResponse = [||] |> ByteSegment
44+
do! webSocket.send Close emptyResponse true
45+
loop <- false
46+
47+
| _ -> ()
48+
}
49+
{% endhighlight %}
50+
51+
Then, use the `handShake` function to fit it in your web server:
52+
53+
{% highlight fsharp %}
54+
let app : WebPart =
55+
choose [
56+
path "/websocket" >=> handShake ws
57+
GET >=> choose [ path "/" >=> file "index.html"; browseHome ]
58+
NOT_FOUND "Found no handlers." ]
59+
{% endhighlight %}
60+
61+
The complete example can be found [here](https://github.com/SuaveIO/suave/tree/master/examples/WebSocket).

examples/WebSocket/Program.fs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,69 @@ open Suave.Sockets
1616
open Suave.Sockets.Control
1717
open Suave.WebSocket
1818

19-
let echo (webSocket : WebSocket) =
20-
fun cx -> socket {
21-
let loop = ref true
22-
while !loop do
19+
let ws (webSocket : WebSocket) (context: HttpContext) =
20+
socket {
21+
// if `loop` is set to false, the server will stop receiving messages
22+
let mutable loop = true
23+
24+
while loop do
25+
// the server will wait for a message to be received without blocking the thread
2326
let! msg = webSocket.read()
27+
2428
match msg with
29+
// the message has type (Opcode * byte [] * bool)
30+
//
31+
// Opcode type:
32+
// type Opcode = Continuation | Text | Binary | Reserved | Close | Ping | Pong
33+
//
34+
// byte [] contains the actual message
35+
//
36+
// the last element is the FIN byte, explained later
2537
| (Text, data, true) ->
38+
// the message can be converted to a string
2639
let str = UTF8.toString data
27-
do! webSocket.send Text (ArraySegment data) true
28-
| (Ping, _, _) ->
29-
do! webSocket.send Pong (ArraySegment([||])) true
40+
let response = sprintf "response to %s" str
41+
42+
// the response needs to be converted to a ByteSegment
43+
let byteResponse =
44+
response
45+
|> System.Text.Encoding.ASCII.GetBytes
46+
|> ByteSegment
47+
48+
// the `send` function sends a message back to the client
49+
do! webSocket.send Text byteResponse true
50+
3051
| (Close, _, _) ->
31-
do! webSocket.send Close (ArraySegment([||])) true
32-
loop := false
52+
let emptyResponse = [||] |> ByteSegment
53+
do! webSocket.send Close emptyResponse true
54+
55+
// after sending a Close message, stop the loop
56+
loop <- false
57+
3358
| _ -> ()
34-
}
59+
}
3560

3661
let app : WebPart =
3762
choose [
38-
path "/websocket" >=> handShake echo
39-
GET >=> choose [ path "/" >=> file "index.html"; browseHome ];
40-
NOT_FOUND "Found no handlers."
41-
]
63+
path "/websocket" >=> handShake ws
64+
GET >=> choose [ path "/" >=> file "index.html"; browseHome ]
65+
NOT_FOUND "Found no handlers." ]
4266

4367
[<EntryPoint>]
4468
let main _ =
4569
startWebServer { defaultConfig with logger = Targets.create Verbose [||] } app
46-
0
70+
0
71+
72+
//
73+
// The FIN byte:
74+
//
75+
// A single message can be sent separated by fragments. The FIN byte indicates the final fragment. Fragments
76+
//
77+
// As an example, this is valid code, and will send only one message to the client:
78+
//
79+
// do! webSocket.send Text firstPart false
80+
// do! webSocket.send Continuation secondPart false
81+
// do! webSocket.send Continuation thirdPart true
82+
//
83+
// More information on the WebSocket protocol can be found at: https://tools.ietf.org/html/rfc6455#page-34
84+
//

0 commit comments

Comments
 (0)