96 lines
2.3 KiB
Go
Executable File
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
|
|
}
|