manager.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package server
  2. import (
  3. "fmt"
  4. cache "github.com/pmylund/go-cache"
  5. "math/rand"
  6. "net"
  7. "strings"
  8. "sync"
  9. "time"
  10. )
  11. /**
  12. * TunnelManager: Manages a set of tunnels
  13. */
  14. type TunnelManager struct {
  15. domain string
  16. tunnels map[string]*Tunnel
  17. domainAffinity *cache.Cache
  18. sync.RWMutex
  19. }
  20. func NewTunnelManager(domain string) *TunnelManager {
  21. return &TunnelManager{
  22. domain: domain,
  23. tunnels: make(map[string]*Tunnel),
  24. domainAffinity: cache.New(24*time.Hour, time.Minute),
  25. }
  26. }
  27. func (m *TunnelManager) Add(t *Tunnel) {
  28. assignTunnel := func(url string) bool {
  29. m.Lock()
  30. defer m.Unlock()
  31. if m.tunnels[url] == nil {
  32. m.tunnels[url] = t
  33. return true
  34. }
  35. return false
  36. }
  37. url := ""
  38. switch t.regMsg.Protocol {
  39. case "tcp":
  40. addr := t.listener.Addr().(*net.TCPAddr)
  41. url = fmt.Sprintf("tcp://%s:%d", m.domain, addr.Port)
  42. if !assignTunnel(url) {
  43. panic("TCP at %s already registered!")
  44. }
  45. metrics.tcpTunnelMeter.Mark(1)
  46. case "http":
  47. if strings.TrimSpace(t.regMsg.Hostname) != "" {
  48. url = fmt.Sprintf("http://%s", t.regMsg.Hostname)
  49. } else if strings.TrimSpace(t.regMsg.Subdomain) != "" {
  50. url = fmt.Sprintf("http://%s.%s", t.regMsg.Subdomain, m.domain)
  51. }
  52. if url != "" {
  53. if !assignTunnel(url) {
  54. panic(fmt.Sprintf("The tunnel address %s is already registered!", url))
  55. }
  56. } else {
  57. // try to give the same subdomain back if it's available
  58. subdomain, ok := m.domainAffinity.Get(t.regMsg.ClientId)
  59. if !ok {
  60. subdomain = fmt.Sprintf("%x", rand.Int31())
  61. }
  62. // pick one randomly
  63. for {
  64. url = fmt.Sprintf("http://%s.%s", subdomain, m.domain)
  65. if assignTunnel(url) {
  66. break
  67. } else {
  68. subdomain = fmt.Sprintf("%x", rand.Int31())
  69. }
  70. }
  71. // save our choice for later
  72. // XXX: this is going to leak memory
  73. m.domainAffinity.Set(t.regMsg.ClientId, subdomain, 0)
  74. }
  75. default:
  76. panic(t.Error("Unrecognized protocol type %s", t.regMsg.Protocol))
  77. }
  78. t.url = url
  79. metrics.tunnelMeter.Mark(1)
  80. //metrics.tunnelGauge.Update(int64(len(m.tunnels)))
  81. switch t.regMsg.OS {
  82. case "windows":
  83. metrics.windowsCounter.Inc(1)
  84. case "linux":
  85. metrics.linuxCounter.Inc(1)
  86. case "darwin":
  87. metrics.osxCounter.Inc(1)
  88. default:
  89. metrics.otherCounter.Inc(1)
  90. }
  91. }
  92. func (m *TunnelManager) Del(url string) {
  93. m.Lock()
  94. defer m.Unlock()
  95. delete(m.tunnels, url)
  96. }
  97. func (m *TunnelManager) Get(url string) *Tunnel {
  98. m.RLock()
  99. defer m.RUnlock()
  100. return m.tunnels[url]
  101. }