github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/lib/client/http_client.go (about)

     1  package rpcclient
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"net/url"
    11  	"reflect"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/pkg/errors"
    16  
    17  	amino "github.com/evdatsion/go-amino"
    18  
    19  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    20  	types "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/types"
    21  )
    22  
    23  const (
    24  	protoHTTP  = "http"
    25  	protoHTTPS = "https"
    26  	protoWSS   = "wss"
    27  	protoWS    = "ws"
    28  	protoTCP   = "tcp"
    29  )
    30  
    31  // HTTPClient is a common interface for JSONRPCClient and URIClient.
    32  type HTTPClient interface {
    33  	Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
    34  	Codec() *amino.Codec
    35  	SetCodec(*amino.Codec)
    36  }
    37  
    38  // TODO: Deprecate support for IP:PORT or /path/to/socket
    39  func makeHTTPDialer(remoteAddr string) (string, string, func(string, string) (net.Conn, error)) {
    40  	// protocol to use for http operations, to support both http and https
    41  	clientProtocol := protoHTTP
    42  
    43  	parts := strings.SplitN(remoteAddr, "://", 2)
    44  	var protocol, address string
    45  	if len(parts) == 1 {
    46  		// default to tcp if nothing specified
    47  		protocol, address = protoTCP, remoteAddr
    48  	} else if len(parts) == 2 {
    49  		protocol, address = parts[0], parts[1]
    50  	} else {
    51  		// return a invalid message
    52  		msg := fmt.Sprintf("Invalid addr: %s", remoteAddr)
    53  		return clientProtocol, msg, func(_ string, _ string) (net.Conn, error) {
    54  			return nil, errors.New(msg)
    55  		}
    56  	}
    57  
    58  	// accept http as an alias for tcp and set the client protocol
    59  	switch protocol {
    60  	case protoHTTP, protoHTTPS:
    61  		clientProtocol = protocol
    62  		protocol = protoTCP
    63  	case protoWS, protoWSS:
    64  		clientProtocol = protocol
    65  	}
    66  
    67  	// replace / with . for http requests (kvstore domain)
    68  	trimmedAddress := strings.Replace(address, "/", ".", -1)
    69  	return clientProtocol, trimmedAddress, func(proto, addr string) (net.Conn, error) {
    70  		return net.Dial(protocol, address)
    71  	}
    72  }
    73  
    74  // We overwrite the http.Client.Dial so we can do http over tcp or unix.
    75  // remoteAddr should be fully featured (eg. with tcp:// or unix://)
    76  func makeHTTPClient(remoteAddr string) (string, *http.Client) {
    77  	protocol, address, dialer := makeHTTPDialer(remoteAddr)
    78  	return protocol + "://" + address, &http.Client{
    79  		Transport: &http.Transport{
    80  			// Set to true to prevent GZIP-bomb DoS attacks
    81  			DisableCompression: true,
    82  			Dial:               dialer,
    83  		},
    84  	}
    85  }
    86  
    87  //------------------------------------------------------------------------------------
    88  
    89  // jsonRPCBufferedRequest encapsulates a single buffered request, as well as its
    90  // anticipated response structure.
    91  type jsonRPCBufferedRequest struct {
    92  	request types.RPCRequest
    93  	result  interface{} // The result will be deserialized into this object.
    94  }
    95  
    96  // JSONRPCRequestBatch allows us to buffer multiple request/response structures
    97  // into a single batch request. Note that this batch acts like a FIFO queue, and
    98  // is thread-safe.
    99  type JSONRPCRequestBatch struct {
   100  	client *JSONRPCClient
   101  
   102  	mtx      sync.Mutex
   103  	requests []*jsonRPCBufferedRequest
   104  }
   105  
   106  // JSONRPCClient takes params as a slice
   107  type JSONRPCClient struct {
   108  	address string
   109  	client  *http.Client
   110  	id      types.JSONRPCStringID
   111  	cdc     *amino.Codec
   112  }
   113  
   114  // JSONRPCCaller implementers can facilitate calling the JSON RPC endpoint.
   115  type JSONRPCCaller interface {
   116  	Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
   117  }
   118  
   119  // Both JSONRPCClient and JSONRPCRequestBatch can facilitate calls to the JSON
   120  // RPC endpoint.
   121  var _ JSONRPCCaller = (*JSONRPCClient)(nil)
   122  var _ JSONRPCCaller = (*JSONRPCRequestBatch)(nil)
   123  
   124  // NewJSONRPCClient returns a JSONRPCClient pointed at the given address.
   125  func NewJSONRPCClient(remote string) *JSONRPCClient {
   126  	address, client := makeHTTPClient(remote)
   127  	return &JSONRPCClient{
   128  		address: address,
   129  		client:  client,
   130  		id:      types.JSONRPCStringID("jsonrpc-client-" + cmn.RandStr(8)),
   131  		cdc:     amino.NewCodec(),
   132  	}
   133  }
   134  
   135  // Call will send the request for the given method through to the RPC endpoint
   136  // immediately, without buffering of requests.
   137  func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
   138  	request, err := types.MapToRequest(c.cdc, c.id, method, params)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	requestBytes, err := json.Marshal(request)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	requestBuf := bytes.NewBuffer(requestBytes)
   147  	httpResponse, err := c.client.Post(c.address, "text/json", requestBuf)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	defer httpResponse.Body.Close() // nolint: errcheck
   152  
   153  	responseBytes, err := ioutil.ReadAll(httpResponse.Body)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return unmarshalResponseBytes(c.cdc, responseBytes, c.id, result)
   158  }
   159  
   160  // NewRequestBatch starts a batch of requests for this client.
   161  func (c *JSONRPCClient) NewRequestBatch() *JSONRPCRequestBatch {
   162  	return &JSONRPCRequestBatch{
   163  		requests: make([]*jsonRPCBufferedRequest, 0),
   164  		client:   c,
   165  	}
   166  }
   167  
   168  func (c *JSONRPCClient) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, error) {
   169  	reqs := make([]types.RPCRequest, 0, len(requests))
   170  	results := make([]interface{}, 0, len(requests))
   171  	for _, req := range requests {
   172  		reqs = append(reqs, req.request)
   173  		results = append(results, req.result)
   174  	}
   175  	// serialize the array of requests into a single JSON object
   176  	requestBytes, err := json.Marshal(reqs)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	httpResponse, err := c.client.Post(c.address, "text/json", bytes.NewBuffer(requestBytes))
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	defer httpResponse.Body.Close() // nolint: errcheck
   185  
   186  	responseBytes, err := ioutil.ReadAll(httpResponse.Body)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	return unmarshalResponseBytesArray(c.cdc, responseBytes, c.id, results)
   191  }
   192  
   193  func (c *JSONRPCClient) Codec() *amino.Codec {
   194  	return c.cdc
   195  }
   196  
   197  func (c *JSONRPCClient) SetCodec(cdc *amino.Codec) {
   198  	c.cdc = cdc
   199  }
   200  
   201  //-------------------------------------------------------------
   202  
   203  // Count returns the number of enqueued requests waiting to be sent.
   204  func (b *JSONRPCRequestBatch) Count() int {
   205  	b.mtx.Lock()
   206  	defer b.mtx.Unlock()
   207  	return len(b.requests)
   208  }
   209  
   210  func (b *JSONRPCRequestBatch) enqueue(req *jsonRPCBufferedRequest) {
   211  	b.mtx.Lock()
   212  	defer b.mtx.Unlock()
   213  	b.requests = append(b.requests, req)
   214  }
   215  
   216  // Clear empties out the request batch.
   217  func (b *JSONRPCRequestBatch) Clear() int {
   218  	b.mtx.Lock()
   219  	defer b.mtx.Unlock()
   220  	return b.clear()
   221  }
   222  
   223  func (b *JSONRPCRequestBatch) clear() int {
   224  	count := len(b.requests)
   225  	b.requests = make([]*jsonRPCBufferedRequest, 0)
   226  	return count
   227  }
   228  
   229  // Send will attempt to send the current batch of enqueued requests, and then
   230  // will clear out the requests once done. On success, this returns the
   231  // deserialized list of results from each of the enqueued requests.
   232  func (b *JSONRPCRequestBatch) Send() ([]interface{}, error) {
   233  	b.mtx.Lock()
   234  	defer func() {
   235  		b.clear()
   236  		b.mtx.Unlock()
   237  	}()
   238  	return b.client.sendBatch(b.requests)
   239  }
   240  
   241  // Call enqueues a request to call the given RPC method with the specified
   242  // parameters, in the same way that the `JSONRPCClient.Call` function would.
   243  func (b *JSONRPCRequestBatch) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
   244  	request, err := types.MapToRequest(b.client.cdc, b.client.id, method, params)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	b.enqueue(&jsonRPCBufferedRequest{request: request, result: result})
   249  	return result, nil
   250  }
   251  
   252  //-------------------------------------------------------------
   253  
   254  // URI takes params as a map
   255  type URIClient struct {
   256  	address string
   257  	client  *http.Client
   258  	cdc     *amino.Codec
   259  }
   260  
   261  func NewURIClient(remote string) *URIClient {
   262  	address, client := makeHTTPClient(remote)
   263  	return &URIClient{
   264  		address: address,
   265  		client:  client,
   266  		cdc:     amino.NewCodec(),
   267  	}
   268  }
   269  
   270  func (c *URIClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
   271  	values, err := argsToURLValues(c.cdc, params)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	// log.Info(Fmt("URI request to %v (%v): %v", c.address, method, values))
   276  	resp, err := c.client.PostForm(c.address+"/"+method, values)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	defer resp.Body.Close() // nolint: errcheck
   281  
   282  	responseBytes, err := ioutil.ReadAll(resp.Body)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	return unmarshalResponseBytes(c.cdc, responseBytes, "", result)
   287  }
   288  
   289  func (c *URIClient) Codec() *amino.Codec {
   290  	return c.cdc
   291  }
   292  
   293  func (c *URIClient) SetCodec(cdc *amino.Codec) {
   294  	c.cdc = cdc
   295  }
   296  
   297  //------------------------------------------------
   298  
   299  func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, expectedID types.JSONRPCStringID, result interface{}) (interface{}, error) {
   300  	// Read response.  If rpc/core/types is imported, the result will unmarshal
   301  	// into the correct type.
   302  	// log.Notice("response", "response", string(responseBytes))
   303  	var err error
   304  	response := &types.RPCResponse{}
   305  	err = json.Unmarshal(responseBytes, response)
   306  	if err != nil {
   307  		return nil, errors.Wrap(err, "error unmarshalling rpc response")
   308  	}
   309  	if response.Error != nil {
   310  		return nil, errors.Wrap(response.Error, "response error")
   311  	}
   312  	// From the JSON-RPC 2.0 spec:
   313  	//  id: It MUST be the same as the value of the id member in the Request Object.
   314  	if err := validateResponseID(response, expectedID); err != nil {
   315  		return nil, err
   316  	}
   317  	// Unmarshal the RawMessage into the result.
   318  	err = cdc.UnmarshalJSON(response.Result, result)
   319  	if err != nil {
   320  		return nil, errors.Wrap(err, "error unmarshalling rpc response result")
   321  	}
   322  	return result, nil
   323  }
   324  
   325  func unmarshalResponseBytesArray(cdc *amino.Codec, responseBytes []byte, expectedID types.JSONRPCStringID, results []interface{}) ([]interface{}, error) {
   326  	var (
   327  		err       error
   328  		responses []types.RPCResponse
   329  	)
   330  	err = json.Unmarshal(responseBytes, &responses)
   331  	if err != nil {
   332  		return nil, errors.Wrap(err, "error unmarshalling rpc response")
   333  	}
   334  	// No response error checking here as there may be a mixture of successful
   335  	// and unsuccessful responses.
   336  
   337  	if len(results) != len(responses) {
   338  		return nil, errors.Errorf("expected %d result objects into which to inject responses, but got %d", len(responses), len(results))
   339  	}
   340  
   341  	for i, response := range responses {
   342  		// From the JSON-RPC 2.0 spec:
   343  		//  id: It MUST be the same as the value of the id member in the Request Object.
   344  		if err := validateResponseID(&response, expectedID); err != nil {
   345  			return nil, errors.Wrapf(err, "failed to validate response ID in response %d", i)
   346  		}
   347  		if err := cdc.UnmarshalJSON(responses[i].Result, results[i]); err != nil {
   348  			return nil, errors.Wrap(err, "error unmarshalling rpc response result")
   349  		}
   350  	}
   351  	return results, nil
   352  }
   353  
   354  func validateResponseID(res *types.RPCResponse, expectedID types.JSONRPCStringID) error {
   355  	// we only validate a response ID if the expected ID is non-empty
   356  	if len(expectedID) == 0 {
   357  		return nil
   358  	}
   359  	if res.ID == nil {
   360  		return errors.Errorf("missing ID in response")
   361  	}
   362  	id, ok := res.ID.(types.JSONRPCStringID)
   363  	if !ok {
   364  		return errors.Errorf("expected ID string in response but got: %v", id)
   365  	}
   366  	if expectedID != id {
   367  		return errors.Errorf("response ID (%s) does not match request ID (%s)", id, expectedID)
   368  	}
   369  	return nil
   370  }
   371  
   372  func argsToURLValues(cdc *amino.Codec, args map[string]interface{}) (url.Values, error) {
   373  	values := make(url.Values)
   374  	if len(args) == 0 {
   375  		return values, nil
   376  	}
   377  	err := argsToJSON(cdc, args)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	for key, val := range args {
   382  		values.Set(key, val.(string))
   383  	}
   384  	return values, nil
   385  }
   386  
   387  func argsToJSON(cdc *amino.Codec, args map[string]interface{}) error {
   388  	for k, v := range args {
   389  		rt := reflect.TypeOf(v)
   390  		isByteSlice := rt.Kind() == reflect.Slice && rt.Elem().Kind() == reflect.Uint8
   391  		if isByteSlice {
   392  			bytes := reflect.ValueOf(v).Bytes()
   393  			args[k] = fmt.Sprintf("0x%X", bytes)
   394  			continue
   395  		}
   396  
   397  		data, err := cdc.MarshalJSON(v)
   398  		if err != nil {
   399  			return err
   400  		}
   401  		args[k] = string(data)
   402  	}
   403  	return nil
   404  }