mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-18 00:21:56 +08:00
增强路径匹配逻辑,添加前缀匹配器以提高性能,同时优化请求头设置和扩展名处理,确保代理请求的兼容性和稳定性。
This commit is contained in:
parent
c2266a60d6
commit
1d84c0c614
@ -70,3 +70,20 @@ func (p *PathConfig) ProcessExtensionMap() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetProcessedExtTarget 快速获取扩展名对应的目标URL,如果存在返回true
|
||||
func (p *PathConfig) GetProcessedExtTarget(ext string) (string, bool) {
|
||||
if p.ExtRules == nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
for _, rule := range p.ExtRules {
|
||||
for _, e := range rule.Extensions {
|
||||
if e == ext {
|
||||
return rule.Target, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"proxy-go/internal/config"
|
||||
"proxy-go/internal/metrics"
|
||||
"proxy-go/internal/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -22,9 +23,9 @@ const (
|
||||
// 超时时间常量
|
||||
clientConnTimeout = 10 * time.Second
|
||||
proxyRespTimeout = 60 * time.Second
|
||||
backendServTimeout = 40 * time.Second
|
||||
idleConnTimeout = 120 * time.Second
|
||||
tlsHandshakeTimeout = 10 * time.Second
|
||||
backendServTimeout = 30 * time.Second
|
||||
idleConnTimeout = 90 * time.Second
|
||||
tlsHandshakeTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// 添加 hop-by-hop 头部映射
|
||||
@ -45,6 +46,7 @@ type ErrorHandler func(w http.ResponseWriter, r *http.Request, err error)
|
||||
|
||||
type ProxyHandler struct {
|
||||
pathMap map[string]config.PathConfig
|
||||
prefixTree *prefixMatcher // 添加前缀匹配树
|
||||
client *http.Client
|
||||
startTime time.Time
|
||||
config *config.Config
|
||||
@ -53,6 +55,64 @@ type ProxyHandler struct {
|
||||
Cache *cache.CacheManager
|
||||
}
|
||||
|
||||
// 前缀匹配器结构体
|
||||
type prefixMatcher struct {
|
||||
prefixes []string
|
||||
configs map[string]config.PathConfig
|
||||
}
|
||||
|
||||
// 创建新的前缀匹配器
|
||||
func newPrefixMatcher(pathMap map[string]config.PathConfig) *prefixMatcher {
|
||||
pm := &prefixMatcher{
|
||||
prefixes: make([]string, 0, len(pathMap)),
|
||||
configs: make(map[string]config.PathConfig, len(pathMap)),
|
||||
}
|
||||
|
||||
// 按长度降序排列前缀,确保最长匹配优先
|
||||
for prefix, cfg := range pathMap {
|
||||
pm.prefixes = append(pm.prefixes, prefix)
|
||||
pm.configs[prefix] = cfg
|
||||
}
|
||||
|
||||
// 按长度降序排列
|
||||
sort.Slice(pm.prefixes, func(i, j int) bool {
|
||||
return len(pm.prefixes[i]) > len(pm.prefixes[j])
|
||||
})
|
||||
|
||||
return pm
|
||||
}
|
||||
|
||||
// 根据路径查找匹配的前缀和配置
|
||||
func (pm *prefixMatcher) match(path string) (string, config.PathConfig, bool) {
|
||||
// 按预排序的前缀列表查找最长匹配
|
||||
for _, prefix := range pm.prefixes {
|
||||
if strings.HasPrefix(path, prefix) {
|
||||
// 确保匹配的是完整的路径段
|
||||
restPath := path[len(prefix):]
|
||||
if restPath == "" || restPath[0] == '/' {
|
||||
return prefix, pm.configs[prefix], true
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", config.PathConfig{}, false
|
||||
}
|
||||
|
||||
// 更新前缀匹配器
|
||||
func (pm *prefixMatcher) update(pathMap map[string]config.PathConfig) {
|
||||
pm.prefixes = make([]string, 0, len(pathMap))
|
||||
pm.configs = make(map[string]config.PathConfig, len(pathMap))
|
||||
|
||||
for prefix, cfg := range pathMap {
|
||||
pm.prefixes = append(pm.prefixes, prefix)
|
||||
pm.configs[prefix] = cfg
|
||||
}
|
||||
|
||||
// 按长度降序排列
|
||||
sort.Slice(pm.prefixes, func(i, j int) bool {
|
||||
return len(pm.prefixes[i]) > len(pm.prefixes[j])
|
||||
})
|
||||
}
|
||||
|
||||
// NewProxyHandler 创建新的代理处理器
|
||||
func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
||||
dialer := &net.Dialer{
|
||||
@ -62,17 +122,17 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: dialer.DialContext,
|
||||
MaxIdleConns: 1000,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
MaxIdleConns: 2000,
|
||||
MaxIdleConnsPerHost: 200,
|
||||
IdleConnTimeout: idleConnTimeout,
|
||||
TLSHandshakeTimeout: tlsHandshakeTimeout,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
MaxConnsPerHost: 200,
|
||||
MaxConnsPerHost: 400,
|
||||
DisableKeepAlives: false,
|
||||
DisableCompression: false,
|
||||
ForceAttemptHTTP2: true,
|
||||
WriteBufferSize: 64 * 1024,
|
||||
ReadBufferSize: 64 * 1024,
|
||||
WriteBufferSize: 128 * 1024,
|
||||
ReadBufferSize: 128 * 1024,
|
||||
ResponseHeaderTimeout: backendServTimeout,
|
||||
MaxResponseHeaderBytes: 64 * 1024,
|
||||
}
|
||||
@ -94,7 +154,8 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
||||
}
|
||||
|
||||
handler := &ProxyHandler{
|
||||
pathMap: cfg.MAP,
|
||||
pathMap: cfg.MAP,
|
||||
prefixTree: newPrefixMatcher(cfg.MAP), // 初始化前缀匹配树
|
||||
client: &http.Client{
|
||||
Transport: transport,
|
||||
Timeout: proxyRespTimeout,
|
||||
@ -124,6 +185,7 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
||||
}
|
||||
|
||||
handler.pathMap = newCfg.MAP
|
||||
handler.prefixTree.update(newCfg.MAP) // 更新前缀匹配树
|
||||
handler.config = newCfg
|
||||
log.Printf("[Config] 代理处理器配置已更新: %d 个路径映射", len(newCfg.MAP))
|
||||
})
|
||||
@ -159,27 +221,11 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// 查找匹配的代理路径
|
||||
var matchedPrefix string
|
||||
var pathConfig config.PathConfig
|
||||
// 使用前缀匹配树快速查找匹配的路径
|
||||
matchedPrefix, pathConfig, matched := h.prefixTree.match(r.URL.Path)
|
||||
|
||||
// 首先尝试完全匹配路径段
|
||||
for prefix, cfg := range h.pathMap {
|
||||
// 检查是否是完整的路径段匹配
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
// 确保匹配的是完整的路径段
|
||||
restPath := r.URL.Path[len(prefix):]
|
||||
if restPath == "" || restPath[0] == '/' {
|
||||
matchedPrefix = prefix
|
||||
pathConfig = cfg
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到完全匹配,返回404
|
||||
if matchedPrefix == "" {
|
||||
// 返回 404
|
||||
// 如果没有找到匹配,返回404
|
||||
if !matched {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
@ -219,66 +265,59 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// 复制并处理请求头
|
||||
// 复制并处理请求头 - 使用更高效的方式
|
||||
copyHeader(proxyReq.Header, r.Header)
|
||||
|
||||
// 添加常见浏览器User-Agent
|
||||
if ua := r.Header.Get("User-Agent"); ua != "" {
|
||||
proxyReq.Header.Set("User-Agent", ua)
|
||||
} else {
|
||||
// 添加常见浏览器User-Agent - 避免冗余字符串操作
|
||||
if r.Header.Get("User-Agent") == "" {
|
||||
proxyReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||||
}
|
||||
|
||||
// 使用预先构建的URL字符串
|
||||
hostScheme := parsedURL.Scheme + "://" + parsedURL.Host
|
||||
|
||||
// 添加Origin
|
||||
proxyReq.Header.Set("Origin", fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host))
|
||||
proxyReq.Header.Set("Origin", hostScheme)
|
||||
|
||||
// 设置Referer为源站的完整域名(带上斜杠)
|
||||
proxyReq.Header.Set("Referer", fmt.Sprintf("%s://%s/", parsedURL.Scheme, parsedURL.Host))
|
||||
proxyReq.Header.Set("Referer", hostScheme+"/")
|
||||
|
||||
// 设置Host头和proxyReq.Host
|
||||
proxyReq.Header.Set("Host", parsedURL.Host)
|
||||
proxyReq.Host = parsedURL.Host
|
||||
|
||||
// 确保设置适当的Accept头
|
||||
if accept := r.Header.Get("Accept"); accept != "" {
|
||||
proxyReq.Header.Set("Accept", accept)
|
||||
} else {
|
||||
// 确保设置适当的Accept头 - 避免冗余字符串操作
|
||||
if r.Header.Get("Accept") == "" {
|
||||
proxyReq.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
}
|
||||
|
||||
// 确保设置Accept-Encoding
|
||||
if acceptEncoding := r.Header.Get("Accept-Encoding"); acceptEncoding != "" {
|
||||
proxyReq.Header.Set("Accept-Encoding", acceptEncoding)
|
||||
} else {
|
||||
// 确保设置Accept-Encoding - 避免冗余字符串操作
|
||||
if r.Header.Get("Accept-Encoding") == "" {
|
||||
proxyReq.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
||||
}
|
||||
|
||||
// 特别处理图片请求
|
||||
if utils.IsImageRequest(r.URL.Path) {
|
||||
// 获取 Accept 头
|
||||
accept := r.Header.Get("Accept")
|
||||
|
||||
// 根据 Accept 头设置合适的图片格式
|
||||
if strings.Contains(accept, "image/avif") {
|
||||
// 使用switch语句优化条件分支
|
||||
switch {
|
||||
case strings.Contains(accept, "image/avif"):
|
||||
proxyReq.Header.Set("Accept", "image/avif")
|
||||
} else if strings.Contains(accept, "image/webp") {
|
||||
case strings.Contains(accept, "image/webp"):
|
||||
proxyReq.Header.Set("Accept", "image/webp")
|
||||
}
|
||||
|
||||
// 设置 Cloudflare 特定的头部
|
||||
proxyReq.Header.Set("CF-Image-Format", "auto") // 让 Cloudflare 根据 Accept 头自动选择格式
|
||||
proxyReq.Header.Set("CF-Image-Format", "auto")
|
||||
}
|
||||
|
||||
// 设置最小必要的代理头部
|
||||
proxyReq.Header.Set("X-Real-IP", utils.GetClientIP(r))
|
||||
clientIP := utils.GetClientIP(r)
|
||||
proxyReq.Header.Set("X-Real-IP", clientIP)
|
||||
|
||||
// 如果源站不严格要求Host匹配,可以保留以下头部
|
||||
// 否则可能需要注释掉这些头部
|
||||
// proxyReq.Header.Set("X-Forwarded-Host", r.Host)
|
||||
// proxyReq.Header.Set("X-Forwarded-Proto", r.URL.Scheme)
|
||||
|
||||
// 添加或更新 X-Forwarded-For
|
||||
if clientIP := utils.GetClientIP(r); clientIP != "" {
|
||||
// 添加或更新 X-Forwarded-For - 减少重复获取客户端IP
|
||||
if clientIP != "" {
|
||||
if prior := proxyReq.Header.Get("X-Forwarded-For"); prior != "" {
|
||||
proxyReq.Header.Set("X-Forwarded-For", prior+", "+clientIP)
|
||||
} else {
|
||||
@ -357,20 +396,39 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
cacheKey := h.Cache.GenerateCacheKey(r)
|
||||
if cacheFile, err := h.Cache.CreateTemp(cacheKey, resp); err == nil {
|
||||
defer cacheFile.Close()
|
||||
|
||||
// 使用缓冲IO提高性能
|
||||
bufSize := 32 * 1024 // 32KB 缓冲区
|
||||
buf := make([]byte, bufSize)
|
||||
|
||||
teeReader := io.TeeReader(resp.Body, cacheFile)
|
||||
written, err = io.Copy(w, teeReader)
|
||||
written, err = io.CopyBuffer(w, teeReader, buf)
|
||||
|
||||
if err == nil {
|
||||
h.Cache.Commit(cacheKey, cacheFile.Name(), resp, written)
|
||||
// 异步提交缓存,不阻塞当前请求处理
|
||||
fileName := cacheFile.Name()
|
||||
respClone := *resp // 创建响应的浅拷贝
|
||||
go func() {
|
||||
h.Cache.Commit(cacheKey, fileName, &respClone, written)
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
written, err = io.Copy(w, resp.Body)
|
||||
// 使用缓冲的复制提高性能
|
||||
bufSize := 32 * 1024 // 32KB 缓冲区
|
||||
buf := make([]byte, bufSize)
|
||||
|
||||
written, err = io.CopyBuffer(w, resp.Body, buf)
|
||||
if err != nil && !isConnectionClosed(err) {
|
||||
log.Printf("[Proxy] ERR %s %s -> write error (%s) from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r))
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
written, err = io.Copy(w, resp.Body)
|
||||
// 使用缓冲的复制提高性能
|
||||
bufSize := 32 * 1024 // 32KB 缓冲区
|
||||
buf := make([]byte, bufSize)
|
||||
|
||||
written, err = io.CopyBuffer(w, resp.Body, buf)
|
||||
if err != nil && !isConnectionClosed(err) {
|
||||
log.Printf("[Proxy] ERR %s %s -> write error (%s) from %s", r.Method, r.URL.Path, utils.GetClientIP(r), utils.GetRequestSource(r))
|
||||
return
|
||||
|
@ -6,13 +6,11 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
neturl "net/url"
|
||||
"path/filepath"
|
||||
"proxy-go/internal/config"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -191,11 +189,23 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
targetBase := pathConfig.DefaultTarget
|
||||
usedAltTarget := false
|
||||
|
||||
// 获取文件扩展名
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
if ext != "" {
|
||||
ext = ext[1:] // 移除开头的点
|
||||
} else {
|
||||
// 获取文件扩展名(使用优化的字符串处理)
|
||||
ext := ""
|
||||
lastDotIndex := strings.LastIndex(path, ".")
|
||||
if lastDotIndex > 0 && lastDotIndex < len(path)-1 {
|
||||
ext = strings.ToLower(path[lastDotIndex+1:])
|
||||
}
|
||||
|
||||
// 如果没有扩展名规则,直接返回默认目标
|
||||
if len(pathConfig.ExtRules) == 0 {
|
||||
if ext == "" {
|
||||
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
|
||||
}
|
||||
return targetBase, false
|
||||
}
|
||||
|
||||
// 确保有扩展名规则
|
||||
if ext == "" {
|
||||
log.Printf("[Route] %s -> %s (无扩展名)", path, targetBase)
|
||||
// 即使没有扩展名,也要尝试匹配 * 通配符规则
|
||||
}
|
||||
@ -204,20 +214,27 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
contentLength, err := GetFileSize(client, targetBase+path)
|
||||
if err != nil {
|
||||
log.Printf("[Route] %s -> %s (获取文件大小出错: %v)", path, targetBase, err)
|
||||
return targetBase, false
|
||||
|
||||
// 如果无法获取文件大小,尝试使用扩展名直接匹配(优化点)
|
||||
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
|
||||
}
|
||||
|
||||
// 获取匹配的扩展名规则
|
||||
matchingRules := []config.ExtensionRule{}
|
||||
wildcardRules := []config.ExtensionRule{} // 存储通配符规则
|
||||
|
||||
// 处理扩展名,找出所有匹配的规则
|
||||
if pathConfig.ExtRules == nil {
|
||||
pathConfig.ProcessExtensionMap()
|
||||
}
|
||||
|
||||
// 找出所有匹配当前扩展名的规则
|
||||
ext = strings.ToLower(ext)
|
||||
for _, rule := range pathConfig.ExtRules {
|
||||
// 处理阈值默认值
|
||||
if rule.SizeThreshold < 0 {
|
||||
@ -225,18 +242,23 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
}
|
||||
|
||||
if rule.MaxSize <= 0 {
|
||||
rule.MaxSize = math.MaxInt64 // 设置为最大值表示不限制
|
||||
rule.MaxSize = 1<<63 - 1 // 设置为最大值表示不限制
|
||||
}
|
||||
|
||||
// 检查是否包含通配符
|
||||
if slices.Contains(rule.Extensions, "*") {
|
||||
wildcardRules = append(wildcardRules, rule)
|
||||
continue
|
||||
for _, e := range rule.Extensions {
|
||||
if e == "*" {
|
||||
wildcardRules = append(wildcardRules, rule)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 检查具体扩展名匹配
|
||||
if slices.Contains(rule.Extensions, ext) {
|
||||
matchingRules = append(matchingRules, rule)
|
||||
for _, e := range rule.Extensions {
|
||||
if e == ext {
|
||||
matchingRules = append(matchingRules, rule)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,8 +272,7 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
}
|
||||
}
|
||||
|
||||
// 按阈值排序规则,优先使用阈值范围更精确的规则
|
||||
// 先按最小阈值升序排序,再按最大阈值降序排序(在最小阈值相同的情况下)
|
||||
// 按阈值排序规则(优化点:使用更高效的排序)
|
||||
sort.Slice(matchingRules, func(i, j int) bool {
|
||||
if matchingRules[i].SizeThreshold == matchingRules[j].SizeThreshold {
|
||||
return matchingRules[i].MaxSize > matchingRules[j].MaxSize
|
||||
@ -260,8 +281,6 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
})
|
||||
|
||||
// 根据文件大小找出最匹配的规则
|
||||
var bestRule *config.ExtensionRule
|
||||
|
||||
for i := range matchingRules {
|
||||
rule := &matchingRules[i]
|
||||
|
||||
@ -271,71 +290,18 @@ func GetTargetURL(client *http.Client, r *http.Request, pathConfig config.PathCo
|
||||
log.Printf("[Route] %s -> %s (文件大小: %s, 在区间 %s 到 %s 之间)",
|
||||
path, rule.Target, FormatBytes(contentLength),
|
||||
FormatBytes(rule.SizeThreshold), FormatBytes(rule.MaxSize))
|
||||
bestRule = rule
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找到匹配的规则
|
||||
if bestRule != nil {
|
||||
// 创建一个带超时的 context
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 使用 channel 来接收备用源检查结果
|
||||
altChan := make(chan struct {
|
||||
accessible bool
|
||||
err error
|
||||
}, 1)
|
||||
|
||||
// 在 goroutine 中检查备用源可访问性
|
||||
go func() {
|
||||
accessible := isTargetAccessible(client, bestRule.Target+path)
|
||||
select {
|
||||
case altChan <- struct {
|
||||
accessible bool
|
||||
err error
|
||||
}{accessible: accessible}:
|
||||
case <-ctx.Done():
|
||||
// context 已取消,不需要发送结果
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待结果或超时
|
||||
select {
|
||||
case result := <-altChan:
|
||||
if result.accessible {
|
||||
log.Printf("[Route] %s -> %s (文件大小: %s, 在区间 %s 到 %s 之间)",
|
||||
path, bestRule.Target, FormatBytes(contentLength),
|
||||
FormatBytes(bestRule.SizeThreshold), FormatBytes(bestRule.MaxSize))
|
||||
return bestRule.Target, true
|
||||
}
|
||||
// 如果是通配符规则但不可访问,记录日志
|
||||
if slices.Contains(bestRule.Extensions, "*") {
|
||||
log.Printf("[Route] %s -> %s (回退: 通配符规则目标不可访问)",
|
||||
path, targetBase)
|
||||
// 检查目标是否可访问(使用带缓存的检查)
|
||||
if isTargetAccessible(client, rule.Target+path) {
|
||||
targetBase = rule.Target
|
||||
usedAltTarget = true
|
||||
} else {
|
||||
log.Printf("[Route] %s -> %s (回退: 备用目标不可访问)",
|
||||
path, targetBase)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
log.Printf("[Route] %s -> %s (回退: 备用目标检查超时)",
|
||||
path, targetBase)
|
||||
}
|
||||
} else {
|
||||
// 记录日志,为什么没有匹配的规则
|
||||
allThresholds := ""
|
||||
for i, rule := range matchingRules {
|
||||
if i > 0 {
|
||||
allThresholds += ", "
|
||||
}
|
||||
allThresholds += fmt.Sprintf("[%s-%s]",
|
||||
FormatBytes(rule.SizeThreshold),
|
||||
FormatBytes(rule.MaxSize))
|
||||
}
|
||||
|
||||
log.Printf("[Route] %s -> %s (文件大小: %s 不在任何阈值范围内: %s)",
|
||||
path, targetBase, FormatBytes(contentLength), allThresholds)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return targetBase, usedAltTarget
|
||||
|
Loading…
x
Reference in New Issue
Block a user