endpoints.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package dynamicproxy
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "strings"
  6. "golang.org/x/text/cases"
  7. "golang.org/x/text/language"
  8. "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
  9. )
  10. /*
  11. endpoint.go
  12. author: tobychui
  13. This script handle the proxy endpoint object actions
  14. so proxyEndpoint can be handled like a proper oop object
  15. Most of the functions are implemented in dynamicproxy.go
  16. */
  17. /*
  18. User Defined Header Functions
  19. */
  20. // Check if a user define header exists in this endpoint, ignore case
  21. func (ep *ProxyEndpoint) UserDefinedHeaderExists(key string) bool {
  22. for _, header := range ep.UserDefinedHeaders {
  23. if strings.EqualFold(header.Key, key) {
  24. return true
  25. }
  26. }
  27. return false
  28. }
  29. // Remvoe a user defined header from the list
  30. func (ep *ProxyEndpoint) RemoveUserDefinedHeader(key string) error {
  31. newHeaderList := []*UserDefinedHeader{}
  32. for _, header := range ep.UserDefinedHeaders {
  33. if !strings.EqualFold(header.Key, key) {
  34. newHeaderList = append(newHeaderList, header)
  35. }
  36. }
  37. ep.UserDefinedHeaders = newHeaderList
  38. return nil
  39. }
  40. // Add a user defined header to the list, duplicates will be automatically removed
  41. func (ep *ProxyEndpoint) AddUserDefinedHeader(newHeaderRule *UserDefinedHeader) error {
  42. if ep.UserDefinedHeaderExists(newHeaderRule.Key) {
  43. ep.RemoveUserDefinedHeader(newHeaderRule.Key)
  44. }
  45. newHeaderRule.Key = cases.Title(language.Und, cases.NoLower).String(newHeaderRule.Key)
  46. ep.UserDefinedHeaders = append(ep.UserDefinedHeaders, newHeaderRule)
  47. return nil
  48. }
  49. /*
  50. Virtual Directory Functions
  51. */
  52. // Get virtual directory handler from given URI
  53. func (ep *ProxyEndpoint) GetVirtualDirectoryHandlerFromRequestURI(requestURI string) *VirtualDirectoryEndpoint {
  54. for _, vdir := range ep.VirtualDirectories {
  55. if strings.HasPrefix(requestURI, vdir.MatchingPath) {
  56. thisVdir := vdir
  57. return thisVdir
  58. }
  59. }
  60. return nil
  61. }
  62. // Get virtual directory handler by matching path (exact match required)
  63. func (ep *ProxyEndpoint) GetVirtualDirectoryRuleByMatchingPath(matchingPath string) *VirtualDirectoryEndpoint {
  64. for _, vdir := range ep.VirtualDirectories {
  65. if vdir.MatchingPath == matchingPath {
  66. thisVdir := vdir
  67. return thisVdir
  68. }
  69. }
  70. return nil
  71. }
  72. // Delete a vdir rule by its matching path
  73. func (ep *ProxyEndpoint) RemoveVirtualDirectoryRuleByMatchingPath(matchingPath string) error {
  74. entryFound := false
  75. newVirtualDirectoryList := []*VirtualDirectoryEndpoint{}
  76. for _, vdir := range ep.VirtualDirectories {
  77. if vdir.MatchingPath == matchingPath {
  78. entryFound = true
  79. } else {
  80. newVirtualDirectoryList = append(newVirtualDirectoryList, vdir)
  81. }
  82. }
  83. if entryFound {
  84. //Update the list of vdirs
  85. ep.VirtualDirectories = newVirtualDirectoryList
  86. return nil
  87. }
  88. return errors.New("target virtual directory routing rule not found")
  89. }
  90. // Delete a vdir rule by its matching path
  91. func (ep *ProxyEndpoint) AddVirtualDirectoryRule(vdir *VirtualDirectoryEndpoint) (*ProxyEndpoint, error) {
  92. //Check for matching path duplicate
  93. if ep.GetVirtualDirectoryRuleByMatchingPath(vdir.MatchingPath) != nil {
  94. return nil, errors.New("rule with same matching path already exists")
  95. }
  96. //Append it to the list of virtual directory
  97. ep.VirtualDirectories = append(ep.VirtualDirectories, vdir)
  98. //Prepare to replace the current routing rule
  99. parentRouter := ep.parent
  100. readyRoutingRule, err := parentRouter.PrepareProxyRoute(ep)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if ep.ProxyType == ProxyType_Root {
  105. parentRouter.Root = readyRoutingRule
  106. } else if ep.ProxyType == ProxyType_Host {
  107. ep.Remove()
  108. parentRouter.AddProxyRouteToRuntime(readyRoutingRule)
  109. } else {
  110. return nil, errors.New("unsupported proxy type")
  111. }
  112. return readyRoutingRule, nil
  113. }
  114. /* Upstream related wrapper functions */
  115. //Check if there already exists another upstream with identical origin
  116. func (ep *ProxyEndpoint) UpstreamOriginExists(originURL string) bool {
  117. for _, origin := range ep.ActiveOrigins {
  118. if origin.OriginIpOrDomain == originURL {
  119. return true
  120. }
  121. }
  122. for _, origin := range ep.InactiveOrigins {
  123. if origin.OriginIpOrDomain == originURL {
  124. return true
  125. }
  126. }
  127. return false
  128. }
  129. // Get a upstream origin from given origin ip or domain
  130. func (ep *ProxyEndpoint) GetUpstreamOriginByMatchingIP(originIpOrDomain string) (*loadbalance.Upstream, error) {
  131. for _, origin := range ep.ActiveOrigins {
  132. if origin.OriginIpOrDomain == originIpOrDomain {
  133. return origin, nil
  134. }
  135. }
  136. for _, origin := range ep.InactiveOrigins {
  137. if origin.OriginIpOrDomain == originIpOrDomain {
  138. return origin, nil
  139. }
  140. }
  141. return nil, errors.New("target upstream origin not found")
  142. }
  143. // Add upstream to endpoint and update it to runtime
  144. func (ep *ProxyEndpoint) AddUpstreamOrigin(newOrigin *loadbalance.Upstream, activate bool) error {
  145. //Check if the upstream already exists
  146. if ep.UpstreamOriginExists(newOrigin.OriginIpOrDomain) {
  147. return errors.New("upstream with same origin already exists")
  148. }
  149. if activate {
  150. //Add it to the active origin list
  151. err := newOrigin.StartProxy()
  152. if err != nil {
  153. return err
  154. }
  155. ep.ActiveOrigins = append(ep.ActiveOrigins, newOrigin)
  156. } else {
  157. //Add to inactive origin list
  158. ep.InactiveOrigins = append(ep.InactiveOrigins, newOrigin)
  159. }
  160. ep.UpdateToRuntime()
  161. return nil
  162. }
  163. // Remove upstream from endpoint and update it to runtime
  164. func (ep *ProxyEndpoint) RemoveUpstreamOrigin(originIpOrDomain string) error {
  165. //Just to make sure there are no spaces
  166. originIpOrDomain = strings.TrimSpace(originIpOrDomain)
  167. //Check if the upstream already been removed
  168. if !ep.UpstreamOriginExists(originIpOrDomain) {
  169. //Not exists in the first place
  170. return nil
  171. }
  172. newActiveOriginList := []*loadbalance.Upstream{}
  173. for _, origin := range ep.ActiveOrigins {
  174. if origin.OriginIpOrDomain != originIpOrDomain {
  175. newActiveOriginList = append(newActiveOriginList, origin)
  176. }
  177. }
  178. newInactiveOriginList := []*loadbalance.Upstream{}
  179. for _, origin := range ep.InactiveOrigins {
  180. if origin.OriginIpOrDomain != originIpOrDomain {
  181. newInactiveOriginList = append(newInactiveOriginList, origin)
  182. }
  183. }
  184. //Ok, set the origin list to the new one
  185. ep.ActiveOrigins = newActiveOriginList
  186. ep.InactiveOrigins = newInactiveOriginList
  187. ep.UpdateToRuntime()
  188. return nil
  189. }
  190. // Check if the proxy endpoint hostname or alias name contains subdomain wildcard
  191. func (ep *ProxyEndpoint) ContainsWildcardName(skipAliasCheck bool) bool {
  192. hostname := ep.RootOrMatchingDomain
  193. aliasHostnames := ep.MatchingDomainAlias
  194. wildcardCheck := func(hostname string) bool {
  195. return len(hostname) > 0 && hostname[0] == '*'
  196. }
  197. if wildcardCheck(hostname) {
  198. return true
  199. }
  200. if !skipAliasCheck {
  201. for _, aliasHostname := range aliasHostnames {
  202. if wildcardCheck(aliasHostname) {
  203. return true
  204. }
  205. }
  206. }
  207. return false
  208. }
  209. // Create a deep clone object of the proxy endpoint
  210. // Note the returned object is not activated. Call to prepare function before pushing into runtime
  211. func (ep *ProxyEndpoint) Clone() *ProxyEndpoint {
  212. clonedProxyEndpoint := ProxyEndpoint{}
  213. js, _ := json.Marshal(ep)
  214. json.Unmarshal(js, &clonedProxyEndpoint)
  215. return &clonedProxyEndpoint
  216. }
  217. // Remove this proxy endpoint from running proxy endpoint list
  218. func (ep *ProxyEndpoint) Remove() error {
  219. ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
  220. return nil
  221. }
  222. // Write changes to runtime without respawning the proxy handler
  223. // use prepare -> remove -> add if you change anything in the endpoint
  224. // that effects the proxy routing src / dest
  225. func (ep *ProxyEndpoint) UpdateToRuntime() {
  226. ep.parent.ProxyEndpoints.Store(ep.RootOrMatchingDomain, ep)
  227. }