325 lines
8.3 KiB
Go
325 lines
8.3 KiB
Go
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
|
|
}
|
|
}
|