/ client / client.go
client.go
 1  package client
 2  
 3  import (
 4  	"fmt"
 5  	"io"
 6  	"net/http"
 7  	"strings"
 8  	"time"
 9  )
10  
11  type Client struct {
12  	baseURL     string
13  	httpClient  *http.Client
14  	userAgent   string
15  	accessToken string
16  }
17  
18  type Option func(*Client)
19  
20  func WithHTTPClient(hc *http.Client) Option {
21  	return func(c *Client) { c.httpClient = hc }
22  }
23  
24  func WithTimeout(d time.Duration) Option {
25  	return func(c *Client) { c.httpClient.Timeout = d }
26  }
27  
28  func WithUserAgent(ua string) Option {
29  	return func(c *Client) { c.userAgent = ua }
30  }
31  
32  func WithAccessToken(token string) Option {
33  	return func(c *Client) { c.accessToken = token }
34  }
35  
36  func New(baseURL string, opts ...Option) *Client {
37  	c := &Client{
38  		baseURL:    strings.TrimRight(baseURL, "/"),
39  		httpClient: &http.Client{Timeout: 10 * time.Second},
40  	}
41  	for _, o := range opts {
42  		o(c)
43  	}
44  	return c
45  }
46  
47  func checkStatus(resp *http.Response) error {
48  	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
49  		return nil
50  	}
51  	body, _ := io.ReadAll(resp.Body)
52  	msg := strings.TrimSpace(string(body))
53  	if msg == "" {
54  		msg = resp.Status
55  	}
56  	return fmt.Errorf("invalid status code (%d): %s", resp.StatusCode, msg)
57  }
58  
59  // builds an http.Request with Origin: hister:// set for CSRF bypass.
60  func (c *Client) newRequest(method, path string, body io.Reader) (*http.Request, error) {
61  	req, err := http.NewRequest(method, c.baseURL+path, body)
62  	if err != nil {
63  		return nil, err
64  	}
65  	req.Header.Set("Origin", "hister://")
66  	if c.userAgent != "" {
67  		req.Header.Set("User-Agent", c.userAgent)
68  	}
69  	if c.accessToken != "" {
70  		req.Header.Set("X-Access-Token", c.accessToken)
71  	}
72  	return req, nil
73  }