mirror of
https://github.com/go-micro/go-micro.git
synced 2024-12-24 10:07:04 +02:00
Tunnel discover/announce/open/session/close
This commit is contained in:
parent
eb4a709195
commit
b9c437fbfe
@ -718,7 +718,7 @@ func (n *network) Connect() error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// dial into ControlChannel to send route adverts
|
// dial into ControlChannel to send route adverts
|
||||||
ctrlClient, err := n.Tunnel.Dial(ControlChannel)
|
ctrlClient, err := n.Tunnel.Dial(ControlChannel, tunnel.DialMulticast())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -732,7 +732,7 @@ func (n *network) Connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dial into NetworkChannel to send network messages
|
// dial into NetworkChannel to send network messages
|
||||||
netClient, err := n.Tunnel.Dial(NetworkChannel)
|
netClient, err := n.Tunnel.Dial(NetworkChannel, tunnel.DialMulticast())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,12 @@ func (t *tun) getSession(channel, session string) (*session, bool) {
|
|||||||
return s, ok
|
return s, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tun) delSession(channel, session string) {
|
||||||
|
t.Lock()
|
||||||
|
delete(t.sessions, channel+session)
|
||||||
|
t.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// newSession creates a new session and saves it
|
// newSession creates a new session and saves it
|
||||||
func (t *tun) newSession(channel, sessionId string) (*session, bool) {
|
func (t *tun) newSession(channel, sessionId string) (*session, bool) {
|
||||||
// new session
|
// new session
|
||||||
@ -150,7 +156,9 @@ func (t *tun) monitor() {
|
|||||||
log.Debugf("Tunnel failed to setup node link to %s: %v", node, err)
|
log.Debugf("Tunnel failed to setup node link to %s: %v", node, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// set the link id to the node
|
||||||
|
// TODO: hash it
|
||||||
|
link.id = node
|
||||||
// save the link
|
// save the link
|
||||||
t.Lock()
|
t.Lock()
|
||||||
t.links[node] = link
|
t.links[node] = link
|
||||||
@ -169,12 +177,15 @@ func (t *tun) process() {
|
|||||||
case msg := <-t.send:
|
case msg := <-t.send:
|
||||||
newMsg := &transport.Message{
|
newMsg := &transport.Message{
|
||||||
Header: make(map[string]string),
|
Header: make(map[string]string),
|
||||||
Body: msg.data.Body,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the data
|
||||||
|
if msg.data != nil {
|
||||||
for k, v := range msg.data.Header {
|
for k, v := range msg.data.Header {
|
||||||
newMsg.Header[k] = v
|
newMsg.Header[k] = v
|
||||||
}
|
}
|
||||||
|
newMsg.Body = msg.data.Body
|
||||||
|
}
|
||||||
|
|
||||||
// set message head
|
// set message head
|
||||||
newMsg.Header["Micro-Tunnel"] = msg.typ
|
newMsg.Header["Micro-Tunnel"] = msg.typ
|
||||||
@ -195,7 +206,7 @@ func (t *tun) process() {
|
|||||||
t.Lock()
|
t.Lock()
|
||||||
|
|
||||||
if len(t.links) == 0 {
|
if len(t.links) == 0 {
|
||||||
log.Debugf("No links to send to")
|
log.Debugf("No links to send message type: %s channel: %s", msg.typ, msg.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sent bool
|
var sent bool
|
||||||
@ -232,25 +243,55 @@ func (t *tun) process() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check the multicast mappings
|
||||||
|
if msg.multicast {
|
||||||
|
link.RLock()
|
||||||
|
_, ok := link.channels[msg.channel]
|
||||||
|
link.RUnlock()
|
||||||
|
// channel mapping not found in link
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// send the message via the current link
|
// send the message via the current link
|
||||||
log.Debugf("Sending %+v to %s", newMsg, node)
|
log.Debugf("Sending %+v to %s", newMsg, node)
|
||||||
|
|
||||||
if errr := link.Send(newMsg); errr != nil {
|
if errr := link.Send(newMsg); errr != nil {
|
||||||
log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, errr)
|
log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, errr)
|
||||||
err = errors.New(errr.Error())
|
err = errors.New(errr.Error())
|
||||||
|
// kill the link
|
||||||
|
link.Close()
|
||||||
|
// delete the link
|
||||||
delete(t.links, node)
|
delete(t.links, node)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// is sent
|
// is sent
|
||||||
sent = true
|
sent = true
|
||||||
|
|
||||||
|
// keep sending broadcast messages
|
||||||
|
if msg.broadcast || msg.multicast {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// break on unicast
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
|
|
||||||
|
// set the error if not sent
|
||||||
var gerr error
|
var gerr error
|
||||||
if !sent {
|
if !sent {
|
||||||
gerr = err
|
gerr = err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip if its not been set
|
||||||
|
if msg.errChan == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// return error non blocking
|
// return error non blocking
|
||||||
select {
|
select {
|
||||||
case msg.errChan <- gerr:
|
case msg.errChan <- gerr:
|
||||||
@ -262,14 +303,25 @@ func (t *tun) process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tun) delLink(id string) {
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
// get the link
|
||||||
|
link, ok := t.links[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// close and delete
|
||||||
|
link.Close()
|
||||||
|
delete(t.links, id)
|
||||||
|
}
|
||||||
|
|
||||||
// process incoming messages
|
// process incoming messages
|
||||||
func (t *tun) listen(link *link) {
|
func (t *tun) listen(link *link) {
|
||||||
// remove the link on exit
|
// remove the link on exit
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debugf("Tunnel deleting connection from %s", link.Remote())
|
log.Debugf("Tunnel deleting connection from %s", link.Remote())
|
||||||
t.Lock()
|
t.delLink(link.Remote())
|
||||||
delete(t.links, link.Remote())
|
|
||||||
t.Unlock()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// let us know if its a loopback
|
// let us know if its a loopback
|
||||||
@ -301,6 +353,13 @@ func (t *tun) listen(link *link) {
|
|||||||
// the session id
|
// the session id
|
||||||
sessionId := msg.Header["Micro-Tunnel-Session"]
|
sessionId := msg.Header["Micro-Tunnel-Session"]
|
||||||
|
|
||||||
|
// if its not connected throw away the link
|
||||||
|
// the first message we process needs to be connect
|
||||||
|
if !link.connected && mtype != "connect" {
|
||||||
|
log.Debugf("Tunnel link %s not connected", link.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch mtype {
|
switch mtype {
|
||||||
case "connect":
|
case "connect":
|
||||||
log.Debugf("Tunnel link %s received connect message", link.Remote())
|
log.Debugf("Tunnel link %s received connect message", link.Remote())
|
||||||
@ -311,6 +370,8 @@ func (t *tun) listen(link *link) {
|
|||||||
loopback = true
|
loopback = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set to remote node
|
||||||
|
link.id = id
|
||||||
// set as connected
|
// set as connected
|
||||||
link.connected = true
|
link.connected = true
|
||||||
|
|
||||||
@ -322,10 +383,31 @@ func (t *tun) listen(link *link) {
|
|||||||
// nothing more to do
|
// nothing more to do
|
||||||
continue
|
continue
|
||||||
case "close":
|
case "close":
|
||||||
log.Debugf("Tunnel link %s closing connection", link.Remote())
|
|
||||||
// TODO: handle the close message
|
// TODO: handle the close message
|
||||||
// maybe report io.EOF or kill the link
|
// maybe report io.EOF or kill the link
|
||||||
|
|
||||||
|
// close the link entirely
|
||||||
|
if len(channel) == 0 {
|
||||||
|
log.Debugf("Tunnel link %s received close message", link.Remote())
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the entire listener was closed so remove it from the mapping
|
||||||
|
if sessionId == "listener" {
|
||||||
|
link.Lock()
|
||||||
|
delete(link.channels, channel)
|
||||||
|
link.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// try get the dialing socket
|
||||||
|
s, exists := t.getSession(channel, sessionId)
|
||||||
|
if exists {
|
||||||
|
// close and continue
|
||||||
|
s.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// otherwise its a session mapping of sorts
|
||||||
case "keepalive":
|
case "keepalive":
|
||||||
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
log.Debugf("Tunnel link %s received keepalive", link.Remote())
|
||||||
t.Lock()
|
t.Lock()
|
||||||
@ -333,18 +415,62 @@ func (t *tun) listen(link *link) {
|
|||||||
link.lastKeepAlive = time.Now()
|
link.lastKeepAlive = time.Now()
|
||||||
t.Unlock()
|
t.Unlock()
|
||||||
continue
|
continue
|
||||||
|
// a new connection dialled outbound
|
||||||
|
case "open":
|
||||||
|
// we just let it pass through to be processed
|
||||||
|
// an accept returned by the listener
|
||||||
|
case "accept":
|
||||||
|
|
||||||
|
// a continued session
|
||||||
case "session":
|
case "session":
|
||||||
// process message
|
// process message
|
||||||
log.Debugf("Received %+v from %s", msg, link.Remote())
|
log.Debugf("Received %+v from %s", msg, link.Remote())
|
||||||
default:
|
// an announcement of a channel listener
|
||||||
// blackhole it
|
case "announce":
|
||||||
|
// update mapping in the link
|
||||||
|
link.Lock()
|
||||||
|
link.channels[channel] = time.Now()
|
||||||
|
link.Unlock()
|
||||||
|
|
||||||
|
// get the session that asked for the discovery
|
||||||
|
s, exists := t.getSession(channel, sessionId)
|
||||||
|
if exists {
|
||||||
|
// don't bother it's already discovered
|
||||||
|
if s.discovered {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// if its not connected throw away the link
|
// send the announce back to the caller
|
||||||
if !link.connected {
|
s.recv <- &message{
|
||||||
log.Debugf("Tunnel link %s not connected", link.id)
|
typ: "announce",
|
||||||
return
|
tunnel: id,
|
||||||
|
channel: channel,
|
||||||
|
session: sessionId,
|
||||||
|
link: link.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
case "discover":
|
||||||
|
// looking for existing mapping
|
||||||
|
_, exists := t.getSession(channel, "listener")
|
||||||
|
if exists {
|
||||||
|
log.Debugf("Tunnel sending announce for discovery of channel %s", channel)
|
||||||
|
// send back the announcement
|
||||||
|
link.Send(&transport.Message{
|
||||||
|
Header: map[string]string{
|
||||||
|
"Micro-Tunnel": "announce",
|
||||||
|
"Micro-Tunnel-Id": t.id,
|
||||||
|
"Micro-Tunnel-Channel": channel,
|
||||||
|
"Micro-Tunnel-Session": sessionId,
|
||||||
|
"Micro-Tunnel-Link": link.id,
|
||||||
|
"Micro-Tunnel-Token": t.token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
// blackhole it
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// strip tunnel message header
|
// strip tunnel message header
|
||||||
@ -368,8 +494,15 @@ func (t *tun) listen(link *link) {
|
|||||||
// If its a loopback connection then we've enabled link direction
|
// If its a loopback connection then we've enabled link direction
|
||||||
// listening side is used for listening, the dialling side for dialling
|
// listening side is used for listening, the dialling side for dialling
|
||||||
switch {
|
switch {
|
||||||
case loopback:
|
case loopback, mtype == "open":
|
||||||
s, exists = t.getSession(channel, "listener")
|
s, exists = t.getSession(channel, "listener")
|
||||||
|
// only return accept to the session
|
||||||
|
case mtype == "accept":
|
||||||
|
log.Debugf("Received accept message for %s %s", channel, sessionId)
|
||||||
|
s, exists = t.getSession(channel, sessionId)
|
||||||
|
if exists && s.accepted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// get the session based on the tunnel id and session
|
// get the session based on the tunnel id and session
|
||||||
// this could be something we dialed in which case
|
// this could be something we dialed in which case
|
||||||
@ -383,7 +516,7 @@ func (t *tun) listen(link *link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bail if no session has been found
|
// bail if no session or listener has been found
|
||||||
if !exists {
|
if !exists {
|
||||||
log.Debugf("Tunnel skipping no session exists")
|
log.Debugf("Tunnel skipping no session exists")
|
||||||
// drop it, we don't care about
|
// drop it, we don't care about
|
||||||
@ -391,8 +524,6 @@ func (t *tun) listen(link *link) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Tunnel using session %s %s", s.channel, s.session)
|
|
||||||
|
|
||||||
// is the session closed?
|
// is the session closed?
|
||||||
select {
|
select {
|
||||||
case <-s.closed:
|
case <-s.closed:
|
||||||
@ -403,6 +534,8 @@ func (t *tun) listen(link *link) {
|
|||||||
// process
|
// process
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Tunnel using channel %s session %s", s.channel, s.session)
|
||||||
|
|
||||||
// is the session new?
|
// is the session new?
|
||||||
select {
|
select {
|
||||||
// if its new the session is actually blocked waiting
|
// if its new the session is actually blocked waiting
|
||||||
@ -462,9 +595,7 @@ func (t *tun) keepalive(link *link) {
|
|||||||
},
|
},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err)
|
log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err)
|
||||||
t.Lock()
|
t.delLink(link.Remote())
|
||||||
delete(t.links, link.Remote())
|
|
||||||
t.Unlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,6 +613,7 @@ func (t *tun) setupLink(node string) (*link, error) {
|
|||||||
}
|
}
|
||||||
log.Debugf("Tunnel connected to %s", node)
|
log.Debugf("Tunnel connected to %s", node)
|
||||||
|
|
||||||
|
// send the first connect message
|
||||||
if err := c.Send(&transport.Message{
|
if err := c.Send(&transport.Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
"Micro-Tunnel": "connect",
|
"Micro-Tunnel": "connect",
|
||||||
@ -494,9 +626,11 @@ func (t *tun) setupLink(node string) (*link, error) {
|
|||||||
|
|
||||||
// create a new link
|
// create a new link
|
||||||
link := newLink(c)
|
link := newLink(c)
|
||||||
link.connected = true
|
// set link id to remote side
|
||||||
|
link.id = c.Remote()
|
||||||
// we made the outbound connection
|
// we made the outbound connection
|
||||||
// and sent the connect message
|
// and sent the connect message
|
||||||
|
link.connected = true
|
||||||
|
|
||||||
// process incoming messages
|
// process incoming messages
|
||||||
go t.listen(link)
|
go t.listen(link)
|
||||||
@ -554,7 +688,7 @@ func (t *tun) connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// save the link
|
// save the link
|
||||||
t.links[node] = link
|
t.links[link.Remote()] = link
|
||||||
}
|
}
|
||||||
|
|
||||||
// process outbound messages to be sent
|
// process outbound messages to be sent
|
||||||
@ -628,6 +762,8 @@ func (t *tun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("Tunnel closing")
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return nil
|
return nil
|
||||||
@ -651,7 +787,7 @@ func (t *tun) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dial an address
|
// Dial an address
|
||||||
func (t *tun) Dial(channel string) (Session, error) {
|
func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
||||||
log.Debugf("Tunnel dialing %s", channel)
|
log.Debugf("Tunnel dialing %s", channel)
|
||||||
c, ok := t.newSession(channel, t.newSessionId())
|
c, ok := t.newSession(channel, t.newSessionId())
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -664,18 +800,87 @@ func (t *tun) Dial(channel string) (Session, error) {
|
|||||||
// outbound session
|
// outbound session
|
||||||
c.outbound = true
|
c.outbound = true
|
||||||
|
|
||||||
|
// get opts
|
||||||
|
options := DialOptions{
|
||||||
|
Timeout: DefaultDialTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the multicast option
|
||||||
|
c.multicast = options.Multicast
|
||||||
|
// set the dial timeout
|
||||||
|
c.timeout = options.Timeout
|
||||||
|
|
||||||
|
t.RLock()
|
||||||
|
for _, link := range t.links {
|
||||||
|
link.RLock()
|
||||||
|
_, ok := link.channels[channel]
|
||||||
|
link.RUnlock()
|
||||||
|
|
||||||
|
// we have at least one channel mapping
|
||||||
|
if ok {
|
||||||
|
c.discovered = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.RUnlock()
|
||||||
|
|
||||||
|
// shit fuck
|
||||||
|
if !c.discovered {
|
||||||
|
t.send <- &message{
|
||||||
|
typ: "discover",
|
||||||
|
tunnel: t.id,
|
||||||
|
channel: channel,
|
||||||
|
session: c.session,
|
||||||
|
broadcast: true,
|
||||||
|
outbound: true,
|
||||||
|
errChan: c.errChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-c.errChan:
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for announce
|
||||||
|
select {
|
||||||
|
case msg := <-c.recv:
|
||||||
|
if msg.typ != "announce" {
|
||||||
|
return nil, errors.New("failed to discover channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to open the session
|
||||||
|
err := c.Open()
|
||||||
|
if err != nil {
|
||||||
|
// delete the session
|
||||||
|
t.delSession(c.channel, c.session)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept a connection on the address
|
// Accept a connection on the address
|
||||||
func (t *tun) Listen(channel string) (Listener, error) {
|
func (t *tun) Listen(channel string) (Listener, error) {
|
||||||
log.Debugf("Tunnel listening on %s", channel)
|
log.Debugf("Tunnel listening on %s", channel)
|
||||||
|
|
||||||
// create a new session by hashing the address
|
// create a new session by hashing the address
|
||||||
c, ok := t.newSession(channel, "listener")
|
c, ok := t.newSession(channel, "listener")
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("already listening on " + channel)
|
return nil, errors.New("already listening on " + channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delFunc := func() {
|
||||||
|
t.delSession(channel, "listener")
|
||||||
|
}
|
||||||
|
|
||||||
// set remote. it will be replaced by the first message received
|
// set remote. it will be replaced by the first message received
|
||||||
c.remote = "remote"
|
c.remote = "remote"
|
||||||
// set local
|
// set local
|
||||||
@ -691,6 +896,8 @@ func (t *tun) Listen(channel string) (Listener, error) {
|
|||||||
tunClosed: t.closed,
|
tunClosed: t.closed,
|
||||||
// the listener session
|
// the listener session
|
||||||
session: c,
|
session: c,
|
||||||
|
// delete session
|
||||||
|
delFunc: delFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
// this kicks off the internal message processor
|
// this kicks off the internal message processor
|
||||||
@ -699,6 +906,9 @@ func (t *tun) Listen(channel string) (Listener, error) {
|
|||||||
// to the existign sessions
|
// to the existign sessions
|
||||||
go tl.process()
|
go tl.process()
|
||||||
|
|
||||||
|
// announces the listener channel to others
|
||||||
|
go tl.announce()
|
||||||
|
|
||||||
// return the listener
|
// return the listener
|
||||||
return tl, nil
|
return tl, nil
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type link struct {
|
type link struct {
|
||||||
|
transport.Socket
|
||||||
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
||||||
transport.Socket
|
|
||||||
// unique id of this link e.g uuid
|
// unique id of this link e.g uuid
|
||||||
// which we define for ourselves
|
// which we define for ourselves
|
||||||
id string
|
id string
|
||||||
@ -27,11 +28,67 @@ type link struct {
|
|||||||
// the last time we received a keepalive
|
// the last time we received a keepalive
|
||||||
// on this link from the remote side
|
// on this link from the remote side
|
||||||
lastKeepAlive time.Time
|
lastKeepAlive time.Time
|
||||||
|
// channels keeps a mapping of channels and last seen
|
||||||
|
channels map[string]time.Time
|
||||||
|
// stop the link
|
||||||
|
closed chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLink(s transport.Socket) *link {
|
func newLink(s transport.Socket) *link {
|
||||||
return &link{
|
l := &link{
|
||||||
Socket: s,
|
Socket: s,
|
||||||
id: uuid.New().String(),
|
id: uuid.New().String(),
|
||||||
|
channels: make(map[string]time.Time),
|
||||||
|
closed: make(chan bool),
|
||||||
|
}
|
||||||
|
go l.run()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *link) run() {
|
||||||
|
t := time.NewTicker(time.Minute)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-l.closed:
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
// drop any channel mappings older than 2 minutes
|
||||||
|
var kill []string
|
||||||
|
killTime := time.Minute * 2
|
||||||
|
|
||||||
|
l.RLock()
|
||||||
|
for ch, t := range l.channels {
|
||||||
|
if d := time.Since(t); d > killTime {
|
||||||
|
kill = append(kill, ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.RUnlock()
|
||||||
|
|
||||||
|
// if nothing to kill don't both with a wasted lock
|
||||||
|
if len(kill) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// kill the channels!
|
||||||
|
l.Lock()
|
||||||
|
for _, ch := range kill {
|
||||||
|
delete(l.channels, ch)
|
||||||
|
}
|
||||||
|
l.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *link) Close() {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-l.closed:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
close(l.closed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package tunnel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/micro/go-micro/util/log"
|
"github.com/micro/go-micro/util/log"
|
||||||
)
|
)
|
||||||
@ -17,22 +18,77 @@ type tunListener struct {
|
|||||||
tunClosed chan bool
|
tunClosed chan bool
|
||||||
// the listener session
|
// the listener session
|
||||||
session *session
|
session *session
|
||||||
|
// del func to kill listener
|
||||||
|
delFunc func()
|
||||||
|
}
|
||||||
|
|
||||||
|
// periodically announce self
|
||||||
|
func (t *tunListener) announce() {
|
||||||
|
tick := time.NewTicker(time.Minute)
|
||||||
|
defer tick.Stop()
|
||||||
|
|
||||||
|
announce := func() {
|
||||||
|
msg := &message{
|
||||||
|
typ: "announce",
|
||||||
|
tunnel: t.session.tunnel,
|
||||||
|
channel: t.session.channel,
|
||||||
|
session: t.session.session,
|
||||||
|
outbound: t.session.outbound,
|
||||||
|
loopback: t.session.loopback,
|
||||||
|
multicast: t.session.multicast,
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case t.session.send <- msg:
|
||||||
|
case <-t.session.closed:
|
||||||
|
return
|
||||||
|
case <-t.closed:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first announcement
|
||||||
|
announce()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tick.C:
|
||||||
|
announce()
|
||||||
|
case <-t.closed:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunListener) process() {
|
func (t *tunListener) process() {
|
||||||
// our connection map for session
|
// our connection map for session
|
||||||
conns := make(map[string]*session)
|
conns := make(map[string]*session)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// close the sessions
|
||||||
|
for _, conn := range conns {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return
|
return
|
||||||
|
case <-t.tunClosed:
|
||||||
|
t.Close()
|
||||||
|
return
|
||||||
// receive a new message
|
// receive a new message
|
||||||
case m := <-t.session.recv:
|
case m := <-t.session.recv:
|
||||||
// get a session
|
// get a session
|
||||||
sess, ok := conns[m.session]
|
sess, ok := conns[m.session]
|
||||||
log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok)
|
log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
// only create new sessions on open message
|
||||||
|
if m.typ != "open" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// create a new session session
|
// create a new session session
|
||||||
sess = &session{
|
sess = &session{
|
||||||
// the id of the remote side
|
// the id of the remote side
|
||||||
@ -45,6 +101,8 @@ func (t *tunListener) process() {
|
|||||||
loopback: m.loopback,
|
loopback: m.loopback,
|
||||||
// the link the message was received on
|
// the link the message was received on
|
||||||
link: m.link,
|
link: m.link,
|
||||||
|
// set multicast
|
||||||
|
multicast: m.multicast,
|
||||||
// close chan
|
// close chan
|
||||||
closed: make(chan bool),
|
closed: make(chan bool),
|
||||||
// recv called by the acceptor
|
// recv called by the acceptor
|
||||||
@ -60,12 +118,39 @@ func (t *tunListener) process() {
|
|||||||
// save the session
|
// save the session
|
||||||
conns[m.session] = sess
|
conns[m.session] = sess
|
||||||
|
|
||||||
// send to accept chan
|
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return
|
return
|
||||||
|
// send to accept chan
|
||||||
case t.accept <- sess:
|
case t.accept <- sess:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// continue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// an existing session was found
|
||||||
|
|
||||||
|
// received a close message
|
||||||
|
switch m.typ {
|
||||||
|
case "close":
|
||||||
|
select {
|
||||||
|
case <-sess.closed:
|
||||||
|
// no op
|
||||||
|
delete(conns, m.session)
|
||||||
|
default:
|
||||||
|
// close and delete session
|
||||||
|
close(sess.closed)
|
||||||
|
delete(conns, m.session)
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue
|
||||||
|
continue
|
||||||
|
case "session":
|
||||||
|
// operate on this
|
||||||
|
default:
|
||||||
|
// non operational type
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// send this to the accept chan
|
// send this to the accept chan
|
||||||
@ -89,6 +174,9 @@ func (t *tunListener) Close() error {
|
|||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
// close and delete
|
||||||
|
t.delFunc()
|
||||||
|
t.session.Close()
|
||||||
close(t.closed)
|
close(t.closed)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -102,13 +190,17 @@ func (t *tunListener) Accept() (Session, error) {
|
|||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
case <-t.tunClosed:
|
case <-t.tunClosed:
|
||||||
// close the listener when the tunnel closes
|
// close the listener when the tunnel closes
|
||||||
t.Close()
|
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
// wait for a new connection
|
// wait for a new connection
|
||||||
case c, ok := <-t.accept:
|
case c, ok := <-t.accept:
|
||||||
|
// check if the accept chan is closed
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
// send back the accept
|
||||||
|
if err := c.Accept(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package tunnel
|
package tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/micro/go-micro/transport"
|
"github.com/micro/go-micro/transport"
|
||||||
"github.com/micro/go-micro/transport/quic"
|
"github.com/micro/go-micro/transport/quic"
|
||||||
@ -29,6 +31,15 @@ type Options struct {
|
|||||||
Transport transport.Transport
|
Transport transport.Transport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DialOption func(*DialOptions)
|
||||||
|
|
||||||
|
type DialOptions struct {
|
||||||
|
// specify a multicast connection
|
||||||
|
Multicast bool
|
||||||
|
// the dial timeout
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// The tunnel id
|
// The tunnel id
|
||||||
func Id(id string) Option {
|
func Id(id string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -73,3 +84,18 @@ func DefaultOptions() Options {
|
|||||||
Transport: quic.NewTransport(),
|
Transport: quic.NewTransport(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dial options
|
||||||
|
|
||||||
|
// Dial multicast sets the multicast option to send only to those mapped
|
||||||
|
func DialMulticast() DialOption {
|
||||||
|
return func(o *DialOptions) {
|
||||||
|
o.Multicast = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialTimeout(t time.Duration) DialOption {
|
||||||
|
return func(o *DialOptions) {
|
||||||
|
o.Timeout = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package tunnel
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/micro/go-micro/transport"
|
"github.com/micro/go-micro/transport"
|
||||||
"github.com/micro/go-micro/util/log"
|
"github.com/micro/go-micro/util/log"
|
||||||
@ -28,10 +29,20 @@ type session struct {
|
|||||||
recv chan *message
|
recv chan *message
|
||||||
// wait until we have a connection
|
// wait until we have a connection
|
||||||
wait chan bool
|
wait chan bool
|
||||||
|
// if the discovery worked
|
||||||
|
discovered bool
|
||||||
|
// if the session was accepted
|
||||||
|
accepted bool
|
||||||
// outbound marks the session as outbound dialled connection
|
// outbound marks the session as outbound dialled connection
|
||||||
outbound bool
|
outbound bool
|
||||||
// lookback marks the session as a loopback on the inbound
|
// lookback marks the session as a loopback on the inbound
|
||||||
loopback bool
|
loopback bool
|
||||||
|
// if the session is multicast
|
||||||
|
multicast bool
|
||||||
|
// if the session is broadcast
|
||||||
|
broadcast bool
|
||||||
|
// the timeout
|
||||||
|
timeout time.Duration
|
||||||
// the link on which this message was received
|
// the link on which this message was received
|
||||||
link string
|
link string
|
||||||
// the error response
|
// the error response
|
||||||
@ -52,6 +63,10 @@ type message struct {
|
|||||||
outbound bool
|
outbound bool
|
||||||
// loopback marks the message intended for loopback
|
// loopback marks the message intended for loopback
|
||||||
loopback bool
|
loopback bool
|
||||||
|
// whether to send as multicast
|
||||||
|
multicast bool
|
||||||
|
// broadcast sets the broadcast type
|
||||||
|
broadcast bool
|
||||||
// the link to send the message on
|
// the link to send the message on
|
||||||
link string
|
link string
|
||||||
// transport data
|
// transport data
|
||||||
@ -76,10 +91,97 @@ func (s *session) Channel() string {
|
|||||||
return s.channel
|
return s.channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open will fire the open message for the session
|
||||||
|
func (s *session) Open() error {
|
||||||
|
msg := &message{
|
||||||
|
typ: "open",
|
||||||
|
tunnel: s.tunnel,
|
||||||
|
channel: s.channel,
|
||||||
|
session: s.session,
|
||||||
|
outbound: s.outbound,
|
||||||
|
loopback: s.loopback,
|
||||||
|
multicast: s.multicast,
|
||||||
|
link: s.link,
|
||||||
|
errChan: s.errChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
// send open message
|
||||||
|
s.send <- msg
|
||||||
|
|
||||||
|
// wait for an error response for send
|
||||||
|
select {
|
||||||
|
case err := <-msg.errChan:
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-s.closed:
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// we don't wait on multicast
|
||||||
|
if s.multicast {
|
||||||
|
s.accepted = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// now wait for the accept
|
||||||
|
select {
|
||||||
|
case msg = <-s.recv:
|
||||||
|
if msg.typ != "accept" {
|
||||||
|
log.Debugf("Received non accept message in Open %s", msg.typ)
|
||||||
|
return errors.New("failed to connect")
|
||||||
|
}
|
||||||
|
// set to accepted
|
||||||
|
s.accepted = true
|
||||||
|
// set link
|
||||||
|
s.link = msg.link
|
||||||
|
case <-time.After(s.timeout):
|
||||||
|
return ErrDialTimeout
|
||||||
|
case <-s.closed:
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) Accept() error {
|
||||||
|
msg := &message{
|
||||||
|
typ: "accept",
|
||||||
|
tunnel: s.tunnel,
|
||||||
|
channel: s.channel,
|
||||||
|
session: s.session,
|
||||||
|
outbound: s.outbound,
|
||||||
|
loopback: s.loopback,
|
||||||
|
multicast: s.multicast,
|
||||||
|
link: s.link,
|
||||||
|
errChan: s.errChan,
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the accept message
|
||||||
|
select {
|
||||||
|
case <-s.closed:
|
||||||
|
return io.EOF
|
||||||
|
case s.send <- msg:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for send response
|
||||||
|
select {
|
||||||
|
case err := <-s.errChan:
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-s.closed:
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *session) Send(m *transport.Message) error {
|
func (s *session) Send(m *transport.Message) error {
|
||||||
select {
|
select {
|
||||||
case <-s.closed:
|
case <-s.closed:
|
||||||
return errors.New("session is closed")
|
return io.EOF
|
||||||
default:
|
default:
|
||||||
// no op
|
// no op
|
||||||
}
|
}
|
||||||
@ -102,6 +204,7 @@ func (s *session) Send(m *transport.Message) error {
|
|||||||
session: s.session,
|
session: s.session,
|
||||||
outbound: s.outbound,
|
outbound: s.outbound,
|
||||||
loopback: s.loopback,
|
loopback: s.loopback,
|
||||||
|
multicast: s.multicast,
|
||||||
data: data,
|
data: data,
|
||||||
// specify the link on which to send this
|
// specify the link on which to send this
|
||||||
// it will be blank for dialled sessions
|
// it will be blank for dialled sessions
|
||||||
@ -109,6 +212,12 @@ func (s *session) Send(m *transport.Message) error {
|
|||||||
// error chan
|
// error chan
|
||||||
errChan: s.errChan,
|
errChan: s.errChan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if not multicast then set link
|
||||||
|
if !s.multicast {
|
||||||
|
msg.link = s.link
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("Appending %+v to send backlog", msg)
|
log.Debugf("Appending %+v to send backlog", msg)
|
||||||
s.send <- msg
|
s.send <- msg
|
||||||
|
|
||||||
@ -154,6 +263,25 @@ func (s *session) Close() error {
|
|||||||
// no op
|
// no op
|
||||||
default:
|
default:
|
||||||
close(s.closed)
|
close(s.closed)
|
||||||
|
|
||||||
|
// append to backlog
|
||||||
|
msg := &message{
|
||||||
|
typ: "close",
|
||||||
|
tunnel: s.tunnel,
|
||||||
|
channel: s.channel,
|
||||||
|
session: s.session,
|
||||||
|
outbound: s.outbound,
|
||||||
|
loopback: s.loopback,
|
||||||
|
multicast: s.multicast,
|
||||||
|
link: s.link,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send the close message
|
||||||
|
select {
|
||||||
|
case s.send <- msg:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
package tunnel
|
package tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/micro/go-micro/transport"
|
"github.com/micro/go-micro/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,7 +21,7 @@ type Tunnel interface {
|
|||||||
// Close closes the tunnel
|
// Close closes the tunnel
|
||||||
Close() error
|
Close() error
|
||||||
// Connect to a channel
|
// Connect to a channel
|
||||||
Dial(channel string) (Session, error)
|
Dial(channel string, opts ...DialOption) (Session, error)
|
||||||
// Accept connections on a channel
|
// Accept connections on a channel
|
||||||
Listen(channel string) (Listener, error)
|
Listen(channel string) (Listener, error)
|
||||||
// Name of the tunnel implementation
|
// Name of the tunnel implementation
|
||||||
@ -42,6 +45,12 @@ type Session interface {
|
|||||||
transport.Socket
|
transport.Socket
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDialTimeout = errors.New("dial timeout")
|
||||||
|
|
||||||
|
DefaultDialTimeout = time.Second * 5
|
||||||
|
)
|
||||||
|
|
||||||
// NewTunnel creates a new tunnel
|
// NewTunnel creates a new tunnel
|
||||||
func NewTunnel(opts ...Option) Tunnel {
|
func NewTunnel(opts ...Option) Tunnel {
|
||||||
return newTunnel(opts...)
|
return newTunnel(opts...)
|
||||||
|
Loading…
Reference in New Issue
Block a user