go.dedis.ch/onet/v4@v4.0.0-pre1/network/local.go (about)

     1  package network
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"golang.org/x/xerrors"
     8  )
     9  
    10  // NewLocalRouter returns a fresh router which uses only local queues. It uses
    11  // the default local manager.
    12  // If you need multiple independent local-queues, use NewLocalRouterWithManager.
    13  // In case of an error it is returned together with a nil-Router.
    14  func NewLocalRouter(sid *ServerIdentity, s Suite) (*Router, error) {
    15  	r, err := NewLocalRouterWithManager(defaultLocalManager, sid, s)
    16  	if err != nil {
    17  		return nil, xerrors.Errorf("local router: %v", err)
    18  	}
    19  	return r, nil
    20  }
    21  
    22  // NewLocalRouterWithManager is the same as NewLocalRouter but takes a specific
    23  // LocalManager. This is useful to run parallel different local overlays.
    24  // In case of an error it is returned together with a nil-Router.
    25  func NewLocalRouterWithManager(lm *LocalManager, sid *ServerIdentity, s Suite) (*Router, error) {
    26  	h, err := NewLocalHostWithManager(lm, sid.Address, s)
    27  	if err != nil {
    28  		return nil, xerrors.Errorf("local router: %v", err)
    29  	}
    30  	r := NewRouter(sid, h)
    31  	r.UnauthOk = true
    32  	return r, nil
    33  }
    34  
    35  // LocalManager keeps a reference to all opened local connections.
    36  // It also keeps track of who is "listening", so it's possible to mimic
    37  // Conn & Listener.
    38  type LocalManager struct {
    39  	// conns maps a remote endpoint to the remote connection.
    40  	conns map[endpoint]*LocalConn
    41  	sync.Mutex
    42  	// The listening-functions used when a new connection-request arrives.
    43  	listening map[Address]func(Conn)
    44  
    45  	// connection-counter for giving unique IDs to each connection.
    46  	counter uint64
    47  	// close on this channel indicates that connection retries should stop
    48  	stopping chan bool
    49  	stopped  bool
    50  	// a waitgroup to check that all serving goroutines are done
    51  	wg sync.WaitGroup
    52  }
    53  
    54  // NewLocalManager returns a fresh new manager that can be used by LocalConn,
    55  // LocalListener & LocalHost.
    56  func NewLocalManager() *LocalManager {
    57  	return &LocalManager{
    58  		conns:     make(map[endpoint]*LocalConn),
    59  		listening: make(map[Address]func(Conn)),
    60  		stopping:  make(chan bool),
    61  	}
    62  }
    63  
    64  // defaultLocalManager can be used if you need only one LocalManager.
    65  var defaultLocalManager = NewLocalManager()
    66  
    67  // endpoint represents one endpoint of a connection.
    68  type endpoint struct {
    69  	// addr is the Address of this endpoint.
    70  	addr Address
    71  	// uid is a unique identifier of the remote endpoint
    72  	// it's unique  for each direction:
    73  	// 127.0.0.1:2000 -> 127.0.0.1:7869 => 14
    74  	// 127.0.0.1:7869 <- 127.0.0.1:2000 => 15
    75  	uid uint64
    76  }
    77  
    78  // LocalReset resets the map of connections + listeners for the defaultLocalManager.
    79  func LocalReset() {
    80  	defaultLocalManager = NewLocalManager()
    81  
    82  }
    83  
    84  // isListening returns true if the remote address is listening for connections.
    85  func (lm *LocalManager) isListening(remote Address) bool {
    86  	lm.Lock()
    87  	defer lm.Unlock()
    88  	_, ok := lm.listening[remote]
    89  	return ok
    90  }
    91  
    92  // setListening marks the address as being able to accept incoming connections.
    93  // For each incoming connection, fn will be called in a go routine.
    94  func (lm *LocalManager) setListening(addr Address, fn func(Conn)) {
    95  	lm.Lock()
    96  	defer lm.Unlock()
    97  	lm.listening[addr] = fn
    98  }
    99  
   100  // unsetListening marks the address as *not* being able to accept incoming
   101  // connections.
   102  func (lm *LocalManager) unsetListening(addr Address) {
   103  	lm.Lock()
   104  	defer lm.Unlock()
   105  	delete(lm.listening, addr)
   106  }
   107  
   108  // connect checks if the remote address is listening. Then it creates
   109  // the two connections, and launches the listening function in a go routine.
   110  // It returns the outgoing connection, or nil followed by an error, if any.
   111  func (lm *LocalManager) connect(local, remote Address, s Suite) (*LocalConn, error) {
   112  	lm.Lock()
   113  	defer lm.Unlock()
   114  	if lm.stopped {
   115  		return nil, xerrors.New("system is stopped")
   116  	}
   117  
   118  	fn, ok := lm.listening[remote]
   119  	if !ok {
   120  		return nil, xerrors.Errorf("%s can't connect to %s: it's not listening", local, remote)
   121  	}
   122  
   123  	outEndpoint := endpoint{local, lm.counter}
   124  	lm.counter++
   125  
   126  	incEndpoint := endpoint{remote, lm.counter}
   127  	lm.counter++
   128  
   129  	outgoing := newLocalConn(lm, outEndpoint, incEndpoint, s)
   130  	incoming := newLocalConn(lm, incEndpoint, outEndpoint, s)
   131  
   132  	// map the endpoint to the connection
   133  	lm.conns[outEndpoint] = outgoing
   134  	lm.conns[incEndpoint] = incoming
   135  
   136  	go fn(incoming)
   137  	return outgoing, nil
   138  }
   139  
   140  // send gets the connection denoted by this endpoint and calls queueMsg
   141  // with the packet as argument to it.
   142  // It returns ErrClosed if it does not find the connection.
   143  func (lm *LocalManager) send(e endpoint, msg []byte) error {
   144  	lm.Lock()
   145  	defer lm.Unlock()
   146  	q, ok := lm.conns[e]
   147  	if !ok {
   148  		return xerrors.Errorf("closing: %w", ErrClosed)
   149  	}
   150  
   151  	q.incomingQueue <- msg
   152  	return nil
   153  }
   154  
   155  // close gets the connection denoted by this endpoint and closes it if
   156  // it is present.
   157  func (lm *LocalManager) close(conn *LocalConn) error {
   158  	lm.Lock()
   159  	defer lm.Unlock()
   160  	_, ok := lm.conns[conn.local]
   161  	if !ok {
   162  		// connection already closed
   163  		return xerrors.Errorf("closing: %w", ErrClosed)
   164  	}
   165  	// delete this conn
   166  	delete(lm.conns, conn.local)
   167  	conn.closeChannels()
   168  	// and delete the remote one + close it
   169  	remote, ok := lm.conns[conn.remote]
   170  	if !ok {
   171  		return nil
   172  	}
   173  	delete(lm.conns, conn.remote)
   174  	remote.closeChannels()
   175  	return nil
   176  }
   177  
   178  // len returns how many local connections are open.
   179  func (lm *LocalManager) count() int {
   180  	lm.Lock()
   181  	defer lm.Unlock()
   182  	return len(lm.conns)
   183  }
   184  
   185  // Stop tells any connections that are sleeping on retry
   186  // to stop sleeping and return an error.
   187  func (lm *LocalManager) Stop() {
   188  	lm.Lock()
   189  	lm.stopped = true
   190  	close(lm.stopping)
   191  	lm.Unlock()
   192  	for _, v := range lm.conns {
   193  		lm.close(v)
   194  	}
   195  
   196  	lm.wg.Wait()
   197  }
   198  
   199  // LocalConn is a connection that sends and receives messages to other
   200  // connections locally.
   201  type LocalConn struct {
   202  	local  endpoint
   203  	remote endpoint
   204  
   205  	// the channel where incoming messages are dispatched
   206  	incomingQueue chan []byte
   207  	// the channel where messages stored can be retrieved with Receive()
   208  	outgoingQueue chan []byte
   209  	// the channel used to communicate the stopping of the operations
   210  	closeCh chan bool
   211  	// the confirmation channel for the go routine
   212  	closeConfirm chan bool
   213  
   214  	// counter to keep track of how many bytes read / written this connection
   215  	// has seen.
   216  	counterSafe
   217  	// the localManager responsible for that connection.
   218  	manager *LocalManager
   219  
   220  	// the suite used to unmarshal
   221  	suite Suite
   222  }
   223  
   224  // newLocalConn initializes the fields of a LocalConn but doesn't
   225  // connect. It should not be used from the outside, most user want
   226  // to use NewLocalConn.
   227  func newLocalConn(lm *LocalManager, local, remote endpoint, s Suite) *LocalConn {
   228  	lc := &LocalConn{
   229  		remote:        remote,
   230  		local:         local,
   231  		manager:       lm,
   232  		incomingQueue: make(chan []byte, LocalMaxBuffer),
   233  		outgoingQueue: make(chan []byte, LocalMaxBuffer),
   234  		closeCh:       make(chan bool),
   235  		closeConfirm:  make(chan bool),
   236  		suite:         s,
   237  	}
   238  	lm.wg.Add(1)
   239  	go lc.start(&lm.wg)
   240  	return lc
   241  }
   242  
   243  // NewLocalConnWithManager is similar to NewLocalConn but takes a specific
   244  // LocalManager.
   245  func NewLocalConnWithManager(lm *LocalManager, local, remote Address, s Suite) (*LocalConn, error) {
   246  	for i := 0; i < MaxRetryConnect; i++ {
   247  		c, err := lm.connect(local, remote, s)
   248  		if err == nil {
   249  			return c, nil
   250  		} else if i == MaxRetryConnect-1 {
   251  			return nil, xerrors.Errorf("connect: %v", err)
   252  		}
   253  		time.Sleep(WaitRetry)
   254  	}
   255  	return nil, xerrors.New("Could not connect")
   256  }
   257  
   258  func (lc *LocalConn) start(wg *sync.WaitGroup) {
   259  	for {
   260  		select {
   261  		case buff := <-lc.incomingQueue:
   262  			lc.outgoingQueue <- buff
   263  		case <-lc.closeCh:
   264  			// to signal that the conn is closed
   265  			close(lc.outgoingQueue)
   266  			close(lc.incomingQueue)
   267  			lc.closeConfirm <- true
   268  			wg.Done()
   269  			return
   270  		}
   271  	}
   272  }
   273  
   274  // Send takes a context (that is not used in any way) and a message that
   275  // will be sent to the remote endpoint.
   276  // If there is an error in the connection, it will be returned.
   277  func (lc *LocalConn) Send(msg Message) (uint64, error) {
   278  	buff, err := Marshal(msg)
   279  	if err != nil {
   280  		return 0, xerrors.Errorf("marshal: %v", err)
   281  	}
   282  	sentLen := uint64(len(buff))
   283  	lc.updateTx(sentLen)
   284  	err = lc.manager.send(lc.remote, buff)
   285  	if err != nil {
   286  		return sentLen, xerrors.Errorf("sending: %w", err)
   287  	}
   288  	return sentLen, nil
   289  }
   290  
   291  // Receive takes a context (that is not used) and waits for a packet to
   292  // be ready. It returns the received packet.
   293  // In case of an error the packet is nil and the error is returned.
   294  func (lc *LocalConn) Receive() (*Envelope, error) {
   295  	buff, opened := <-lc.outgoingQueue
   296  	if !opened {
   297  		return nil, xerrors.Errorf("closing: %w", ErrClosed)
   298  	}
   299  	lc.updateRx(uint64(len(buff)))
   300  
   301  	id, body, err := Unmarshal(buff, lc.suite)
   302  	if err != nil {
   303  		return nil, xerrors.Errorf("unmarshaling: %v", err)
   304  	}
   305  
   306  	return &Envelope{
   307  		MsgType: id,
   308  		Msg:     body,
   309  		Size:    Size(len(buff)),
   310  	}, nil
   311  }
   312  
   313  // Local returns the local address.
   314  func (lc *LocalConn) Local() Address {
   315  	return lc.local.addr
   316  }
   317  
   318  // Remote returns the remote address.
   319  func (lc *LocalConn) Remote() Address {
   320  	return lc.remote.addr
   321  }
   322  
   323  // Close shuts down the connection on the local and the remote
   324  // side.
   325  // If the connection is not open, it returns an error.
   326  func (lc *LocalConn) Close() error {
   327  	select {
   328  	case _, o := <-lc.closeCh:
   329  		if !o {
   330  			return xerrors.Errorf("closing: %w", ErrClosed)
   331  		}
   332  	default:
   333  	}
   334  	err := lc.manager.close(lc)
   335  	if err != nil {
   336  		return xerrors.Errorf("closing: %v", err)
   337  	}
   338  	return nil
   339  }
   340  
   341  func (lc *LocalConn) closeChannels() {
   342  	close(lc.closeCh)
   343  	<-lc.closeConfirm
   344  	close(lc.closeConfirm)
   345  }
   346  
   347  // Type implements the Conn interface
   348  func (lc *LocalConn) Type() ConnType {
   349  	return Local
   350  }
   351  
   352  // connQueue manages the message queue of a LocalConn.
   353  // Messages are pushed and retrieved in a FIFO-queue.
   354  // All operations are thread-safe.
   355  // The messages are marshalled and stored in the queue as a slice of bytes.
   356  type connQueue struct {
   357  	wg sync.WaitGroup
   358  }
   359  
   360  // LocalMaxBuffer is the number of packets that can be sent simultaneously to the
   361  // same address.
   362  const LocalMaxBuffer = 200
   363  
   364  // LocalListener implements Listener and uses LocalConn to communicate. It
   365  // behaves as much as possible as a real golang net.Listener but using LocalConn
   366  // as the underlying communication layer.
   367  type LocalListener struct {
   368  	// addr is the addr we're listening to.
   369  	addr Address
   370  	// whether the listening started or not.
   371  	listening bool
   372  	// suite given to connections
   373  	suite Suite
   374  
   375  	sync.Mutex
   376  
   377  	// quit is used to stop the listening routine.
   378  	quit chan bool
   379  
   380  	// the LocalManager used.
   381  	manager *LocalManager
   382  }
   383  
   384  // NewLocalListener returns a fresh LocalListener using the defaultLocalManager.
   385  // In case of an error the LocalListener is nil and the error is returned.
   386  func NewLocalListener(addr Address, s Suite) (*LocalListener, error) {
   387  	listener, err := NewLocalListenerWithManager(defaultLocalManager, addr, s)
   388  	if err != nil {
   389  		return nil, xerrors.Errorf("local listener: %v", err)
   390  	}
   391  	return listener, nil
   392  }
   393  
   394  // NewLocalListenerWithManager returns a new LocalListener using the
   395  // given LocalManager.
   396  // In case of an error, the LocalListener is nil and the error is returned.
   397  // An error occurs in case the address is invalid or the manager is already
   398  // listening on that address.
   399  func NewLocalListenerWithManager(lm *LocalManager, addr Address, s Suite) (*LocalListener, error) {
   400  	l := &LocalListener{
   401  		quit:    make(chan bool),
   402  		manager: lm,
   403  		suite:   s,
   404  	}
   405  	if addr.ConnType() != Local {
   406  		return nil, xerrors.New("Wrong address type for local listener")
   407  	}
   408  	if l.manager.isListening(addr) {
   409  		return nil, xerrors.Errorf("%s is already listening: can't listen again", addr)
   410  	}
   411  	l.addr = addr
   412  	return l, nil
   413  }
   414  
   415  // Listen calls fn every time a connection-request is received. This call blocks until Stop() is
   416  // called on the listener.
   417  // It returns an error if the LocalListener is already listening.
   418  func (ll *LocalListener) Listen(fn func(Conn)) error {
   419  	ll.Lock()
   420  	if ll.listening {
   421  		ll.Unlock()
   422  		return xerrors.Errorf("Already listening on %s", ll.addr)
   423  	}
   424  	ll.quit = make(chan bool)
   425  	ll.manager.setListening(ll.addr, fn)
   426  	ll.listening = true
   427  	ll.Unlock()
   428  
   429  	<-ll.quit
   430  	return nil
   431  }
   432  
   433  // Stop shuts down listening.
   434  // It always returns nil whether ll is listening or not.
   435  func (ll *LocalListener) Stop() error {
   436  	ll.Lock()
   437  	defer ll.Unlock()
   438  	if !ll.listening {
   439  		return nil
   440  	}
   441  
   442  	ll.manager.unsetListening(ll.addr)
   443  	close(ll.quit)
   444  	ll.listening = false
   445  	return nil
   446  }
   447  
   448  // Address returns the address used to listen.
   449  func (ll *LocalListener) Address() Address {
   450  	ll.Lock()
   451  	defer ll.Unlock()
   452  	return ll.addr
   453  }
   454  
   455  // Listening returns true if this Listener is listening for incoming connections.
   456  func (ll *LocalListener) Listening() bool {
   457  	ll.Lock()
   458  	defer ll.Unlock()
   459  	return ll.listening
   460  }
   461  
   462  // LocalHost implements the Host interface. It uses LocalConn and LocalListener as
   463  // the underlying means of communication.
   464  type LocalHost struct {
   465  	addr  Address
   466  	suite Suite
   467  	*LocalListener
   468  	lm *LocalManager
   469  }
   470  
   471  // NewLocalHost returns a new Host using Local communication. It listens
   472  // on the given addr.
   473  // If an error happened during setup, it returns a nil LocalHost and the error.
   474  func NewLocalHost(addr Address, s Suite) (*LocalHost, error) {
   475  	host, err := NewLocalHostWithManager(defaultLocalManager, addr, s)
   476  	if err != nil {
   477  		return nil, xerrors.Errorf("local host: %v", err)
   478  	}
   479  	return host, nil
   480  }
   481  
   482  // NewLocalHostWithManager is similar to NewLocalHost but takes a
   483  // LocalManager used for communication.
   484  // If an error happened during setup, it returns a nil LocalHost and the error.
   485  func NewLocalHostWithManager(lm *LocalManager, addr Address, s Suite) (*LocalHost, error) {
   486  	lh := &LocalHost{
   487  		addr:  addr,
   488  		lm:    lm,
   489  		suite: s,
   490  	}
   491  	var err error
   492  	lh.LocalListener, err = NewLocalListenerWithManager(lm, addr, s)
   493  	if err != nil {
   494  		return nil, xerrors.Errorf("local host: %v", err)
   495  	}
   496  	return lh, nil
   497  
   498  }
   499  
   500  // Connect sets up a connection to addr. It retries up to
   501  // MaxRetryConnect while waiting between each try.
   502  // In case of an error, it will return a nil Conn.
   503  func (lh *LocalHost) Connect(si *ServerIdentity) (Conn, error) {
   504  	if si.Address.ConnType() != Local {
   505  		return nil, xerrors.New("Can't connect to non-Local address")
   506  	}
   507  	var finalErr error
   508  	for i := 0; i < MaxRetryConnect; i++ {
   509  		c, err := NewLocalConnWithManager(lh.lm, lh.addr, si.Address, lh.suite)
   510  		if err == nil {
   511  			return c, nil
   512  		}
   513  		finalErr = xerrors.Errorf("local connection: %v", err)
   514  		select {
   515  		case <-time.After(WaitRetry):
   516  			// sleep done, go try again
   517  		case <-lh.lm.stopping:
   518  			// stop sleeping and return immediately
   519  			return nil, finalErr
   520  		}
   521  	}
   522  	return nil, finalErr
   523  
   524  }
   525  
   526  // NewLocalAddress returns an Address of type Local with the given raw addr.
   527  func NewLocalAddress(addr string) Address {
   528  	return NewAddress(Local, addr)
   529  }