mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-19 08:51:55 +08:00
添加扩展名匹配器和缓存机制,优化302跳转规则选择逻辑,增强缓存统计功能,确保配置更新时清理缓存以提高性能和准确性。
This commit is contained in:
parent
8dd410fad4
commit
9e45b3e38a
@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -14,6 +15,11 @@ type PathConfig struct {
|
|||||||
ExtensionMap []ExtRuleConfig `json:"ExtensionMap"` // 扩展名映射规则
|
ExtensionMap []ExtRuleConfig `json:"ExtensionMap"` // 扩展名映射规则
|
||||||
ExtRules []ExtensionRule `json:"-"` // 内部使用,存储处理后的扩展名规则
|
ExtRules []ExtensionRule `json:"-"` // 内部使用,存储处理后的扩展名规则
|
||||||
RedirectMode bool `json:"RedirectMode"` // 是否使用302跳转模式
|
RedirectMode bool `json:"RedirectMode"` // 是否使用302跳转模式
|
||||||
|
|
||||||
|
// 缓存相关字段(不参与JSON序列化)
|
||||||
|
matcherCache interface{} `json:"-"` // 缓存的ExtensionMatcher,使用interface{}避免循环导入
|
||||||
|
matcherCacheMux sync.RWMutex `json:"-"` // 缓存读写锁
|
||||||
|
cacheValid bool `json:"-"` // 缓存是否有效
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtensionRule 表示一个扩展名映射规则(内部使用)
|
// ExtensionRule 表示一个扩展名映射规则(内部使用)
|
||||||
@ -73,6 +79,32 @@ func (p *PathConfig) ProcessExtensionMap() {
|
|||||||
p.ExtRules = append(p.ExtRules, extRule)
|
p.ExtRules = append(p.ExtRules, extRule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清除缓存,因为规则已经改变
|
||||||
|
p.InvalidateMatcherCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidateMatcherCache 清除ExtensionMatcher缓存
|
||||||
|
func (p *PathConfig) InvalidateMatcherCache() {
|
||||||
|
p.matcherCacheMux.Lock()
|
||||||
|
defer p.matcherCacheMux.Unlock()
|
||||||
|
p.matcherCache = nil
|
||||||
|
p.cacheValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMatcherCache 获取缓存的ExtensionMatcher
|
||||||
|
func (p *PathConfig) GetMatcherCache() (interface{}, bool) {
|
||||||
|
p.matcherCacheMux.RLock()
|
||||||
|
defer p.matcherCacheMux.RUnlock()
|
||||||
|
return p.matcherCache, p.cacheValid
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMatcherCache 设置ExtensionMatcher缓存
|
||||||
|
func (p *PathConfig) SetMatcherCache(matcher interface{}) {
|
||||||
|
p.matcherCacheMux.Lock()
|
||||||
|
defer p.matcherCacheMux.Unlock()
|
||||||
|
p.matcherCache = matcher
|
||||||
|
p.cacheValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProcessedExtTarget 快速获取扩展名对应的目标URL,如果存在返回true
|
// GetProcessedExtTarget 快速获取扩展名对应的目标URL,如果存在返回true
|
||||||
|
@ -182,6 +182,10 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
|||||||
// 注册配置更新回调
|
// 注册配置更新回调
|
||||||
config.RegisterUpdateCallback(func(newCfg *config.Config) {
|
config.RegisterUpdateCallback(func(newCfg *config.Config) {
|
||||||
// 注意:config包已经在回调触发前处理了所有ExtRules,这里无需再次处理
|
// 注意:config包已经在回调触发前处理了所有ExtRules,这里无需再次处理
|
||||||
|
|
||||||
|
// 清理所有ExtensionMatcher缓存,因为配置已更新
|
||||||
|
utils.ClearAllMatcherCaches(newCfg.MAP)
|
||||||
|
|
||||||
handler.pathMap = newCfg.MAP
|
handler.pathMap = newCfg.MAP
|
||||||
handler.prefixTree.update(newCfg.MAP) // 更新前缀匹配树
|
handler.prefixTree.update(newCfg.MAP) // 更新前缀匹配树
|
||||||
handler.config = newCfg
|
handler.config = newCfg
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
|
||||||
"proxy-go/internal/config"
|
"proxy-go/internal/config"
|
||||||
"proxy-go/internal/utils"
|
"proxy-go/internal/utils"
|
||||||
"strings"
|
"strings"
|
||||||
@ -32,43 +31,21 @@ func (rh *RedirectHandler) HandleRedirect(w http.ResponseWriter, r *http.Request
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldRedirect 判断是否应该进行302跳转,并返回目标URL
|
// shouldRedirect 判断是否应该进行302跳转,并返回目标URL(优化版本)
|
||||||
func (rh *RedirectHandler) shouldRedirect(r *http.Request, pathConfig config.PathConfig, targetPath string, client *http.Client) (bool, string) {
|
func (rh *RedirectHandler) shouldRedirect(r *http.Request, pathConfig config.PathConfig, targetPath string, client *http.Client) (bool, string) {
|
||||||
// 获取文件扩展名
|
// 使用优化的规则选择函数
|
||||||
ext := strings.ToLower(filepath.Ext(targetPath))
|
result := utils.SelectRuleForRedirect(client, pathConfig, targetPath)
|
||||||
if ext != "" {
|
|
||||||
ext = ext[1:] // 去掉点号
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用统一的规则选择逻辑,考虑文件大小
|
if result.ShouldRedirect {
|
||||||
if rule, found, _ := utils.SelectBestRule(client, pathConfig, targetPath); found && rule != nil && rule.RedirectMode {
|
// 构建完整的目标URL
|
||||||
// 使用选中规则的目标URL进行302跳转
|
targetURL := rh.buildTargetURL(result.TargetURL, targetPath, r.URL.RawQuery)
|
||||||
targetURL := rh.buildTargetURL(rule.Target, targetPath, r.URL.RawQuery)
|
|
||||||
log.Printf("[Redirect] %s -> 使用选中规则进行302跳转: %s", targetPath, targetURL)
|
|
||||||
return true, targetURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查默认目标是否配置为302跳转
|
if result.Rule != nil {
|
||||||
if pathConfig.RedirectMode {
|
log.Printf("[Redirect] %s -> 使用选中规则进行302跳转: %s", targetPath, targetURL)
|
||||||
// 使用默认目标URL进行302跳转
|
} else {
|
||||||
targetURL := rh.buildTargetURL(pathConfig.DefaultTarget, targetPath, r.URL.RawQuery)
|
log.Printf("[Redirect] %s -> 使用默认目标进行302跳转: %s", targetPath, targetURL)
|
||||||
log.Printf("[Redirect] %s -> 使用默认目标进行302跳转: %s", targetPath, targetURL)
|
}
|
||||||
return true, targetURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果默认目标没有配置302跳转,检查是否有简单的扩展名匹配(向后兼容)
|
|
||||||
if rule, found := pathConfig.GetProcessedExtRule(ext); found && rule.RedirectMode {
|
|
||||||
// 使用扩展名规则的目标URL进行302跳转
|
|
||||||
targetURL := rh.buildTargetURL(rule.Target, targetPath, r.URL.RawQuery)
|
|
||||||
log.Printf("[Redirect] %s -> 使用扩展名规则进行302跳转: %s", targetPath, targetURL)
|
|
||||||
return true, targetURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查通配符规则
|
|
||||||
if rule, found := pathConfig.GetProcessedExtRule("*"); found && rule.RedirectMode {
|
|
||||||
// 使用通配符规则的目标URL进行302跳转
|
|
||||||
targetURL := rh.buildTargetURL(rule.Target, targetPath, r.URL.RawQuery)
|
|
||||||
log.Printf("[Redirect] %s -> 使用通配符规则进行302跳转: %s", targetPath, targetURL)
|
|
||||||
return true, targetURL
|
return true, targetURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@ type OldPathConfig struct {
|
|||||||
SizeThreshold int64 `json:"SizeThreshold,omitempty"`
|
SizeThreshold int64 `json:"SizeThreshold,omitempty"`
|
||||||
MaxSize int64 `json:"MaxSize,omitempty"`
|
MaxSize int64 `json:"MaxSize,omitempty"`
|
||||||
Path string `json:"Path,omitempty"`
|
Path string `json:"Path,omitempty"`
|
||||||
|
RedirectMode bool `json:"RedirectMode,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新配置结构
|
// 新配置结构
|
||||||
type NewPathConfig struct {
|
type NewPathConfig struct {
|
||||||
DefaultTarget string `json:"DefaultTarget"`
|
DefaultTarget string `json:"DefaultTarget"`
|
||||||
ExtensionMap []ExtRuleConfig `json:"ExtensionMap"`
|
ExtensionMap []ExtRuleConfig `json:"ExtensionMap"`
|
||||||
|
RedirectMode bool `json:"RedirectMode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtRuleConfig struct {
|
type ExtRuleConfig struct {
|
||||||
@ -26,6 +28,7 @@ type ExtRuleConfig struct {
|
|||||||
Target string `json:"Target"`
|
Target string `json:"Target"`
|
||||||
SizeThreshold int64 `json:"SizeThreshold"`
|
SizeThreshold int64 `json:"SizeThreshold"`
|
||||||
MaxSize int64 `json:"MaxSize"`
|
MaxSize int64 `json:"MaxSize"`
|
||||||
|
RedirectMode bool `json:"RedirectMode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompressionConfig struct {
|
type CompressionConfig struct {
|
||||||
@ -79,6 +82,7 @@ func MigrateConfig(configPath string) error {
|
|||||||
newPathConfig := NewPathConfig{
|
newPathConfig := NewPathConfig{
|
||||||
DefaultTarget: oldPathConfig.DefaultTarget,
|
DefaultTarget: oldPathConfig.DefaultTarget,
|
||||||
ExtensionMap: []ExtRuleConfig{},
|
ExtensionMap: []ExtRuleConfig{},
|
||||||
|
RedirectMode: oldPathConfig.RedirectMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查ExtensionMap类型
|
// 检查ExtensionMap类型
|
||||||
@ -94,6 +98,7 @@ func MigrateConfig(configPath string) error {
|
|||||||
Target: target,
|
Target: target,
|
||||||
SizeThreshold: oldPathConfig.SizeThreshold,
|
SizeThreshold: oldPathConfig.SizeThreshold,
|
||||||
MaxSize: oldPathConfig.MaxSize,
|
MaxSize: oldPathConfig.MaxSize,
|
||||||
|
RedirectMode: oldPathConfig.RedirectMode,
|
||||||
}
|
}
|
||||||
newPathConfig.ExtensionMap = append(newPathConfig.ExtensionMap, rule)
|
newPathConfig.ExtensionMap = append(newPathConfig.ExtensionMap, rule)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +38,10 @@ var (
|
|||||||
cacheTTL = 5 * time.Minute
|
cacheTTL = 5 * time.Minute
|
||||||
accessTTL = 30 * time.Second
|
accessTTL = 30 * time.Second
|
||||||
maxCacheSize = 10000 // 最大缓存条目数
|
maxCacheSize = 10000 // 最大缓存条目数
|
||||||
|
|
||||||
|
// 缓存统计
|
||||||
|
matcherCacheHits int64
|
||||||
|
matcherCacheMisses int64
|
||||||
)
|
)
|
||||||
|
|
||||||
// 清理过期缓存
|
// 清理过期缓存
|
||||||
@ -81,6 +86,19 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// 定期打印缓存统计信息
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(5 * time.Minute)
|
||||||
|
for range ticker.C {
|
||||||
|
hits, misses := GetMatcherCacheStats()
|
||||||
|
if hits+misses > 0 {
|
||||||
|
hitRate := float64(hits) / float64(hits+misses) * 100
|
||||||
|
log.Printf("[Cache] ExtensionMatcher stats: hits=%d, misses=%d, hit_rate=%.2f%%",
|
||||||
|
hits, misses, hitRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateRequestID 生成唯一的请求ID
|
// GenerateRequestID 生成唯一的请求ID
|
||||||
@ -183,104 +201,168 @@ func GetFileSize(client *http.Client, url string) (int64, error) {
|
|||||||
return resp.ContentLength, nil
|
return resp.ContentLength, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectBestRule 根据文件大小和扩展名选择最合适的规则
|
// RuleSelectionResult 规则选择结果,用于缓存和传递结果
|
||||||
// 返回值: (选中的规则, 是否找到匹配的规则, 是否使用了备用目标)
|
type RuleSelectionResult struct {
|
||||||
func SelectBestRule(client *http.Client, pathConfig config.PathConfig, path string) (*config.ExtensionRule, bool, bool) {
|
Rule *config.ExtensionRule
|
||||||
// 获取文件扩展名(使用优化的字符串处理)
|
Found bool
|
||||||
ext := ""
|
UsedAltTarget bool
|
||||||
lastDotIndex := strings.LastIndex(path, ".")
|
TargetURL string
|
||||||
if lastDotIndex > 0 && lastDotIndex < len(path)-1 {
|
ShouldRedirect bool
|
||||||
ext = strings.ToLower(path[lastDotIndex+1:])
|
}
|
||||||
|
|
||||||
|
// ExtensionMatcher 扩展名匹配器,用于优化扩展名匹配性能
|
||||||
|
type ExtensionMatcher struct {
|
||||||
|
exactMatches map[string][]*config.ExtensionRule // 精确匹配的扩展名
|
||||||
|
wildcardRules []*config.ExtensionRule // 通配符规则
|
||||||
|
hasRedirectRule bool // 是否有任何302跳转规则
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewExtensionMatcher 创建扩展名匹配器
|
||||||
|
func NewExtensionMatcher(rules []config.ExtensionRule) *ExtensionMatcher {
|
||||||
|
matcher := &ExtensionMatcher{
|
||||||
|
exactMatches: make(map[string][]*config.ExtensionRule),
|
||||||
|
wildcardRules: make([]*config.ExtensionRule, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := range rules {
|
||||||
|
rule := &rules[i]
|
||||||
|
|
||||||
|
// 处理阈值默认值
|
||||||
|
if rule.SizeThreshold < 0 {
|
||||||
|
rule.SizeThreshold = 0
|
||||||
|
}
|
||||||
|
if rule.MaxSize <= 0 {
|
||||||
|
rule.MaxSize = 1<<63 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有302跳转规则
|
||||||
|
if rule.RedirectMode {
|
||||||
|
matcher.hasRedirectRule = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分类存储规则
|
||||||
|
for _, ext := range rule.Extensions {
|
||||||
|
if ext == "*" {
|
||||||
|
matcher.wildcardRules = append(matcher.wildcardRules, rule)
|
||||||
|
} else {
|
||||||
|
if matcher.exactMatches[ext] == nil {
|
||||||
|
matcher.exactMatches[ext] = make([]*config.ExtensionRule, 0, 1)
|
||||||
|
}
|
||||||
|
matcher.exactMatches[ext] = append(matcher.exactMatches[ext], rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预排序所有规则组
|
||||||
|
for ext := range matcher.exactMatches {
|
||||||
|
sortRulesByThreshold(matcher.exactMatches[ext])
|
||||||
|
}
|
||||||
|
sortRulesByThreshold(matcher.wildcardRules)
|
||||||
|
|
||||||
|
return matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortRulesByThreshold 按阈值排序规则
|
||||||
|
func sortRulesByThreshold(rules []*config.ExtensionRule) {
|
||||||
|
sort.Slice(rules, func(i, j int) bool {
|
||||||
|
if rules[i].SizeThreshold == rules[j].SizeThreshold {
|
||||||
|
return rules[i].MaxSize > rules[j].MaxSize
|
||||||
|
}
|
||||||
|
return rules[i].SizeThreshold < rules[j].SizeThreshold
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMatchingRules 获取匹配的规则
|
||||||
|
func (em *ExtensionMatcher) GetMatchingRules(ext string) []*config.ExtensionRule {
|
||||||
|
// 先查找精确匹配
|
||||||
|
if rules, exists := em.exactMatches[ext]; exists {
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
// 返回通配符规则
|
||||||
|
return em.wildcardRules
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasRedirectRule 检查是否有任何302跳转规则
|
||||||
|
func (em *ExtensionMatcher) HasRedirectRule() bool {
|
||||||
|
return em.hasRedirectRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractExtension 提取文件扩展名(优化版本)
|
||||||
|
func extractExtension(path string) string {
|
||||||
|
lastDotIndex := strings.LastIndex(path, ".")
|
||||||
|
if lastDotIndex > 0 && lastDotIndex < len(path)-1 {
|
||||||
|
return strings.ToLower(path[lastDotIndex+1:])
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMatcherCacheStats 获取缓存统计信息
|
||||||
|
func GetMatcherCacheStats() (hits, misses int64) {
|
||||||
|
return atomic.LoadInt64(&matcherCacheHits), atomic.LoadInt64(&matcherCacheMisses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetMatcherCacheStats 重置缓存统计
|
||||||
|
func ResetMatcherCacheStats() {
|
||||||
|
atomic.StoreInt64(&matcherCacheHits, 0)
|
||||||
|
atomic.StoreInt64(&matcherCacheMisses, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCreateExtensionMatcher 获取或创建ExtensionMatcher(带缓存和统计)
|
||||||
|
func getOrCreateExtensionMatcher(pathConfig *config.PathConfig) *ExtensionMatcher {
|
||||||
|
// 尝试从缓存获取
|
||||||
|
if cached, valid := pathConfig.GetMatcherCache(); valid && cached != nil {
|
||||||
|
if matcher, ok := cached.(*ExtensionMatcher); ok {
|
||||||
|
atomic.AddInt64(&matcherCacheHits, 1)
|
||||||
|
return matcher
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存未命中,创建新的匹配器
|
||||||
|
atomic.AddInt64(&matcherCacheMisses, 1)
|
||||||
|
matcher := NewExtensionMatcher(pathConfig.ExtRules)
|
||||||
|
|
||||||
|
// 存储到缓存
|
||||||
|
pathConfig.SetMatcherCache(matcher)
|
||||||
|
|
||||||
|
log.Printf("[Cache] ExtensionMatcher created and cached for %d rules", len(pathConfig.ExtRules))
|
||||||
|
|
||||||
|
return matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectBestRule 根据文件大小和扩展名选择最合适的规则(优化版本,带缓存)
|
||||||
|
// 返回值: (选中的规则, 是否找到匹配的规则, 是否使用了备用目标)
|
||||||
|
func SelectBestRule(client *http.Client, pathConfig config.PathConfig, path string) (*config.ExtensionRule, bool, bool) {
|
||||||
// 如果没有扩展名规则,返回nil
|
// 如果没有扩展名规则,返回nil
|
||||||
if len(pathConfig.ExtRules) == 0 {
|
if len(pathConfig.ExtRules) == 0 {
|
||||||
return nil, false, false
|
return nil, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取扩展名
|
||||||
|
ext := extractExtension(path)
|
||||||
|
|
||||||
|
// 获取或创建缓存的扩展名匹配器
|
||||||
|
matcher := getOrCreateExtensionMatcher(&pathConfig)
|
||||||
|
|
||||||
|
// 获取匹配的规则
|
||||||
|
matchingRules := matcher.GetMatchingRules(ext)
|
||||||
|
if len(matchingRules) == 0 {
|
||||||
|
return nil, false, false
|
||||||
|
}
|
||||||
|
|
||||||
// 获取文件大小
|
// 获取文件大小
|
||||||
contentLength, err := GetFileSize(client, pathConfig.DefaultTarget+path)
|
contentLength, err := GetFileSize(client, pathConfig.DefaultTarget+path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[SelectRule] %s -> 获取文件大小出错: %v", path, err)
|
log.Printf("[SelectRule] %s -> 获取文件大小出错: %v", path, err)
|
||||||
|
// 如果无法获取文件大小,返回第一个匹配的规则
|
||||||
// 如果无法获取文件大小,尝试使用扩展名直接匹配
|
if len(matchingRules) > 0 {
|
||||||
for _, rule := range pathConfig.ExtRules {
|
log.Printf("[SelectRule] %s -> 基于扩展名直接匹配规则", path)
|
||||||
// 检查具体扩展名匹配
|
return matchingRules[0], true, true
|
||||||
for _, e := range rule.Extensions {
|
|
||||||
if e == ext {
|
|
||||||
log.Printf("[SelectRule] %s -> 基于扩展名直接匹配规则", path)
|
|
||||||
return &rule, true, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试使用通配符规则
|
|
||||||
for _, rule := range pathConfig.ExtRules {
|
|
||||||
for _, e := range rule.Extensions {
|
|
||||||
if e == "*" {
|
|
||||||
log.Printf("[SelectRule] %s -> 基于通配符匹配规则", path)
|
|
||||||
return &rule, true, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, false, false
|
return nil, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取匹配的扩展名规则
|
// 根据文件大小找出最匹配的规则(规则已经预排序)
|
||||||
matchingRules := []config.ExtensionRule{}
|
for _, rule := range matchingRules {
|
||||||
wildcardRules := []config.ExtensionRule{} // 存储通配符规则
|
|
||||||
|
|
||||||
// 找出所有匹配当前扩展名的规则
|
|
||||||
for _, rule := range pathConfig.ExtRules {
|
|
||||||
// 处理阈值默认值
|
|
||||||
if rule.SizeThreshold < 0 {
|
|
||||||
rule.SizeThreshold = 0 // 默认不限制
|
|
||||||
}
|
|
||||||
|
|
||||||
if rule.MaxSize <= 0 {
|
|
||||||
rule.MaxSize = 1<<63 - 1 // 设置为最大值表示不限制
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否包含通配符
|
|
||||||
for _, e := range rule.Extensions {
|
|
||||||
if e == "*" {
|
|
||||||
wildcardRules = append(wildcardRules, rule)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查具体扩展名匹配
|
|
||||||
for _, e := range rule.Extensions {
|
|
||||||
if e == ext {
|
|
||||||
matchingRules = append(matchingRules, rule)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有找到匹配的具体扩展名规则,使用通配符规则
|
|
||||||
if len(matchingRules) == 0 {
|
|
||||||
if len(wildcardRules) > 0 {
|
|
||||||
log.Printf("[SelectRule] %s -> 使用通配符规则", path)
|
|
||||||
matchingRules = wildcardRules
|
|
||||||
} else {
|
|
||||||
return nil, false, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按阈值排序规则(优化点:使用更高效的排序)
|
|
||||||
sort.Slice(matchingRules, func(i, j int) bool {
|
|
||||||
if matchingRules[i].SizeThreshold == matchingRules[j].SizeThreshold {
|
|
||||||
return matchingRules[i].MaxSize > matchingRules[j].MaxSize
|
|
||||||
}
|
|
||||||
return matchingRules[i].SizeThreshold < matchingRules[j].SizeThreshold
|
|
||||||
})
|
|
||||||
|
|
||||||
// 根据文件大小找出最匹配的规则
|
|
||||||
for i := range matchingRules {
|
|
||||||
rule := &matchingRules[i]
|
|
||||||
|
|
||||||
// 检查文件大小是否在阈值范围内
|
// 检查文件大小是否在阈值范围内
|
||||||
if contentLength >= rule.SizeThreshold && contentLength <= rule.MaxSize {
|
if contentLength >= rule.SizeThreshold && contentLength <= rule.MaxSize {
|
||||||
// 找到匹配的规则
|
// 找到匹配的规则
|
||||||
@ -303,33 +385,84 @@ func SelectBestRule(client *http.Client, pathConfig config.PathConfig, path stri
|
|||||||
return nil, false, false
|
return nil, false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTargetURL 根据路径和配置决定目标URL
|
// SelectRuleForRedirect 专门为302跳转优化的规则选择函数(带缓存)
|
||||||
|
func SelectRuleForRedirect(client *http.Client, pathConfig config.PathConfig, path string) *RuleSelectionResult {
|
||||||
|
result := &RuleSelectionResult{}
|
||||||
|
|
||||||
|
// 快速检查:如果没有任何302跳转配置,直接返回
|
||||||
|
if !pathConfig.RedirectMode && len(pathConfig.ExtRules) == 0 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果默认目标配置了302跳转,优先使用
|
||||||
|
if pathConfig.RedirectMode {
|
||||||
|
result.Found = true
|
||||||
|
result.ShouldRedirect = true
|
||||||
|
result.TargetURL = pathConfig.DefaultTarget
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查扩展名规则
|
||||||
|
if len(pathConfig.ExtRules) > 0 {
|
||||||
|
ext := extractExtension(path)
|
||||||
|
|
||||||
|
// 获取或创建缓存的扩展名匹配器
|
||||||
|
matcher := getOrCreateExtensionMatcher(&pathConfig)
|
||||||
|
|
||||||
|
// 快速检查:如果没有任何302跳转规则,跳过复杂逻辑
|
||||||
|
if !matcher.HasRedirectRule() {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试选择最佳规则
|
||||||
|
if rule, found, usedAlt := SelectBestRule(client, pathConfig, path); found && rule != nil && rule.RedirectMode {
|
||||||
|
result.Rule = rule
|
||||||
|
result.Found = found
|
||||||
|
result.UsedAltTarget = usedAlt
|
||||||
|
result.ShouldRedirect = true
|
||||||
|
result.TargetURL = rule.Target
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退到简单的扩展名匹配
|
||||||
|
if rule, found := pathConfig.GetProcessedExtRule(ext); found && rule.RedirectMode {
|
||||||
|
result.Rule = rule
|
||||||
|
result.Found = found
|
||||||
|
result.UsedAltTarget = true
|
||||||
|
result.ShouldRedirect = true
|
||||||
|
result.TargetURL = rule.Target
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查通配符规则
|
||||||
|
if rule, found := pathConfig.GetProcessedExtRule("*"); found && rule.RedirectMode {
|
||||||
|
result.Rule = rule
|
||||||
|
result.Found = found
|
||||||
|
result.UsedAltTarget = true
|
||||||
|
result.ShouldRedirect = true
|
||||||
|
result.TargetURL = rule.Target
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTargetURL 根据路径和配置决定目标URL(优化版本)
|
||||||
func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathConfig, path string) (string, bool) {
|
func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathConfig, path string) (string, bool) {
|
||||||
// 默认使用默认目标
|
// 默认使用默认目标
|
||||||
targetBase := pathConfig.DefaultTarget
|
targetBase := pathConfig.DefaultTarget
|
||||||
usedAltTarget := false
|
usedAltTarget := false
|
||||||
|
|
||||||
// 获取文件扩展名(使用优化的字符串处理)
|
|
||||||
ext := ""
|
|
||||||
lastDotIndex := strings.LastIndex(path, ".")
|
|
||||||
if lastDotIndex > 0 && lastDotIndex < len(path)-1 {
|
|
||||||
ext = strings.ToLower(path[lastDotIndex+1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有扩展名规则,直接返回默认目标
|
// 如果没有扩展名规则,直接返回默认目标
|
||||||
if len(pathConfig.ExtRules) == 0 {
|
if len(pathConfig.ExtRules) == 0 {
|
||||||
|
ext := extractExtension(path)
|
||||||
if ext == "" {
|
if ext == "" {
|
||||||
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
|
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
|
||||||
}
|
}
|
||||||
return targetBase, false
|
return targetBase, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保有扩展名规则
|
|
||||||
if ext == "" {
|
|
||||||
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
|
|
||||||
// 即使没有扩展名,也要尝试匹配 * 通配符规则
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用新的统一规则选择逻辑
|
// 使用新的统一规则选择逻辑
|
||||||
rule, found, usedAlt := SelectBestRule(client, pathConfig, path)
|
rule, found, usedAlt := SelectBestRule(client, pathConfig, path)
|
||||||
if found && rule != nil {
|
if found && rule != nil {
|
||||||
@ -338,6 +471,7 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
|||||||
log.Printf("[Route] %s -> %s (使用选中的规则)", path, targetBase)
|
log.Printf("[Route] %s -> %s (使用选中的规则)", path, targetBase)
|
||||||
} else {
|
} else {
|
||||||
// 如果无法获取文件大小,尝试使用扩展名直接匹配(优化点)
|
// 如果无法获取文件大小,尝试使用扩展名直接匹配(优化点)
|
||||||
|
ext := extractExtension(path)
|
||||||
if altTarget, exists := pathConfig.GetProcessedExtTarget(ext); exists {
|
if altTarget, exists := pathConfig.GetProcessedExtTarget(ext); exists {
|
||||||
usedAltTarget = true
|
usedAltTarget = true
|
||||||
targetBase = altTarget
|
targetBase = altTarget
|
||||||
@ -457,3 +591,15 @@ func ParseInt(s string, defaultValue int) int {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClearAllMatcherCaches 清理所有ExtensionMatcher缓存(用于配置更新时)
|
||||||
|
func ClearAllMatcherCaches(configMap map[string]config.PathConfig) {
|
||||||
|
cleared := 0
|
||||||
|
for path := range configMap {
|
||||||
|
pathConfig := configMap[path]
|
||||||
|
pathConfig.InvalidateMatcherCache()
|
||||||
|
configMap[path] = pathConfig
|
||||||
|
cleared++
|
||||||
|
}
|
||||||
|
log.Printf("[Cache] Cleared %d ExtensionMatcher caches due to config update", cleared)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user