mirror of
https://github.com/woodchen-ink/proxy-go.git
synced 2025-07-18 00:21:56 +08:00
添加302跳转支持,更新相关配置和处理逻辑
This commit is contained in:
parent
6bdcaf6f83
commit
1a2c7bd06d
@ -13,6 +13,7 @@ type PathConfig struct {
|
||||
DefaultTarget string `json:"DefaultTarget"` // 默认目标URL
|
||||
ExtensionMap []ExtRuleConfig `json:"ExtensionMap"` // 扩展名映射规则
|
||||
ExtRules []ExtensionRule `json:"-"` // 内部使用,存储处理后的扩展名规则
|
||||
RedirectMode bool `json:"RedirectMode"` // 是否使用302跳转模式
|
||||
}
|
||||
|
||||
// ExtensionRule 表示一个扩展名映射规则(内部使用)
|
||||
@ -21,6 +22,7 @@ type ExtensionRule struct {
|
||||
Target string // 目标服务器
|
||||
SizeThreshold int64 // 最小阈值
|
||||
MaxSize int64 // 最大阈值
|
||||
RedirectMode bool // 是否使用302跳转模式
|
||||
}
|
||||
|
||||
type CompressionConfig struct {
|
||||
@ -39,6 +41,7 @@ type ExtRuleConfig struct {
|
||||
Target string `json:"Target"` // 目标服务器
|
||||
SizeThreshold int64 `json:"SizeThreshold"` // 最小阈值
|
||||
MaxSize int64 `json:"MaxSize"` // 最大阈值
|
||||
RedirectMode bool `json:"RedirectMode"` // 是否使用302跳转模式
|
||||
}
|
||||
|
||||
// 处理扩展名映射的方法
|
||||
@ -55,6 +58,7 @@ func (p *PathConfig) ProcessExtensionMap() {
|
||||
Target: rule.Target,
|
||||
SizeThreshold: rule.SizeThreshold,
|
||||
MaxSize: rule.MaxSize,
|
||||
RedirectMode: rule.RedirectMode,
|
||||
}
|
||||
|
||||
// 处理扩展名列表
|
||||
@ -87,3 +91,20 @@ func (p *PathConfig) GetProcessedExtTarget(ext string) (string, bool) {
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// GetProcessedExtRule 获取扩展名对应的完整规则信息,包括RedirectMode
|
||||
func (p *PathConfig) GetProcessedExtRule(ext string) (*ExtensionRule, bool) {
|
||||
if p.ExtRules == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
for _, rule := range p.ExtRules {
|
||||
for _, e := range rule.Extensions {
|
||||
if e == ext {
|
||||
return &rule, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
@ -45,14 +45,15 @@ var hopHeadersBase = map[string]bool{
|
||||
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
|
||||
auth *authManager
|
||||
errorHandler ErrorHandler
|
||||
Cache *cache.CacheManager
|
||||
pathMap map[string]config.PathConfig
|
||||
prefixTree *prefixMatcher // 添加前缀匹配树
|
||||
client *http.Client
|
||||
startTime time.Time
|
||||
config *config.Config
|
||||
auth *authManager
|
||||
errorHandler ErrorHandler
|
||||
Cache *cache.CacheManager
|
||||
redirectHandler *RedirectHandler // 添加302跳转处理器
|
||||
}
|
||||
|
||||
// 前缀匹配器结构体
|
||||
@ -166,10 +167,11 @@ func NewProxyHandler(cfg *config.Config) *ProxyHandler {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
startTime: time.Now(),
|
||||
config: cfg,
|
||||
auth: newAuthManager(),
|
||||
Cache: cacheManager,
|
||||
startTime: time.Now(),
|
||||
config: cfg,
|
||||
auth: newAuthManager(),
|
||||
Cache: cacheManager,
|
||||
redirectHandler: NewRedirectHandler(), // 初始化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)
|
||||
@ -236,6 +238,13 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否需要进行302跳转
|
||||
if h.redirectHandler != nil && h.redirectHandler.HandleRedirect(w, r, pathConfig, decodedPath) {
|
||||
// 如果进行了302跳转,直接返回,不继续处理
|
||||
collector.RecordRequest(r.URL.Path, http.StatusFound, time.Since(start), 0, utils.GetClientIP(r), r)
|
||||
return
|
||||
}
|
||||
|
||||
// 使用统一的路由选择逻辑
|
||||
targetBase, usedAltTarget := utils.GetTargetURL(h.client, r, pathConfig, decodedPath)
|
||||
|
||||
|
129
internal/handler/redirect.go
Normal file
129
internal/handler/redirect.go
Normal file
@ -0,0 +1,129 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"proxy-go/internal/config"
|
||||
"proxy-go/internal/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RedirectHandler 处理302跳转逻辑
|
||||
type RedirectHandler struct{}
|
||||
|
||||
// NewRedirectHandler 创建新的跳转处理器
|
||||
func NewRedirectHandler() *RedirectHandler {
|
||||
return &RedirectHandler{}
|
||||
}
|
||||
|
||||
// HandleRedirect 处理302跳转请求
|
||||
func (rh *RedirectHandler) HandleRedirect(w http.ResponseWriter, r *http.Request, pathConfig config.PathConfig, targetPath string) bool {
|
||||
// 检查是否需要进行302跳转
|
||||
shouldRedirect, targetURL := rh.shouldRedirect(r, pathConfig, targetPath)
|
||||
|
||||
if !shouldRedirect {
|
||||
return false
|
||||
}
|
||||
|
||||
// 执行302跳转
|
||||
rh.performRedirect(w, r, targetURL)
|
||||
return true
|
||||
}
|
||||
|
||||
// shouldRedirect 判断是否应该进行302跳转,并返回目标URL
|
||||
func (rh *RedirectHandler) shouldRedirect(r *http.Request, pathConfig config.PathConfig, targetPath string) (bool, string) {
|
||||
// 获取文件扩展名
|
||||
ext := strings.ToLower(filepath.Ext(targetPath))
|
||||
if ext != "" {
|
||||
ext = ext[1:] // 去掉点号
|
||||
}
|
||||
|
||||
// 首先检查扩展名规则是否有302跳转配置
|
||||
if rule, found := pathConfig.GetProcessedExtRule(ext); found && rule.RedirectMode {
|
||||
// 使用扩展名规则的目标URL进行302跳转
|
||||
targetURL := rh.buildTargetURL(rule.Target, targetPath, r.URL.RawQuery)
|
||||
return true, targetURL
|
||||
}
|
||||
|
||||
// 检查通配符规则
|
||||
if rule, found := pathConfig.GetProcessedExtRule("*"); found && rule.RedirectMode {
|
||||
// 使用通配符规则的目标URL进行302跳转
|
||||
targetURL := rh.buildTargetURL(rule.Target, targetPath, r.URL.RawQuery)
|
||||
return true, targetURL
|
||||
}
|
||||
|
||||
// 检查默认目标是否配置为302跳转
|
||||
if pathConfig.RedirectMode {
|
||||
// 使用默认目标URL进行302跳转
|
||||
targetURL := rh.buildTargetURL(pathConfig.DefaultTarget, targetPath, r.URL.RawQuery)
|
||||
return true, targetURL
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// buildTargetURL 构建完整的目标URL
|
||||
func (rh *RedirectHandler) buildTargetURL(baseURL, targetPath, rawQuery string) string {
|
||||
// URL 解码,然后重新编码,确保特殊字符被正确处理
|
||||
decodedPath, err := url.QueryUnescape(targetPath)
|
||||
if err != nil {
|
||||
// 如果解码失败,使用原始路径
|
||||
decodedPath = targetPath
|
||||
}
|
||||
|
||||
// 重新编码路径,保留 '/'
|
||||
parts := strings.Split(decodedPath, "/")
|
||||
for i, part := range parts {
|
||||
parts[i] = url.PathEscape(part)
|
||||
}
|
||||
encodedPath := strings.Join(parts, "/")
|
||||
|
||||
// 构建完整URL
|
||||
targetURL := baseURL + encodedPath
|
||||
|
||||
// 添加查询参数
|
||||
if rawQuery != "" {
|
||||
targetURL = targetURL + "?" + rawQuery
|
||||
}
|
||||
|
||||
return targetURL
|
||||
}
|
||||
|
||||
// performRedirect 执行302跳转
|
||||
func (rh *RedirectHandler) performRedirect(w http.ResponseWriter, r *http.Request, targetURL string) {
|
||||
// 设置302跳转响应头
|
||||
w.Header().Set("Location", targetURL)
|
||||
w.Header().Set("Proxy-Go-Redirect", "1")
|
||||
|
||||
// 添加缓存控制头,避免浏览器缓存跳转响应
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Expires", "0")
|
||||
|
||||
// 设置状态码为302
|
||||
w.WriteHeader(http.StatusFound)
|
||||
|
||||
// 记录跳转日志
|
||||
clientIP := utils.GetClientIP(r)
|
||||
log.Printf("[Redirect] %s %s -> 302 %s (%s) from %s",
|
||||
r.Method, r.URL.Path, targetURL, clientIP, utils.GetRequestSource(r))
|
||||
}
|
||||
|
||||
// IsRedirectEnabled 检查路径配置是否启用了任何形式的302跳转
|
||||
func (rh *RedirectHandler) IsRedirectEnabled(pathConfig config.PathConfig) bool {
|
||||
// 检查默认目标是否启用跳转
|
||||
if pathConfig.RedirectMode {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查扩展名规则是否有启用跳转的
|
||||
for _, rule := range pathConfig.ExtRules {
|
||||
if rule.RedirectMode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
@ -34,6 +34,7 @@ interface ExtRuleConfig {
|
||||
Target: string; // 目标服务器
|
||||
SizeThreshold: number; // 最小阈值(字节)
|
||||
MaxSize: number; // 最大阈值(字节)
|
||||
RedirectMode?: boolean; // 是否使用302跳转模式
|
||||
}
|
||||
|
||||
interface PathMapping {
|
||||
@ -41,6 +42,7 @@ interface PathMapping {
|
||||
ExtensionMap?: ExtRuleConfig[] // 只支持新格式
|
||||
SizeThreshold?: number // 保留全局阈值字段(向后兼容)
|
||||
MaxSize?: number // 保留全局阈值字段(向后兼容)
|
||||
RedirectMode?: boolean // 是否使用302跳转模式
|
||||
}
|
||||
|
||||
interface CompressionConfig {
|
||||
@ -77,6 +79,7 @@ export default function ConfigPage() {
|
||||
const [newPathData, setNewPathData] = useState({
|
||||
path: "",
|
||||
defaultTarget: "",
|
||||
redirectMode: false,
|
||||
extensionMap: {} as Record<string, string>,
|
||||
sizeThreshold: 0,
|
||||
maxSize: 0,
|
||||
@ -281,6 +284,7 @@ export default function ConfigPage() {
|
||||
setNewPathData({
|
||||
path: "",
|
||||
defaultTarget: "",
|
||||
redirectMode: false,
|
||||
extensionMap: {},
|
||||
sizeThreshold: 0,
|
||||
maxSize: 0,
|
||||
@ -327,6 +331,7 @@ export default function ConfigPage() {
|
||||
setNewPathData({
|
||||
path: "",
|
||||
defaultTarget: "",
|
||||
redirectMode: false,
|
||||
extensionMap: {},
|
||||
sizeThreshold: 0,
|
||||
maxSize: 0,
|
||||
@ -375,6 +380,7 @@ export default function ConfigPage() {
|
||||
setNewPathData({
|
||||
path: "",
|
||||
defaultTarget: "",
|
||||
redirectMode: false,
|
||||
extensionMap: {},
|
||||
sizeThreshold: 0,
|
||||
maxSize: 0,
|
||||
@ -1109,4 +1115,4 @@ const convertBytesToUnit = (bytes: number): { value: number, unit: 'B' | 'KB' |
|
||||
value: Number((bytes / Math.pow(k, i)).toFixed(2)),
|
||||
unit: sizes[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user