go.nanomsg.org/mangos/v3@v3.4.3-0.20240217232803-46464076f1f5/protocol/req/req_test.go (about)

     1  // Copyright 2019 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 req
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/binary"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"go.nanomsg.org/mangos/v3"
    25  	. "go.nanomsg.org/mangos/v3/internal/test"
    26  	"go.nanomsg.org/mangos/v3/protocol/rep"
    27  	_ "go.nanomsg.org/mangos/v3/transport/inproc"
    28  )
    29  
    30  func TestReqIdentity(t *testing.T) {
    31  	id := MustGetInfo(t, NewSocket)
    32  	MustBeTrue(t, id.Self == mangos.ProtoReq)
    33  	MustBeTrue(t, id.SelfName == "req")
    34  	MustBeTrue(t, id.Peer == mangos.ProtoRep)
    35  	MustBeTrue(t, id.PeerName == "rep")
    36  }
    37  
    38  func TestReqCooked(t *testing.T) {
    39  	VerifyCooked(t, NewSocket)
    40  }
    41  
    42  func TestReqOptions(t *testing.T) {
    43  	VerifyInvalidOption(t, NewSocket)
    44  	VerifyOptionDuration(t, NewSocket, mangos.OptionRecvDeadline)
    45  	VerifyOptionDuration(t, NewSocket, mangos.OptionSendDeadline)
    46  	VerifyOptionDuration(t, NewSocket, mangos.OptionRetryTime)
    47  	VerifyOptionBool(t, NewSocket, mangos.OptionBestEffort)
    48  	VerifyOptionBool(t, NewSocket, mangos.OptionFailNoPeers)
    49  }
    50  
    51  func TestReqClosed(t *testing.T) {
    52  	VerifyClosedRecv(t, NewSocket)
    53  	VerifyClosedSend(t, NewSocket)
    54  	VerifyClosedClose(t, NewSocket)
    55  	VerifyClosedDial(t, NewSocket)
    56  	VerifyClosedListen(t, NewSocket)
    57  	VerifyClosedAddPipe(t, NewSocket)
    58  }
    59  
    60  func TestReqRecvState(t *testing.T) {
    61  	s := GetSocket(t, NewSocket)
    62  	v, e := s.Recv()
    63  	MustBeError(t, e, mangos.ErrProtoState)
    64  	MustBeNil(t, v)
    65  	MustSucceed(t, s.Close())
    66  }
    67  
    68  func TestReqRecvDeadline(t *testing.T) {
    69  	self := GetSocket(t, NewSocket)
    70  	peer := GetSocket(t, rep.NewSocket)
    71  	ConnectPair(t, self, peer)
    72  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Millisecond))
    73  	MustSucceed(t, self.Send([]byte{}))
    74  	_ = MustRecv(t, peer)
    75  	m, e := self.RecvMsg()
    76  	MustBeError(t, e, mangos.ErrRecvTimeout)
    77  	MustBeNil(t, m)
    78  	MustSucceed(t, self.Close())
    79  	MustSucceed(t, peer.Close())
    80  }
    81  
    82  func TestReqContextClosed(t *testing.T) {
    83  	s := GetSocket(t, NewSocket)
    84  	c, e := s.OpenContext()
    85  	MustSucceed(t, e)
    86  	MustNotBeNil(t, c)
    87  	MustSucceed(t, c.Close())
    88  	MustBeError(t, c.Close(), mangos.ErrClosed)
    89  
    90  	c, e = s.OpenContext()
    91  	MustSucceed(t, e)
    92  	MustNotBeNil(t, c)
    93  
    94  	MustSucceed(t, s.Close())
    95  
    96  	MustBeError(t, c.Close(), mangos.ErrClosed)
    97  
    98  	_, e = s.OpenContext()
    99  	MustBeError(t, e, mangos.ErrClosed)
   100  }
   101  
   102  // This test demonstrates that sending a second request cancels any Rx on the
   103  // earlier outstanding ones.
   104  func TestReqCancel(t *testing.T) {
   105  	s := GetSocket(t, NewSocket)
   106  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Second))
   107  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true))
   108  	MustSendString(t, s, "first")
   109  	var wg sync.WaitGroup
   110  	wg.Add(1)
   111  	pass := false
   112  	go func() {
   113  		defer wg.Done()
   114  		v, e := s.Recv()
   115  		MustBeError(t, e, mangos.ErrCanceled)
   116  		MustBeNil(t, v)
   117  		pass = true
   118  	}()
   119  	time.Sleep(time.Millisecond * 50) // to allow go routine to run
   120  	MustSendString(t, s, "second")
   121  	wg.Wait()
   122  	MustBeTrue(t, pass)
   123  	MustSucceed(t, s.Close())
   124  }
   125  
   126  // This test demonstrates that sending a second request cancels any Rx on the
   127  // earlier outstanding ones.
   128  func TestReqCancelDisconnect(t *testing.T) {
   129  	s := GetSocket(t, NewSocket)
   130  	peer := GetSocket(t, rep.NewSocket)
   131  	ConnectPair(t, s, peer)
   132  	MustSucceed(t, s.SetOption(mangos.OptionRecvDeadline, time.Second*3))
   133  	MustSucceed(t, s.SetOption(mangos.OptionRetryTime, time.Duration(0)))
   134  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true))
   135  	MustSendString(t, s, "first")
   136  	go func() {
   137  		time.Sleep(time.Millisecond * 100)
   138  		peer.Close()
   139  	}()
   140  	v, e := s.Recv()
   141  	MustBeError(t, e, mangos.ErrCanceled)
   142  	MustBeNil(t, v)
   143  	MustSucceed(t, s.Close())
   144  }
   145  
   146  // This test demonstrates cancellation before calling receive but after the
   147  // message is received causes the original message to be discarded.
   148  func TestReqCancelReply(t *testing.T) {
   149  	self := GetSocket(t, NewSocket)
   150  	peer := GetSocket(t, rep.NewSocket)
   151  	ConnectPair(t, self, peer)
   152  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Second))
   153  	MustSucceed(t, peer.SetOption(mangos.OptionRecvDeadline, time.Second))
   154  
   155  	MustSendString(t, self, "query1")
   156  	MustRecvString(t, peer, "query1")
   157  	MustSendString(t, peer, "reply1")
   158  	// And we don't pick up the reply
   159  
   160  	time.Sleep(time.Millisecond * 50)
   161  
   162  	MustSendString(t, self, "query2")
   163  	MustRecvString(t, peer, "query2")
   164  	MustSendString(t, peer, "reply2")
   165  	MustRecvString(t, self, "reply2")
   166  
   167  	MustSucceed(t, self.Close())
   168  	MustSucceed(t, peer.Close())
   169  }
   170  
   171  func TestReqBestEffort(t *testing.T) {
   172  	timeout := time.Millisecond
   173  	msg := []byte{'0', '1', '2', '3'}
   174  
   175  	s := GetSocket(t, NewSocket)
   176  	MustSucceed(t, s.SetOption(mangos.OptionSendDeadline, timeout))
   177  	MustSucceed(t, s.Listen(AddrTestInp()))
   178  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, true))
   179  	MustSucceed(t, s.Send(msg))
   180  	MustSucceed(t, s.Send(msg))
   181  	MustSucceed(t, s.SetOption(mangos.OptionBestEffort, false))
   182  	MustBeError(t, s.Send(msg), mangos.ErrSendTimeout)
   183  	MustBeError(t, s.Send(msg), mangos.ErrSendTimeout)
   184  }
   185  
   186  // This test demonstrates cancellation before calling receive but after the
   187  // message is received causes the original message to be discarded.
   188  func TestReqRetry(t *testing.T) {
   189  	self := GetSocket(t, NewSocket)
   190  	peer := GetSocket(t, rep.NewSocket)
   191  	ConnectPair(t, self, peer)
   192  
   193  	MustSucceed(t, self.SetOption(mangos.OptionRetryTime, time.Millisecond*10))
   194  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Millisecond*10))
   195  	MustSucceed(t, peer.SetOption(mangos.OptionRecvDeadline, time.Millisecond*200))
   196  
   197  	start := time.Now()
   198  
   199  	MustSendString(t, self, "query")
   200  	MustRecvString(t, peer, "query")
   201  	MustRecvString(t, peer, "query")
   202  	MustSendString(t, peer, "reply")
   203  
   204  	MustBeTrue(t, time.Since(start) < time.Second)
   205  	MustNotRecv(t, peer, mangos.ErrRecvTimeout)
   206  	MustBeTrue(t, time.Since(start) < time.Second)
   207  
   208  	MustSucceed(t, self.Close())
   209  	MustSucceed(t, peer.Close())
   210  }
   211  
   212  // This test repeats he retry at very frequent intervals.  The idea here is
   213  // to demonstrate that there are multiple resend entries in the queue.
   214  // This case covers github issue #179.
   215  func TestReqRetryFast(t *testing.T) {
   216  	self := GetSocket(t, NewSocket)
   217  	peer := GetSocket(t, rep.NewSocket)
   218  	ConnectPair(t, self, peer)
   219  
   220  	mp, p := MockConnect(t, self)
   221  	MustSucceed(t, self.SetOption(mangos.OptionRetryTime, time.Nanosecond))
   222  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Millisecond*10))
   223  	MustSucceed(t, peer.SetOption(mangos.OptionRecvDeadline, time.Millisecond*200))
   224  
   225  	start := time.Now()
   226  
   227  	MustSendString(t, self, "query")
   228  	MockMustRecvStr(t, mp, "query", time.Second)
   229  	MockMustRecvStr(t, mp, "query", time.Second)
   230  	time.Sleep(time.Millisecond * 10)
   231  	MustSucceed(t, p.Close())
   232  
   233  	ConnectPair(t, self, peer)
   234  	MustRecvString(t, peer, "query")
   235  	MustRecvString(t, peer, "query")
   236  	MustSendString(t, peer, "reply")
   237  
   238  	MustBeTrue(t, time.Since(start) < time.Second)
   239  	MustBeTrue(t, time.Since(start) < time.Second)
   240  
   241  	MustSucceed(t, self.Close())
   242  	MustSucceed(t, peer.Close())
   243  }
   244  
   245  func TestReqRetryLateConnect(t *testing.T) {
   246  	self := GetSocket(t, NewSocket)
   247  	peer := GetSocket(t, rep.NewSocket)
   248  
   249  	MustSucceed(t, self.SetOption(mangos.OptionReconnectTime,
   250  		time.Millisecond*100))
   251  
   252  	MustSucceed(t, self.SetOption(mangos.OptionBestEffort, true))
   253  	MustSendString(t, self, "hello")
   254  
   255  	ConnectPair(t, self, peer)
   256  
   257  	MustRecvString(t, peer, "hello")
   258  	MustSendString(t, peer, "world")
   259  
   260  	MustRecvString(t, self, "world")
   261  
   262  	MustSucceed(t, self.Close())
   263  	MustSucceed(t, peer.Close())
   264  }
   265  
   266  func TestReqRetryReconnect(t *testing.T) {
   267  	self := GetSocket(t, NewSocket)
   268  	peer1 := GetSocket(t, rep.NewSocket)
   269  	peer2 := GetSocket(t, rep.NewSocket)
   270  
   271  	MustSucceed(t, self.SetOption(mangos.OptionReconnectTime, time.Second))
   272  	MustSucceed(t, self.SetOption(mangos.OptionRetryTime, time.Second*10))
   273  
   274  	ConnectPair(t, self, peer1)
   275  
   276  	start := time.Now()
   277  
   278  	MustSendString(t, self, "ping")
   279  	MustRecvString(t, peer1, "ping")
   280  
   281  	MustSucceed(t, peer1.Close())
   282  	time.Sleep(time.Millisecond * 10)
   283  
   284  	ConnectPair(t, self, peer2)
   285  	MustRecvString(t, peer2, "ping")
   286  	MustSendString(t, peer2, "pong")
   287  	MustRecvString(t, self, "pong")
   288  
   289  	// Reconnect needs to happen immediately, and start a timely reply.
   290  	MustBeTrue(t, time.Since(start) < time.Second)
   291  
   292  	MustSucceed(t, self.Close())
   293  	MustSucceed(t, peer2.Close())
   294  }
   295  
   296  // This demonstrates receiving a frame with garbage data.
   297  func TestReqRecvGarbage(t *testing.T) {
   298  	self := GetSocket(t, NewSocket)
   299  	mock, pipe := MockConnect(t, self)
   300  	expire := time.Millisecond * 10
   301  
   302  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, expire))
   303  	MustSucceed(t, self.SetOption(mangos.OptionBestEffort, true))
   304  
   305  	MustSendString(t, self, "")
   306  	MockMustSendStr(t, mock, "abc", expire)
   307  	MustNotRecv(t, self, mangos.ErrRecvTimeout)
   308  
   309  	var msg []byte
   310  	// No header
   311  	MustSendString(t, self, "")
   312  	MockMustSend(t, mock, msg, expire)
   313  	MustNotRecv(t, self, mangos.ErrRecvTimeout)
   314  
   315  	// No request ID
   316  	MustSendString(t, self, "")
   317  	msg = append(msg, 0, 1, 2, 3)
   318  	MockMustSend(t, mock, msg, expire)
   319  	MustNotRecv(t, self, mangos.ErrRecvTimeout)
   320  
   321  	// Incorrect pipe ID
   322  	MustSendString(t, self, "")
   323  	binary.BigEndian.PutUint32(msg, pipe.ID()^0xff)
   324  	msg = append(msg, 0x80, 4, 3, 2)
   325  	MockMustSend(t, mock, msg, expire)
   326  	MustNotRecv(t, self, mangos.ErrRecvTimeout)
   327  
   328  	// Also send a bogus header -- no request ID
   329  	MustSendString(t, self, "")
   330  	MockMustSendStr(t, mock, "\001\002\003\004", time.Millisecond*10)
   331  	MustNotRecv(t, self, mangos.ErrRecvTimeout)
   332  
   333  	MustSucceed(t, self.Close())
   334  }
   335  
   336  func TestReqCtxCloseSend(t *testing.T) {
   337  	self := GetSocket(t, NewSocket)
   338  	_, _ = MockConnect(t, self)
   339  
   340  	c, e := self.OpenContext()
   341  	MustSucceed(t, e)
   342  
   343  	// This gets something on the pipe.
   344  	MustSendString(t, self, "")
   345  	var wg sync.WaitGroup
   346  	wg.Add(1)
   347  
   348  	time.AfterFunc(time.Millisecond*10, func() {
   349  		MustSucceed(t, c.Close())
   350  		wg.Done()
   351  	})
   352  	MustBeError(t, c.Send([]byte{}), mangos.ErrClosed)
   353  	wg.Wait()
   354  
   355  	MustSucceed(t, self.Close())
   356  }
   357  
   358  func TestReqCtxCloseRecv(t *testing.T) {
   359  	self := GetSocket(t, NewSocket)
   360  	peer := GetSocket(t, rep.NewSocket)
   361  
   362  	ConnectPair(t, self, peer)
   363  	c, e := self.OpenContext()
   364  	MustSucceed(t, e)
   365  
   366  	MustSucceed(t, c.Send([]byte{}))
   367  	MustSucceed(t, self.SetOption(mangos.OptionRecvDeadline, time.Second))
   368  
   369  	var wg sync.WaitGroup
   370  	wg.Add(1)
   371  
   372  	time.AfterFunc(time.Millisecond*20, func() {
   373  		MustSucceed(t, c.Close())
   374  		wg.Done()
   375  	})
   376  	b, e := c.Recv()
   377  	MustBeError(t, e, mangos.ErrClosed)
   378  	MustBeNil(t, b)
   379  	wg.Wait()
   380  	MustSucceed(t, self.Close())
   381  	MustSucceed(t, peer.Close())
   382  }
   383  
   384  // This sets up a bunch of contexts to run in parallel, and verifies that
   385  // they all seem to run with no mis-deliveries.
   386  func TestReqMultiContexts(t *testing.T) {
   387  	count := 300
   388  	repeat := 20
   389  
   390  	self := GetSocket(t, NewSocket)
   391  	peer := GetSocket(t, rep.NewSocket)
   392  
   393  	ConnectPair(t, self, peer)
   394  
   395  	recv := make([]int, count)
   396  	ctxs := make([]mangos.Context, 0, count)
   397  	var wg1 sync.WaitGroup
   398  	var wg2 sync.WaitGroup
   399  	wg1.Add(count)
   400  	fn := func(index int) {
   401  		defer wg1.Done()
   402  		c, e := self.OpenContext()
   403  		MustSucceed(t, e)
   404  		MustNotBeNil(t, c)
   405  		defer c.Close()
   406  
   407  		ctxs = append(ctxs, c)
   408  		topic := make([]byte, 4)
   409  		binary.BigEndian.PutUint32(topic, uint32(index))
   410  
   411  		MustSucceed(t, c.SetOption(mangos.OptionSendDeadline, time.Second))
   412  		MustSucceed(t, c.SetOption(mangos.OptionRecvDeadline, time.Second))
   413  
   414  		for i := 0; i < repeat; i++ {
   415  			MustSucceed(t, c.Send(topic))
   416  			m, e := c.RecvMsg()
   417  			MustSucceed(t, e)
   418  			MustBeTrue(t, len(m.Body) == 4)
   419  			MustBeTrue(t, bytes.Equal(m.Body, topic))
   420  			recv[index]++
   421  		}
   422  	}
   423  
   424  	for i := 0; i < count; i++ {
   425  		go fn(i)
   426  	}
   427  
   428  	wg2.Add(1)
   429  	go func() {
   430  		defer wg2.Done()
   431  		var e error
   432  		var m *mangos.Message
   433  		for {
   434  			if m, e = peer.RecvMsg(); e != nil {
   435  				break
   436  			}
   437  			if e = peer.SendMsg(m); e != nil {
   438  				break
   439  			}
   440  		}
   441  		MustBeError(t, e, mangos.ErrClosed)
   442  	}()
   443  
   444  	// Give time for everything to be delivered.
   445  	wg1.Wait()
   446  	MustSucceed(t, peer.Close())
   447  	wg2.Wait()
   448  	MustSucceed(t, self.Close())
   449  
   450  	for i := 0; i < count; i++ {
   451  		MustBeTrue(t, recv[i] == repeat)
   452  	}
   453  }
   454  
   455  func TestReqSendNoPeers(t *testing.T) {
   456  	s := GetSocket(t, NewSocket)
   457  	MustSucceed(t, s.SetOption(mangos.OptionFailNoPeers, true))
   458  	MustBeError(t, s.Send([]byte("junk")), mangos.ErrNoPeers)
   459  	MustSucceed(t, s.Close())
   460  }
   461  
   462  func TestReqSendNoPeerDisconnect(t *testing.T) {
   463  	VerifyOptionBool(t, NewSocket, mangos.OptionFailNoPeers)
   464  
   465  	s := GetSocket(t, NewSocket)
   466  	p := GetSocket(t, rep.NewSocket)
   467  	c1, err := s.OpenContext()
   468  	MustSucceed(t, err)
   469  	c2, err := s.OpenContext()
   470  	MustSucceed(t, err)
   471  	MustSucceed(t, s.SetOption(mangos.OptionSendDeadline, time.Second))
   472  
   473  	MustSucceed(t, s.SetOption(mangos.OptionFailNoPeers, true))
   474  
   475  	// Now connect them so they can drain -- we should only have 3 messages
   476  	// that arrive at the peer.
   477  	ConnectPair(t, s, p)
   478  	time.Sleep(time.Millisecond * 20)
   479  	// this logic fills the queue
   480  	go func() {
   481  		_ = c1.Send([]byte("one"))
   482  	}()
   483  	go func() {
   484  		_ = c2.Send([]byte("one"))
   485  	}()
   486  	time.Sleep(time.Millisecond * 10)
   487  	go func() {
   488  		time.Sleep(time.Millisecond * 20)
   489  		MustSucceed(t, p.Close())
   490  	}()
   491  	time.Sleep(time.Millisecond * 20)
   492  
   493  	MustBeError(t, s.Send([]byte("three")), mangos.ErrNoPeers)
   494  	MustSucceed(t, s.Close())
   495  }
   496  
   497  
   498  func TestReqRecvNoPeer(t *testing.T) {
   499  	VerifyOptionBool(t, NewSocket, mangos.OptionFailNoPeers)
   500  
   501  	s := GetSocket(t, NewSocket)
   502  	p := GetSocket(t, rep.NewSocket)
   503  	MustSucceed(t, s.SetOption(mangos.OptionSendDeadline, time.Second))
   504  
   505  	MustSucceed(t, s.SetOption(mangos.OptionFailNoPeers, true))
   506  
   507  	// Now connect them so they can drain -- we should only have 3 messages
   508  	// that arrive at the peer.
   509  	ConnectPair(t, s, p)
   510  	time.Sleep(time.Millisecond * 20)
   511  	MustSucceed(t, s.Send([]byte("one"))) // sent by the socket, picked up by rep socket
   512  	time.Sleep(time.Millisecond * 20)
   513  	MustSucceed(t, p.Close())
   514  	time.Sleep(time.Millisecond*20)
   515  	MustNotRecv(t, s, mangos.ErrNoPeers)
   516  	MustSucceed(t, s.Close())
   517  }
   518  
   519  func TestReqRecvNoPeerDisconnect(t *testing.T) {
   520  	VerifyOptionBool(t, NewSocket, mangos.OptionFailNoPeers)
   521  
   522  	s := GetSocket(t, NewSocket)
   523  	p := GetSocket(t, rep.NewSocket)
   524  	MustSucceed(t, s.SetOption(mangos.OptionSendDeadline, time.Second))
   525  
   526  	MustSucceed(t, s.SetOption(mangos.OptionFailNoPeers, true))
   527  
   528  	// Now connect them so they can drain -- we should only have 3 messages
   529  	// that arrive at the peer.
   530  	ConnectPair(t, s, p)
   531  	time.Sleep(time.Millisecond * 20)
   532  	MustSucceed(t, s.Send([]byte("one"))) // sent by the socket, picked up by rep socket
   533  	go func() {
   534  		time.Sleep(time.Millisecond*10)
   535  		MustSucceed(t, p.Close())
   536  	}()
   537  	MustNotRecv(t, s, mangos.ErrNoPeers)
   538  	MustSucceed(t, s.Close())
   539  }