github.com/gdamore/mangos@v1.4.0/test/transport.go (about) 1 // Copyright 2018 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package test 16 17 import ( 18 "bytes" 19 "crypto/tls" 20 "net" 21 "strings" 22 "sync" 23 "testing" 24 "time" 25 26 "nanomsg.org/go-mangos" 27 "nanomsg.org/go-mangos/protocol/rep" 28 "nanomsg.org/go-mangos/protocol/req" 29 ) 30 31 // TranTest provides a common test structure for transports, so that they 32 // can implement a battery of standard tests. 33 type TranTest struct { 34 addr string 35 tran mangos.Transport 36 cliCfg *tls.Config 37 srvCfg *tls.Config 38 sockRep mangos.Socket 39 sockReq mangos.Socket 40 } 41 42 // NewTranTest creates a TranTest. 43 func NewTranTest(tran mangos.Transport, addr string) *TranTest { 44 tt := &TranTest{addr: addr, tran: tran} 45 if strings.HasPrefix(tt.addr, "tls+tcp://") || strings.HasPrefix(tt.addr, "wss://") { 46 tt.cliCfg, _ = GetTLSConfig(false) 47 tt.srvCfg, _ = GetTLSConfig(true) 48 } 49 tt.sockRep, _ = rep.NewSocket() 50 tt.sockReq, _ = req.NewSocket() 51 return tt 52 } 53 54 // TestListenAndAccept tests that we can both listen and accept connections 55 // for the given transport. 56 func (tt *TranTest) TestListenAndAccept(t *testing.T) { 57 t.Logf("Establishing listener for %s", tt.addr) 58 l, err := tt.tran.NewListener(tt.addr, tt.sockRep) 59 if err != nil { 60 t.Errorf("NewListener failed: %v", err) 61 return 62 } 63 defer l.Close() 64 if tt.srvCfg != nil { 65 if err = l.SetOption(mangos.OptionTLSConfig, tt.srvCfg); err != nil { 66 t.Errorf("Failed setting TLS config: %v", err) 67 return 68 } 69 } 70 if err = l.Listen(); err != nil { 71 t.Errorf("Listen failed: %v", err) 72 return 73 } 74 75 var wg sync.WaitGroup 76 77 wg.Add(1) 78 go func() { 79 defer wg.Done() 80 t.Logf("Connecting on %s", tt.addr) 81 d, err := tt.tran.NewDialer(tt.addr, tt.sockReq) 82 if err != nil { 83 t.Errorf("NewDialer failed: %v", err) 84 return 85 } 86 if tt.cliCfg != nil { 87 if err = d.SetOption(mangos.OptionTLSConfig, tt.cliCfg); err != nil { 88 t.Errorf("Failed setting TLS config: %v", err) 89 return 90 } 91 } 92 client, err := d.Dial() 93 if err != nil { 94 t.Errorf("Dial failed: %v", err) 95 return 96 } 97 if v, err := client.GetProp(mangos.PropLocalAddr); err == nil { 98 addr := v.(net.Addr) 99 t.Logf("Dialed on local net %s addr %s", addr.Network(), addr.String()) 100 } else { 101 t.Logf("err is %v", err.Error()) 102 } 103 if v, err := client.GetProp(mangos.PropRemoteAddr); err == nil { 104 addr := v.(net.Addr) 105 t.Logf("Dialed remote peer %s addr %s", addr.Network(), addr.String()) 106 } 107 t.Logf("Connected client: %d (server %d)", 108 client.LocalProtocol(), client.RemoteProtocol()) 109 t.Logf("Client open: %t", client.IsOpen()) 110 if !client.IsOpen() { 111 t.Error("Client is closed") 112 return 113 } 114 }() 115 116 server, err := l.Accept() 117 if err != nil { 118 t.Errorf("Accept failed: %v", err) 119 return 120 } 121 if v, err := server.GetProp(mangos.PropLocalAddr); err == nil { 122 addr := v.(net.Addr) 123 t.Logf("Accepted on local net %s addr %s", addr.Network(), addr.String()) 124 } 125 if v, err := server.GetProp(mangos.PropRemoteAddr); err == nil { 126 addr := v.(net.Addr) 127 t.Logf("Accepted remote peer %s addr %s", addr.Network(), addr.String()) 128 } 129 defer server.Close() 130 131 t.Logf("Connected server: %d (client %d)", 132 server.LocalProtocol(), server.RemoteProtocol()) 133 t.Logf("Server open: %t", server.IsOpen()) 134 if !server.IsOpen() { 135 t.Error("Server is closed") 136 } 137 wg.Wait() 138 } 139 140 // TestDuplicateListen checks to make sure that an attempt to listen 141 // on a second socket, when another listener is already present, properly 142 // fails with ErrAddrInUse. 143 func (tt *TranTest) TestDuplicateListen(t *testing.T) { 144 var err error 145 time.Sleep(100 * time.Millisecond) 146 t.Logf("Testing Duplicate Listen on %s", tt.addr) 147 l1, err := tt.tran.NewListener(tt.addr, tt.sockRep) 148 if err != nil { 149 t.Errorf("NewListener failed: %v", err) 150 return 151 } 152 defer l1.Close() 153 if tt.srvCfg != nil { 154 if err = l1.SetOption(mangos.OptionTLSConfig, tt.srvCfg); err != nil { 155 t.Errorf("Failed setting TLS config: %v", err) 156 return 157 } 158 } 159 if err = l1.Listen(); err != nil { 160 t.Errorf("Listen failed: %v", err) 161 return 162 } 163 164 l2, err := tt.tran.NewListener(tt.addr, tt.sockReq) 165 if err != nil { 166 t.Errorf("NewListener faield: %v", err) 167 return 168 } 169 defer l2.Close() 170 if tt.srvCfg != nil { 171 if err = l2.SetOption(mangos.OptionTLSConfig, tt.srvCfg); err != nil { 172 t.Errorf("Failed setting TLS config: %v", err) 173 return 174 } 175 } 176 if err = l2.Listen(); err == nil { 177 t.Errorf("Duplicate listen should not be permitted!") 178 return 179 } 180 t.Logf("Got expected error: %v", err) 181 } 182 183 // TestConnRefused tests that attempts to dial to an address without a listener 184 // properly fail with EConnRefused. 185 func (tt *TranTest) TestConnRefused(t *testing.T) { 186 d, err := tt.tran.NewDialer(tt.addr, tt.sockReq) 187 if err != nil || d == nil { 188 t.Errorf("New Dialer failed: %v", err) 189 } 190 if tt.cliCfg != nil { 191 if err = d.SetOption(mangos.OptionTLSConfig, tt.cliCfg); err != nil { 192 t.Errorf("Failed setting TLS config: %v", err) 193 return 194 } 195 } 196 c, err := d.Dial() 197 if err == nil || c != nil { 198 t.Errorf("Connection not refused (%s)!", tt.addr) 199 return 200 } 201 t.Logf("Got expected error: %v", err) 202 } 203 204 // TestSendRecv test that the transport can send and receive. It uses the 205 // REQ/REP protocol for messages. 206 func (tt *TranTest) TestSendRecv(t *testing.T) { 207 ping := []byte("REQUEST_MESSAGE") 208 ack := []byte("RESPONSE_MESSAGE") 209 210 ch := make(chan *mangos.Message) 211 212 t.Logf("Establishing REP listener on %s", tt.addr) 213 l, err := tt.tran.NewListener(tt.addr, tt.sockRep) 214 if err != nil { 215 t.Errorf("NewListener failed: %v", err) 216 return 217 } 218 defer l.Close() 219 if tt.srvCfg != nil { 220 if err = l.SetOption(mangos.OptionTLSConfig, tt.srvCfg); err != nil { 221 t.Errorf("Failed setting TLS config: %v", err) 222 return 223 } 224 } 225 if err = l.Listen(); err != nil { 226 t.Errorf("Listen failed: %v", err) 227 return 228 } 229 230 go func() { 231 defer close(ch) 232 233 // Client side 234 t.Logf("Connecting REQ on %s", tt.addr) 235 d, err := tt.tran.NewDialer(tt.addr, tt.sockReq) 236 if tt.cliCfg != nil { 237 if err = d.SetOption(mangos.OptionTLSConfig, tt.cliCfg); err != nil { 238 t.Errorf("Failed setting TLS config: %v", err) 239 return 240 } 241 } 242 243 client, err := d.Dial() 244 if err != nil { 245 t.Errorf("Dial failed: %v", err) 246 return 247 } 248 t.Logf("Connected client: %t", client.IsOpen()) 249 defer client.Close() 250 251 req := mangos.NewMessage(len(ping)) 252 req.Body = append(req.Body, ping...) 253 254 // Now try to send data 255 t.Logf("Sending %d bytes", len(req.Body)) 256 257 err = client.Send(req) 258 if err != nil { 259 t.Errorf("Client send error: %v", err) 260 return 261 } 262 t.Logf("Client sent") 263 264 rep, err := client.Recv() 265 if err != nil { 266 t.Errorf("Client receive error: %v", err) 267 return 268 } 269 270 if !bytes.Equal(rep.Body, ack) { 271 t.Errorf("Reply mismatch: %v, %v", rep.Body, ack) 272 return 273 } 274 if len(rep.Header) != 0 { 275 t.Errorf("Client reply non-empty header: %v", 276 rep.Header) 277 return 278 } 279 select { 280 case ch <- rep: 281 t.Log("Client reply forwarded") 282 case <-time.After(5 * time.Second): // 5 secs should be plenty 283 t.Error("Client timeout forwarding reply") 284 return 285 } 286 }() 287 288 server, err := l.Accept() 289 if err != nil { 290 t.Errorf("Accept failed: %v", err) 291 return 292 } 293 t.Logf("Connected server: %t", server.IsOpen()) 294 defer server.Close() 295 296 // Now we can try to send and receive 297 req, err := server.Recv() 298 if err != nil { 299 t.Errorf("Server receive error: %v", err) 300 return 301 } 302 t.Logf("Server received %d bytes", len(req.Body)) 303 if !bytes.Equal(req.Body, ping) { 304 t.Errorf("Request mismatch: %v, %v", req.Body, ping) 305 return 306 } 307 308 if len(req.Header) != 0 { 309 t.Errorf("Server request non-empty header: %v", req.Header) 310 return 311 } 312 313 // Now reply 314 rep := mangos.NewMessage(len(ack)) 315 rep.Body = append(rep.Body, ack...) 316 317 t.Logf("Server sending %d bytes", len(rep.Body)) 318 319 err = server.Send(rep) 320 if err != nil { 321 t.Errorf("Server send error: %v", err) 322 return 323 } 324 t.Log("Server reply sent") 325 326 // Wait for client to ack reply over back channel. 327 select { 328 case nrep := <-ch: 329 if !bytes.Equal(nrep.Body, ack) { 330 t.Errorf("Client forward mismatch: %v, %v", ack, rep) 331 return 332 } 333 case <-time.After(5 * time.Second): 334 t.Error("Client timeout?") 335 return 336 } 337 } 338 339 // TestScheme tests the Scheme() entry point on the transport. 340 func (tt *TranTest) TestScheme(t *testing.T) { 341 scheme := tt.tran.Scheme() 342 t.Log("Checking scheme") 343 if !strings.HasPrefix(tt.addr, scheme+"://") { 344 t.Errorf("Wrong scheme: addr %s, scheme %s", tt.addr, scheme) 345 return 346 } 347 t.Log("Scheme match") 348 } 349 350 // TestListenerSetOptionInvalid tests passing invalid options to a listener. 351 func (tt *TranTest) TestListenerSetOptionInvalid(t *testing.T) { 352 t.Log("Trying invalid listener SetOption") 353 l, err := tt.tran.NewListener(tt.addr, tt.sockRep) 354 if err != nil { 355 t.Errorf("Unable to create listener") 356 return 357 } 358 err = l.SetOption("NO-SUCH-OPTION", true) 359 switch err { 360 case mangos.ErrBadOption: 361 t.Log("Got expected err BadOption") 362 case nil: 363 t.Errorf("Got nil err, but expected BadOption!") 364 default: 365 t.Errorf("Got unexpected error %v, expected BadOption", err) 366 } 367 } 368 369 // TestListenerGetOptionInvalid tests trying to get an invalid option on 370 // a listener. 371 func (tt *TranTest) TestListenerGetOptionInvalid(t *testing.T) { 372 t.Log("Trying invalid listener GetOption") 373 l, err := tt.tran.NewListener(tt.addr, tt.sockRep) 374 if err != nil { 375 t.Errorf("Unable to create listener") 376 return 377 } 378 _, err = l.GetOption("NO-SUCH-OPTION") 379 switch err { 380 case mangos.ErrBadOption: 381 t.Log("Got expected err BadOption") 382 case nil: 383 t.Errorf("Got nil err, but expected BadOption!") 384 default: 385 t.Errorf("Got unexpected error %v, expected BadOption", err) 386 } 387 } 388 389 // TestDialerSetOptionInvalid tests trying to set an invalid option on a Dialer. 390 func (tt *TranTest) TestDialerSetOptionInvalid(t *testing.T) { 391 t.Log("Trying invalid dialer SetOption") 392 d, err := tt.tran.NewDialer(tt.addr, tt.sockRep) 393 if err != nil { 394 t.Errorf("Unable to create dialer") 395 return 396 } 397 err = d.SetOption("NO-SUCH-OPTION", true) 398 switch err { 399 case mangos.ErrBadOption: 400 t.Log("Got expected err BadOption") 401 case nil: 402 t.Errorf("Got nil err, but expected BadOption!") 403 default: 404 t.Errorf("Got unexpected error %v, expected BadOption", err) 405 } 406 } 407 408 // TestDialerGetOptionInvalid tests attempting to get an invalid option on 409 // a Dialer. 410 func (tt *TranTest) TestDialerGetOptionInvalid(t *testing.T) { 411 t.Log("Trying invalid listener GetOption") 412 d, err := tt.tran.NewDialer(tt.addr, tt.sockRep) 413 if err != nil { 414 t.Errorf("Unable to create dialer") 415 return 416 } 417 _, err = d.GetOption("NO-SUCH-OPTION") 418 switch err { 419 case mangos.ErrBadOption: 420 t.Log("Got expected err BadOption") 421 case nil: 422 t.Errorf("Got nil err, but expected BadOption!") 423 default: 424 t.Errorf("Got unexpected error %v, expected BadOption", err) 425 } 426 } 427 428 // TestDialerBadScheme tests to makes sure that giving a bogus scheme 429 // to create a dialer fails properly. 430 func (tt *TranTest) TestDialerBadScheme(t *testing.T) { 431 t.Logf("NewDialer with bogus scheme") 432 d, err := tt.tran.NewDialer("bogus://address", tt.sockRep) 433 if err == nil { 434 t.Errorf("Expected error, got nil") 435 } else if d != nil { 436 t.Errorf("Got non-nil error, and non-nil dialer") 437 } else { 438 t.Logf("Got expected error %v", err) 439 } 440 } 441 442 // TestListenerBadScheme tests to makes sure that giving a bogus scheme 443 // to create a listener fails properly. 444 func (tt *TranTest) TestListenerBadScheme(t *testing.T) { 445 t.Logf("NewListener with bogus scheme") 446 d, err := tt.tran.NewListener("bogus://address", tt.sockRep) 447 if err == nil { 448 t.Errorf("Expected error, got nil") 449 } else if d != nil { 450 t.Errorf("Got non-nil error, and non-nil listener") 451 } else { 452 t.Logf("Got expected error %v", err) 453 } 454 } 455 456 // TestAll runs a full battery of standard tests on the transport. 457 func (tt *TranTest) TestAll(t *testing.T) { 458 tt.TestScheme(t) 459 tt.TestListenAndAccept(t) 460 tt.TestConnRefused(t) 461 tt.TestDuplicateListen(t) 462 tt.TestSendRecv(t) 463 tt.TestDialerSetOptionInvalid(t) 464 tt.TestDialerGetOptionInvalid(t) 465 tt.TestListenerSetOptionInvalid(t) 466 tt.TestListenerGetOptionInvalid(t) 467 tt.TestDialerBadScheme(t) 468 tt.TestListenerBadScheme(t) 469 }