improve: 优化 detector 请求数量
This commit is contained in:
@@ -25,11 +25,13 @@ func Login(eth *tools.Eth, debugOutput bool) error {
|
|||||||
CustomHeader: config.Settings.CustomHeader,
|
CustomHeader: config.Settings.CustomHeader,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
srunDetector := srunClient.Api.NewDetector()
|
||||||
|
|
||||||
// Reality 与 Acid
|
// Reality 与 Acid
|
||||||
var acidOnReality bool
|
var acidOnReality bool
|
||||||
if config.Settings.Reality.Enable {
|
if config.Settings.Reality.Enable {
|
||||||
log.Debugln("开始 Reality 流程")
|
log.Debugln("开始 Reality 流程")
|
||||||
acid, _, err := srunClient.Api.Reality(config.Settings.Reality.Addr, flags.AutoAcid)
|
acid, _, err := srunDetector.Reality(config.Settings.Reality.Addr, flags.AutoAcid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Reality 请求异常:", err)
|
log.Errorln("Reality 请求异常:", err)
|
||||||
return err
|
return err
|
||||||
@@ -46,7 +48,7 @@ func Login(eth *tools.Eth, debugOutput bool) error {
|
|||||||
}
|
}
|
||||||
if !acidOnReality && flags.AutoAcid {
|
if !acidOnReality && flags.AutoAcid {
|
||||||
log.Debugln("开始嗅探 acid")
|
log.Debugln("开始嗅探 acid")
|
||||||
acid, err := srunClient.Api.DetectAcid()
|
acid, err := srunDetector.DetectAcid()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, srun.ErrAcidCannotFound) {
|
if errors.Is(err, srun.ErrAcidCannotFound) {
|
||||||
log.Warnln("找不到 acid,使用配置 acid")
|
log.Warnln("找不到 acid,使用配置 acid")
|
||||||
@@ -61,7 +63,7 @@ func Login(eth *tools.Eth, debugOutput bool) error {
|
|||||||
|
|
||||||
if flags.AutoEnc {
|
if flags.AutoEnc {
|
||||||
log.Debugln("开始嗅探 enc")
|
log.Debugln("开始嗅探 enc")
|
||||||
enc, err := srunClient.Api.DetectEnc()
|
enc, err := srunDetector.DetectEnc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, srun.ErrEnvCannotFound) {
|
if errors.Is(err, srun.ErrEnvCannotFound) {
|
||||||
log.Warnln("找不到 enc,使用配置 enc")
|
log.Warnln("找不到 enc,使用配置 enc")
|
||||||
|
|||||||
@@ -13,14 +13,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (a *Api) NewDetector() Detector {
|
func (a *Api) NewDetector() Detector {
|
||||||
|
redirectReg, err := regexp.Compile(
|
||||||
|
`<script>top\.self\.location\.href='(.*)'</script>|<meta http-equiv="refresh" content=".*url=(.*?)">`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return Detector{
|
return Detector{
|
||||||
api: a,
|
api: a,
|
||||||
|
redirectReg: redirectReg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Detector struct {
|
type Detector struct {
|
||||||
api *Api
|
api *Api
|
||||||
|
|
||||||
|
redirectReg *regexp.Regexp
|
||||||
|
|
||||||
|
// 登录页 html data
|
||||||
page []byte
|
page []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,58 +48,67 @@ func (a *Detector) _JoinRedirectLocation(addr *url.URL, loc string) (*url.URL, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
type _FollowRedirectConfig struct {
|
type _FollowRedirectConfig struct {
|
||||||
// 覆盖响应处理逻辑,设置后 onNextAddr 无效
|
|
||||||
onResponse func(res *http.Response) (next *url.URL, err error)
|
|
||||||
// 获取到下一个请求地址时触发
|
// 获取到下一个请求地址时触发
|
||||||
onNextAddr func(addr *url.URL) error
|
onNextAddr func(addr *url.URL) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Detector) _FollowRedirect(addr *url.URL, conf _FollowRedirectConfig) (*url.URL, error) {
|
func (a *Detector) _FollowRedirect(addr *url.URL, conf _FollowRedirectConfig) (*http.Response, []byte, error) {
|
||||||
addrCopy := *addr
|
addrCopy := *addr
|
||||||
addr = &addrCopy
|
addr = &addrCopy
|
||||||
|
|
||||||
|
var body []byte
|
||||||
|
var res *http.Response
|
||||||
for {
|
for {
|
||||||
log.Debugln("HTTP GET", addr)
|
log.Debugln("HTTP GET", addr)
|
||||||
req, err := http.NewRequest("GET", addr.String(), nil)
|
req, err := http.NewRequest("GET", addr.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for k, v := range a.api.CustomHeader {
|
for k, v := range a.api.CustomHeader {
|
||||||
req.Header.Set(k, fmt.Sprint(v))
|
req.Header.Set(k, fmt.Sprint(v))
|
||||||
}
|
}
|
||||||
res, err := a.api.NoDirect.Do(req)
|
res, err = a.api.NoDirect.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if conf.onResponse != nil {
|
|
||||||
var nextAddr *url.URL
|
if res.StatusCode < 300 {
|
||||||
nextAddr, err = conf.onResponse(res)
|
body, err = io.ReadAll(res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
} else if nextAddr == nil {
|
}
|
||||||
|
locMatch := a.redirectReg.FindSubmatch(body)
|
||||||
|
if len(locMatch) > 2 {
|
||||||
|
locBytes := locMatch[1]
|
||||||
|
addr, err = a._JoinRedirectLocation(addr, unsafe.String(unsafe.SliceData(locBytes), len(locBytes)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
break
|
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 {
|
} else {
|
||||||
return nil, fmt.Errorf("server return http status %d", res.StatusCode)
|
_, _ = io.Copy(io.Discard, res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
|
||||||
|
if res.StatusCode < 400 {
|
||||||
|
addr, err = a._JoinRedirectLocation(addr, res.Header.Get("location"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, nil, fmt.Errorf("server return http status %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.onNextAddr != nil {
|
||||||
|
if err = conf.onNextAddr(addr); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addr, nil
|
return res, body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Detector) _SearchAcid(query url.Values) (string, bool) {
|
func (a *Detector) _SearchAcid(query url.Values) (string, bool) {
|
||||||
@@ -97,116 +117,109 @@ func (a *Detector) _SearchAcid(query url.Values) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Detector) DetectEnc() (string, error) {
|
func (a *Detector) DetectEnc() (string, error) {
|
||||||
log.Debugln("HTTP GET", a.api.BaseUrl)
|
if a.page == nil {
|
||||||
res, err := a.api.Client.Get(a.api.BaseUrl)
|
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 {
|
||||||
|
_, _ = io.Copy(io.Discard, res.Body)
|
||||||
|
return "", fmt.Errorf("server return http status: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
a.page, err = io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsReg, err := regexp.Compile(`(?i)<script src="\.?(.+[./]portal[0-9]*\.js)(\?.*)?">`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
jsPathMatch := jsReg.FindSubmatch(a.page)
|
||||||
if res.StatusCode == 200 {
|
if len(jsPathMatch) == 3 {
|
||||||
indexHtml, err := io.ReadAll(res.Body)
|
jsPathBytes := jsPathMatch[1]
|
||||||
|
jsPath := unsafe.String(unsafe.SliceData(jsPathBytes), len(jsPathBytes))
|
||||||
|
jsUrl, err := url.Parse(a.api.BaseUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
jsReg, err := regexp.Compile(`(?i)<script src="\.?(.+[./]portal[0-9]*\.js)(\?.*)?">`)
|
jsUrl.Path = jsPath
|
||||||
|
jsAddr := jsUrl.String()
|
||||||
|
log.Debugln("HTTP GET", jsAddr)
|
||||||
|
jsRes, err := a.api.Client.Get(jsAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
jsPathMatch := jsReg.FindSubmatch(indexHtml)
|
defer jsRes.Body.Close()
|
||||||
if len(jsPathMatch) == 3 {
|
if jsRes.StatusCode == 200 {
|
||||||
jsPathBytes := jsPathMatch[1]
|
jsContent, err := io.ReadAll(jsRes.Body)
|
||||||
jsPath := unsafe.String(unsafe.SliceData(jsPathBytes), len(jsPathBytes))
|
if err == nil {
|
||||||
jsUrl, err := url.Parse(a.api.BaseUrl)
|
reg, err := regexp.Compile(`var enc = (.*?)[,;]`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
jsUrl.Path = jsPath
|
encMatch := reg.FindSubmatch(jsContent)
|
||||||
jsAddr := jsUrl.String()
|
if len(encMatch) == 2 {
|
||||||
log.Debugln("HTTP GET", jsAddr)
|
encBytes := encMatch[1]
|
||||||
jsRes, err := a.api.Client.Get(jsAddr)
|
encStr := unsafe.String(unsafe.SliceData(encBytes), len(encBytes))
|
||||||
if err != nil {
|
encSplit := strings.Split(encStr, "+")
|
||||||
return "", err
|
for i, v := range encSplit {
|
||||||
}
|
encSplit[i] = strings.Trim(strings.TrimSpace(v), "'\"")
|
||||||
defer jsRes.Body.Close()
|
}
|
||||||
if jsRes.StatusCode == 200 {
|
enc := strings.Join(encSplit, "")
|
||||||
jsContent, err := io.ReadAll(jsRes.Body)
|
return enc, nil
|
||||||
if err == nil {
|
|
||||||
reg, err := regexp.Compile(`var enc = (.*?)[,;]`)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
encMatch := reg.FindSubmatch(jsContent)
|
|
||||||
if len(encMatch) == 2 {
|
|
||||||
encBytes := encMatch[1]
|
|
||||||
encStr := unsafe.String(unsafe.SliceData(encBytes), len(encBytes))
|
|
||||||
encSplit := strings.Split(encStr, "+")
|
|
||||||
for i, v := range encSplit {
|
|
||||||
encSplit[i] = strings.Trim(strings.TrimSpace(v), "'\"")
|
|
||||||
}
|
|
||||||
enc := strings.Join(encSplit, "")
|
|
||||||
return enc, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_, _ = io.Copy(io.Discard, jsRes.Body)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_, _ = io.Copy(io.Discard, jsRes.Body)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_, _ = io.Copy(io.Discard, res.Body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", ErrEnvCannotFound
|
return "", ErrEnvCannotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// DetectAcid err 为 nil 时 acid 一定存在
|
// DetectAcid err 为 nil 时 acid 一定存在
|
||||||
func (a *Detector) DetectAcid() (string, error) {
|
func (a *Detector) DetectAcid() (string, error) {
|
||||||
// 从入口地址 url query 寻找 acid
|
if a.page == nil {
|
||||||
baseUrl, err := url.Parse(a.api.BaseUrl)
|
// page 有值时说明 reality 已进行过 query match,此部分可跳过
|
||||||
if err != nil {
|
// 从入口地址 url query 寻找 acid
|
||||||
return "", err
|
baseUrl, err := url.Parse(a.api.BaseUrl)
|
||||||
}
|
if err != nil {
|
||||||
|
return "", err
|
||||||
var AcidFound = errors.New("acid found")
|
}
|
||||||
var acid string
|
|
||||||
finalAddr, err := a._FollowRedirect(baseUrl, _FollowRedirectConfig{
|
var AcidFound = errors.New("acid found")
|
||||||
onNextAddr: func(addr *url.URL) error {
|
var acid string
|
||||||
var ok bool
|
_, a.page, err = a._FollowRedirect(baseUrl, _FollowRedirectConfig{
|
||||||
acid, ok = a._SearchAcid(addr.Query())
|
onNextAddr: func(addr *url.URL) error {
|
||||||
if ok {
|
var ok bool
|
||||||
return AcidFound
|
acid, ok = a._SearchAcid(addr.Query())
|
||||||
}
|
if ok {
|
||||||
return nil
|
return AcidFound
|
||||||
},
|
}
|
||||||
})
|
return nil
|
||||||
if err != nil {
|
},
|
||||||
if errors.Is(err, AcidFound) {
|
})
|
||||||
return acid, nil
|
if err != nil {
|
||||||
|
if errors.Is(err, AcidFound) {
|
||||||
|
return acid, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 html 寻找 acid
|
// 从 html 寻找 acid
|
||||||
log.Debugln("HTTP GET", finalAddr.String())
|
var reg *regexp.Regexp
|
||||||
res, err := a.api.Client.Get(a.api.BaseUrl)
|
reg, err := regexp.Compile(`"ac_id".*?value="(.+)"`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
result := reg.FindSubmatch(a.page)
|
||||||
if res.StatusCode == 200 {
|
if len(result) == 2 {
|
||||||
var indexHtml []byte
|
return string(result[1]), nil
|
||||||
indexHtml, err = io.ReadAll(res.Body)
|
|
||||||
if err == nil {
|
|
||||||
var reg *regexp.Regexp
|
|
||||||
reg, err = regexp.Compile(`"ac_id".*?value="(.+)"`)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
result := reg.FindSubmatch(indexHtml)
|
|
||||||
if len(result) == 2 {
|
|
||||||
return string(result[1]), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_, _ = io.Copy(io.Discard, res.Body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", ErrAcidCannotFound
|
return "", ErrAcidCannotFound
|
||||||
@@ -219,36 +232,12 @@ func (a *Detector) Reality(addr string, getAcid bool) (acid string, online bool,
|
|||||||
return "", false, err
|
return "", false, err
|
||||||
}
|
}
|
||||||
var AlreadyOnline = errors.New("already online")
|
var AlreadyOnline = errors.New("already online")
|
||||||
var finalUrl *url.URL
|
finalRes, pageBytes, err := a._FollowRedirect(startUrl, _FollowRedirectConfig{
|
||||||
finalUrl, err = a._FollowRedirect(startUrl, _FollowRedirectConfig{
|
onNextAddr: func(addr *url.URL) error {
|
||||||
onResponse: func(res *http.Response) (next *url.URL, err error) {
|
if getAcid {
|
||||||
defer res.Body.Close()
|
acid, _ = a._SearchAcid(addr.Query())
|
||||||
if res.StatusCode < 300 {
|
|
||||||
var body []byte
|
|
||||||
body, err = io.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var reg *regexp.Regexp
|
|
||||||
reg, err = regexp.Compile(`<script>top\.self\.location\.href='(.*)'</script>`)
|
|
||||||
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 {
|
return nil
|
||||||
acid, _ = a._SearchAcid(next.Query())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -259,6 +248,11 @@ func (a *Detector) Reality(addr string, getAcid bool) (acid string, online bool,
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
online = finalUrl.Host == startUrl.Host
|
online = finalRes.Request.URL.Host == startUrl.Host
|
||||||
|
a.page = pageBytes
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Detector) Reset() {
|
||||||
|
a.page = nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user