forked from TrueCloudLab/neoneo-go
_pkg.dev: drop connmgr
I don't think it's of any use for us now. Refs. #307.
This commit is contained in:
parent
5bdf3682ed
commit
eb95758ddc
5 changed files with 0 additions and 414 deletions
|
@ -1,25 +0,0 @@
|
|||
package connmgr
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Config contains all methods which will be set by the caller to setup the connection manager.
|
||||
type Config struct {
|
||||
// GetAddress will return a single address for the connection manager to connect to
|
||||
// This will be the source of addresses for the connection manager
|
||||
GetAddress func() (string, error)
|
||||
|
||||
// OnConnection is called by the connection manager when we successfully connect to a peer
|
||||
// The caller should ideally inform the address manager that we have connected to this address in this function
|
||||
OnConnection func(conn net.Conn, addr string)
|
||||
|
||||
// OnAccept will take an established connection
|
||||
OnAccept func(net.Conn)
|
||||
|
||||
// AddressPort is the address port of the local node in the format "address:port"
|
||||
AddressPort string
|
||||
|
||||
// DialTimeout is the amount of time to wait, before we can disconnect a pending dialed connection
|
||||
DialTimeout int
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
package connmgr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// maxOutboundConn is the maximum number of active peers
|
||||
// that the connection manager will try to have
|
||||
maxOutboundConn = 10
|
||||
|
||||
// maxRetries is the maximum amount of successive retries that
|
||||
// we can have before we stop dialing that peer
|
||||
maxRetries = uint8(5)
|
||||
)
|
||||
|
||||
// Connmgr manages pending/active/failed cnnections
|
||||
type Connmgr struct {
|
||||
config Config
|
||||
PendingList map[string]*Request
|
||||
ConnectedList map[string]*Request
|
||||
actionch chan func()
|
||||
}
|
||||
|
||||
//New creates a new connection manager
|
||||
func New(cfg Config) (*Connmgr, error) {
|
||||
listener, err := net.Listen("tcp", cfg.AddressPort)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cnnmgr := &Connmgr{
|
||||
cfg,
|
||||
make(map[string]*Request),
|
||||
make(map[string]*Request),
|
||||
make(chan func(), 300),
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
listener.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
|
||||
conn, err := listener.Accept()
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
go cfg.OnAccept(conn)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
return cnnmgr, nil
|
||||
}
|
||||
|
||||
// NewRequest will make a new connection gets the address from address func in config
|
||||
// Then dials it and assigns it to pending
|
||||
func (c *Connmgr) NewRequest() error {
|
||||
|
||||
// Fetch address
|
||||
addr, err := c.config.GetAddress()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting address " + err.Error())
|
||||
}
|
||||
|
||||
r := &Request{
|
||||
Addr: addr,
|
||||
}
|
||||
return c.Connect(r)
|
||||
}
|
||||
|
||||
// Connect will dial the address in the Request
|
||||
// Updating the request object depending on the outcome
|
||||
func (c *Connmgr) Connect(r *Request) error {
|
||||
|
||||
r.Retries++
|
||||
|
||||
conn, err := c.dial(r.Addr)
|
||||
if err != nil {
|
||||
c.failed(r)
|
||||
return err
|
||||
}
|
||||
|
||||
r.Conn = conn
|
||||
r.Inbound = true
|
||||
|
||||
// r.Permanent is set by the address manager/caller. default is false
|
||||
// The permanent connections will be the ones that are hardcoded, e.g seed3.ngd.network
|
||||
// or are reliable. The connmgr will be more leniennt to permanent addresses as they have
|
||||
// a track record or reputation of being reliable.
|
||||
|
||||
return c.connected(r)
|
||||
}
|
||||
|
||||
//Disconnect will remove the request from the connected/pending list and close the connection
|
||||
func (c *Connmgr) Disconnect(addr string) {
|
||||
|
||||
var r *Request
|
||||
|
||||
// fetch from connected list
|
||||
r, ok := c.ConnectedList[addr]
|
||||
if !ok {
|
||||
// If not in connected, check pending
|
||||
r, _ = c.PendingList[addr]
|
||||
}
|
||||
|
||||
c.disconnected(r)
|
||||
|
||||
}
|
||||
|
||||
// Dial is used to dial up connections given the addres and ip in the form address:port
|
||||
func (c *Connmgr) dial(addr string) (net.Conn, error) {
|
||||
dialTimeout := 1 * time.Second
|
||||
conn, err := net.DialTimeout("tcp", addr, dialTimeout)
|
||||
if err != nil {
|
||||
if !isConnected() {
|
||||
return nil, errors.New("Fatal Error: You do not seem to be connected to the internet")
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
func (c *Connmgr) failed(r *Request) {
|
||||
|
||||
c.actionch <- func() {
|
||||
// priority to check if it is permanent or inbound
|
||||
// if so then these peers are valuable in NEO and so we will just retry another time
|
||||
if r.Inbound || r.Permanent {
|
||||
multiplier := time.Duration(r.Retries * 10)
|
||||
time.AfterFunc(multiplier*time.Second,
|
||||
func() {
|
||||
c.Connect(r)
|
||||
},
|
||||
)
|
||||
// if not then we should check if this request has had maxRetries
|
||||
// if it has then get a new address
|
||||
// if not then call Connect on it again
|
||||
} else if r.Retries > maxRetries {
|
||||
if c.config.GetAddress != nil {
|
||||
go c.NewRequest()
|
||||
}
|
||||
} else {
|
||||
go c.Connect(r)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Disconnected is called when a peer disconnects.
|
||||
// we take the addr from peer, which is also it's key in the map
|
||||
// and we use it to remove it from the connectedList
|
||||
func (c *Connmgr) disconnected(r *Request) error {
|
||||
|
||||
if r == nil {
|
||||
// if object is nil, we return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// if for some reason the underlying connection is not closed, close it
|
||||
err := r.Conn.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove from any pending/connected list
|
||||
delete(c.PendingList, r.Addr)
|
||||
delete(c.ConnectedList, r.Addr)
|
||||
|
||||
// If permanent,then lets retry
|
||||
if r.Permanent {
|
||||
return c.Connect(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Connected is called when the connection manager makes a successful connection.
|
||||
func (c *Connmgr) connected(r *Request) error {
|
||||
|
||||
// This should not be the case, since we connected
|
||||
if r == nil {
|
||||
return errors.New("request object as nil inside of the connected function")
|
||||
}
|
||||
|
||||
// reset retries to 0
|
||||
r.Retries = 0
|
||||
|
||||
// add to connectedList
|
||||
c.ConnectedList[r.Addr] = r
|
||||
|
||||
// remove from pending if it was there
|
||||
delete(c.PendingList, r.Addr)
|
||||
|
||||
if c.config.OnConnection != nil {
|
||||
c.config.OnConnection(r.Conn, r.Addr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pending is synchronous, we do not want to continue with logic
|
||||
// until we are certain it has been added to the pendingList
|
||||
func (c *Connmgr) pending(r *Request) error {
|
||||
|
||||
if r == nil {
|
||||
return errors.New("request object is nil")
|
||||
}
|
||||
|
||||
c.PendingList[r.Addr] = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run will start the connection manager
|
||||
func (c *Connmgr) Run() error {
|
||||
fmt.Println("Connection manager started")
|
||||
go c.loop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connmgr) loop() {
|
||||
for {
|
||||
select {
|
||||
case f := <-c.actionch:
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/50056144/check-for-internet-connection-from-application
|
||||
func isConnected() (ok bool) {
|
||||
_, err := http.Get("http://clients3.google.com/generate_204")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package connmgr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
cfg := Config{
|
||||
GetAddress: nil,
|
||||
OnConnection: nil,
|
||||
OnAccept: nil,
|
||||
AddressPort: "",
|
||||
DialTimeout: 0,
|
||||
}
|
||||
|
||||
cm := New(cfg)
|
||||
err := cm.Run()
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
ipport := "google.com:80" // google unlikely to go offline, a better approach to test Dialing is welcome.
|
||||
|
||||
conn, err := cm.dial(ipport)
|
||||
assert.Equal(t, nil, err)
|
||||
assert.NotEqual(t, nil, conn)
|
||||
}
|
||||
func TestConnect(t *testing.T) {
|
||||
cfg := Config{
|
||||
GetAddress: nil,
|
||||
OnConnection: nil,
|
||||
OnAccept: nil,
|
||||
AddressPort: "",
|
||||
DialTimeout: 0,
|
||||
}
|
||||
|
||||
cm := New(cfg)
|
||||
cm.Run()
|
||||
|
||||
ipport := "google.com:80"
|
||||
|
||||
r := Request{Addr: ipport}
|
||||
|
||||
err := cm.Connect(&r)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, 1, len(cm.ConnectedList))
|
||||
|
||||
}
|
||||
func TestNewRequest(t *testing.T) {
|
||||
|
||||
address := "google.com:80"
|
||||
|
||||
var getAddr = func() (string, error) {
|
||||
return address, nil
|
||||
}
|
||||
|
||||
cfg := Config{
|
||||
GetAddress: getAddr,
|
||||
OnConnection: nil,
|
||||
OnAccept: nil,
|
||||
AddressPort: "",
|
||||
DialTimeout: 0,
|
||||
}
|
||||
|
||||
cm := New(cfg)
|
||||
|
||||
cm.Run()
|
||||
|
||||
cm.NewRequest()
|
||||
|
||||
if _, ok := cm.ConnectedList[address]; ok {
|
||||
assert.Equal(t, true, ok)
|
||||
assert.Equal(t, 1, len(cm.ConnectedList))
|
||||
return
|
||||
}
|
||||
|
||||
assert.Fail(t, "Could not find the address in the connected lists")
|
||||
|
||||
}
|
||||
func TestDisconnect(t *testing.T) {
|
||||
|
||||
address := "google.com:80"
|
||||
|
||||
var getAddr = func() (string, error) {
|
||||
return address, nil
|
||||
}
|
||||
|
||||
cfg := Config{
|
||||
GetAddress: getAddr,
|
||||
OnConnection: nil,
|
||||
OnAccept: nil,
|
||||
AddressPort: "",
|
||||
DialTimeout: 0,
|
||||
}
|
||||
|
||||
cm := New(cfg)
|
||||
|
||||
cm.Run()
|
||||
|
||||
cm.NewRequest()
|
||||
|
||||
cm.Disconnect(address)
|
||||
|
||||
assert.Equal(t, 0, len(cm.ConnectedList))
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
# Package - Connection Manager
|
||||
|
||||
## Responsibility
|
||||
|
||||
- Manages the active, failed and pending connections for the node.
|
||||
|
||||
## Features
|
||||
|
||||
- Takes an Request, dials it and logs information based on the connectivity.
|
||||
|
||||
- Retry failed connections.
|
||||
|
||||
- Removable address source. The connection manager does not manage addresses, only connections.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
The following methods are exposed from the Connection manager:
|
||||
|
||||
- Connect(r *Request) : This takes a Request object and connects to it. It follow the same logic as NewRequest() however instead of getting the address from the datasource given upon initialisation, you directly feed the address you want to connect to.
|
||||
|
||||
- Disconnect(addrport string) : Given an address:port, this will disconnect it, close the connection and remove it from the connected and pending list, if it was there.
|
|
@ -1,15 +0,0 @@
|
|||
package connmgr
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Request is a layer on top of connection and allows us to add metadata to the net.Conn
|
||||
// that the connection manager can use to determine whether to retry and other useful heuristics
|
||||
type Request struct {
|
||||
Conn net.Conn
|
||||
Addr string
|
||||
Permanent bool
|
||||
Inbound bool
|
||||
Retries uint8 // should not be trying more than 255 tries
|
||||
}
|
Loading…
Reference in a new issue