mirror of
https://github.com/foomo/gotsrpc.git
synced 2025-10-16 12:35:35 +00:00
139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
package gotsrpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/ugorji/go/codec"
|
|
)
|
|
|
|
const (
|
|
HeaderServiceToService = "X-Foomo-S2s"
|
|
)
|
|
|
|
// ClientTransport to use for calls
|
|
// var ClientTransport = &http.Transport{}
|
|
|
|
var _ Client = &bufferedClient{}
|
|
|
|
type Client interface {
|
|
Call(ctx context.Context, url string, endpoint string, method string, args []interface{}, reply []interface{}) (err error)
|
|
SetClientEncoding(encoding ClientEncoding)
|
|
SetTransportHttpClient(client *http.Client)
|
|
SetDefaultHeaders(headers http.Header)
|
|
}
|
|
|
|
func NewClient() Client {
|
|
return &bufferedClient{client: defaultHttpFactory(), handle: getHandleForEncoding(EncodingMsgpack), headers: nil}
|
|
}
|
|
|
|
func NewClientWithHttpClient(client *http.Client) Client { //nolint:staticcheck
|
|
if client != nil {
|
|
return &bufferedClient{client: client, handle: getHandleForEncoding(EncodingMsgpack), headers: nil}
|
|
} else {
|
|
return &bufferedClient{client: defaultHttpFactory(), handle: getHandleForEncoding(EncodingMsgpack), headers: nil}
|
|
}
|
|
}
|
|
|
|
func newRequest(ctx context.Context, url string, contentType string, buffer *bytes.Buffer, headers http.Header) (r *http.Request, err error) {
|
|
if buffer == nil {
|
|
buffer = &bytes.Buffer{}
|
|
}
|
|
request, errRequest := http.NewRequestWithContext(ctx, http.MethodPost, url, buffer)
|
|
if errRequest != nil {
|
|
return nil, errors.Wrap(errRequest, "could not create a request")
|
|
}
|
|
if len(headers) > 0 {
|
|
request.Header = headers
|
|
}
|
|
request.Header.Set("Content-Type", contentType)
|
|
request.Header.Set("Accept", contentType)
|
|
request.Header.Set(HeaderServiceToService, "true")
|
|
|
|
return request, nil
|
|
}
|
|
|
|
type bufferedClient struct {
|
|
client *http.Client
|
|
handle *clientHandle
|
|
headers http.Header
|
|
}
|
|
|
|
func (c *bufferedClient) SetDefaultHeaders(headers http.Header) {
|
|
c.headers = headers
|
|
}
|
|
|
|
func (c *bufferedClient) SetClientEncoding(encoding ClientEncoding) {
|
|
c.handle = getHandleForEncoding(encoding)
|
|
}
|
|
|
|
func (c *bufferedClient) SetTransportHttpClient(client *http.Client) { //nolint:staticcheck
|
|
c.client = client
|
|
}
|
|
|
|
// Call calls a method on the remove service
|
|
func (c *bufferedClient) Call(ctx context.Context, url string, endpoint string, method string, args []interface{}, reply []interface{}) error {
|
|
// Marshall args
|
|
b := new(bytes.Buffer)
|
|
|
|
// If no arguments are set, remove
|
|
if len(args) > 0 {
|
|
if err := codec.NewEncoder(b, c.handle.handle).Encode(args); err != nil {
|
|
return NewClientError(errors.Wrap(err, "failed to encode arguments"))
|
|
}
|
|
}
|
|
|
|
// Create post url
|
|
postURL := fmt.Sprintf("%s%s/%s", url, endpoint, method)
|
|
|
|
// Create request
|
|
request, errRequest := newRequest(ctx, postURL, c.handle.contentType, b, c.headers.Clone())
|
|
if errRequest != nil {
|
|
return NewClientError(errors.Wrap(errRequest, "failed to create request"))
|
|
}
|
|
|
|
resp, errDo := c.client.Do(request)
|
|
if errDo != nil {
|
|
return NewClientError(errors.Wrap(errDo, "failed to send request"))
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
if _, err := io.Copy(buf, resp.Body); err != nil {
|
|
return NewClientError(errors.Wrap(err, "failed to read response body"))
|
|
}
|
|
|
|
// Check status
|
|
if resp.StatusCode != http.StatusOK {
|
|
return NewClientError(NewHTTPError(buf.String(), resp.StatusCode))
|
|
}
|
|
|
|
clientHandle := getHandlerForContentType(resp.Header.Get("Content-Type"))
|
|
|
|
wrappedReply := reply
|
|
if clientHandle.beforeDecodeReply != nil {
|
|
if value, err := clientHandle.beforeDecodeReply(reply); err != nil {
|
|
return NewClientError(errors.Wrap(err, "failed to call beforeDecodeReply hook"))
|
|
} else {
|
|
wrappedReply = value
|
|
}
|
|
}
|
|
|
|
if err := codec.NewDecoder(buf, clientHandle.handle).Decode(wrappedReply); err != nil {
|
|
return NewClientError(errors.Wrap(err, "failed to decode response"))
|
|
}
|
|
|
|
// replace error
|
|
if clientHandle.afterDecodeReply != nil {
|
|
if err := clientHandle.afterDecodeReply(&reply, wrappedReply); err != nil {
|
|
return NewClientError(errors.Wrap(err, "failed to call afterDecodeReply hook"))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|