Source file
src/k8s.io/utils/net/multi_listen_test.go
1
16
17 package net
18
19 import (
20 "context"
21 "fmt"
22 "io"
23 "net"
24 "net/http"
25 "strconv"
26 "sync/atomic"
27 "testing"
28 "time"
29 )
30
31 type fakeCon struct {
32 remoteAddr net.Addr
33 }
34
35 func (f *fakeCon) Read(_ []byte) (n int, err error) {
36 return 0, nil
37 }
38
39 func (f *fakeCon) Write(_ []byte) (n int, err error) {
40 return 0, nil
41 }
42
43 func (f *fakeCon) Close() error {
44 return nil
45 }
46
47 func (f *fakeCon) LocalAddr() net.Addr {
48 return nil
49 }
50
51 func (f *fakeCon) RemoteAddr() net.Addr {
52 return f.remoteAddr
53 }
54
55 func (f *fakeCon) SetDeadline(_ time.Time) error {
56 return nil
57 }
58
59 func (f *fakeCon) SetReadDeadline(_ time.Time) error {
60 return nil
61 }
62
63 func (f *fakeCon) SetWriteDeadline(_ time.Time) error {
64 return nil
65 }
66
67 var _ net.Conn = &fakeCon{}
68
69 type fakeListener struct {
70 addr net.Addr
71 index int
72 err error
73 closed atomic.Bool
74 connErrPairs []connErrPair
75 }
76
77 func (f *fakeListener) Accept() (net.Conn, error) {
78 if f.index < len(f.connErrPairs) {
79 index := f.index
80 connErr := f.connErrPairs[index]
81 f.index++
82 return connErr.conn, connErr.err
83 }
84 for {
85 if f.closed.Load() {
86 return nil, fmt.Errorf("use of closed network connection")
87 }
88 }
89 }
90
91 func (f *fakeListener) Close() error {
92 f.closed.Store(true)
93 return nil
94 }
95
96 func (f *fakeListener) Addr() net.Addr {
97 return f.addr
98 }
99
100 var _ net.Listener = &fakeListener{}
101
102 func listenFuncFactory(listeners []*fakeListener) func(_ context.Context, network string, address string) (net.Listener, error) {
103 index := 0
104 return func(_ context.Context, network string, address string) (net.Listener, error) {
105 if index < len(listeners) {
106 host, portStr, err := net.SplitHostPort(address)
107 if err != nil {
108 return nil, err
109 }
110 port, err := strconv.Atoi(portStr)
111 if err != nil {
112 return nil, err
113 }
114 listener := listeners[index]
115 addr := &net.TCPAddr{
116 IP: ParseIPSloppy(host),
117 Port: port,
118 }
119 if err != nil {
120 return nil, err
121 }
122 listener.addr = addr
123 index++
124
125 if listener.err != nil {
126 return nil, listener.err
127 }
128 return listener, nil
129 }
130 return nil, nil
131 }
132 }
133
134 func TestMultiListen(t *testing.T) {
135 testCases := []struct {
136 name string
137 network string
138 addrs []string
139 fakeListeners []*fakeListener
140 errString string
141 }{
142 {
143 name: "unsupported network",
144 network: "udp",
145 errString: "network \"udp\" not supported",
146 },
147 {
148 name: "no host",
149 network: "tcp",
150 errString: "no address provided to listen on",
151 },
152 {
153 name: "valid",
154 network: "tcp",
155 addrs: []string{"127.0.0.1:12345"},
156 fakeListeners: []*fakeListener{{connErrPairs: []connErrPair{}}},
157 },
158 }
159
160 for _, tc := range testCases {
161 t.Run(tc.name, func(t *testing.T) {
162 ctx := context.TODO()
163 ml, err := multiListen(ctx, tc.network, tc.addrs, listenFuncFactory(tc.fakeListeners))
164
165 if tc.errString != "" {
166 assertError(t, tc.errString, err)
167 } else {
168 assertNoError(t, err)
169 }
170 if ml != nil {
171 err = ml.Close()
172 if err != nil {
173 t.Errorf("Did not expect error: %v", err)
174 }
175 }
176 })
177 }
178 }
179
180 func TestMultiListen_Addr(t *testing.T) {
181 ctx := context.TODO()
182 ml, err := multiListen(ctx, "tcp", []string{"10.10.10.10:5000", "192.168.1.10:5000", "127.0.0.1:5000"}, listenFuncFactory(
183 []*fakeListener{{}, {}, {}},
184 ))
185 if err != nil {
186 t.Errorf("Did not expect error: %v", err)
187 }
188
189 if ml.Addr().String() != "10.10.10.10:5000" {
190 t.Errorf("Expected '10.10.10.10:5000' but got '%s'", ml.Addr().String())
191 }
192
193 err = ml.Close()
194 if err != nil {
195 t.Errorf("Did not expect error: %v", err)
196 }
197 }
198
199 func TestMultiListen_Addrs(t *testing.T) {
200 ctx := context.TODO()
201 addrs := []string{"10.10.10.10:5000", "192.168.1.10:5000", "127.0.0.1:5000"}
202 ml, err := multiListen(ctx, "tcp", addrs, listenFuncFactory(
203 []*fakeListener{{}, {}, {}},
204 ))
205 if err != nil {
206 t.Errorf("Did not expect error: %v", err)
207 }
208
209 gotAddrs := ml.(*multiListener).Addrs()
210 for i := range gotAddrs {
211 if gotAddrs[i].String() != addrs[i] {
212 t.Errorf("expected %q; got %q", addrs[i], gotAddrs[i].String())
213 }
214
215 }
216
217 err = ml.Close()
218 if err != nil {
219 t.Errorf("Did not expect error: %v", err)
220 }
221 }
222
223 func TestMultiListen_Close(t *testing.T) {
224 testCases := []struct {
225 name string
226 addrs []string
227 runner func(listener net.Listener, acceptCalls int) error
228 fakeListeners []*fakeListener
229 acceptCalls int
230 errString string
231 }{
232 {
233 name: "close",
234 addrs: []string{"10.10.10.10:5000", "192.168.1.10:5000", "127.0.0.1:5000"},
235 runner: func(ml net.Listener, acceptCalls int) error {
236 for i := 0; i < acceptCalls; i++ {
237 _, err := ml.Accept()
238 if err != nil {
239 return err
240 }
241 }
242 err := ml.Close()
243 if err != nil {
244 return err
245 }
246 return nil
247 },
248 fakeListeners: []*fakeListener{{}, {}, {}},
249 },
250 {
251 name: "close with pending connections",
252 addrs: []string{"10.10.10.10:5001", "192.168.1.10:5002", "127.0.0.1:5003"},
253 runner: func(ml net.Listener, acceptCalls int) error {
254 for i := 0; i < acceptCalls; i++ {
255 _, err := ml.Accept()
256 if err != nil {
257 return err
258 }
259 }
260 err := ml.Close()
261 if err != nil {
262 return err
263 }
264 return nil
265 },
266 fakeListeners: []*fakeListener{{
267 connErrPairs: []connErrPair{{
268 conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("10.10.10.10"), Port: 50001}},
269 }}}, {
270 connErrPairs: []connErrPair{{
271 conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 50002}},
272 },
273 }}, {
274 connErrPairs: []connErrPair{{
275 conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 50003}},
276 }},
277 }},
278 },
279 {
280 name: "close with no pending connections",
281 addrs: []string{"10.10.10.10:3001", "192.168.1.10:3002", "127.0.0.1:3003"},
282 runner: func(ml net.Listener, acceptCalls int) error {
283 for i := 0; i < acceptCalls; i++ {
284 _, err := ml.Accept()
285 if err != nil {
286 return err
287 }
288 }
289 err := ml.Close()
290 if err != nil {
291 return err
292 }
293 return nil
294 },
295 fakeListeners: []*fakeListener{{
296 connErrPairs: []connErrPair{
297 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("10.10.10.10"), Port: 50001}}},
298 }}, {
299 connErrPairs: []connErrPair{
300 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 50002}}},
301 }}, {
302 connErrPairs: []connErrPair{
303 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 50003}}},
304 },
305 }},
306 acceptCalls: 3,
307 },
308 {
309 name: "close on close",
310 addrs: []string{"10.10.10.10:5000", "192.168.1.10:5000", "127.0.0.1:5000"},
311 runner: func(ml net.Listener, acceptCalls int) error {
312 for i := 0; i < acceptCalls; i++ {
313 _, err := ml.Accept()
314 if err != nil {
315 return err
316 }
317 }
318 err := ml.Close()
319 if err != nil {
320 return err
321 }
322
323 err = ml.Close()
324 if err != nil {
325 return err
326 }
327 return nil
328 },
329 fakeListeners: []*fakeListener{{}, {}, {}},
330 errString: "use of closed network connection",
331 },
332 }
333
334 for _, tc := range testCases {
335 t.Run(tc.name, func(t *testing.T) {
336 ctx := context.TODO()
337 ml, err := multiListen(ctx, "tcp", tc.addrs, listenFuncFactory(tc.fakeListeners))
338 if err != nil {
339 t.Errorf("Did not expect error: %v", err)
340 }
341 err = tc.runner(ml, tc.acceptCalls)
342 if tc.errString != "" {
343 assertError(t, tc.errString, err)
344 } else {
345 assertNoError(t, err)
346 }
347
348 for _, f := range tc.fakeListeners {
349 if !f.closed.Load() {
350 t.Errorf("Expeted sub-listener to be closed")
351 }
352 }
353 })
354 }
355 }
356
357 func TestMultiListen_Accept(t *testing.T) {
358 testCases := []struct {
359 name string
360 addrs []string
361 runner func(listener net.Listener, acceptCalls int) error
362 fakeListeners []*fakeListener
363 acceptCalls int
364 errString string
365 }{
366 {
367 name: "accept all connections",
368 addrs: []string{"10.10.10.10:3000", "192.168.1.103:4000", "127.0.0.1:5000"},
369 runner: func(ml net.Listener, acceptCalls int) error {
370 for i := 0; i < acceptCalls; i++ {
371 _, err := ml.Accept()
372 if err != nil {
373 return err
374 }
375 }
376 err := ml.Close()
377 if err != nil {
378 return err
379 }
380 return nil
381 },
382 fakeListeners: []*fakeListener{{
383 connErrPairs: []connErrPair{
384 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("10.10.10.10"), Port: 50001}}},
385 }}, {
386 connErrPairs: []connErrPair{
387 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 50002}}},
388 }}, {
389 connErrPairs: []connErrPair{
390 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 50003}}},
391 },
392 }},
393 acceptCalls: 3,
394 },
395 {
396 name: "accept some connections",
397 addrs: []string{"10.10.10.10:3000", "192.168.1.103:4000", "172.16.20.10:5000", "127.0.0.1:6000"},
398 runner: func(ml net.Listener, acceptCalls int) error {
399
400 for i := 0; i < acceptCalls; i++ {
401 _, err := ml.Accept()
402 if err != nil {
403 return err
404 }
405
406 }
407 err := ml.Close()
408 if err != nil {
409 return err
410 }
411 return nil
412 },
413 fakeListeners: []*fakeListener{{
414 connErrPairs: []connErrPair{
415 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("10.10.10.10"), Port: 30001}}},
416 }}, {
417 connErrPairs: []connErrPair{
418 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 40001}}},
419 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 40002}}},
420 }}, {
421 connErrPairs: []connErrPair{
422 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("172.16.20.10"), Port: 50001}}},
423 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("172.16.20.10"), Port: 50002}}},
424 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("172.16.20.10"), Port: 50003}}},
425 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("172.16.20.10"), Port: 50004}}},
426 }}, {
427 connErrPairs: []connErrPair{
428 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 60001}}},
429 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 60002}}},
430 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 60003}}},
431 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 60004}}},
432 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 60005}}},
433 },
434 }},
435 acceptCalls: 3,
436 },
437 {
438 name: "accept on closed listener",
439 addrs: []string{"10.10.10.10:3001", "192.168.1.10:3002", "127.0.0.1:3003"},
440 runner: func(ml net.Listener, acceptCalls int) error {
441 err := ml.Close()
442 if err != nil {
443 return err
444 }
445 for i := 0; i < acceptCalls; i++ {
446 _, err := ml.Accept()
447 if err != nil {
448 return err
449 }
450 }
451 return nil
452 },
453 fakeListeners: []*fakeListener{{
454 connErrPairs: []connErrPair{
455 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("10.10.10.10"), Port: 50001}}},
456 }}, {
457 connErrPairs: []connErrPair{
458 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("192.168.1.10"), Port: 50002}}},
459 }}, {
460 connErrPairs: []connErrPair{
461 {conn: &fakeCon{remoteAddr: &net.TCPAddr{IP: ParseIPSloppy("127.0.0.1"), Port: 50003}}},
462 },
463 }},
464 acceptCalls: 1,
465 errString: "use of closed network connection",
466 },
467 }
468
469 for _, tc := range testCases {
470 t.Run(tc.name, func(t *testing.T) {
471 ctx := context.TODO()
472 ml, err := multiListen(ctx, "tcp", tc.addrs, listenFuncFactory(tc.fakeListeners))
473 if err != nil {
474 t.Errorf("Did not expect error: %v", err)
475 }
476
477 err = tc.runner(ml, tc.acceptCalls)
478 if tc.errString != "" {
479 assertError(t, tc.errString, err)
480 } else {
481 assertNoError(t, err)
482 }
483 })
484 }
485 }
486
487 func TestMultiListen_HTTP(t *testing.T) {
488 ctx := context.TODO()
489 ml, err := MultiListen(ctx, "tcp", ":0", ":0", ":0")
490 if err != nil {
491 t.Fatalf("unexpected error: %v", err)
492 }
493
494 addrs := ml.(*multiListener).Addrs()
495 if len(addrs) != 3 {
496 t.Fatalf("expected 3 listeners, got %v", addrs)
497 }
498
499
500 handler := func(w http.ResponseWriter, _ *http.Request) {
501 io.WriteString(w, "hello")
502 }
503 server := http.Server{
504 Handler: http.HandlerFunc(handler),
505 }
506 go func() { _ = server.Serve(ml) }()
507 defer server.Close()
508
509
510 awake := false
511 for i := 0; i < 5; i++ {
512 _, err = http.Get("http://" + addrs[0].String())
513 if err == nil {
514 awake = true
515 break
516 }
517 time.Sleep(50 * time.Millisecond)
518 }
519 if !awake {
520 t.Fatalf("http server did not respond in time")
521 }
522
523
524 for _, addr := range addrs {
525 _, err = http.Get("http://" + addr.String())
526 if err != nil {
527 t.Errorf("error connecting to %q: %v", addr.String(), err)
528 }
529 }
530 }
531
532 func assertError(t *testing.T, errString string, err error) {
533 if err == nil {
534 t.Errorf("Expected error '%s' but got none", errString)
535 }
536 if err.Error() != errString {
537 t.Errorf("Expected error '%s' but got '%s'", errString, err.Error())
538 }
539 }
540
541 func assertNoError(t *testing.T, err error) {
542 if err != nil {
543 t.Errorf("Did not expect error: %v", err)
544 }
545 }
546
View as plain text