package tunnel import ( "errors" "io" ) type Manager struct { Tunnel io.ReadWriter Connections map[ID]*Connection incoming chan *DataFrame outgoing chan *DataFrame accept chan *DataFrame closed chan bool newConnection chan *Connection delConnection chan *Connection } func NewManager(tun io.ReadWriter) *Manager { return &Manager{ Tunnel: tun, Connections: make(map[ID]*Connection), incoming: make(chan *DataFrame, 1024), outgoing: make(chan *DataFrame, 1024), accept: make(chan *DataFrame, 1024), newConnection: make(chan *Connection), delConnection: make(chan *Connection), } } func (m *Manager) Run() { for { select { case <-m.closed: return case df := <-m.incoming: go df.Encode(m.Tunnel) case connection := <-m.newConnection: m.Connections[connection.ID] = connection case connection := <-m.delConnection: connection.Close() delete(m.Connections, connection.ID) default: go m.onReceive() } } } func (m *Manager) onReceive() { df := new(DataFrame) df.Decode(m.Tunnel) switch df.Type { case TypeRequest: m.accept <- df case TypeClosed: connection, ok := m.Connections[df.ID] if ok { m.delConnection <- connection } case TypeNormal: connection, ok := m.Connections[df.ID] if ok { connection.RX = m.outgoing m.outgoing <- df } } } func (m *Manager) Connect() (*Connection, error) { df := &DataFrame{ ID: NewID(), Type: TypeRequest, } connection := &Connection{ ID: df.ID, TX: m.incoming, } m.newConnection <- connection m.incoming <- df df = <-connection.RX if df.Type != TypeConnected { return nil, errors.New("failed to connect") } return connection, nil } func (m *Manager) Accept() (*Connection, error) { df := <-m.accept if df.Type != TypeRequest { return nil, errors.New("failed to accept") } connection := &Connection{ ID: df.ID, TX: m.incoming, } m.newConnection <- connection df.Type = TypeConnected m.incoming <- df return connection, nil } func (m *Manager) Close() error { defer close(m.closed) defer close(m.accept) defer close(m.incoming) defer close(m.outgoing) for _, connection := range m.Connections { connection.Close() } m.closed <- true return nil }