ngn 843f23f518
All checks were successful
docker / docker (push) Successful in 27m21s
add flaresolverr timeout option
Signed-off-by: ngn <ngn@ngn.tf>
2025-05-18 15:11:22 +03:00

175 lines
3.5 KiB
Go

package utils
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"time"
"github.com/go-resty/resty/v2"
)
// https://github.com/FlareSolverr/FlareSolverr#-requestget
type Request struct {
Cmd string `json:"cmd"`
Url string `json:"url"`
MaxTimeout int `json:"maxTimeout"`
OnlyCookies bool `json:"returnOnlyCookies"`
}
type Cookie struct {
Name string `json:"name"`
Value string `json:"value"`
Domain string `json:"domain"`
Path string `json:"path"`
Expires time.Time `json:"expires"`
Size uint64 `json:"size"`
HttpOnly bool `json:"httpOnly"`
Secure bool `json:"secure"`
Session bool `json:"session"`
SameSite string `json:"sameSite"`
}
type Solution struct {
Status int `json:"status"`
Cookies []Cookie `json:"cookies"`
Agent string `json:"userAgent"`
}
type Response struct {
Solution Solution `json:"solution"`
}
func (c *Cookie) ToCookie() *http.Cookie {
ss := http.SameSiteNoneMode
switch c.SameSite {
case "Lax":
ss = http.SameSiteLaxMode
case "Strict":
ss = http.SameSiteStrictMode
case "Default":
ss = http.SameSiteDefaultMode
}
return &http.Cookie{
Name: c.Name,
Value: c.Value,
Domain: c.Domain,
Path: c.Path,
Expires: c.Expires,
HttpOnly: c.HttpOnly,
Secure: c.Secure,
SameSite: ss,
}
}
func (s *Solution) HttpCookies() []*http.Cookie {
cookies := []*http.Cookie{}
for i := range s.Cookies {
cookies = append(cookies, s.Cookies[i].ToCookie())
}
return cookies
}
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.3"
const DEFAULT_TIMEOUT = 120
var (
solution *Solution = nil
fs_url string = ""
fs_timeout int = DEFAULT_TIMEOUT
)
func Solve(target string) error {
if fs_url == "" {
return fmt.Errorf("flaresolver is not configured")
}
url, _ := url.JoinPath(fs_url, "/v1")
response := Response{}
client := resty.New()
res, err := client.R().
SetBody(Request{
Cmd: "request.get",
Url: target,
MaxTimeout: fs_timeout * 100,
OnlyCookies: true,
}).
Post(url)
if err != nil {
return fmt.Errorf("request failed: %s", err.Error())
}
if res.StatusCode() != 200 {
return fmt.Errorf("bad status code: %d", res.StatusCode())
}
if err := json.Unmarshal(res.Body(), &response); err != nil {
return fmt.Errorf("failed to parse body: %s", err.Error())
}
solution = &response.Solution
return nil
}
func GET(target string, no_retry ...bool) (*resty.Response, error) {
client := resty.New()
req := client.R()
if solution != nil {
req.SetCookies(solution.HttpCookies())
req.SetHeader("User-Agent", solution.Agent)
} else {
req.SetHeader("User-Agent", USER_AGENT)
}
res, err := req.Get(target)
if err != nil {
return nil, err
}
if res.StatusCode() != http.StatusForbidden {
return res, err
}
if err = Solve(target); err != nil {
return nil, err
}
if len(no_retry) == 0 {
return GET(target)
}
return nil, fmt.Errorf("solution did not work")
}
func ReqSetup() {
fs_url = os.Getenv("FLARESOLVER")
timeout := os.Getenv("TIMEOUT")
if timeout == "" {
return
}
if to, err := strconv.Atoi(timeout); err != nil {
fmt.Printf("failed to parse timeout: %s\n", err.Error())
} else if to <= 0 {
fmt.Println("invalid timeout, timeout should be greater than zero")
} else {
return
}
fmt.Printf("using the default timeout instead (%d secs)\n", DEFAULT_TIMEOUT)
fs_timeout = DEFAULT_TIMEOUT
}