Skip to content

Commit 32b68e9

Browse files
committed
Merge pull request #3 from meteorhacks/refactor-handlers
Refactor handlers
2 parents 14a5bc3 + 535d079 commit 32b68e9

File tree

10 files changed

+637
-118
lines changed

10 files changed

+637
-118
lines changed

README.md

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,3 @@
11
# Go DDP
22

33
DDP server and client implemented with go.
4-
5-
## Server Example
6-
7-
```go
8-
package main
9-
10-
import (
11-
"github.com/meteorhacks/goddp"
12-
)
13-
14-
func main() {
15-
server := goddp.NewServer()
16-
server.Method("hello", methodHandler)
17-
server.Listen(":1337")
18-
}
19-
20-
func methodHandler(p []interface{}) (interface{}, error) {
21-
return "result", nil
22-
}
23-
```

goddp.go

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

server/README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,20 @@ import (
1212
)
1313

1414
func main() {
15-
server := server.New()
16-
server.Method("hello", methodHandler)
17-
server.Listen(":1337")
15+
s := server.New()
16+
s.Method("double", handler)
17+
s.Listen(":1337")
1818
}
1919

20-
func methodHandler(p []interface{}) (interface{}, error) {
21-
return "result", nil
20+
func handler(ctx server.MethodContext) {
21+
n, ok := ctx.Args[0].(float64)
22+
23+
if !ok {
24+
ctx.SendError("invalid parameters")
25+
} else {
26+
ctx.SendResult(n * 2)
27+
}
28+
29+
ctx.SendUpdated()
2230
}
2331
```
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package integration
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"io/ioutil"
7+
"net"
8+
"net/http"
9+
"net/url"
10+
"testing"
11+
"time"
12+
13+
"github.com/gorilla/websocket"
14+
"github.com/meteorhacks/goddp/server"
15+
)
16+
17+
var (
18+
URL = "http://localhost:1337/websocket"
19+
ORIGIN = "http://localhost:1337"
20+
ADDR = "localhost:1337"
21+
s server.Server
22+
)
23+
24+
type MethodError struct {
25+
Error string `json:"error"`
26+
}
27+
28+
type Message struct {
29+
Msg string `json:"msg"`
30+
Session string `json:"session"`
31+
ID string `json:"id"`
32+
Result float64 `json:"result"`
33+
Error MethodError `json:"error"`
34+
}
35+
36+
func TestStartServer(t *testing.T) {
37+
s = server.New()
38+
39+
s.Method("double", func(ctx server.MethodContext) {
40+
n, ok := ctx.Args[0].(float64)
41+
42+
if !ok {
43+
ctx.SendError("invalid parameters")
44+
} else {
45+
ctx.SendResult(n * 2)
46+
}
47+
48+
ctx.SendUpdated()
49+
})
50+
51+
go s.Listen(":1337")
52+
time.Sleep(100 * time.Millisecond)
53+
}
54+
55+
func TestConnect(t *testing.T) {
56+
ws, err := newClient()
57+
if err != nil {
58+
t.Error("websocket connection failed")
59+
}
60+
61+
defer ws.Close()
62+
63+
writeMessage(ws, `{"msg": "connect", "version": "1", "support": ["1"]}`, t)
64+
msg := readMessage(ws, t)
65+
66+
if msg.Msg != "connected" {
67+
t.Error("inconnect DDP message type")
68+
}
69+
70+
if len(msg.Session) != 17 {
71+
t.Error("session field should be have 17 characters")
72+
}
73+
}
74+
75+
func TestPingWithoutId(t *testing.T) {
76+
ws, err := newClient()
77+
if err != nil {
78+
t.Error("websocket connection failed")
79+
}
80+
81+
defer ws.Close()
82+
83+
writeMessage(ws, `{"msg": "connect", "version": "1", "support": ["1"]}`, t)
84+
_ = readMessage(ws, t) // ignore "connected" message
85+
86+
writeMessage(ws, `{"msg": "ping"}`, t)
87+
msg := readMessage(ws, t)
88+
89+
if msg.Msg != "pong" {
90+
t.Error("inconnect DDP message type")
91+
}
92+
}
93+
94+
func TestPingWithId(t *testing.T) {
95+
ws, err := newClient()
96+
if err != nil {
97+
t.Error("websocket connection failed")
98+
}
99+
100+
defer ws.Close()
101+
102+
writeMessage(ws, `{"msg": "connect", "version": "1", "support": ["1"]}`, t)
103+
_ = readMessage(ws, t) // ignore "connected" message
104+
105+
writeMessage(ws, `{"msg": "ping", "id": "test-id"}`, t)
106+
msg := readMessage(ws, t)
107+
108+
if msg.Msg != "pong" {
109+
t.Error("inconnect DDP message type")
110+
}
111+
112+
if msg.ID != "test-id" {
113+
t.Error("inconnect random id")
114+
}
115+
}
116+
117+
func TestMethodResult(t *testing.T) {
118+
ws, err := newClient()
119+
if err != nil {
120+
t.Error("websocket connection failed")
121+
}
122+
123+
defer ws.Close()
124+
125+
writeMessage(ws, `{"msg": "connect", "version": "1", "support": ["1"]}`, t)
126+
_ = readMessage(ws, t) // ignore "connected" message
127+
128+
writeMessage(ws, `{"msg": "method", "id": "test-id", "method": "double", "params": [2]}`, t)
129+
msg := readMessage(ws, t)
130+
131+
if msg.Msg != "result" {
132+
t.Error("inconnect DDP message type")
133+
}
134+
135+
if msg.ID != "test-id" {
136+
t.Error("inconnect random id")
137+
}
138+
139+
if msg.Result != 4 {
140+
t.Error("inconnect method result")
141+
}
142+
}
143+
144+
func TestMethodError(t *testing.T) {
145+
ws, err := newClient()
146+
if err != nil {
147+
t.Error("websocket connection failed")
148+
}
149+
150+
defer ws.Close()
151+
152+
writeMessage(ws, `{"msg": "connect", "version": "1", "support": ["1"]}`, t)
153+
_ = readMessage(ws, t) // ignore "connected" message
154+
155+
writeMessage(ws, `{"msg": "method", "id": "test-id", "method": "double", "params": ["two"]}`, t)
156+
msg := readMessage(ws, t)
157+
158+
if msg.Msg != "result" {
159+
t.Error("inconnect DDP message type")
160+
}
161+
162+
if msg.ID != "test-id" {
163+
t.Error("inconnect random id")
164+
}
165+
166+
if msg.Error.Error == "" {
167+
t.Error("method error should be set")
168+
}
169+
}
170+
171+
func newClient() (*websocket.Conn, error) {
172+
u, _ := url.Parse(URL)
173+
conn, err := net.Dial("tcp", ADDR)
174+
175+
if err != nil {
176+
return nil, err
177+
}
178+
179+
header := http.Header{"Origin": {ORIGIN}}
180+
ws, _, err := websocket.NewClient(conn, u, header, 1024, 1024)
181+
return ws, err
182+
}
183+
184+
func writeMessage(c *websocket.Conn, str string, t *testing.T) {
185+
w, err := c.NextWriter(websocket.TextMessage)
186+
187+
if err != nil {
188+
t.Error("cannot create websocket write")
189+
}
190+
191+
io.WriteString(w, str)
192+
w.Close()
193+
}
194+
195+
func readMessage(c *websocket.Conn, t *testing.T) Message {
196+
op, r, err := c.NextReader()
197+
198+
if op != websocket.TextMessage {
199+
t.Error("expecting a text message")
200+
}
201+
202+
if err != nil {
203+
t.Error("cannot create reader")
204+
}
205+
206+
str, err := ioutil.ReadAll(r)
207+
if err != nil {
208+
t.Error("websocket read error")
209+
}
210+
211+
msg := Message{}
212+
if err := json.Unmarshal(str, &msg); err != nil {
213+
t.Error("cannot parse websocket response")
214+
}
215+
216+
return msg
217+
}

server/method_context.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package server
2+
3+
import (
4+
"errors"
5+
)
6+
7+
type MethodContext struct {
8+
ID string
9+
Args []interface{}
10+
Res Response
11+
Done bool
12+
Updated bool
13+
}
14+
15+
func NewMethodContext(m Message, res Response) MethodContext {
16+
ctx := MethodContext{}
17+
ctx.ID = m.ID
18+
ctx.Args = m.Params
19+
ctx.Res = res
20+
return ctx
21+
}
22+
23+
func (ctx *MethodContext) SendResult(r interface{}) error {
24+
if ctx.Done {
25+
err := errors.New("already sent results for method")
26+
return err
27+
}
28+
29+
ctx.Done = true
30+
return ctx.Res.WriteJSON(map[string]interface{}{
31+
"msg": "result",
32+
"id": ctx.ID,
33+
"result": r,
34+
})
35+
}
36+
37+
func (ctx *MethodContext) SendError(e string) error {
38+
if ctx.Done {
39+
err := errors.New("already sent results for method")
40+
return err
41+
}
42+
43+
ctx.Done = true
44+
return ctx.Res.WriteJSON(map[string]interface{}{
45+
"msg": "result",
46+
"id": ctx.ID,
47+
"error": map[string]string{
48+
"error": e,
49+
},
50+
})
51+
}
52+
53+
func (ctx *MethodContext) SendUpdated() error {
54+
if ctx.Updated {
55+
err := errors.New("already sent updated for method")
56+
return err
57+
}
58+
59+
ctx.Updated = true
60+
return ctx.Res.WriteJSON(map[string]interface{}{
61+
"msg": "updated",
62+
"methods": []string{ctx.ID},
63+
})
64+
}

0 commit comments

Comments
 (0)