添加ExtensionMatcher缓存机制,优化缓存管理器和302跳转处理逻辑,增强规则服务集成,提升代码可读性和性能。

This commit is contained in:
wood chen 2025-06-02 07:18:40 +08:00
parent 1c9d5bc326
commit 605b26b883
6 changed files with 512 additions and 184 deletions

216
internal/cache/extension_matcher.go vendored Normal file
View File

@ -0,0 +1,216 @@
package cache
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"log"
"proxy-go/internal/config"
"proxy-go/internal/utils"
"sync"
"time"
)
// ExtensionMatcherCacheItem 扩展名匹配器缓存项
type ExtensionMatcherCacheItem struct {
Matcher *utils.ExtensionMatcher
Hash string // 配置的哈希值,用于检测配置变化
CreatedAt time.Time
LastUsed time.Time
UseCount int64
}
// ExtensionMatcherCache 扩展名匹配器缓存管理器
type ExtensionMatcherCache struct {
cache sync.Map
maxAge time.Duration
cleanupTick time.Duration
stopCleanup chan struct{}
mu sync.RWMutex
}
// NewExtensionMatcherCache 创建新的扩展名匹配器缓存
func NewExtensionMatcherCache() *ExtensionMatcherCache {
emc := &ExtensionMatcherCache{
maxAge: 10 * time.Minute, // 缓存10分钟
cleanupTick: 2 * time.Minute, // 每2分钟清理一次
stopCleanup: make(chan struct{}),
}
// 启动清理协程
go emc.startCleanup()
return emc
}
// generateConfigHash 生成配置的哈希值
func (emc *ExtensionMatcherCache) generateConfigHash(rules []config.ExtensionRule) string {
// 将规则序列化为JSON
data, err := json.Marshal(rules)
if err != nil {
// 如果序列化失败,使用时间戳作为哈希
return hex.EncodeToString([]byte(time.Now().String()))
}
// 计算SHA256哈希
hash := sha256.Sum256(data)
return hex.EncodeToString(hash[:])
}
// GetOrCreate 获取或创建扩展名匹配器
func (emc *ExtensionMatcherCache) GetOrCreate(pathKey string, rules []config.ExtensionRule) *utils.ExtensionMatcher {
// 如果没有规则,直接创建新的匹配器
if len(rules) == 0 {
return utils.NewExtensionMatcher(rules)
}
// 生成配置哈希
configHash := emc.generateConfigHash(rules)
// 尝试从缓存获取
if value, ok := emc.cache.Load(pathKey); ok {
item := value.(*ExtensionMatcherCacheItem)
// 检查配置是否变化
if item.Hash == configHash {
// 配置未变化,更新使用信息
emc.mu.Lock()
item.LastUsed = time.Now()
item.UseCount++
emc.mu.Unlock()
log.Printf("[ExtensionMatcherCache] HIT %s (使用次数: %d)", pathKey, item.UseCount)
return item.Matcher
} else {
// 配置已变化,删除旧缓存
emc.cache.Delete(pathKey)
log.Printf("[ExtensionMatcherCache] CONFIG_CHANGED %s", pathKey)
}
}
// 创建新的匹配器
matcher := utils.NewExtensionMatcher(rules)
// 创建缓存项
item := &ExtensionMatcherCacheItem{
Matcher: matcher,
Hash: configHash,
CreatedAt: time.Now(),
LastUsed: time.Now(),
UseCount: 1,
}
// 存储到缓存
emc.cache.Store(pathKey, item)
log.Printf("[ExtensionMatcherCache] NEW %s (规则数量: %d)", pathKey, len(rules))
return matcher
}
// InvalidatePath 使指定路径的缓存失效
func (emc *ExtensionMatcherCache) InvalidatePath(pathKey string) {
if _, ok := emc.cache.LoadAndDelete(pathKey); ok {
log.Printf("[ExtensionMatcherCache] INVALIDATED %s", pathKey)
}
}
// InvalidateAll 清空所有缓存
func (emc *ExtensionMatcherCache) InvalidateAll() {
count := 0
emc.cache.Range(func(key, value interface{}) bool {
emc.cache.Delete(key)
count++
return true
})
log.Printf("[ExtensionMatcherCache] INVALIDATED_ALL (清理了 %d 个缓存项)", count)
}
// GetStats 获取缓存统计信息
func (emc *ExtensionMatcherCache) GetStats() ExtensionMatcherCacheStats {
stats := ExtensionMatcherCacheStats{
MaxAge: int64(emc.maxAge.Minutes()),
CleanupTick: int64(emc.cleanupTick.Minutes()),
}
emc.cache.Range(func(key, value interface{}) bool {
item := value.(*ExtensionMatcherCacheItem)
stats.TotalItems++
stats.TotalUseCount += item.UseCount
// 计算平均年龄
age := time.Since(item.CreatedAt)
stats.AverageAge += int64(age.Minutes())
return true
})
if stats.TotalItems > 0 {
stats.AverageAge /= int64(stats.TotalItems)
}
return stats
}
// ExtensionMatcherCacheStats 扩展名匹配器缓存统计信息
type ExtensionMatcherCacheStats struct {
TotalItems int `json:"total_items"` // 缓存项数量
TotalUseCount int64 `json:"total_use_count"` // 总使用次数
AverageAge int64 `json:"average_age"` // 平均年龄(分钟)
MaxAge int64 `json:"max_age"` // 最大缓存时间(分钟)
CleanupTick int64 `json:"cleanup_tick"` // 清理间隔(分钟)
}
// startCleanup 启动清理协程
func (emc *ExtensionMatcherCache) startCleanup() {
ticker := time.NewTicker(emc.cleanupTick)
defer ticker.Stop()
for {
select {
case <-ticker.C:
emc.cleanup()
case <-emc.stopCleanup:
return
}
}
}
// cleanup 清理过期的缓存项
func (emc *ExtensionMatcherCache) cleanup() {
now := time.Now()
expiredKeys := make([]interface{}, 0)
// 收集过期的键
emc.cache.Range(func(key, value interface{}) bool {
item := value.(*ExtensionMatcherCacheItem)
if now.Sub(item.LastUsed) > emc.maxAge {
expiredKeys = append(expiredKeys, key)
}
return true
})
// 删除过期的缓存项
for _, key := range expiredKeys {
emc.cache.Delete(key)
}
if len(expiredKeys) > 0 {
log.Printf("[ExtensionMatcherCache] CLEANUP 清理了 %d 个过期缓存项", len(expiredKeys))
}
}
// Stop 停止缓存管理器
func (emc *ExtensionMatcherCache) Stop() {
close(emc.stopCleanup)
}
// UpdateConfig 更新缓存配置
func (emc *ExtensionMatcherCache) UpdateConfig(maxAge, cleanupTick time.Duration) {
emc.mu.Lock()
defer emc.mu.Unlock()
emc.maxAge = maxAge
emc.cleanupTick = cleanupTick
log.Printf("[ExtensionMatcherCache] CONFIG_UPDATED maxAge=%v cleanupTick=%v", maxAge, cleanupTick)
}

View File

@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"proxy-go/internal/config"
"proxy-go/internal/utils"
"sort"
"strings"
@ -80,6 +81,9 @@ type CacheManager struct {
bytesSaved atomic.Int64 // 节省的带宽
cleanupTimer *time.Ticker // 添加清理定时器
stopCleanup chan struct{} // 添加停止信号通道
// ExtensionMatcher缓存
extensionMatcherCache *ExtensionMatcherCache
}
// NewCacheManager 创建新的缓存管理器
@ -94,6 +98,9 @@ func NewCacheManager(cacheDir string) (*CacheManager, error) {
cleanupTick: 5 * time.Minute,
maxCacheSize: 10 * 1024 * 1024 * 1024, // 10GB
stopCleanup: make(chan struct{}),
// 初始化ExtensionMatcher缓存
extensionMatcherCache: NewExtensionMatcherCache(),
}
cm.enabled.Store(true) // 默认启用缓存
@ -591,3 +598,54 @@ func (cm *CacheManager) loadConfig() error {
return nil
}
// GetExtensionMatcher 获取缓存的ExtensionMatcher
func (cm *CacheManager) GetExtensionMatcher(pathKey string, rules []config.ExtensionRule) *utils.ExtensionMatcher {
if cm.extensionMatcherCache == nil {
return utils.NewExtensionMatcher(rules)
}
return cm.extensionMatcherCache.GetOrCreate(pathKey, rules)
}
// InvalidateExtensionMatcherPath 使指定路径的ExtensionMatcher缓存失效
func (cm *CacheManager) InvalidateExtensionMatcherPath(pathKey string) {
if cm.extensionMatcherCache != nil {
cm.extensionMatcherCache.InvalidatePath(pathKey)
}
}
// InvalidateAllExtensionMatchers 清空所有ExtensionMatcher缓存
func (cm *CacheManager) InvalidateAllExtensionMatchers() {
if cm.extensionMatcherCache != nil {
cm.extensionMatcherCache.InvalidateAll()
}
}
// GetExtensionMatcherStats 获取ExtensionMatcher缓存统计信息
func (cm *CacheManager) GetExtensionMatcherStats() ExtensionMatcherCacheStats {
if cm.extensionMatcherCache != nil {
return cm.extensionMatcherCache.GetStats()
}
return ExtensionMatcherCacheStats{}
}
// UpdateExtensionMatcherConfig 更新ExtensionMatcher缓存配置
func (cm *CacheManager) UpdateExtensionMatcherConfig(maxAge, cleanupTick time.Duration) {
if cm.extensionMatcherCache != nil {
cm.extensionMatcherCache.UpdateConfig(maxAge, cleanupTick)
}
}
// Stop 停止缓存管理器包括ExtensionMatcher缓存
func (cm *CacheManager) Stop() {
// 停止主缓存清理
if cm.cleanupTimer != nil {
cm.cleanupTimer.Stop()
}
close(cm.stopCleanup)
// 停止ExtensionMatcher缓存
if cm.extensionMatcherCache != nil {
cm.extensionMatcherCache.Stop()
}
}

View File

@ -11,6 +11,7 @@ import (
"proxy-go/internal/cache"
"proxy-go/internal/config"
"proxy-go/internal/metrics"
"proxy-go/internal/service"
"proxy-go/internal/utils"
"sort"
"strings"
@ -53,7 +54,8 @@ type ProxyHandler struct {
auth *authManager
errorHandler ErrorHandler
Cache *cache.CacheManager
redirectHandler *RedirectHandler // 添加302跳转处理器
redirectHandler *RedirectHandler // 添加302跳转处理器
ruleService *service.RuleService // 添加规则服务
}
// 前缀匹配器结构体
@ -154,6 +156,9 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
log.Printf("[Cache] Failed to initialize cache manager: %v", err)
}
// 初始化规则服务
ruleService := service.NewRuleService(cacheManager)
handler := &ProxyHandler{
pathMap: cfg.MAP,
prefixTree: newPrefixMatcher(cfg.MAP), // 初始化前缀匹配树
@ -171,7 +176,8 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
config: cfg,
auth: newAuthManager(),
Cache: cacheManager,
redirectHandler: NewRedirectHandler(), // 初始化302跳转处理器
ruleService: ruleService,
redirectHandler: NewRedirectHandler(ruleService), // 初始化302跳转处理器
errorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
log.Printf("[Error] %s %s -> %v from %s", r.Method, r.URL.Path, err, utils.GetRequestSource(r))
w.WriteHeader(http.StatusInternalServerError)
@ -246,7 +252,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// 使用统一的路由选择逻辑
targetBase, usedAltTarget := utils.GetTargetURL(h.client, r, pathConfig, decodedPath)
targetBase, usedAltTarget := h.ruleService.GetTargetURL(h.client, r, pathConfig, decodedPath)
// 重新编码路径,保留 '/'
parts := strings.Split(decodedPath, "/")

View File

@ -5,16 +5,21 @@ import (
"net/http"
"net/url"
"proxy-go/internal/config"
"proxy-go/internal/service"
"proxy-go/internal/utils"
"strings"
)
// RedirectHandler 处理302跳转逻辑
type RedirectHandler struct{}
type RedirectHandler struct {
ruleService *service.RuleService
}
// NewRedirectHandler 创建新的跳转处理器
func NewRedirectHandler() *RedirectHandler {
return &RedirectHandler{}
func NewRedirectHandler(ruleService *service.RuleService) *RedirectHandler {
return &RedirectHandler{
ruleService: ruleService,
}
}
// HandleRedirect 处理302跳转请求
@ -33,8 +38,8 @@ func (rh *RedirectHandler) HandleRedirect(w http.ResponseWriter, r *http.Request
// shouldRedirect 判断是否应该进行302跳转并返回目标URL优化版本
func (rh *RedirectHandler) shouldRedirect(r *http.Request, pathConfig config.PathConfig, targetPath string, client *http.Client) (bool, string) {
// 使用优化的规则选择函数
result := utils.SelectRuleForRedirect(client, pathConfig, targetPath)
// 使用service包的规则选择函数
result := rh.ruleService.SelectRuleForRedirect(client, pathConfig, targetPath)
if result.ShouldRedirect {
// 构建完整的目标URL

View File

@ -0,0 +1,217 @@
package service
import (
"fmt"
"log"
"net/http"
"proxy-go/internal/config"
"proxy-go/internal/utils"
"strings"
)
// RuleService 规则选择服务
type RuleService struct {
cacheManager CacheManager
}
// CacheManager 缓存管理器接口
type CacheManager interface {
GetExtensionMatcher(pathKey string, rules []config.ExtensionRule) *utils.ExtensionMatcher
}
// NewRuleService 创建规则选择服务
func NewRuleService(cacheManager CacheManager) *RuleService {
return &RuleService{
cacheManager: cacheManager,
}
}
// SelectBestRule 选择最合适的规则
func (rs *RuleService) SelectBestRule(client *http.Client, pathConfig config.PathConfig, path string) (*config.ExtensionRule, bool, bool) {
// 如果没有扩展名规则返回nil
if len(pathConfig.ExtRules) == 0 {
return nil, false, false
}
// 提取扩展名
ext := extractExtension(path)
var matcher *utils.ExtensionMatcher
// 尝试使用缓存管理器
if rs.cacheManager != nil {
pathKey := fmt.Sprintf("path_%p", &pathConfig)
matcher = rs.cacheManager.GetExtensionMatcher(pathKey, pathConfig.ExtRules)
} else {
// 直接创建新的匹配器
matcher = utils.NewExtensionMatcher(pathConfig.ExtRules)
}
// 获取匹配的规则
matchingRules := matcher.GetMatchingRules(ext)
if len(matchingRules) == 0 {
return nil, false, false
}
// 获取文件大小
contentLength, err := utils.GetFileSize(client, pathConfig.DefaultTarget+path)
if err != nil {
log.Printf("[SelectRule] %s -> 获取文件大小出错: %v", path, err)
// 如果无法获取文件大小,返回第一个匹配的规则
if len(matchingRules) > 0 {
log.Printf("[SelectRule] %s -> 基于扩展名直接匹配规则", path)
return matchingRules[0], true, true
}
return nil, false, false
}
// 根据文件大小找出最匹配的规则(规则已经预排序)
for _, rule := range matchingRules {
// 检查文件大小是否在阈值范围内
if contentLength >= rule.SizeThreshold && contentLength <= rule.MaxSize {
// 找到匹配的规则
log.Printf("[SelectRule] %s -> 选中规则 (文件大小: %s, 在区间 %s 到 %s 之间)",
path, utils.FormatBytes(contentLength),
utils.FormatBytes(rule.SizeThreshold), utils.FormatBytes(rule.MaxSize))
// 检查目标是否可访问
if utils.IsTargetAccessible(client, rule.Target+path) {
return rule, true, true
} else {
log.Printf("[SelectRule] %s -> 规则目标不可访问,继续查找", path)
// 继续查找下一个匹配的规则
continue
}
}
}
// 没有找到合适的规则
return nil, false, false
}
// RuleSelectionResult 规则选择结果
type RuleSelectionResult struct {
Rule *config.ExtensionRule
Found bool
UsedAltTarget bool
TargetURL string
ShouldRedirect bool
}
// SelectRuleForRedirect 专门为302跳转优化的规则选择函数
func (rs *RuleService) 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)
var matcher *utils.ExtensionMatcher
// 尝试使用缓存管理器
if rs.cacheManager != nil {
pathKey := fmt.Sprintf("redirect_%p", &pathConfig)
matcher = rs.cacheManager.GetExtensionMatcher(pathKey, pathConfig.ExtRules)
} else {
matcher = utils.NewExtensionMatcher(pathConfig.ExtRules)
}
// 快速检查如果没有任何302跳转规则跳过复杂逻辑
if !matcher.HasRedirectRule() {
return result
}
// 尝试选择最佳规则
if rule, found, usedAlt := rs.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 (rs *RuleService) GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathConfig, path string) (string, bool) {
// 默认使用默认目标
targetBase := pathConfig.DefaultTarget
usedAltTarget := false
// 如果没有扩展名规则,直接返回默认目标
if len(pathConfig.ExtRules) == 0 {
ext := extractExtension(path)
if ext == "" {
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
}
return targetBase, false
}
// 使用新的统一规则选择逻辑
rule, found, usedAlt := rs.SelectBestRule(client, pathConfig, path)
if found && rule != nil {
targetBase = rule.Target
usedAltTarget = usedAlt
log.Printf("[Route] %s -> %s (使用选中的规则)", path, targetBase)
} else {
// 如果无法获取文件大小,尝试使用扩展名直接匹配
ext := extractExtension(path)
if altTarget, exists := pathConfig.GetProcessedExtTarget(ext); exists {
usedAltTarget = true
targetBase = altTarget
log.Printf("[Route] %s -> %s (基于扩展名直接匹配)", path, targetBase)
} else if altTarget, exists := pathConfig.GetProcessedExtTarget("*"); exists {
// 尝试使用通配符
usedAltTarget = true
targetBase = altTarget
log.Printf("[Route] %s -> %s (基于通配符匹配)", path, targetBase)
}
}
return targetBase, usedAltTarget
}
// extractExtension 提取文件扩展名
func extractExtension(path string) string {
lastDotIndex := strings.LastIndex(path, ".")
if lastDotIndex > 0 && lastDotIndex < len(path)-1 {
return strings.ToLower(path[lastDotIndex+1:])
}
return ""
}

View File

@ -183,15 +183,6 @@ func GetFileSize(client *http.Client, url string) (int64, error) {
return resp.ContentLength, nil
}
// RuleSelectionResult 规则选择结果,用于缓存和传递结果
type RuleSelectionResult struct {
Rule *config.ExtensionRule
Found bool
UsedAltTarget bool
TargetURL string
ShouldRedirect bool
}
// ExtensionMatcher 扩展名匹配器,用于优化扩展名匹配性能
type ExtensionMatcher struct {
exactMatches map[string][]*config.ExtensionRule // 精确匹配的扩展名
@ -269,173 +260,8 @@ 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 ""
}
// SelectBestRule 根据文件大小和扩展名选择最合适的规则(优化版本)
// 返回值: (选中的规则, 是否找到匹配的规则, 是否使用了备用目标)
func SelectBestRule(client *http.Client, pathConfig config.PathConfig, path string) (*config.ExtensionRule, bool, bool) {
// 如果没有扩展名规则返回nil
if len(pathConfig.ExtRules) == 0 {
return nil, false, false
}
// 提取扩展名
ext := extractExtension(path)
// 创建扩展名匹配器(可以考虑缓存这个匹配器)
matcher := NewExtensionMatcher(pathConfig.ExtRules)
// 获取匹配的规则
matchingRules := matcher.GetMatchingRules(ext)
if len(matchingRules) == 0 {
return nil, false, false
}
// 获取文件大小
contentLength, err := GetFileSize(client, pathConfig.DefaultTarget+path)
if err != nil {
log.Printf("[SelectRule] %s -> 获取文件大小出错: %v", path, err)
// 如果无法获取文件大小,返回第一个匹配的规则
if len(matchingRules) > 0 {
log.Printf("[SelectRule] %s -> 基于扩展名直接匹配规则", path)
return matchingRules[0], true, true
}
return nil, false, false
}
// 根据文件大小找出最匹配的规则(规则已经预排序)
for _, rule := range matchingRules {
// 检查文件大小是否在阈值范围内
if contentLength >= rule.SizeThreshold && contentLength <= rule.MaxSize {
// 找到匹配的规则
log.Printf("[SelectRule] %s -> 选中规则 (文件大小: %s, 在区间 %s 到 %s 之间)",
path, FormatBytes(contentLength),
FormatBytes(rule.SizeThreshold), FormatBytes(rule.MaxSize))
// 检查目标是否可访问(使用带缓存的检查)
if isTargetAccessible(client, rule.Target+path) {
return rule, true, true
} else {
log.Printf("[SelectRule] %s -> 规则目标不可访问,继续查找", path)
// 继续查找下一个匹配的规则
continue
}
}
}
// 没有找到合适的规则
return nil, false, false
}
// 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 := NewExtensionMatcher(pathConfig.ExtRules)
// 快速检查如果没有任何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) {
// 默认使用默认目标
targetBase := pathConfig.DefaultTarget
usedAltTarget := false
// 如果没有扩展名规则,直接返回默认目标
if len(pathConfig.ExtRules) == 0 {
ext := extractExtension(path)
if ext == "" {
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
}
return targetBase, false
}
// 使用新的统一规则选择逻辑
rule, found, usedAlt := SelectBestRule(client, pathConfig, path)
if found && rule != nil {
targetBase = rule.Target
usedAltTarget = usedAlt
log.Printf("[Route] %s -> %s (使用选中的规则)", path, targetBase)
} else {
// 如果无法获取文件大小,尝试使用扩展名直接匹配(优化点)
ext := extractExtension(path)
if altTarget, exists := pathConfig.GetProcessedExtTarget(ext); exists {
usedAltTarget = true
targetBase = altTarget
log.Printf("[Route] %s -> %s (基于扩展名直接匹配)", path, targetBase)
} else if altTarget, exists := pathConfig.GetProcessedExtTarget("*"); exists {
// 尝试使用通配符
usedAltTarget = true
targetBase = altTarget
log.Printf("[Route] %s -> %s (基于通配符匹配)", path, targetBase)
}
}
return targetBase, usedAltTarget
}
// isTargetAccessible 检查目标URL是否可访问
func isTargetAccessible(client *http.Client, targetURL string) bool {
// IsTargetAccessible 检查目标URL是否可访问
func IsTargetAccessible(client *http.Client, targetURL string) bool {
// 先查缓存
if cache, ok := accessCache.Load(targetURL); ok {
cacheItem := cache.(accessibilityCache)