Files
goyomi/internal/httpclient/flaresolverr.go
T
2026-05-11 06:48:23 +00:00

96 lines
2.3 KiB
Go
Executable File

package httpclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
type FlareSolverrClient struct {
endpoint string
client *http.Client
}
func NewFlareSolverrClient() (*FlareSolverrClient, error) {
ep := os.Getenv("FLARESOLVERR_URL")
if ep == "" {
return nil, fmt.Errorf("FLARESOLVERR_URL not set")
}
return &FlareSolverrClient{
endpoint: ep,
client: &http.Client{},
}, nil
}
type flareSolverrRequest struct {
Cmd string `json:"cmd"`
URL string `json:"url"`
MaxTimeout int `json:"maxTimeout"`
}
type FlareSolverrResponse struct {
Status string `json:"status"`
Solution struct {
Response string `json:"response"`
Cookies []fsCookie `json:"cookies"`
Headers map[string]any `json:"headers"`
URL string `json:"url"`
Status int `json:"status"`
} `json:"solution"`
}
type fsCookie struct {
Name string `json:"name"`
Value string `json:"value"`
Domain string `json:"domain"`
Path string `json:"path"`
Expires float64 `json:"expires"`
HTTPOnly bool `json:"httpOnly"`
Secure bool `json:"secure"`
}
// Get fetches a Cloudflare-protected URL via FlareSolverr.
// Returns rendered HTML and extracted cookies.
func (f *FlareSolverrClient) Get(ctx context.Context, url string) (html string, cookies []*http.Cookie, err error) {
payload, _ := json.Marshal(flareSolverrRequest{
Cmd: "request.get",
URL: url,
MaxTimeout: 60000,
})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, f.endpoint+"/v1", bytes.NewReader(payload))
if err != nil {
return "", nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := f.client.Do(req)
if err != nil {
return "", nil, err
}
defer resp.Body.Close()
var fsResp FlareSolverrResponse
if err := json.NewDecoder(resp.Body).Decode(&fsResp); err != nil {
return "", nil, err
}
if fsResp.Status != "ok" {
return "", nil, fmt.Errorf("flaresolverr: status %q", fsResp.Status)
}
for _, c := range fsResp.Solution.Cookies {
cookies = append(cookies, &http.Cookie{
Name: c.Name,
Value: c.Value,
Domain: c.Domain,
Path: c.Path,
HttpOnly: c.HTTPOnly,
Secure: c.Secure,
})
}
return fsResp.Solution.Response, cookies, nil
}