github.com/braveheart12/insolar-09-08-19@v0.8.7/api/requester/requester.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package requester
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"strconv"
    27  	"time"
    28  
    29  	"github.com/insolar/insolar/core"
    30  	"github.com/insolar/insolar/instrumentation/inslogger"
    31  	"github.com/insolar/insolar/platformpolicy"
    32  	"github.com/pkg/errors"
    33  )
    34  
    35  var httpClient *http.Client
    36  
    37  const (
    38  	RequestTimeout = 15 * time.Second
    39  )
    40  
    41  func init() {
    42  	httpClient = createHTTPClient()
    43  }
    44  
    45  func SetTimeout(timeout uint) {
    46  	if timeout > 0 {
    47  		httpClient.Timeout = time.Duration(timeout) * time.Second
    48  	} else {
    49  		httpClient.Timeout = RequestTimeout
    50  	}
    51  }
    52  
    53  // createHTTPClient for connection re-use
    54  func createHTTPClient() *http.Client {
    55  	client := &http.Client{
    56  		Transport: &http.Transport{},
    57  		Timeout:   RequestTimeout,
    58  	}
    59  
    60  	return client
    61  }
    62  
    63  // verbose switches on verbose mode
    64  var verbose = false
    65  var scheme = platformpolicy.NewPlatformCryptographyScheme()
    66  
    67  func verboseInfo(ctx context.Context, msg string) {
    68  	if verbose {
    69  		inslogger.FromContext(ctx).Info(msg)
    70  	}
    71  }
    72  
    73  // SetVerbose switches on verbose mode
    74  func SetVerbose(verb bool) {
    75  	verbose = verb
    76  }
    77  
    78  // PostParams represents params struct
    79  type PostParams = map[string]interface{}
    80  
    81  // GetResponseBody makes request and extracts body
    82  func GetResponseBody(url string, postP PostParams) ([]byte, error) {
    83  	jsonValue, err := json.Marshal(postP)
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "[ getResponseBody ] Problem with marshaling params")
    86  	}
    87  
    88  	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue))
    89  	if err != nil {
    90  		return nil, errors.Wrap(err, "[ getResponseBody ] Problem with creating request")
    91  	}
    92  	req.Header.Set("Content-Type", "application/json")
    93  	postResp, err := httpClient.Do(req)
    94  	if err != nil {
    95  		return nil, errors.Wrap(err, "[ getResponseBody ] Problem with sending request")
    96  	}
    97  	if http.StatusOK != postResp.StatusCode {
    98  		return nil, errors.New("[ getResponseBody ] Bad http response code: " + strconv.Itoa(postResp.StatusCode))
    99  	}
   100  
   101  	body, err := ioutil.ReadAll(postResp.Body)
   102  	defer postResp.Body.Close()
   103  	if err != nil {
   104  		return nil, errors.Wrap(err, "[ getResponseBody ] Problem with reading body")
   105  	}
   106  
   107  	return body, nil
   108  }
   109  
   110  // GetSeed makes rpc request to seed.Get method and extracts it
   111  func GetSeed(url string) ([]byte, error) {
   112  	body, err := GetResponseBody(url+"/rpc", PostParams{
   113  		"jsonrpc": "2.0",
   114  		"method":  "seed.Get",
   115  		"id":      "",
   116  	})
   117  	if err != nil {
   118  		return nil, errors.Wrap(err, "[ getSeed ]")
   119  	}
   120  
   121  	seedResp := rpcSeedResponse{}
   122  
   123  	err = json.Unmarshal(body, &seedResp)
   124  	if err != nil {
   125  		return nil, errors.Wrap(err, "[ getSeed ] Can't unmarshal")
   126  	}
   127  	if seedResp.Error != nil {
   128  		return nil, errors.New("[ getSeed ] Field 'error' is not nil: " + fmt.Sprint(seedResp.Error))
   129  	}
   130  	res := &seedResp.Result
   131  	if res == nil {
   132  		return nil, errors.New("[ getSeed ] Field 'result' is nil")
   133  	}
   134  
   135  	return res.Seed, nil
   136  }
   137  
   138  func constructParams(params []interface{}) ([]byte, error) {
   139  	args, err := core.MarshalArgs(params...)
   140  	if err != nil {
   141  		return nil, errors.Wrap(err, "[ constructParams ]")
   142  	}
   143  	return args, nil
   144  }
   145  
   146  // SendWithSeed sends request with known seed
   147  func SendWithSeed(ctx context.Context, url string, userCfg *UserConfigJSON, reqCfg *RequestConfigJSON, seed []byte) ([]byte, error) {
   148  	if userCfg == nil || reqCfg == nil {
   149  		return nil, errors.New("[ Send ] Configs must be initialized")
   150  	}
   151  
   152  	params, err := constructParams(reqCfg.Params)
   153  	if err != nil {
   154  		return nil, errors.Wrap(err, "[ Send ] Problem with serializing params")
   155  	}
   156  
   157  	callerRef, err := core.NewRefFromBase58(userCfg.Caller)
   158  	if err != nil {
   159  		return nil, errors.Wrap(err, "[ Send ] Failed to parse userCfg.Caller")
   160  	}
   161  
   162  	serRequest, err := core.MarshalArgs(
   163  		*callerRef,
   164  		reqCfg.Method,
   165  		params,
   166  		seed)
   167  	if err != nil {
   168  		return nil, errors.Wrap(err, "[ Send ] Problem with serializing request")
   169  	}
   170  
   171  	verboseInfo(ctx, "Signing request ...")
   172  	cs := scheme.Signer(userCfg.privateKeyObject)
   173  	signature, err := cs.Sign(serRequest)
   174  	if err != nil {
   175  		return nil, errors.Wrap(err, "[ Send ] Problem with signing request")
   176  	}
   177  	verboseInfo(ctx, "Signing request completed")
   178  
   179  	body, err := GetResponseBody(url, PostParams{
   180  		"params":    params,
   181  		"method":    reqCfg.Method,
   182  		"reference": userCfg.Caller,
   183  		"seed":      seed,
   184  		"signature": signature.Bytes(),
   185  	})
   186  
   187  	if err != nil {
   188  		return nil, errors.Wrap(err, "[ Send ] Problem with sending target request")
   189  	}
   190  
   191  	return body, nil
   192  }
   193  
   194  // Send first gets seed and after that makes target request
   195  func Send(ctx context.Context, url string, userCfg *UserConfigJSON, reqCfg *RequestConfigJSON) ([]byte, error) {
   196  	verboseInfo(ctx, "Sending GETSEED request ...")
   197  	seed, err := GetSeed(url)
   198  	if err != nil {
   199  		return nil, errors.Wrap(err, "[ Send ] Problem with getting seed")
   200  	}
   201  	verboseInfo(ctx, "GETSEED request completed. seed: "+string(seed))
   202  
   203  	response, err := SendWithSeed(ctx, url+"/call", userCfg, reqCfg, seed)
   204  	if err != nil {
   205  		return nil, errors.Wrap(err, "[ Send ]")
   206  	}
   207  
   208  	return response, nil
   209  }
   210  
   211  func getDefaultRPCParams(method string) PostParams {
   212  	return PostParams{
   213  		"jsonrpc": "2.0",
   214  		"id":      "",
   215  		"method":  method,
   216  	}
   217  }
   218  
   219  // Info makes rpc request to info.Get method and extracts it
   220  func Info(url string) (*InfoResponse, error) {
   221  	params := getDefaultRPCParams("info.Get")
   222  
   223  	body, err := GetResponseBody(url+"/rpc", params)
   224  	if err != nil {
   225  		return nil, errors.Wrap(err, "[ Info ]")
   226  	}
   227  
   228  	infoResp := rpcInfoResponse{}
   229  
   230  	err = json.Unmarshal(body, &infoResp)
   231  	if err != nil {
   232  		return nil, errors.Wrap(err, "[ Info ] Can't unmarshal")
   233  	}
   234  	if infoResp.Error != nil {
   235  		return nil, errors.New("[ Info ] Field 'error' is not nil: " + fmt.Sprint(infoResp.Error))
   236  	}
   237  	res := &infoResp.Result
   238  	if res == nil {
   239  		return nil, errors.New("[ Info ] Field 'result' is nil")
   240  	}
   241  
   242  	return res, nil
   243  }
   244  
   245  // Status makes rpc request to info.Status method and extracts it
   246  func Status(url string) (*StatusResponse, error) {
   247  	params := getDefaultRPCParams("status.Get")
   248  
   249  	body, err := GetResponseBody(url+"/rpc", params)
   250  	if err != nil {
   251  		return nil, errors.Wrap(err, "[ Status ]")
   252  	}
   253  
   254  	statusResp := rpcStatusResponse{}
   255  
   256  	err = json.Unmarshal(body, &statusResp)
   257  	if err != nil {
   258  		return nil, errors.Wrap(err, "[ Status ] Can't unmarshal")
   259  	}
   260  	if statusResp.Error != nil {
   261  		return nil, errors.New("[ Status ] Field 'error' is not nil: " + fmt.Sprint(statusResp.Error))
   262  	}
   263  	res := &statusResp.Result
   264  	if res == nil {
   265  		return nil, errors.New("[ Status ] Field 'result' is nil")
   266  	}
   267  
   268  	return res, nil
   269  }