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 }