isle/go/daemon/jsonrpc2/client_http.go

73 lines
1.5 KiB
Go

package jsonrpc2
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"isle/toolkit"
"net/http"
)
// HTTPClient makes JSONRPC2 requests over an HTTP endpoint.
//
// Close should be called once the HTTPClient is not longer needed.
type HTTPClient struct {
c toolkit.HTTPClient
url string
}
// NewHTTPClient returns an HTTPClient which will use HTTP POST requests against
// the given URL as a transport for JSONRPC2 method calls.
func NewHTTPClient(httpClient toolkit.HTTPClient, urlStr string) *HTTPClient {
return &HTTPClient{
c: httpClient,
url: urlStr,
}
}
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
}
func (c *HTTPClient) Close() error {
return c.c.Close()
}