rpc/client: take context as first param (#5347)

Closes #5145

also applies to light/client
This commit is contained in:
Anton Kaliaev
2020-09-23 09:21:57 +04:00
committed by GitHub
parent 0aecda68fc
commit 85a4be87a7
41 changed files with 706 additions and 503 deletions

View File

@@ -2,6 +2,7 @@ package client
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
@@ -78,12 +79,12 @@ func (u parsedURL) GetTrimmedURL() string {
// HTTPClient is a common interface for JSON-RPC HTTP clients.
type HTTPClient interface {
// Call calls the given method with the params and returns a result.
Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
Call(ctx context.Context, method string, params map[string]interface{}, result interface{}) (interface{}, error)
}
// Caller implementers can facilitate calling the JSON-RPC endpoint.
type Caller interface {
Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
Call(ctx context.Context, method string, params map[string]interface{}, result interface{}) (interface{}, error)
}
//-------------------------------------------------------------
@@ -151,7 +152,9 @@ func NewWithHTTPClient(remote string, client *http.Client) (*Client, error) {
// Call issues a POST HTTP request. Requests are JSON encoded. Content-Type:
// text/json.
func (c *Client) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
func (c *Client) Call(ctx context.Context, method string,
params map[string]interface{}, result interface{}) (interface{}, error) {
id := c.nextRequestID()
request, err := types.MapToRequest(id, method, params)
@@ -165,7 +168,7 @@ func (c *Client) Call(method string, params map[string]interface{}, result inter
}
requestBuf := bytes.NewBuffer(requestBytes)
httpRequest, err := http.NewRequest(http.MethodPost, c.address, requestBuf)
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, c.address, requestBuf)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
@@ -195,7 +198,7 @@ func (c *Client) NewRequestBatch() *RequestBatch {
}
}
func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, error) {
func (c *Client) sendBatch(ctx context.Context, requests []*jsonRPCBufferedRequest) ([]interface{}, error) {
reqs := make([]types.RPCRequest, 0, len(requests))
results := make([]interface{}, 0, len(requests))
for _, req := range requests {
@@ -206,12 +209,12 @@ func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, e
// serialize the array of requests into a single JSON object
requestBytes, err := json.Marshal(reqs)
if err != nil {
return nil, fmt.Errorf("failed to marshal requests: %w", err)
return nil, fmt.Errorf("json marshal: %w", err)
}
httpRequest, err := http.NewRequest(http.MethodPost, c.address, bytes.NewBuffer(requestBytes))
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, c.address, bytes.NewBuffer(requestBytes))
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
return nil, fmt.Errorf("new request: %w", err)
}
httpRequest.Header.Set("Content-Type", "text/json")
if c.username != "" || c.password != "" {
@@ -219,13 +222,13 @@ func (c *Client) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, e
}
httpResponse, err := c.client.Do(httpRequest)
if err != nil {
return nil, fmt.Errorf("post failed: %w", err)
return nil, fmt.Errorf("post: %w", err)
}
defer httpResponse.Body.Close()
responseBytes, err := ioutil.ReadAll(httpResponse.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
return nil, fmt.Errorf("read response body: %w", err)
}
// collect ids to check responses IDs in unmarshalResponseBytesArray
@@ -293,18 +296,19 @@ func (b *RequestBatch) clear() int {
// Send will attempt to send the current batch of enqueued requests, and then
// will clear out the requests once done. On success, this returns the
// deserialized list of results from each of the enqueued requests.
func (b *RequestBatch) Send() ([]interface{}, error) {
func (b *RequestBatch) Send(ctx context.Context) ([]interface{}, error) {
b.mtx.Lock()
defer func() {
b.clear()
b.mtx.Unlock()
}()
return b.client.sendBatch(b.requests)
return b.client.sendBatch(ctx, b.requests)
}
// Call enqueues a request to call the given RPC method with the specified
// parameters, in the same way that the `Client.Call` function would.
func (b *RequestBatch) Call(
_ context.Context,
method string,
params map[string]interface{},
result interface{},

View File

@@ -1,9 +1,11 @@
package client
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"strings"
types "github.com/tendermint/tendermint/rpc/jsonrpc/types"
)
@@ -49,21 +51,34 @@ func NewURI(remote string) (*URIClient, error) {
}
// Call issues a POST form HTTP request.
func (c *URIClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
func (c *URIClient) Call(ctx context.Context, method string,
params map[string]interface{}, result interface{}) (interface{}, error) {
values, err := argsToURLValues(params)
if err != nil {
return nil, fmt.Errorf("failed to encode params: %w", err)
}
resp, err := c.client.PostForm(c.address+"/"+method, values)
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
c.address+"/"+method,
strings.NewReader(values.Encode()),
)
if err != nil {
return nil, fmt.Errorf("post form failed: %w", err)
return nil, fmt.Errorf("new request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("post: %w", err)
}
defer resp.Body.Close()
responseBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
return nil, fmt.Errorf("read response body: %w", err)
}
return unmarshalResponseBytes(responseBytes, URIClientRequestID, result)

View File

@@ -37,6 +37,10 @@ const (
testVal = "acbd"
)
var (
ctx = context.Background()
)
type ResultEcho struct {
Value string `json:"value"`
}
@@ -156,7 +160,7 @@ func echoViaHTTP(cl client.Caller, val string) (string, error) {
"arg": val,
}
result := new(ResultEcho)
if _, err := cl.Call("echo", params, result); err != nil {
if _, err := cl.Call(ctx, "echo", params, result); err != nil {
return "", err
}
return result.Value, nil
@@ -167,7 +171,7 @@ func echoIntViaHTTP(cl client.Caller, val int) (int, error) {
"arg": val,
}
result := new(ResultEchoInt)
if _, err := cl.Call("echo_int", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_int", params, result); err != nil {
return 0, err
}
return result.Value, nil
@@ -178,7 +182,7 @@ func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) {
"arg": bytes,
}
result := new(ResultEchoBytes)
if _, err := cl.Call("echo_bytes", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_bytes", params, result); err != nil {
return []byte{}, err
}
return result.Value, nil
@@ -189,7 +193,7 @@ func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.Hex
"arg": bytes,
}
result := new(ResultEchoDataBytes)
if _, err := cl.Call("echo_data_bytes", params, result); err != nil {
if _, err := cl.Call(ctx, "echo_data_bytes", params, result); err != nil {
return []byte{}, err
}
return result.Value, nil