chaturbate-dvr/internal/internal_req.go
2025-05-04 15:24:39 +08:00

124 lines
3.2 KiB
Go

package internal
import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/teacat/chaturbate-dvr/server"
)
// Req represents an HTTP client with customized settings.
type Req struct {
client *http.Client
}
// NewReq creates a new HTTP client with specific transport configurations.
func NewReq() *Req {
return &Req{
client: &http.Client{
Transport: CreateTransport(),
},
}
}
// CreateTransport initializes a custom HTTP transport.
func CreateTransport() *http.Transport {
// The DefaultTransport allows user changes the proxy settings via environment variables
// such as HTTP_PROXY, HTTPS_PROXY.
defaultTransport := http.DefaultTransport.(*http.Transport)
newTransport := defaultTransport.Clone()
newTransport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
return newTransport
}
// Get sends an HTTP GET request and returns the response as a string.
func (h *Req) Get(ctx context.Context, url string) (string, error) {
resp, err := h.GetBytes(ctx, url)
if err != nil {
return "", fmt.Errorf("get bytes: %w", err)
}
return string(resp), nil
}
// GetBytes sends an HTTP GET request and returns the response as a byte slice.
func (h *Req) GetBytes(ctx context.Context, url string) ([]byte, error) {
req, cancel, err := CreateRequest(ctx, url)
if err != nil {
return nil, fmt.Errorf("new request: %w", err)
}
defer cancel()
resp, err := h.client.Do(req)
if err != nil {
return nil, fmt.Errorf("client do: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusForbidden {
return nil, fmt.Errorf("forbidden: %w", ErrPrivateStream)
}
return ReadResponseBody(resp)
}
// CreateRequest constructs an HTTP GET request with necessary headers.
func CreateRequest(ctx context.Context, url string) (*http.Request, context.CancelFunc, error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) // timed out after 10 seconds
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, cancel, err
}
SetRequestHeaders(req)
return req, cancel, nil
}
// SetRequestHeaders applies necessary headers to the request.
func SetRequestHeaders(req *http.Request) {
if server.Config.UserAgent != "" {
req.Header.Set("User-Agent", server.Config.UserAgent)
}
if server.Config.Cookies != "" {
cookies := ParseCookies(server.Config.Cookies)
for name, value := range cookies {
req.AddCookie(&http.Cookie{Name: name, Value: value})
}
}
}
// ReadResponseBody reads and returns the response body.
func ReadResponseBody(resp *http.Response) ([]byte, error) {
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
return b, nil
}
// ParseCookies converts a cookie string into a map.
func ParseCookies(cookieStr string) map[string]string {
cookies := make(map[string]string)
pairs := strings.Split(cookieStr, ";")
// Iterate over each cookie pair and extract key-value pairs
for _, pair := range pairs {
parts := strings.SplitN(strings.TrimSpace(pair), "=", 2)
if len(parts) == 2 {
// Trim spaces around key and value
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
// Store cookie name and value in the map
cookies[key] = value
}
}
return cookies
}