121 lines
2.0 KiB
Go
121 lines
2.0 KiB
Go
package tunnel
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.xeserv.us/xena/route/lib/tunnel/proto"
|
|
"github.com/cenkalti/backoff"
|
|
)
|
|
|
|
// async is a helper function to convert a blocking function to a function
|
|
// returning an error. Useful for plugging function closures into select and co
|
|
func async(fn func() error) <-chan error {
|
|
errChan := make(chan error, 0)
|
|
go func() {
|
|
select {
|
|
case errChan <- fn():
|
|
default:
|
|
}
|
|
|
|
close(errChan)
|
|
}()
|
|
|
|
return errChan
|
|
}
|
|
|
|
type expBackoff struct {
|
|
mu sync.Mutex
|
|
bk *backoff.ExponentialBackOff
|
|
}
|
|
|
|
func newForeverBackoff() *expBackoff {
|
|
eb := &expBackoff{
|
|
bk: backoff.NewExponentialBackOff(),
|
|
}
|
|
eb.bk.MaxElapsedTime = 0 // never stops
|
|
return eb
|
|
}
|
|
|
|
func (eb *expBackoff) NextBackOff() time.Duration {
|
|
eb.mu.Lock()
|
|
defer eb.mu.Unlock()
|
|
|
|
return eb.bk.NextBackOff()
|
|
}
|
|
|
|
func (eb *expBackoff) Reset() {
|
|
eb.mu.Lock()
|
|
eb.bk.Reset()
|
|
eb.mu.Unlock()
|
|
}
|
|
|
|
type callbacks struct {
|
|
mu sync.Mutex
|
|
name string
|
|
funcs map[string]func() error
|
|
}
|
|
|
|
func newCallbacks(name string) *callbacks {
|
|
return &callbacks{
|
|
name: name,
|
|
funcs: make(map[string]func() error),
|
|
}
|
|
}
|
|
|
|
func (c *callbacks) add(identifier string, fn func() error) {
|
|
c.mu.Lock()
|
|
c.funcs[identifier] = fn
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *callbacks) pop(identifier string) (func() error, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
fn, ok := c.funcs[identifier]
|
|
if !ok {
|
|
return nil, nil // nop
|
|
}
|
|
|
|
delete(c.funcs, identifier)
|
|
|
|
if fn == nil {
|
|
return nil, fmt.Errorf("nil callback set for %q client", identifier)
|
|
}
|
|
|
|
return fn, nil
|
|
}
|
|
|
|
func (c *callbacks) call(identifier string) error {
|
|
fn, err := c.pop(identifier)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if fn == nil {
|
|
return nil // nop
|
|
}
|
|
|
|
return fn()
|
|
}
|
|
|
|
// Returns server control url as a string. Reads scheme and remote address from connection.
|
|
func controlURL(conn net.Conn) string {
|
|
return fmt.Sprint(scheme(conn), "://", conn.RemoteAddr(), proto.ControlPath)
|
|
}
|
|
|
|
func scheme(conn net.Conn) (scheme string) {
|
|
switch conn.(type) {
|
|
case *tls.Conn:
|
|
scheme = "https"
|
|
default:
|
|
scheme = "http"
|
|
}
|
|
|
|
return
|
|
}
|