mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-18 16:41:54 +08:00
218 lines
6.3 KiB
Go
218 lines
6.3 KiB
Go
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 ""
|
||
}
|