1
2
3
4
5 package jsonrpc2_test
6
7 import (
8 "context"
9 "errors"
10 "fmt"
11 "runtime/debug"
12 "testing"
13 "time"
14
15 jsonrpc2 "golang.org/x/tools/internal/jsonrpc2_v2"
16 "golang.org/x/tools/internal/stack/stacktest"
17 "golang.org/x/tools/internal/testenv"
18 )
19
20 func TestIdleTimeout(t *testing.T) {
21 testenv.NeedsLocalhostNet(t)
22 stacktest.NoLeak(t)
23
24
25
26
27
28 timer := time.AfterFunc(10*time.Second, func() {
29 debug.SetTraceback("all")
30 panic("TestIdleTimeout deadlocked")
31 })
32 defer timer.Stop()
33
34 ctx := context.Background()
35
36 try := func(d time.Duration) (longEnough bool) {
37 listener, err := jsonrpc2.NetListener(ctx, "tcp", "localhost:0", jsonrpc2.NetListenOptions{})
38 if err != nil {
39 t.Fatal(err)
40 }
41
42 idleStart := time.Now()
43 listener = jsonrpc2.NewIdleListener(d, listener)
44 defer listener.Close()
45
46 server := jsonrpc2.NewServer(ctx, listener, jsonrpc2.ConnectionOptions{})
47
48
49
50 conn1, err := jsonrpc2.Dial(ctx, listener.Dialer(), jsonrpc2.ConnectionOptions{})
51 if err != nil {
52 if since := time.Since(idleStart); since < d {
53 t.Fatalf("conn1 failed to connect after %v: %v", since, err)
54 }
55 t.Log("jsonrpc2.Dial:", err)
56 return false
57 }
58
59
60
61 ac := conn1.Call(ctx, "ping", nil)
62 if err := ac.Await(ctx, nil); !errors.Is(err, jsonrpc2.ErrMethodNotFound) {
63 if since := time.Since(idleStart); since < d {
64 t.Fatalf("conn1 broken after %v: %v", since, err)
65 }
66 t.Log(`conn1.Call(ctx, "ping", nil):`, err)
67 conn1.Close()
68 return false
69 }
70
71
72
73
74 conn2, err := jsonrpc2.Dial(ctx, listener.Dialer(), jsonrpc2.ConnectionOptions{})
75 if err != nil {
76 conn1.Close()
77 t.Fatalf("conn2 failed to connect while non-idle after %v: %v", time.Since(idleStart), err)
78 return false
79 }
80
81
82
83
84
85
86 ac = conn2.Call(ctx, "ping", nil)
87 if err := ac.Await(ctx, nil); !errors.Is(err, jsonrpc2.ErrMethodNotFound) {
88 t.Fatalf("conn2 broken while non-idle after %v: %v", time.Since(idleStart), err)
89 }
90
91 if err := conn1.Close(); err != nil {
92 t.Fatalf("conn1.Close failed with error: %v", err)
93 }
94 idleStart = time.Now()
95 if err := conn2.Close(); err != nil {
96 t.Fatalf("conn2.Close failed with error: %v", err)
97 }
98
99 conn3, err := jsonrpc2.Dial(ctx, listener.Dialer(), jsonrpc2.ConnectionOptions{})
100 if err != nil {
101 if since := time.Since(idleStart); since < d {
102 t.Fatalf("conn3 failed to connect after %v: %v", since, err)
103 }
104 t.Log("jsonrpc2.Dial:", err)
105 return false
106 }
107
108 ac = conn3.Call(ctx, "ping", nil)
109 if err := ac.Await(ctx, nil); !errors.Is(err, jsonrpc2.ErrMethodNotFound) {
110 if since := time.Since(idleStart); since < d {
111 t.Fatalf("conn3 broken after %v: %v", since, err)
112 }
113 t.Log(`conn3.Call(ctx, "ping", nil):`, err)
114 conn3.Close()
115 return false
116 }
117
118 idleStart = time.Now()
119 if err := conn3.Close(); err != nil {
120 t.Fatalf("conn3.Close failed with error: %v", err)
121 }
122
123 serverError := server.Wait()
124
125 if !errors.Is(serverError, jsonrpc2.ErrIdleTimeout) {
126 t.Errorf("run() returned error %v, want %v", serverError, jsonrpc2.ErrIdleTimeout)
127 }
128 if since := time.Since(idleStart); since < d {
129 t.Errorf("server shut down after %v idle; want at least %v", since, d)
130 }
131 return true
132 }
133
134 d := 1 * time.Millisecond
135 for {
136 t.Logf("testing with idle timeout %v", d)
137 if !try(d) {
138 d *= 2
139 continue
140 }
141 break
142 }
143 }
144
145 type msg struct {
146 Msg string
147 }
148
149 type fakeHandler struct{}
150
151 func (fakeHandler) Handle(ctx context.Context, req *jsonrpc2.Request) (interface{}, error) {
152 switch req.Method {
153 case "ping":
154 return &msg{"pong"}, nil
155 default:
156 return nil, jsonrpc2.ErrNotHandled
157 }
158 }
159
160 func TestServe(t *testing.T) {
161 stacktest.NoLeak(t)
162 ctx := context.Background()
163
164 tests := []struct {
165 name string
166 factory func(context.Context, testing.TB) (jsonrpc2.Listener, error)
167 }{
168 {"tcp", func(ctx context.Context, t testing.TB) (jsonrpc2.Listener, error) {
169 testenv.NeedsLocalhostNet(t)
170 return jsonrpc2.NetListener(ctx, "tcp", "localhost:0", jsonrpc2.NetListenOptions{})
171 }},
172 {"pipe", func(ctx context.Context, t testing.TB) (jsonrpc2.Listener, error) {
173 return jsonrpc2.NetPipeListener(ctx)
174 }},
175 }
176
177 for _, test := range tests {
178 t.Run(test.name, func(t *testing.T) {
179 fake, err := test.factory(ctx, t)
180 if err != nil {
181 t.Fatal(err)
182 }
183 conn, shutdown, err := newFake(t, ctx, fake)
184 if err != nil {
185 t.Fatal(err)
186 }
187 defer shutdown()
188 var got msg
189 if err := conn.Call(ctx, "ping", &msg{"ting"}).Await(ctx, &got); err != nil {
190 t.Fatal(err)
191 }
192 if want := "pong"; got.Msg != want {
193 t.Errorf("conn.Call(...): returned %q, want %q", got, want)
194 }
195 })
196 }
197 }
198
199 func newFake(t *testing.T, ctx context.Context, l jsonrpc2.Listener) (*jsonrpc2.Connection, func(), error) {
200 server := jsonrpc2.NewServer(ctx, l, jsonrpc2.ConnectionOptions{
201 Handler: fakeHandler{},
202 })
203
204 client, err := jsonrpc2.Dial(ctx,
205 l.Dialer(),
206 jsonrpc2.ConnectionOptions{
207 Handler: fakeHandler{},
208 })
209 if err != nil {
210 return nil, nil, err
211 }
212 return client, func() {
213 if err := l.Close(); err != nil {
214 t.Fatal(err)
215 }
216 if err := client.Close(); err != nil {
217 t.Fatal(err)
218 }
219 server.Wait()
220 }, nil
221 }
222
223
224
225
226
227 func TestIdleListenerAcceptCloseRace(t *testing.T) {
228 ctx := context.Background()
229
230 n := 10
231
232
233
234
235
236
237 watchdog := time.Duration(n) * 1000 * time.Millisecond
238 timer := time.AfterFunc(watchdog, func() {
239 debug.SetTraceback("all")
240 panic(fmt.Sprintf("%s deadlocked after %v", t.Name(), watchdog))
241 })
242 defer timer.Stop()
243
244 for ; n > 0; n-- {
245 listener, err := jsonrpc2.NetPipeListener(ctx)
246 if err != nil {
247 t.Fatal(err)
248 }
249 listener = jsonrpc2.NewIdleListener(24*time.Hour, listener)
250
251 done := make(chan struct{})
252 go func() {
253 conn, err := jsonrpc2.Dial(ctx, listener.Dialer(), jsonrpc2.ConnectionOptions{})
254 listener.Close()
255 if err == nil {
256 conn.Close()
257 }
258 close(done)
259 }()
260
261
262
263
264 c, err := listener.Accept(ctx)
265 if err == nil {
266 c.Close()
267 }
268 <-done
269 }
270 }
271
272
273
274
275
276
277
278
279 func TestCloseCallRace(t *testing.T) {
280 ctx := context.Background()
281 n := 10
282
283 watchdog := time.Duration(n) * 1000 * time.Millisecond
284 timer := time.AfterFunc(watchdog, func() {
285 debug.SetTraceback("all")
286 panic(fmt.Sprintf("%s deadlocked after %v", t.Name(), watchdog))
287 })
288 defer timer.Stop()
289
290 for ; n > 0; n-- {
291 listener, err := jsonrpc2.NetPipeListener(ctx)
292 if err != nil {
293 t.Fatal(err)
294 }
295
296 pokec := make(chan *jsonrpc2.AsyncCall, 1)
297
298 s := jsonrpc2.NewServer(ctx, listener, jsonrpc2.BinderFunc(func(_ context.Context, srvConn *jsonrpc2.Connection) jsonrpc2.ConnectionOptions {
299 h := jsonrpc2.HandlerFunc(func(ctx context.Context, _ *jsonrpc2.Request) (interface{}, error) {
300
301
302
303
304
305
306
307 go func() {
308 pokec <- srvConn.Call(ctx, "poke", nil)
309 }()
310
311 return &msg{"pong"}, nil
312 })
313 return jsonrpc2.ConnectionOptions{Handler: h}
314 }))
315
316 dialConn, err := jsonrpc2.Dial(ctx, listener.Dialer(), jsonrpc2.ConnectionOptions{})
317 if err != nil {
318 listener.Close()
319 s.Wait()
320 t.Fatal(err)
321 }
322
323
324
325 if err := dialConn.Call(ctx, "ping", nil).Await(ctx, nil); err != nil {
326 t.Error(err)
327 }
328 if err := dialConn.Close(); err != nil {
329 t.Error(err)
330 }
331
332
333
334 pokeCall := <-pokec
335 if err := pokeCall.Await(ctx, nil); err == nil {
336 t.Errorf("unexpected nil error from server-initited call")
337 } else if errors.Is(err, jsonrpc2.ErrMethodNotFound) {
338
339 } else {
340
341 t.Logf("server-initiated call completed with expected error: %v", err)
342 }
343
344 listener.Close()
345 s.Wait()
346 }
347 }
348
View as plain text