service/gateway.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] = &regexProxiesInfo{
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] = &regexRewriteInfo{
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
}
}