service/gateway.go

426 lines
11 KiB
Go
Raw Permalink Normal View History

package service
import (
"fmt"
2024-10-18 17:54:37 +08:00
"path"
"regexp"
"strings"
"sync"
"time"
"github.com/ssgo/discover"
"github.com/ssgo/s"
"github.com/ssgo/standard"
"github.com/ssgo/u"
)
2024-10-18 17:54:37 +08:00
type regexRedirectInfo struct {
To string
Regex regexp.Regexp
}
var _proxies = map[string]string{}
var _proxiesLock = sync.RWMutex{}
2024-10-18 17:54:37 +08:00
var _regexProxies = map[string]*regexRedirectInfo{}
var _rewrites = map[string]string{}
var _regexRewrites = map[string]*regexRedirectInfo{}
var _rewritesLock = sync.RWMutex{}
var _statics = map[string]string{}
var _staticsLock = sync.RWMutex{}
2024-10-18 17:54:37 +08:00
func UpdateStatic(in map[string]string) bool {
updated := false
for k, v := range in {
_staticsLock.RLock()
v1 := _statics[k]
_staticsLock.RUnlock()
if v == v1 {
continue
}
s.ServerLogger.Info(u.StringIf(v1 != "", "update static set", "new static set"), "key", k, "value", v)
_staticsLock.Lock()
_statics[k] = v
_staticsLock.Unlock()
a := strings.SplitN(k, "/", 2)
if len(a) == 1 {
a = append(a, "/")
}
if a[0] == "*" {
a[0] = ""
}
2024-10-18 17:54:37 +08:00
if a[1] == "" {
a[1] = "/"
}
s.StaticByHost(a[1], v, a[0])
updated = true
}
return updated
}
2024-10-18 17:54:37 +08:00
func UpdateProxy(in map[string]string) bool {
updated := false
//fmt.Println("####000")
for k, v := range in {
//fmt.Println("####111", k, v)
_proxiesLock.RLock()
v1 := _proxies[k]
v2 := _regexProxies[k]
_proxiesLock.RUnlock()
// skip same
if v == v1 {
//fmt.Println("####222", k, v)
continue
}
////fmt.Println("####333", k, v)
2024-10-18 17:54:37 +08:00
if v2 != nil && v == v2.To {
continue
}
////fmt.Println("####444", k, v)
2024-10-18 17:54:37 +08:00
if strings.Contains(k, "(") {
// for regexp
////fmt.Println("####555", k, v)
2024-10-18 17:54:37 +08:00
matcher, err := regexp.Compile("^" + k + "$")
if err != nil {
s.ServerLogger.Error("proxy regexp compile failed", "key", k, "value", v)
//log.Print("Proxy Error Compile ", err)
} else {
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp proxy set", "new regexp proxy set"), "key", k, "value", v)
_proxiesLock.Lock()
2024-10-18 17:54:37 +08:00
_regexProxies[k] = &regexRedirectInfo{
To: v,
Regex: *matcher,
}
_proxiesLock.Unlock()
updated = true
}
} else {
// for simple
////fmt.Println("####666", k, v)
s.ServerLogger.Info(u.StringIf(v1 != "", "update proxy set", "new proxy set"), "key", k, "value", v)
_proxiesLock.Lock()
_proxies[k] = v
_proxiesLock.Unlock()
// add app to discover
////fmt.Println("########2", len((*proxies)))
if !strings.Contains(v, "://") {
if discover.Config.Calls[v] == "" {
callConfig := ""
if strings.ContainsRune(v, ':') {
// support call config in proxy value
a := strings.SplitN(v, ":", 2)
v = a[0]
callConfig = a[1]
} else {
callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
}
2024-10-18 17:54:37 +08:00
if discover.Config.Registry != "" {
if discover.AddExternalApp(v, callConfig) {
updated = true
}
}
} else {
updated = true
}
} else {
updated = true
}
}
}
//fmt.Println("####999")
return updated
}
2024-10-18 17:54:37 +08:00
func UpdateRewrite(in map[string]string) bool {
updated := false
for k, v := range in {
_rewritesLock.RLock()
2024-10-18 17:54:37 +08:00
v1 := _rewrites[k]
v2 := _regexRewrites[k]
_rewritesLock.RUnlock()
// skip same
2024-10-18 17:54:37 +08:00
if v == v1 {
continue
}
if v2 != nil && v == v2.To {
continue
}
2024-10-18 17:54:37 +08:00
if strings.Contains(k, "(") {
matcher, err := regexp.Compile("^" + k + "$")
if err != nil {
s.ServerLogger.Error("rewrite regexp compile failed", "key", k, "value", v)
} else {
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp rewrite set", "new regexp rewrite set"), "key", k, "value", v)
_rewritesLock.Lock()
_regexRewrites[k] = &regexRedirectInfo{
To: v,
Regex: *matcher,
}
_rewritesLock.Unlock()
updated = true
}
} else {
_rewritesLock.Lock()
2024-10-18 17:54:37 +08:00
_rewrites[k] = v
_rewritesLock.Unlock()
updated = true
}
}
return updated
}
2024-10-18 17:54:37 +08:00
// TODO 测试各种情况下的 matchRedirect
func rewrite(request *s.Request) (toPath string, rewrite bool) {
2024-10-18 17:54:37 +08:00
if toApp, toPath, ok := matchRedirect(request, &_rewrites, &_regexRewrites, &_rewritesLock); ok {
return toApp + toPath, true
}
2024-10-18 17:54:37 +08:00
return
}
2024-10-18 17:54:37 +08:00
func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers map[string]string) {
if toApp1, toPath1, ok := matchRedirect(request, &_proxies, &_regexProxies, &_proxiesLock); ok {
outHeaders := map[string]string{
standard.DiscoverHeaderFromApp: "gateway",
standard.DiscoverHeaderFromNode: s.GetServerAddr(),
}
requestPath := request.RequestURI
if requestPath == "" {
requestPath = request.URL.Path
}
2024-10-18 17:54:37 +08:00
pos := strings.Index(requestPath, toPath1)
if pos > 0 {
outHeaders["Proxy-Path"] = requestPath[0:pos]
}
2024-10-18 17:54:37 +08:00
return 0, &toApp1, &toPath1, outHeaders
}
2024-10-18 17:54:37 +08:00
return
}
2024-10-18 17:54:37 +08:00
func ClearRewritesAndProxies() {
_staticsLock.Lock()
_statics = map[string]string{}
_staticsLock.Unlock()
_rewritesLock.Lock()
_rewrites = map[string]string{}
_regexRewrites = map[string]*regexRedirectInfo{}
_rewritesLock.Unlock()
_proxiesLock.Lock()
_proxies = map[string]string{}
_regexProxies = map[string]*regexRedirectInfo{}
_proxiesLock.Unlock()
}
2024-10-18 17:54:37 +08:00
func MatchRewrite(request *s.Request) (toApp, toPath string, ok bool) {
return matchRedirect(request, &_rewrites, &_regexRewrites, &_rewritesLock)
}
func MatchProxy(request *s.Request) (toApp, toPath string, ok bool) {
return matchRedirect(request, &_proxies, &_regexProxies, &_proxiesLock)
}
func matchRedirect(request *s.Request, normalList *map[string]string, regexpList *map[string]*regexRedirectInfo, lock *sync.RWMutex) (toApp, toPath string, ok bool) {
(*lock).RLock()
n1 := len(*normalList)
n2 := len(*regexpList)
(*lock).RUnlock()
if n1 == 0 && n2 == 0 {
return
}
host1 := ""
host2 := ""
if strings.ContainsRune(request.Host, ':') {
hostArr := strings.SplitN(request.Host, ":", 2)
host1 = hostArr[0]
host2 = request.Host
} else {
host1 = request.Host
2024-10-18 17:54:37 +08:00
host2 = request.Host + ":" + u.StringIf(request.URL.Scheme == "https", "443", "80")
}
2024-10-18 17:54:37 +08:00
requestPath := request.RequestURI
if requestPath == "" {
requestPath = request.URL.Path
}
pathMatchers := make([]string, 0)
2024-10-18 17:54:37 +08:00
pathMatchers = append(pathMatchers, fmt.Sprint(request.URL.Scheme, "://", host1, requestPath))
pathMatchers = append(pathMatchers, fmt.Sprint(request.URL.Scheme, "://", host2, requestPath))
pathMatchers = append(pathMatchers, fmt.Sprint(host1, requestPath))
pathMatchers = append(pathMatchers, fmt.Sprint(host2, requestPath))
pathMatchers = append(pathMatchers, requestPath)
hostMatchers := make([]string, 0)
2024-10-18 17:54:37 +08:00
hostMatchers = append(hostMatchers, fmt.Sprint(request.URL.Scheme, "://", host1))
hostMatchers = append(hostMatchers, fmt.Sprint(request.URL.Scheme, "://", host2))
hostMatchers = append(hostMatchers, host1)
hostMatchers = append(hostMatchers, host2)
2024-10-18 17:54:37 +08:00
if n1 > 0 {
list1 := map[string]string{}
(*lock).RLock()
for k, v := range *normalList {
list1[k] = v
}
2024-10-18 17:54:37 +08:00
(*lock).RUnlock()
for setKey, setValue := range list1 {
matchPath := ""
matchPathArr := strings.SplitN(strings.ReplaceAll(setKey, "://", ""), "/", 2)
if len(matchPathArr) == 2 {
matchPath = "/" + matchPathArr[1]
}
2024-10-18 17:54:37 +08:00
toApp, toPath = splitAppAndPath(setValue)
if matchPath == "" {
for _, matchStr := range hostMatchers {
if matchStr == setKey {
// fmt.Println(" >>>>>>>>1", setKey, matchStr, requestPath)
return toApp, path.Join(toPath, requestPath), true
}
}
2024-10-18 17:54:37 +08:00
} else {
for _, matchStr := range pathMatchers {
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.BMagenta(setKey), "|", strings.HasPrefix(matchStr, setKey))
if strings.HasPrefix(matchStr, setKey) {
if strings.HasPrefix(requestPath, matchPath) {
p2 := requestPath[len(matchPath):]
if len(p2) == 0 || p2[0] != '/' {
p2 = "/" + p2
}
// fmt.Println(" >>>>>>>>2", setKey, matchStr, p2)
return toApp, path.Join(toPath, p2), true
} else {
// fmt.Println(" >>>>>>>>3", setKey, matchStr, requestPath)
return toApp, path.Join(toPath, requestPath), true
}
}
}
}
}
}
2024-10-18 17:54:37 +08:00
if n2 > 0 {
// 模糊匹配
list2 := map[string]*regexRedirectInfo{}
(*lock).RLock()
for k, v := range *regexpList {
list2[k] = v
}
(*lock).RUnlock()
2024-10-18 17:54:37 +08:00
// requestUrl := request.Host + requestPath
for setKey, setInfo := range list2 {
matchPath := ""
matchPathArr := strings.SplitN(strings.ReplaceAll(setKey, "://", ""), "/", 2)
if len(matchPathArr) == 2 {
matchPath = "/" + matchPathArr[1]
}
// fmt.Println(" >>>>>>>> matchPath", setKey, matchPath, u.JsonP(matchPathArr), 111)
var matchList []string
if matchPath == "" {
matchList = hostMatchers
} else {
matchList = pathMatchers
}
for _, matchStr := range matchList {
finds := setInfo.Regex.FindStringSubmatch(matchStr)
if len(finds) > 0 {
matchResult := setInfo.To
for i := 1; i < len(finds); i++ {
matchResult = strings.ReplaceAll(matchResult, fmt.Sprintf("$%d", i), finds[i])
}
2024-10-18 17:54:37 +08:00
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.Cyan(matchPath), u.BMagenta(setKey), "|", matchResult, fixAppName(matchResult), "...")
toApp, toPath := splitAppAndPath(matchResult)
return toApp, toPath, true
}
}
2024-10-18 17:54:37 +08:00
// //fmt.Println("check regexp proxy ", rp.Regex, rp.Value)
// // finds := setInfo.Regex.FindAllStringSubmatch(requestUrl, 20)
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.BMagenta(setKey), "|", strings.HasPrefix(matchStr, setKey))
// if len(finds) > 0 && len(finds[0]) > 2 {
// //fmt.Println(" >>>>>>>>2", requestPath, finds[0][2])
// // pos := strings.Index(requestPath, finds[0][2])
// // if pos > 0 {
// // outHeaders["Proxy-Path"] = requestPath[0:pos]
// // }
// if !strings.Contains(finds[0][1], "://") && strings.ContainsRune(finds[0][1], ':') {
// callConfig := ""
// if strings.ContainsRune(finds[0][1], ':') {
// // support call config in proxy value
// a := strings.SplitN(finds[0][1], ":", 2)
// finds[0][1] = a[0]
// callConfig = a[1]
// } else {
// callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
// }
// if discover.Config.Registry != "" {
// discover.AddExternalApp(finds[0][1], callConfig)
// }
// }
// return finds[0][1], finds[0][2], true
// }
}
}
// 不进行代理
return
}
2024-10-18 17:54:37 +08:00
// func fixAppName(appName string) string {
// if !strings.Contains(appName, "://") && strings.ContainsRune(appName, ':') {
// a := strings.SplitN(appName, "/", 2)
// return a[0]
// } else {
// return appName
// }
// }
func splitAppAndPath(to string) (toApp, toPath string) {
if strings.Contains(to, "://") {
to = strings.Replace(to, "://", ":--", 1)
a := strings.SplitN(to, "/", 2)
if len(a) == 1 {
a = append(a, "")
}
a[0] = strings.Replace(a[0], ":--", "://", 1)
return a[0], "/" + a[1]
} else {
2024-10-18 17:54:37 +08:00
if strings.HasPrefix(to, "/") {
toApp = ""
toPath = makeAppConfig(to)
} else {
a := strings.SplitN(to, "/", 2)
if len(a) == 1 {
a = append(a, "")
}
toApp = makeAppConfig(a[0])
toPath = "/" + a[1]
}
}
return
}
func makeAppConfig(toStr string) (toApp string) {
toAppConfig := ""
if strings.ContainsRune(toApp, ':') {
a := strings.SplitN(toApp, ":", 2)
toApp = a[0]
toAppConfig = a[1]
} else {
toApp = toStr
// toAppConfig = (time.Duration(s.Config.RedirectTimeout) * time.Millisecond).String()
}
if discover.Config.Registry != "" {
discover.AddExternalApp(toApp, toAppConfig)
}
2024-10-18 17:54:37 +08:00
return toApp
}