client.go

 1package client
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"net"
 7	"net/http"
 8	"time"
 9
10	"github.com/charmbracelet/crush/internal/config"
11	"github.com/charmbracelet/crush/internal/server"
12)
13
14// Client represents an RPC client connected to a Crush server.
15type Client struct {
16	h *http.Client
17}
18
19// DefaultClient creates a new [Client] connected to the default server address.
20func DefaultClient() (*Client, error) {
21	return NewClient("unix", server.DefaultAddr())
22}
23
24// NewClient creates a new [Client] connected to the server at the given
25// network and address.
26func NewClient(network, address string) (*Client, error) {
27	var p http.Protocols
28	p.SetHTTP1(true)
29	p.SetUnencryptedHTTP2(true)
30	tr := http.DefaultTransport.(*http.Transport).Clone()
31	tr.Protocols = &p
32	tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
33		d := net.Dialer{
34			Timeout:   30 * time.Second,
35			KeepAlive: 30 * time.Second,
36		}
37		return d.DialContext(ctx, network, address)
38	}
39	h := &http.Client{
40		Transport: tr,
41	}
42	return &Client{h: h}, nil
43}
44
45// GetConfig retrieves the server's configuration via RPC.
46func (c *Client) GetConfig() (*config.Config, error) {
47	var cfg config.Config
48	rsp, err := c.h.Get("http://localhost/v1/config")
49	if err != nil {
50		return nil, err
51	}
52	defer rsp.Body.Close()
53	if err := json.NewDecoder(rsp.Body).Decode(&cfg); err != nil {
54		return nil, err
55	}
56	return &cfg, nil
57}