1
0

endpoints.go 7.5 KB

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