http.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package group
  2. import (
  3. "fmt"
  4. "net"
  5. "sync"
  6. "sync/atomic"
  7. "github.com/fatedier/frp/pkg/util/vhost"
  8. )
  9. type HTTPGroupController struct {
  10. // groups by indexKey
  11. groups map[string]*HTTPGroup
  12. // register createConn for each group to vhostRouter.
  13. // createConn will get a connection from one proxy of the group
  14. vhostRouter *vhost.Routers
  15. mu sync.Mutex
  16. }
  17. func NewHTTPGroupController(vhostRouter *vhost.Routers) *HTTPGroupController {
  18. return &HTTPGroupController{
  19. groups: make(map[string]*HTTPGroup),
  20. vhostRouter: vhostRouter,
  21. }
  22. }
  23. func (ctl *HTTPGroupController) Register(
  24. proxyName, group, groupKey string,
  25. routeConfig vhost.RouteConfig,
  26. ) (err error) {
  27. indexKey := group
  28. ctl.mu.Lock()
  29. g, ok := ctl.groups[indexKey]
  30. if !ok {
  31. g = NewHTTPGroup(ctl)
  32. ctl.groups[indexKey] = g
  33. }
  34. ctl.mu.Unlock()
  35. return g.Register(proxyName, group, groupKey, routeConfig)
  36. }
  37. func (ctl *HTTPGroupController) UnRegister(proxyName, group string, routeConfig vhost.RouteConfig) {
  38. indexKey := group
  39. ctl.mu.Lock()
  40. defer ctl.mu.Unlock()
  41. g, ok := ctl.groups[indexKey]
  42. if !ok {
  43. return
  44. }
  45. isEmpty := g.UnRegister(proxyName)
  46. if isEmpty {
  47. delete(ctl.groups, indexKey)
  48. }
  49. }
  50. type HTTPGroup struct {
  51. group string
  52. groupKey string
  53. domain string
  54. location string
  55. routeByHTTPUser string
  56. // CreateConnFuncs indexed by echo proxy name
  57. createFuncs map[string]vhost.CreateConnFunc
  58. pxyNames []string
  59. index uint64
  60. ctl *HTTPGroupController
  61. mu sync.RWMutex
  62. }
  63. func NewHTTPGroup(ctl *HTTPGroupController) *HTTPGroup {
  64. return &HTTPGroup{
  65. createFuncs: make(map[string]vhost.CreateConnFunc),
  66. pxyNames: make([]string, 0),
  67. ctl: ctl,
  68. }
  69. }
  70. func (g *HTTPGroup) Register(
  71. proxyName, group, groupKey string,
  72. routeConfig vhost.RouteConfig,
  73. ) (err error) {
  74. g.mu.Lock()
  75. defer g.mu.Unlock()
  76. if len(g.createFuncs) == 0 {
  77. // the first proxy in this group
  78. tmp := routeConfig // copy object
  79. tmp.CreateConnFn = g.createConn
  80. err = g.ctl.vhostRouter.Add(routeConfig.Domain, routeConfig.Location, routeConfig.RouteByHTTPUser, &tmp)
  81. if err != nil {
  82. return
  83. }
  84. g.group = group
  85. g.groupKey = groupKey
  86. g.domain = routeConfig.Domain
  87. g.location = routeConfig.Location
  88. g.routeByHTTPUser = routeConfig.RouteByHTTPUser
  89. } else {
  90. if g.group != group || g.domain != routeConfig.Domain ||
  91. g.location != routeConfig.Location || g.routeByHTTPUser != routeConfig.RouteByHTTPUser {
  92. err = ErrGroupParamsInvalid
  93. return
  94. }
  95. if g.groupKey != groupKey {
  96. err = ErrGroupAuthFailed
  97. return
  98. }
  99. }
  100. if _, ok := g.createFuncs[proxyName]; ok {
  101. err = ErrProxyRepeated
  102. return
  103. }
  104. g.createFuncs[proxyName] = routeConfig.CreateConnFn
  105. g.pxyNames = append(g.pxyNames, proxyName)
  106. return nil
  107. }
  108. func (g *HTTPGroup) UnRegister(proxyName string) (isEmpty bool) {
  109. g.mu.Lock()
  110. defer g.mu.Unlock()
  111. delete(g.createFuncs, proxyName)
  112. for i, name := range g.pxyNames {
  113. if name == proxyName {
  114. g.pxyNames = append(g.pxyNames[:i], g.pxyNames[i+1:]...)
  115. break
  116. }
  117. }
  118. if len(g.createFuncs) == 0 {
  119. isEmpty = true
  120. g.ctl.vhostRouter.Del(g.domain, g.location, g.routeByHTTPUser)
  121. }
  122. return
  123. }
  124. func (g *HTTPGroup) createConn(remoteAddr string) (net.Conn, error) {
  125. var f vhost.CreateConnFunc
  126. newIndex := atomic.AddUint64(&g.index, 1)
  127. g.mu.RLock()
  128. group := g.group
  129. domain := g.domain
  130. location := g.location
  131. routeByHTTPUser := g.routeByHTTPUser
  132. if len(g.pxyNames) > 0 {
  133. name := g.pxyNames[int(newIndex)%len(g.pxyNames)]
  134. f = g.createFuncs[name]
  135. }
  136. g.mu.RUnlock()
  137. if f == nil {
  138. return nil, fmt.Errorf("no CreateConnFunc for http group [%s], domain [%s], location [%s], routeByHTTPUser [%s]",
  139. group, domain, location, routeByHTTPUser)
  140. }
  141. return f(remoteAddr)
  142. }