Files
2025-01-06 09:47:23 +08:00

207 lines
5.5 KiB
Go

package entity
import (
"fmt"
"net/textproto"
"strings"
"github.com/APIParkLab/APIPark/common"
"github.com/APIParkLab/APIPark/common/enum"
"github.com/APIParkLab/APIPark/gateway"
)
const apintoRestfulRegexp = "([0-9a-zA-Z-_]+)"
type Router struct {
*BasicInfo
Listen int `json:"listen"`
Host []string `json:"host"`
Method []string `json:"method"`
Protocols []string `json:"protocols"`
Location string `json:"location"`
Rules []*Rule `json:"rules"`
Service string `json:"service"`
Plugins map[string]*Plugin `json:"plugins"`
Retry int `json:"retry"`
TimeOut int `json:"time_out"`
Labels map[string]string `json:"labels"`
}
type Rule struct {
Type string `json:"type"`
Name string `json:"name"`
Value string `json:"value"`
}
type Plugin struct {
Disable bool `json:"disable"`
Config interface{} `json:"config"`
}
type PluginProxyRewriteV2Config struct {
PathType string `json:"path_type"`
StaticPath string `json:"static_path,omitempty"` //path_type=static启用
PrefixPath []*PrefixPath `json:"prefix_path,omitempty"` //path_type=prefix 启用
RegexPath []*RegexPath `json:"regex_path,omitempty"` //path_type=regex 启用
NotMatchErr bool `json:"not_match_err"`
HostRewrite bool `json:"host_rewrite"`
Host string `json:"host,omitempty"`
Headers map[string]string `json:"headers"`
}
type RegexPath struct {
RegexPathMatch string `json:"regex_path_match"`
RegexPathReplace string `json:"regex_path_replace"`
}
type PrefixPath struct {
PrefixPathMatch string `json:"prefix_path_match"`
PrefixPathReplace string `json:"prefix_path_replace"`
}
func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) *Router {
headers := make(map[string]string)
for _, h := range r.ProxyHeaders {
key := textproto.CanonicalMIMEHeaderKey(h.Key)
switch h.Opt {
case enum.HeaderOptTypeAdd:
headers[key] = h.Value
case enum.HeaderOptTypeDelete:
headers[key] = ""
}
}
rewritePlugin := PluginProxyRewriteV2Config{
NotMatchErr: true,
HostRewrite: false,
Headers: headers,
}
//若请求路径包含restful参数
if common.IsRestfulPath(r.Path) {
rewritePlugin.PathType = "regex" //正则替换
//如果转发路径包含restful参数
if common.IsRestfulPath(r.ProxyPath) {
r.ProxyPath = formatProxyPath(r.Path, r.ProxyPath)
}
rewritePlugin.RegexPath = []*RegexPath{
{
RegexPathMatch: fmt.Sprintf("^%s$", common.ReplaceRestfulPath(r.Path, apintoRestfulRegexp)),
RegexPathReplace: r.ProxyPath,
},
}
r.Path = fmt.Sprintf("~=^%s$", common.ReplaceRestfulPath(r.Path, apintoRestfulRegexp))
} else {
rewritePlugin.PathType = "prefix" //前缀替换
rewritePlugin.PrefixPath = []*PrefixPath{
{
PrefixPathMatch: strings.TrimSuffix(r.Path, "*"),
PrefixPathReplace: r.ProxyPath,
},
}
}
rules := make([]*Rule, 0, len(r.Rules))
for _, m := range r.Rules {
rule := &Rule{
Type: m.Position,
Name: m.Key,
Value: "",
}
if m.Position == enum.MatchPositionHeader {
rule.Name = textproto.CanonicalMIMEHeaderKey(rule.Name)
}
switch m.MatchType {
case enum.MatchTypeEqual:
rule.Value = m.Pattern
case enum.MatchTypePrefix:
rule.Value = fmt.Sprintf("%s*", m.Pattern)
case enum.MatchTypeSuffix:
rule.Value = fmt.Sprintf("*%s", m.Pattern)
case enum.MatchTypeSubstr:
rule.Value = fmt.Sprintf("*%s*", m.Pattern)
case enum.MatchTypeUnEqual:
rule.Value = fmt.Sprintf("!=%s", m.Pattern)
case enum.MatchTypeNull:
rule.Value = "$"
case enum.MatchTypeExist:
rule.Value = "**"
case enum.MatchTypeUnExist:
rule.Value = "!"
case enum.MatchTypeRegexp:
rule.Value = fmt.Sprintf("~=%s", m.Pattern)
case enum.MatchTypeRegexpG:
rule.Value = fmt.Sprintf("~*=%s", m.Pattern)
case enum.MatchTypeAny:
rule.Value = "*"
}
rules = append(rules, rule)
}
plugin := map[string]*Plugin{
"proxy_rewrite": {
Disable: false,
Config: rewritePlugin,
},
}
for k, v := range r.Plugins {
plugin[k] = &Plugin{
Disable: v.Disable,
Config: v.Config,
}
}
hosts := make([]string, 0, len(r.Host))
if len(r.Host) > 0 {
hosts = r.Host
}
labels := make(map[string]string)
if r.Labels != nil {
labels = r.Labels
}
router := &Router{
BasicInfo: &BasicInfo{
ID: fmt.Sprintf("%s@router", r.ID),
Name: r.ID,
Description: r.Description,
Driver: "http",
Version: version,
Matches: matches,
},
Host: hosts,
Method: r.Methods,
Location: r.Path,
Rules: rules,
Plugins: plugin,
Retry: r.Retry,
TimeOut: r.Timeout,
Labels: labels,
Protocols: []string{"http", "https"},
}
if r.Service != "" {
router.Service = fmt.Sprintf("%s@service", r.Service)
}
return router
}
// formatProxyPath 格式化转发路径上,用于转发重写插件正则替换 比如 请求路径 /path/{A}/{B} 原转发路径:/path/{B} 格式化后 新转发路径: /path/$2
func formatProxyPath(requestPath, proxyPath string) string {
restfulSet := make(map[string]string)
newProxyPath := proxyPath
rList := strings.Split(requestPath, "/")
i := 1
for _, param := range rList {
if common.IsRestfulParam(param) {
restfulSet[param] = fmt.Sprintf("$%d", i)
i += 1
}
}
for param, order := range restfulSet {
newProxyPath = strings.ReplaceAll(newProxyPath, param, order)
}
return newProxyPath
}