Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit55f4a70

Browse files
committed
Add automated test and documentation for echo example
Closes#223
1 parent71a12fb commit55f4a70

File tree

9 files changed

+182
-153
lines changed

9 files changed

+182
-153
lines changed

‎ci/test.mk‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ gotest:
1414
gotest -timeout=30m -covermode=atomic -coverprofile=ci/out/coverage.prof -coverpkg=./...$${GOTESTFLAGS-} ./...
1515
sed -i'/stringer\.go/d' ci/out/coverage.prof
1616
sed -i'/nhooyr.io\/websocket\/internal\/test/d' ci/out/coverage.prof
17-
sed -i'/example/d' ci/out/coverage.prof
17+
sed -i'/examples/d' ci/out/coverage.prof

‎conn_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ func TestWasm(t *testing.T) {
295295
ctx,cancel:=context.WithTimeout(context.Background(),time.Minute)
296296
defercancel()
297297

298-
cmd:=exec.CommandContext(ctx,"go","test","-exec=wasmbrowsertest","./...")
298+
cmd:=exec.CommandContext(ctx,"go","test","-exec=wasmbrowsertest",".")
299299
cmd.Env=append(os.Environ(),"GOOS=js","GOARCH=wasm",fmt.Sprintf("WS_ECHO_SERVER_URL=%v",s.URL))
300300

301301
b,err:=cmd.CombinedOutput()

‎examples/chat/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This directory contains a full stack example of a simple chat webapp using nhooyr.io/websocket.
44

55
```bash
6-
$cd chat-example
6+
$cdexamples/chat
77
$ go run. localhost:0
88
listening on http://127.0.0.1:51055
99
```

‎examples/chat/chat_test.go‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// +build !js
2-
31
package main
42

53
import (

‎examples/chat/go.sum‎

Lines changed: 0 additions & 18 deletions
This file was deleted.

‎examples/echo/README.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#Echo Example
2+
3+
This directory contains a echo server example using nhooyr.io/websocket.
4+
5+
```bash
6+
$cd examples/echo
7+
$ go run. localhost:0
8+
listening on http://127.0.0.1:51055
9+
```
10+
11+
You can use a WebSocket client likehttps://github.com/hashrocket/ws to connect. All messages
12+
written will be echoed back.
13+
14+
##Structure
15+
16+
The server is in`server.go` and is implemented as a`http.HandlerFunc` that accepts the WebSocket
17+
and then reads all messages and writes them exactly as is back to the connection.
18+
19+
`server_test.go` contains a small unit test to verify it works correctly.
20+
21+
`main.go` brings it all together so that you can run it and play around with it.

‎examples/echo/main.go‎

Lines changed: 29 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -3,158 +3,57 @@ package main
33
import (
44
"context"
55
"errors"
6-
"fmt"
7-
"io"
86
"log"
97
"net"
108
"net/http"
9+
"os"
10+
"os/signal"
1111
"time"
12-
13-
"golang.org/x/time/rate"
14-
15-
"nhooyr.io/websocket"
16-
"nhooyr.io/websocket/wsjson"
1712
)
1813

19-
// This example starts a WebSocket echo server,
20-
// dials the server and then sends 5 different messages
21-
// and prints out the server's responses.
2214
funcmain() {
23-
// First we listen on port 0 which means the OS will
24-
// assign us a random free port. This is the listener
25-
// the server will serve on and the client will connect to.
26-
l,err:=net.Listen("tcp","localhost:0")
27-
iferr!=nil {
28-
log.Fatalf("failed to listen: %v",err)
29-
}
30-
deferl.Close()
31-
32-
s:=&http.Server{
33-
Handler:http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
34-
err:=echoServer(w,r)
35-
iferr!=nil {
36-
log.Printf("echo server: %v",err)
37-
}
38-
}),
39-
ReadTimeout:time.Second*15,
40-
WriteTimeout:time.Second*15,
41-
}
42-
defers.Close()
43-
44-
// This starts the echo server on the listener.
45-
gofunc() {
46-
err:=s.Serve(l)
47-
iferr!=http.ErrServerClosed {
48-
log.Fatalf("failed to listen and serve: %v",err)
49-
}
50-
}()
15+
log.SetFlags(0)
5116

52-
// Now we dial the server, send the messages and echo the responses.
53-
err=client("ws://"+l.Addr().String())
17+
err:=run()
5418
iferr!=nil {
55-
log.Fatalf("client failed: %v",err)
56-
}
57-
58-
// Output:
59-
// received: map[i:0]
60-
// received: map[i:1]
61-
// received: map[i:2]
62-
// received: map[i:3]
63-
// received: map[i:4]
64-
}
65-
66-
// echoServer is the WebSocket echo server implementation.
67-
// It ensures the client speaks the echo subprotocol and
68-
// only allows one message every 100ms with a 10 message burst.
69-
funcechoServer(w http.ResponseWriter,r*http.Request)error {
70-
c,err:=websocket.Accept(w,r,&websocket.AcceptOptions{
71-
Subprotocols: []string{"echo"},
72-
})
73-
iferr!=nil {
74-
returnerr
75-
}
76-
deferc.Close(websocket.StatusInternalError,"the sky is falling")
77-
78-
ifc.Subprotocol()!="echo" {
79-
c.Close(websocket.StatusPolicyViolation,"client must speak the echo subprotocol")
80-
returnerrors.New("client does not speak echo sub protocol")
81-
}
82-
83-
l:=rate.NewLimiter(rate.Every(time.Millisecond*100),10)
84-
for {
85-
err=echo(r.Context(),c,l)
86-
ifwebsocket.CloseStatus(err)==websocket.StatusNormalClosure {
87-
returnnil
88-
}
89-
iferr!=nil {
90-
returnfmt.Errorf("failed to echo with %v: %w",r.RemoteAddr,err)
91-
}
19+
log.Fatal(err)
9220
}
9321
}
9422

95-
// echo reads from the WebSocket connection and then writes
96-
// the received message back to it.
97-
// The entire function has 10s to complete.
98-
funcecho(ctx context.Context,c*websocket.Conn,l*rate.Limiter)error {
99-
ctx,cancel:=context.WithTimeout(ctx,time.Second*10)
100-
defercancel()
101-
102-
err:=l.Wait(ctx)
103-
iferr!=nil {
104-
returnerr
23+
// run initializes the chatServer and routes and then
24+
// starts a http.Server for the passed in address.
25+
funcrun()error {
26+
iflen(os.Args)<2 {
27+
returnerrors.New("please provide an address to listen on as the first argument")
10528
}
10629

107-
typ,r,err:=c.Reader(ctx)
30+
l,err:=net.Listen("tcp",os.Args[1])
10831
iferr!=nil {
10932
returnerr
11033
}
34+
log.Printf("listening on http://%v",l.Addr())
11135

112-
w,err:=c.Writer(ctx,typ)
113-
iferr!=nil {
114-
returnerr
36+
s:=&http.Server{
37+
Handler:http.HandlerFunc(echoServer),
38+
ReadTimeout:time.Second*10,
39+
WriteTimeout:time.Second*10,
11540
}
41+
errc:=make(chanerror,1)
42+
gofunc() {
43+
errc<-s.Serve(l)
44+
}()
11645

117-
_,err=io.Copy(w,r)
118-
iferr!=nil {
119-
returnfmt.Errorf("failed to io.Copy: %w",err)
46+
sigs:=make(chan os.Signal,1)
47+
signal.Notify(sigs,os.Interrupt)
48+
select {
49+
caseerr:=<-errc:
50+
log.Printf("failed to serve: %v",err)
51+
casesig:=<-sigs:
52+
log.Printf("terminating: %v",sig)
12053
}
12154

122-
err=w.Close()
123-
returnerr
124-
}
125-
126-
// client dials the WebSocket echo server at the given url.
127-
// It then sends it 5 different messages and echo's the server's
128-
// response to each.
129-
funcclient(urlstring)error {
130-
ctx,cancel:=context.WithTimeout(context.Background(),time.Minute)
55+
ctx,cancel:=context.WithTimeout(context.Background(),time.Second*10)
13156
defercancel()
13257

133-
c,_,err:=websocket.Dial(ctx,url,&websocket.DialOptions{
134-
Subprotocols: []string{"echo"},
135-
})
136-
iferr!=nil {
137-
returnerr
138-
}
139-
deferc.Close(websocket.StatusInternalError,"the sky is falling")
140-
141-
fori:=0;i<5;i++ {
142-
err=wsjson.Write(ctx,c,map[string]int{
143-
"i":i,
144-
})
145-
iferr!=nil {
146-
returnerr
147-
}
148-
149-
v:=map[string]int{}
150-
err=wsjson.Read(ctx,c,&v)
151-
iferr!=nil {
152-
returnerr
153-
}
154-
155-
fmt.Printf("received: %v\n",v)
156-
}
157-
158-
c.Close(websocket.StatusNormalClosure,"")
159-
returnnil
58+
returns.Shutdown(ctx)
16059
}

‎examples/echo/server.go‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"log"
8+
"net/http"
9+
"time"
10+
11+
"golang.org/x/time/rate"
12+
13+
"nhooyr.io/websocket"
14+
)
15+
16+
// echoServer is the WebSocket echo server implementation.
17+
// It ensures the client speaks the echo subprotocol and
18+
// only allows one message every 100ms with a 10 message burst.
19+
funcechoServer(w http.ResponseWriter,r*http.Request) {
20+
c,err:=websocket.Accept(w,r,&websocket.AcceptOptions{
21+
Subprotocols: []string{"echo"},
22+
})
23+
iferr!=nil {
24+
log.Print(err)
25+
return
26+
}
27+
deferc.Close(websocket.StatusInternalError,"the sky is falling")
28+
29+
ifc.Subprotocol()!="echo" {
30+
c.Close(websocket.StatusPolicyViolation,"client must speak the echo subprotocol")
31+
return
32+
}
33+
34+
l:=rate.NewLimiter(rate.Every(time.Millisecond*100),10)
35+
for {
36+
err=echo(r.Context(),c,l)
37+
ifwebsocket.CloseStatus(err)==websocket.StatusNormalClosure {
38+
return
39+
}
40+
iferr!=nil {
41+
log.Printf("failed to echo with %v: %v",r.RemoteAddr,err)
42+
return
43+
}
44+
}
45+
}
46+
47+
// echo reads from the WebSocket connection and then writes
48+
// the received message back to it.
49+
// The entire function has 10s to complete.
50+
funcecho(ctx context.Context,c*websocket.Conn,l*rate.Limiter)error {
51+
ctx,cancel:=context.WithTimeout(ctx,time.Second*10)
52+
defercancel()
53+
54+
err:=l.Wait(ctx)
55+
iferr!=nil {
56+
returnerr
57+
}
58+
59+
typ,r,err:=c.Reader(ctx)
60+
iferr!=nil {
61+
returnerr
62+
}
63+
64+
w,err:=c.Writer(ctx,typ)
65+
iferr!=nil {
66+
returnerr
67+
}
68+
69+
_,err=io.Copy(w,r)
70+
iferr!=nil {
71+
returnfmt.Errorf("failed to io.Copy: %w",err)
72+
}
73+
74+
err=w.Close()
75+
returnerr
76+
}

‎examples/echo/server_test.go‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
"time"
9+
10+
"nhooyr.io/websocket"
11+
"nhooyr.io/websocket/wsjson"
12+
)
13+
14+
// Test_echoServer tests the echoServer by sending it 5 different messages
15+
// and ensuring the responses all match.
16+
funcTest_echoServer(t*testing.T) {
17+
t.Parallel()
18+
19+
s:=httptest.NewServer(http.HandlerFunc(echoServer))
20+
defers.Close()
21+
22+
ctx,cancel:=context.WithTimeout(context.Background(),time.Minute)
23+
defercancel()
24+
25+
c,_,err:=websocket.Dial(ctx,s.URL,&websocket.DialOptions{
26+
Subprotocols: []string{"echo"},
27+
})
28+
iferr!=nil {
29+
t.Fatal(err)
30+
}
31+
deferc.Close(websocket.StatusInternalError,"the sky is falling")
32+
33+
fori:=0;i<5;i++ {
34+
err=wsjson.Write(ctx,c,map[string]int{
35+
"i":i,
36+
})
37+
iferr!=nil {
38+
t.Fatal(err)
39+
}
40+
41+
v:=map[string]int{}
42+
err=wsjson.Read(ctx,c,&v)
43+
iferr!=nil {
44+
t.Fatal(err)
45+
}
46+
47+
ifv["i"]!=i {
48+
t.Fatalf("expected %v but got %v",i,v)
49+
}
50+
}
51+
52+
c.Close(websocket.StatusNormalClosure,"")
53+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp