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  }