package srun import ( "errors" "fmt" log "github.com/sirupsen/logrus" "io" "net/http" "net/url" "regexp" "strings" "unsafe" ) func (a *Api) NewDetector() Detector { return Detector{ api: a, } } type Detector struct { api *Api page []byte } func (a *Detector) _JoinRedirectLocation(addr *url.URL, loc string) (*url.URL, error) { if loc == "" { return nil, errors.New("目标跳转地址缺失") } if strings.HasPrefix(loc, "/") { addr.Path = strings.TrimPrefix(loc, "/") return addr, nil } else { return url.Parse(loc) } } type _FollowRedirectConfig struct { // 覆盖响应处理逻辑,设置后 onNextAddr 无效 onResponse func(res *http.Response) (next *url.URL, err error) // 获取到下一个请求地址时触发 onNextAddr func(addr *url.URL) error } func (a *Detector) _FollowRedirect(addr *url.URL, conf _FollowRedirectConfig) (*url.URL, error) { addrCopy := *addr addr = &addrCopy for { log.Debugln("HTTP GET", addr) req, err := http.NewRequest("GET", addr.String(), nil) if err != nil { return nil, err } for k, v := range a.api.CustomHeader { req.Header.Set(k, fmt.Sprint(v)) } res, err := a.api.NoDirect.Do(req) if err != nil { return nil, err } if conf.onResponse != nil { var nextAddr *url.URL nextAddr, err = conf.onResponse(res) if err != nil { return nil, err } else if nextAddr == nil { break } addr = nextAddr continue } _, _ = io.Copy(io.Discard, res.Body) _ = res.Body.Close() if res.StatusCode < 300 { break } else if res.StatusCode < 400 { addr, err = a._JoinRedirectLocation(addr, res.Header.Get("location")) if err != nil { return nil, err } if conf.onNextAddr != nil { if err = conf.onNextAddr(addr); err != nil { return nil, err } } } else { return nil, fmt.Errorf("server return http status %d", res.StatusCode) } } return addr, nil } func (a *Detector) _SearchAcid(query url.Values) (string, bool) { addr := query.Get(`ac_id`) return addr, addr != "" } func (a *Detector) DetectEnc() (string, error) { log.Debugln("HTTP GET", a.api.BaseUrl) res, err := a.api.Client.Get(a.api.BaseUrl) if err != nil { return "", err } defer res.Body.Close() if res.StatusCode == 200 { indexHtml, err := io.ReadAll(res.Body) if err != nil { return "", err } jsReg, err := regexp.Compile(`(?i)`) if err != nil { return } result := reg.FindSubmatch(body) if len(result) == 2 { nextBytes := result[1] nextAddr := unsafe.String(unsafe.SliceData(nextBytes), len(nextBytes)) next, err = url.Parse(nextAddr) } } else if res.StatusCode < 400 { next, err = a._JoinRedirectLocation(res.Request.URL, res.Header.Get("location")) } if getAcid && next != nil { acid, _ = a._SearchAcid(next.Query()) } return }, }) if err != nil { if errors.Is(err, AlreadyOnline) { online = true err = nil return } return } online = finalUrl.Host == startUrl.Host return }