1
16
17 package net
18
19 import (
20 "fmt"
21 "io"
22 "net"
23 "net/http"
24 "net/http/httptest"
25 "net/url"
26 "os"
27 "sync/atomic"
28 "syscall"
29 "testing"
30 "time"
31
32 "golang.org/x/net/http2"
33
34 netutils "k8s.io/utils/net"
35 )
36
37 func getIPNet(cidr string) *net.IPNet {
38 _, ipnet, _ := netutils.ParseCIDRSloppy(cidr)
39 return ipnet
40 }
41
42 func TestIPNetEqual(t *testing.T) {
43 testCases := []struct {
44 ipnet1 *net.IPNet
45 ipnet2 *net.IPNet
46 expect bool
47 }{
48
49 {
50 getIPNet("10.0.0.1/24"),
51 getIPNet(""),
52 false,
53 },
54 {
55 getIPNet("10.0.0.0/24"),
56 getIPNet("10.0.0.0/24"),
57 true,
58 },
59 {
60 getIPNet("10.0.0.0/24"),
61 getIPNet("10.0.0.1/24"),
62 true,
63 },
64 {
65 getIPNet("10.0.0.0/25"),
66 getIPNet("10.0.0.0/24"),
67 false,
68 },
69 {
70 getIPNet("10.0.1.0/24"),
71 getIPNet("10.0.0.0/24"),
72 false,
73 },
74 }
75
76 for _, tc := range testCases {
77 if tc.expect != IPNetEqual(tc.ipnet1, tc.ipnet2) {
78 t.Errorf("Expect equality of %s and %s be to %v", tc.ipnet1.String(), tc.ipnet2.String(), tc.expect)
79 }
80 }
81 }
82
83 func TestIsConnectionRefused(t *testing.T) {
84 testCases := []struct {
85 err error
86 expect bool
87 }{
88 {
89 &url.Error{Err: &net.OpError{Err: syscall.ECONNRESET}},
90 false,
91 },
92 {
93 &url.Error{Err: &net.OpError{Err: syscall.ECONNREFUSED}},
94 true,
95 },
96 {&url.Error{Err: &net.OpError{Err: &os.SyscallError{Err: syscall.ECONNREFUSED}}},
97 true,
98 },
99 }
100
101 for _, tc := range testCases {
102 if result := IsConnectionRefused(tc.err); result != tc.expect {
103 t.Errorf("Expect to be %v, but actual is %v", tc.expect, result)
104 }
105 }
106 }
107
108 type tcpLB struct {
109 t *testing.T
110 ln net.Listener
111 serverURL string
112 dials int32
113 }
114
115 func (lb *tcpLB) handleConnection(in net.Conn, stopCh chan struct{}) {
116 out, err := net.Dial("tcp", lb.serverURL)
117 if err != nil {
118 lb.t.Log(err)
119 return
120 }
121 go io.Copy(out, in)
122 go io.Copy(in, out)
123 <-stopCh
124 if err := out.Close(); err != nil {
125 lb.t.Fatalf("failed to close connection: %v", err)
126 }
127 }
128
129 func (lb *tcpLB) serve(stopCh chan struct{}) {
130 conn, err := lb.ln.Accept()
131 if err != nil {
132 lb.t.Fatalf("failed to accept: %v", err)
133 }
134 atomic.AddInt32(&lb.dials, 1)
135 go lb.handleConnection(conn, stopCh)
136 }
137
138 func newLB(t *testing.T, serverURL string) *tcpLB {
139 ln, err := net.Listen("tcp", "127.0.0.1:0")
140 if err != nil {
141 t.Fatalf("failed to bind: %v", err)
142 }
143 lb := tcpLB{
144 serverURL: serverURL,
145 ln: ln,
146 t: t,
147 }
148 return &lb
149 }
150
151 func TestIsConnectionReset(t *testing.T) {
152 ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
153 fmt.Fprintf(w, "Hello, %s", r.Proto)
154 }))
155 ts.EnableHTTP2 = true
156 ts.StartTLS()
157 defer ts.Close()
158
159 u, err := url.Parse(ts.URL)
160 if err != nil {
161 t.Fatalf("failed to parse URL from %q: %v", ts.URL, err)
162 }
163 lb := newLB(t, u.Host)
164 defer lb.ln.Close()
165 stopCh := make(chan struct{})
166 go lb.serve(stopCh)
167
168 c := ts.Client()
169 transport, ok := ts.Client().Transport.(*http.Transport)
170 if !ok {
171 t.Fatalf("failed to assert *http.Transport")
172 }
173 t2, err := http2.ConfigureTransports(transport)
174 if err != nil {
175 t.Fatalf("failed to configure *http.Transport: %+v", err)
176 }
177 t2.ReadIdleTimeout = time.Second
178 t2.PingTimeout = time.Second
179
180 resp, err := c.Get("https://" + lb.ln.Addr().String())
181 if err != nil {
182 t.Fatalf("unexpected error: %+v", err)
183 }
184 defer resp.Body.Close()
185 data, err := io.ReadAll(resp.Body)
186 if err != nil {
187 t.Fatalf("unexpected error: %+v", err)
188 }
189 if string(data) != "Hello, HTTP/2.0" {
190 t.Fatalf("unexpected response: %s", data)
191 }
192
193
194
195
196 close(stopCh)
197 _, err = c.Get("https://" + lb.ln.Addr().String())
198 if !IsHTTP2ConnectionLost(err) {
199 t.Fatalf("expected HTTP2ConnectionLost error, got %v", err)
200 }
201 }
202
View as plain text