package service import ( "fmt" "regexp" "strings" "sync" "time" "github.com/ssgo/discover" "github.com/ssgo/s" "github.com/ssgo/standard" "github.com/ssgo/u" ) type regexProxiesInfo struct { Value string Regex regexp.Regexp } type regexRewriteInfo struct { To string Regex regexp.Regexp } var _proxies = map[string]string{} var _proxiesLock = sync.RWMutex{} var _regexProxies = map[string]*regexProxiesInfo{} var _regexRewrites = map[string]*regexRewriteInfo{} var _rewritesLock = sync.RWMutex{} var _statics = map[string]string{} var _staticsLock = sync.RWMutex{} 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] = "" } s.StaticByHost(a[1], v, a[0]) updated = true } return updated } 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) if v2 != nil && v == v2.Value { continue } ////fmt.Println("####444", k, v) if strings.Contains(v, "(") { // for regexp ////fmt.Println("####555", k, v) matcher, err := regexp.Compile("^" + v + "$") 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() _regexProxies[k] = ®exProxiesInfo{ Value: 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() } // if redisPool != nil { if discover.AddExternalApp(v, callConfig) { updated = true } // } } else { updated = true } } else { updated = true } } } //fmt.Println("####999") return updated } func updateRewrite(in map[string]string) bool { updated := false for k, v := range in { _rewritesLock.RLock() v2 := _regexRewrites[k] _rewritesLock.RUnlock() // skip same if v2 != nil && v == v2.To { continue } 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] = ®exRewriteInfo{ To: v, Regex: *matcher, } _rewritesLock.Unlock() updated = true } } return updated } func rewrite(request *s.Request) (toPath string, rewrite bool) { list2 := map[string]*regexRewriteInfo{} _rewritesLock.RLock() for k, v := range _regexRewrites { list2[k] = v } _rewritesLock.RUnlock() if len(list2) > 0 { requestUrl := fmt.Sprint(request.Header.Get("X-Scheme"), "://", request.Host, request.RequestURI) requestUrlWithoutScheme := fmt.Sprint(request.Host, request.RequestURI) for _, rr := range list2 { finds := rr.Regex.FindAllStringSubmatch(requestUrl, 20) if len(finds) == 0 { finds = rr.Regex.FindAllStringSubmatch(requestUrlWithoutScheme, 20) } if len(finds) == 0 { continue } to := rr.To if len(finds[0]) > 1 { for i := 1; i < len(finds[0]); i++ { varName := fmt.Sprintf("$%d", i) to = strings.ReplaceAll(to, varName, finds[0][i]) } return to, true } } } // 不进行代理 return "", false } func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers map[string]string) { //fmt.Println("proxy", len(_proxies)) outHeaders := map[string]string{ standard.DiscoverHeaderFromApp: "gateway", standard.DiscoverHeaderFromNode: s.GetServerAddr(), } scheme := u.StringIf(request.TLS == nil, "http", "https") host1 := "" host2 := "" if strings.ContainsRune(request.Host, ':') { hostArr := strings.SplitN(request.Host, ":", 2) host1 = hostArr[0] host2 = request.Host } else { host1 = request.Host host2 = request.Host + ":" + u.StringIf(request.TLS == nil, "80", "443") } pathMatchers := make([]string, 0) pathMatchers = append(pathMatchers, fmt.Sprint(scheme, "://", host1, request.RequestURI)) pathMatchers = append(pathMatchers, fmt.Sprint(scheme, "://", host2, request.RequestURI)) pathMatchers = append(pathMatchers, fmt.Sprint(host1, request.RequestURI)) pathMatchers = append(pathMatchers, fmt.Sprint(host2, request.RequestURI)) pathMatchers = append(pathMatchers, request.RequestURI) hostMatchers := make([]string, 0) hostMatchers = append(hostMatchers, fmt.Sprint(scheme, "://", host1)) hostMatchers = append(hostMatchers, fmt.Sprint(scheme, "://", host2)) hostMatchers = append(hostMatchers, host1) hostMatchers = append(hostMatchers, host2) list := map[string]string{} _proxiesLock.RLock() for k, v := range _proxies { list[k] = v } _proxiesLock.RUnlock() for p, a := range list { //fmt.Println("check proxy ", p, a) matchPath := "" matchPathArr := strings.SplitN(strings.ReplaceAll(p, "://", ""), "/", 2) if len(matchPathArr) == 2 { matchPath = "/" + matchPathArr[1] } if matchPath == "" { for _, m := range hostMatchers { if m == p { //fmt.Println(" >>>>>>>>1", p, m, request.RequestURI) return 0, fixAppName(a), &request.RequestURI, outHeaders } } } else { for _, m := range pathMatchers { if strings.HasPrefix(m, p) { if strings.HasPrefix(request.RequestURI, matchPath) { p2 := request.RequestURI[len(matchPath):] if len(p2) == 0 || p2[0] != '/' { p2 = "/" + p2 } //fmt.Println(" >>>>>>>>2", p, m, p2) return 0, fixAppName(a), &p2, outHeaders } else { //fmt.Println(" >>>>>>>>3", p, m, request.RequestURI) return 0, fixAppName(a), &request.RequestURI, outHeaders } } } } } // 模糊匹配 list2 := map[string]*regexProxiesInfo{} _proxiesLock.RLock() for k, v := range _regexProxies { list2[k] = v } _proxiesLock.RUnlock() if len(list2) > 0 { requestUrl := request.Host + request.RequestURI for _, rp := range list2 { //fmt.Println("check regexp proxy ", rp.Regex, rp.Value) finds := rp.Regex.FindAllStringSubmatch(requestUrl, 20) if len(finds) > 0 && len(finds[0]) > 2 { //fmt.Println(" >>>>>>>>2", request.RequestURI, finds[0][2]) pos := strings.Index(request.RequestURI, finds[0][2]) if pos > 0 { outHeaders["Proxy-Path"] = request.RequestURI[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 redisPool != nil { discover.AddExternalApp(finds[0][1], callConfig) // } } return 0, &finds[0][1], &finds[0][2], outHeaders } } } // 不进行代理 return } func fixAppName(appName string) *string { if !strings.Contains(appName, "://") && strings.ContainsRune(appName, ':') { a := strings.SplitN(appName, ":", 2) return &a[0] } else { return &appName } }