@@ -6,12 +6,15 @@ import (
66 "MediaWarp/internal/logging"
77 "bytes"
88 "compress/gzip"
9+ "errors"
910 "fmt"
1011 "io"
1112 "net/http"
1213 "net/http/httputil"
14+ "net/url"
1315 "strconv"
1416 "strings"
17+ "time"
1518
1619 "github.com/andybalholm/brotli"
1720 "github.com/gin-gonic/gin"
@@ -135,3 +138,74 @@ func updateBody(rw *http.Response, content []byte) error {
135138
136139 return nil
137140}
141+
142+ const (
143+ MaxRedirectAttempts = 10 // 最大重定向次数限制
144+ RedirectTimeout = 10 * time .Second // 最大超时时间
145+
146+ )
147+
148+ var (
149+ ErrInvalidLocationHeader = errors .New ("重定向 Location 头无效" )
150+ ErrMaxRedirectsExceeded = fmt .Errorf ("超过最大重定向次数限制(%d)" , MaxRedirectAttempts )
151+ )
152+
153+ // 获取URL的最终目标地址(自动跟踪重定向)
154+ func getFinalURL (rawURL string ) (string , error ) {
155+
156+ parsedURL , err := url .Parse (rawURL ) // 验证并解析输入URL
157+ if err != nil {
158+ return "" , fmt .Errorf ("非法 URL: %w" , err )
159+ }
160+ if parsedURL .Scheme == "" {
161+ return "" , fmt .Errorf ("URL 缺少协议头: %s" , parsedURL )
162+ }
163+
164+ // 创建自定义HTTP客户端配置
165+ client := & http.Client {
166+ Timeout : RedirectTimeout ,
167+ CheckRedirect : func (req * http.Request , via []* http.Request ) error {
168+ // 禁止自动重定向,以便手动处理
169+ return http .ErrUseLastResponse
170+ },
171+ }
172+
173+ currentURL := parsedURL .String ()
174+ visited := make (map [string ]struct {}, MaxRedirectAttempts )
175+ redirectChain := make ([]string , 0 , MaxRedirectAttempts + 1 )
176+
177+ // 跟踪重定向链
178+ for i := 0 ; i <= MaxRedirectAttempts ; i ++ {
179+ // 检测循环重定向
180+ if _ , exists := visited [currentURL ]; exists {
181+ return "" , fmt .Errorf ("检测到循环重定向,重定向链: %s" , strings .Join (redirectChain , " -> " ))
182+ }
183+ visited [currentURL ] = struct {}{}
184+ redirectChain = append (redirectChain , currentURL )
185+
186+ // 创建HEAD请求(更高效,只获取头部信息)
187+ resp , err := client .Head (currentURL )
188+ if err != nil {
189+ return "" , fmt .Errorf ("发送 HTTP 请求失败:%w" , err )
190+ }
191+ defer resp .Body .Close ()
192+
193+ // 检查是否需要重定向 (3xx 状态码)
194+ if resp .StatusCode >= http .StatusMultipleChoices && resp .StatusCode < http .StatusBadRequest {
195+ location , err := resp .Location ()
196+ if err != nil {
197+ return "" , ErrInvalidLocationHeader
198+ }
199+
200+ // 处理相对路径重定向
201+ currentURL = location .String ()
202+ continue
203+ }
204+
205+ // 返回最终的非重定向URL
206+ logging .Debug ("重定向链:" , strings .Join (redirectChain , " -> " ))
207+ return resp .Request .URL .String (), nil
208+ }
209+
210+ return "" , ErrMaxRedirectsExceeded
211+ }
0 commit comments