package jsonrpc2 import ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "github.com/tv42/httpunix" ) type httpClient struct { c *http.Client url string } // NewHTTPClient returns a Client which will use HTTP POST requests against the // given URL as a transport for JSONRPC2 method calls. func NewHTTPClient(urlStr string) Client { return &httpClient{ c: &http.Client{ Transport: http.DefaultTransport.(*http.Transport).Clone(), }, url: urlStr, } } // NewUnixHTTPClient returns a Client which will use HTTP POST requests against // the given unix socket as a transport for JSONRPC2 method calls. The given // path will be used as the path portion of the HTTP request. func NewUnixHTTPClient(unixSocketPath, reqPath string) Client { const host = "uds" u := &url.URL{ Scheme: httpunix.Scheme, Host: host, Path: reqPath, } transport := new(httpunix.Transport) transport.RegisterLocation(host, unixSocketPath) return &httpClient{ c: &http.Client{Transport: transport}, url: u.String(), } } func (c *httpClient) Call( ctx context.Context, rcv any, method string, args ...any, ) error { var ( body = new(bytes.Buffer) enc = json.NewEncoder(body) ) id, err := encodeRequest(ctx, enc, method, args) if err != nil { return fmt.Errorf("encoding request: %w", err) } req, err := http.NewRequestWithContext(ctx, "POST", c.url, body) if err != nil { return fmt.Errorf("constructing POST request to %q: %w", c.url, err) } res, err := c.c.Do(req) if err != nil { return fmt.Errorf("performing request: %w", err) } defer res.Body.Close() dec := json.NewDecoder(res.Body) resID, err := decodeResponse(dec, rcv) if errors.As(err, new(Error)) { return err } else if err != nil { return fmt.Errorf("decoding response: %w", err) } else if resID != id { return fmt.Errorf( "expected response with ID %q, got %q", id, resID, ) } return nil }