Skip to content

Commit 6c61818

Browse files
committed
Use grace in chat example
1 parent b0c36b9 commit 6c61818

File tree

8 files changed

+74
-22
lines changed

8 files changed

+74
-22
lines changed

accept.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con
6666
defer errd.Wrap(&err, "failed to accept WebSocket connection")
6767

6868
g := graceFromRequest(r)
69-
if g != nil && g.isClosing() {
70-
err := errors.New("server closing")
69+
if g != nil && g.isShuttingdown() {
70+
err := errors.New("server shutting down")
7171
http.Error(w, err.Error(), http.StatusServiceUnavailable)
7272
return nil, err
7373
}

chat-example/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ module nhooyr.io/websocket/example-chat
22

33
go 1.13
44

5-
require nhooyr.io/websocket v1.8.2
5+
require nhooyr.io/websocket v0.0.0
6+
7+
replace nhooyr.io/websocket => ../

chat-example/go.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
12
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
3+
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
24
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
5+
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
36
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
7+
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
48
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
9+
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
510
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
11+
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
612
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
713
github.com/klauspost/compress v1.10.0 h1:92XGj1AcYzA6UrVdd4qIIBrT8OroryvRvdmg/IfmC7Y=
814
github.com/klauspost/compress v1.10.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
15+
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
916
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
17+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
1018
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
11-
nhooyr.io/websocket v1.8.2 h1:LwdzfyyOZKtVFoXay6A39Acu03KmidSZ3YUUvPa13PA=
12-
nhooyr.io/websocket v1.8.2/go.mod h1:LiqdCg1Cu7TPWxEvPjPa0TGYxCsy4pHNTN9gGluwBpQ=

chat-example/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ body {
55

66
#root {
77
padding: 40px 20px;
8-
max-width: 480px;
8+
max-width: 600px;
99
margin: auto;
1010
height: 100vh;
1111

chat-example/index.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
const conn = new WebSocket(`ws://${location.host}/subscribe`)
88

99
conn.addEventListener("close", ev => {
10-
console.info("websocket disconnected, reconnecting in 1000ms", ev)
11-
setTimeout(dial, 1000)
10+
appendLog(`WebSocket Disconnected code: ${ev.code}, reason: ${ev.reason}`, true)
11+
if (ev.code !== 1001) {
12+
appendLog("Reconnecting in 1s", true)
13+
setTimeout(dial, 1000)
14+
}
1215
})
1316
conn.addEventListener("open", ev => {
1417
console.info("websocket connected")
@@ -34,10 +37,14 @@
3437
const messageInput = document.getElementById("message-input")
3538

3639
// appendLog appends the passed text to messageLog.
37-
function appendLog(text) {
40+
function appendLog(text, error) {
3841
const p = document.createElement("p")
3942
// Adding a timestamp to each message makes the log easier to read.
4043
p.innerText = `${new Date().toLocaleTimeString()}: ${text}`
44+
if (error) {
45+
p.style.color = "red"
46+
p.style.fontStyle = "bold"
47+
}
4148
messageLog.append(p)
4249
return p
4350
}

chat-example/main.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package main
22

33
import (
4+
"context"
45
"errors"
56
"log"
67
"net"
78
"net/http"
89
"os"
10+
"os/signal"
911
"time"
12+
13+
"nhooyr.io/websocket"
1014
)
1115

1216
func main() {
@@ -38,10 +42,31 @@ func run() error {
3842
m.HandleFunc("/subscribe", ws.subscribeHandler)
3943
m.HandleFunc("/publish", ws.publishHandler)
4044

45+
var g websocket.Grace
4146
s := http.Server{
42-
Handler: m,
47+
Handler: g.Handler(m),
4348
ReadTimeout: time.Second * 10,
4449
WriteTimeout: time.Second * 10,
4550
}
46-
return s.Serve(l)
51+
errc := make(chan error, 1)
52+
go func() {
53+
errc <- s.Serve(l)
54+
}()
55+
56+
sigs := make(chan os.Signal, 1)
57+
signal.Notify(sigs, os.Interrupt)
58+
select {
59+
case err := <-errc:
60+
log.Printf("failed to serve: %v", err)
61+
case sig := <-sigs:
62+
log.Printf("terminating: %v", sig)
63+
}
64+
65+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
66+
defer cancel()
67+
68+
s.Shutdown(ctx)
69+
g.Shutdown(ctx)
70+
71+
return nil
4772
}

example_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,20 @@ func ExampleGrace() {
177177
ReadTimeout: time.Second * 15,
178178
WriteTimeout: time.Second * 15,
179179
}
180-
go s.ListenAndServe()
180+
181+
errc := make(chan error, 1)
182+
go func() {
183+
errc <- s.ListenAndServe()
184+
}()
181185

182186
sigs := make(chan os.Signal, 1)
183187
signal.Notify(sigs, os.Interrupt)
184-
sig := <-sigs
185-
log.Printf("recieved %v, shutting down", sig)
188+
select {
189+
case err := <-errc:
190+
log.Printf("failed to listen and serve: %v", err)
191+
case sig := <-sigs:
192+
log.Printf("terminating: %v", sig)
193+
}
186194

187195
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
188196
defer cancel()

grace.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ import (
1515
// and then use Close or Shutdown to gracefully close these connections.
1616
//
1717
// Grace is intended to be used in harmony with net/http.Server's Shutdown and Close methods.
18+
// It's required as net/http's Shutdown and Close methods do not keep track of WebSocket
19+
// connections.
1820
type Grace struct {
19-
mu sync.Mutex
20-
closing bool
21-
conns map[*Conn]struct{}
21+
mu sync.Mutex
22+
closed bool
23+
shuttingDown bool
24+
conns map[*Conn]struct{}
2225
}
2326

2427
// Handler returns a handler that wraps around h to record
@@ -33,10 +36,10 @@ func (g *Grace) Handler(h http.Handler) http.Handler {
3336
})
3437
}
3538

36-
func (g *Grace) isClosing() bool {
39+
func (g *Grace) isShuttingdown() bool {
3740
g.mu.Lock()
3841
defer g.mu.Unlock()
39-
return g.closing
42+
return g.shuttingDown
4043
}
4144

4245
func graceFromRequest(r *http.Request) *Grace {
@@ -47,7 +50,7 @@ func graceFromRequest(r *http.Request) *Grace {
4750
func (g *Grace) addConn(c *Conn) error {
4851
g.mu.Lock()
4952
defer g.mu.Unlock()
50-
if g.closing {
53+
if g.closed {
5154
c.Close(StatusGoingAway, "server shutting down")
5255
return errors.New("server shutting down")
5356
}
@@ -72,7 +75,8 @@ type gracefulContextKey struct{}
7275
// connections with StatusGoingAway.
7376
func (g *Grace) Close() error {
7477
g.mu.Lock()
75-
g.closing = true
78+
g.shuttingDown = true
79+
g.closed = true
7680
var wg sync.WaitGroup
7781
for c := range g.conns {
7882
wg.Add(1)
@@ -97,7 +101,7 @@ func (g *Grace) Shutdown(ctx context.Context) error {
97101
defer g.Close()
98102

99103
g.mu.Lock()
100-
g.closing = true
104+
g.shuttingDown = true
101105
g.mu.Unlock()
102106

103107
// Same poll period used by net/http.

0 commit comments

Comments
 (0)