github.com/0chain/gosdk@v1.17.11/zboxcore/zboxutil/http.go (about) 1 package zboxutil 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net" 11 "net/http" 12 "net/url" 13 "os" 14 "path" 15 "strconv" 16 "sync" 17 "time" 18 19 "github.com/0chain/errors" 20 "github.com/0chain/gosdk/core/conf" 21 "github.com/0chain/gosdk/core/encryption" 22 "github.com/0chain/gosdk/core/logger" 23 "github.com/0chain/gosdk/zboxcore/blockchain" 24 "github.com/0chain/gosdk/zboxcore/client" 25 lru "github.com/hashicorp/golang-lru/v2" 26 "github.com/hashicorp/golang-lru/v2/simplelru" 27 "github.com/hitenjain14/fasthttp" 28 ) 29 30 const SC_REST_API_URL = "v1/screst/" 31 32 const MAX_RETRIES = 5 33 const SLEEP_BETWEEN_RETRIES = 5 34 35 // In percentage 36 const consensusThresh = float32(25.0) 37 38 // SCRestAPIHandler is a function type to handle the response from the SC Rest API 39 // 40 // `response` - the response from the SC Rest API 41 // `numSharders` - the number of sharders that responded 42 // `err` - the error if any 43 type SCRestAPIHandler func(response map[string][]byte, numSharders int, err error) 44 45 type HttpClient interface { 46 Do(req *http.Request) (*http.Response, error) 47 } 48 49 type FastClient interface { 50 DoTimeout(req *fasthttp.Request, resp *fasthttp.Response, timeout time.Duration) error 51 } 52 53 var ( 54 Client HttpClient 55 FastHttpClient FastClient 56 log logger.Logger 57 SignCache simplelru.LRUCache[string, string] 58 ) 59 60 const ( 61 respBodyPoolLimit = 1024 * 1024 * 16 //16MB 62 ) 63 64 func GetLogger() *logger.Logger { 65 return &log 66 } 67 68 const ( 69 ALLOCATION_ENDPOINT = "/allocation" 70 UPLOAD_ENDPOINT = "/v1/file/upload/" 71 RENAME_ENDPOINT = "/v1/file/rename/" 72 COPY_ENDPOINT = "/v1/file/copy/" 73 MOVE_ENDPOINT = "/v1/file/move/" 74 LIST_ENDPOINT = "/v1/file/list/" 75 REFERENCE_ENDPOINT = "/v1/file/referencepath/" 76 CONNECTION_ENDPOINT = "/v1/connection/details/" 77 COMMIT_ENDPOINT = "/v1/connection/commit/" 78 DOWNLOAD_ENDPOINT = "/v1/file/download/" 79 LATEST_READ_MARKER = "/v1/readmarker/latest" 80 FILE_META_ENDPOINT = "/v1/file/meta/" 81 FILE_STATS_ENDPOINT = "/v1/file/stats/" 82 OBJECT_TREE_ENDPOINT = "/v1/file/objecttree/" 83 REFS_ENDPOINT = "/v1/file/refs/" 84 RECENT_REFS_ENDPOINT = "/v1/file/refs/recent/" 85 COLLABORATOR_ENDPOINT = "/v1/file/collaborator/" 86 CALCULATE_HASH_ENDPOINT = "/v1/file/calculatehash/" 87 SHARE_ENDPOINT = "/v1/marketplace/shareinfo/" 88 DIR_ENDPOINT = "/v1/dir/" 89 PLAYLIST_LATEST_ENDPOINT = "/v1/playlist/latest/" 90 PLAYLIST_FILE_ENDPOINT = "/v1/playlist/file/" 91 WM_LOCK_ENDPOINT = "/v1/writemarker/lock/" 92 CREATE_CONNECTION_ENDPOINT = "/v1/connection/create/" 93 LATEST_WRITE_MARKER_ENDPOINT = "/v1/file/latestwritemarker/" 94 ROLLBACK_ENDPOINT = "/v1/connection/rollback/" 95 REDEEM_ENDPOINT = "/v1/connection/redeem/" 96 97 // CLIENT_SIGNATURE_HEADER represents http request header contains signature. 98 CLIENT_SIGNATURE_HEADER = "X-App-Client-Signature" 99 CLIENT_SIGNATURE_HEADER_V2 = "X-App-Client-Signature-V2" 100 ALLOCATION_ID_HEADER = "ALLOCATION-ID" 101 ) 102 103 func getEnvAny(names ...string) string { 104 for _, n := range names { 105 if val := os.Getenv(n); val != "" { 106 return val 107 } 108 } 109 return "" 110 } 111 112 type proxyFromEnv struct { 113 HTTPProxy string 114 HTTPSProxy string 115 NoProxy string 116 117 http, https *url.URL 118 } 119 120 func (pfe *proxyFromEnv) initialize() { 121 pfe.HTTPProxy = getEnvAny("HTTP_PROXY", "http_proxy") 122 pfe.HTTPSProxy = getEnvAny("HTTPS_PROXY", "https_proxy") 123 pfe.NoProxy = getEnvAny("NO_PROXY", "no_proxy") 124 125 if pfe.NoProxy != "" { 126 return 127 } 128 129 if pfe.HTTPProxy != "" { 130 pfe.http, _ = url.Parse(pfe.HTTPProxy) 131 } 132 if pfe.HTTPSProxy != "" { 133 pfe.https, _ = url.Parse(pfe.HTTPSProxy) 134 } 135 } 136 137 func (pfe *proxyFromEnv) isLoopback(host string) (ok bool) { 138 host, _, _ = net.SplitHostPort(host) 139 if host == "localhost" { 140 return true 141 } 142 return net.ParseIP(host).IsLoopback() 143 } 144 145 func GetFastHTTPClient() *fasthttp.Client { 146 fc, ok := FastHttpClient.(*fasthttp.Client) 147 if ok { 148 return fc 149 } 150 return nil 151 } 152 153 func (pfe *proxyFromEnv) Proxy(req *http.Request) (proxy *url.URL, err error) { 154 if pfe.isLoopback(req.URL.Host) { 155 switch req.URL.Scheme { 156 case "http": 157 return pfe.http, nil 158 case "https": 159 return pfe.https, nil 160 default: 161 } 162 } 163 return http.ProxyFromEnvironment(req) 164 } 165 166 var envProxy proxyFromEnv 167 168 func init() { 169 Client = &http.Client{ 170 Transport: DefaultTransport, 171 } 172 173 FastHttpClient = &fasthttp.Client{ 174 MaxIdleConnDuration: 45 * time.Second, 175 NoDefaultUserAgentHeader: true, // Don't send: User-Agent: fasthttp 176 DisableHeaderNamesNormalizing: true, // If you set the case on your headers correctly you can enable this 177 DisablePathNormalizing: true, 178 // increase DNS cache time to an hour instead of default minute 179 Dial: (&fasthttp.TCPDialer{ 180 Concurrency: 4096, 181 DNSCacheDuration: time.Hour, 182 }).Dial, 183 ReadTimeout: 180 * time.Second, 184 WriteTimeout: 180 * time.Second, 185 MaxConnDuration: 45 * time.Second, 186 MaxResponseBodySize: 1024 * 1024 * 64, //64MB 187 MaxConnsPerHost: 1024, 188 } 189 fasthttp.SetBodySizePoolLimit(respBodyPoolLimit, respBodyPoolLimit) 190 envProxy.initialize() 191 log.Init(logger.DEBUG, "0box-sdk") 192 c, err := lru.New[string, string](1000) 193 if err != nil { 194 panic(err) 195 } 196 SignCache = c 197 } 198 199 func NewHTTPRequest(method string, url string, data []byte) (*http.Request, context.Context, context.CancelFunc, error) { 200 var ( 201 req *http.Request 202 err error 203 ) 204 if len(data) > 0 { 205 req, err = http.NewRequest(method, url, bytes.NewBuffer(data)) 206 } else { 207 req, err = http.NewRequest(method, url, nil) 208 } 209 210 req.Header.Set("Content-Type", "application/json; charset=utf-8") 211 req.Header.Set("Access-Control-Allow-Origin", "*") 212 ctx, cncl := context.WithTimeout(context.Background(), time.Second*10) 213 return req, ctx, cncl, err 214 } 215 216 func setClientInfo(req *http.Request) { 217 req.Header.Set("X-App-Client-ID", client.GetClientID()) 218 req.Header.Set("X-App-Client-Key", client.GetClientPublicKey()) 219 } 220 221 func setClientInfoWithSign(req *http.Request, sig, allocation, baseURL string) error { 222 setClientInfo(req) 223 req.Header.Set(CLIENT_SIGNATURE_HEADER, sig) 224 225 hashData := allocation + baseURL 226 sig2, ok := SignCache.Get(hashData) 227 if !ok { 228 var err error 229 sig2, err = client.Sign(encryption.Hash(hashData)) 230 SignCache.Add(hashData, sig2) 231 if err != nil { 232 return err 233 } 234 } 235 req.Header.Set(CLIENT_SIGNATURE_HEADER_V2, sig2) 236 return nil 237 } 238 239 func NewCommitRequest(baseUrl, allocationID string, allocationTx string, body io.Reader) (*http.Request, error) { 240 u, err := joinUrl(baseUrl, COMMIT_ENDPOINT, allocationTx) 241 if err != nil { 242 return nil, err 243 } 244 245 req, err := http.NewRequest(http.MethodPost, u.String(), body) 246 if err != nil { 247 return nil, err 248 } 249 setClientInfo(req) 250 251 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 252 253 return req, nil 254 } 255 256 func NewReferencePathRequest(baseUrl, allocationID string, allocationTx string, sig string, paths []string) (*http.Request, error) { 257 nurl, err := joinUrl(baseUrl, REFERENCE_ENDPOINT, allocationTx) 258 if err != nil { 259 return nil, err 260 } 261 262 pathBytes, err := json.Marshal(paths) 263 if err != nil { 264 return nil, err 265 } 266 params := url.Values{} 267 params.Add("paths", string(pathBytes)) 268 //url := fmt.Sprintf("%s%s%s?path=%s", baseUrl, LIST_ENDPOINT, allocation, path) 269 nurl.RawQuery = params.Encode() // Escape Query Parameters 270 271 req, err := http.NewRequest(http.MethodGet, nurl.String(), nil) 272 if err != nil { 273 return nil, err 274 } 275 276 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 277 return nil, err 278 } 279 280 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 281 282 return req, nil 283 } 284 285 func NewCalculateHashRequest(baseUrl, allocationID string, allocationTx string, paths []string) (*http.Request, error) { 286 nurl, err := joinUrl(baseUrl, CALCULATE_HASH_ENDPOINT, allocationTx) 287 if err != nil { 288 return nil, err 289 } 290 pathBytes, err := json.Marshal(paths) 291 if err != nil { 292 return nil, err 293 } 294 params := url.Values{} 295 params.Add("paths", string(pathBytes)) 296 nurl.RawQuery = params.Encode() // Escape Query Parameters 297 req, err := http.NewRequest(http.MethodPost, nurl.String(), nil) 298 if err != nil { 299 return nil, err 300 } 301 setClientInfo(req) 302 303 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 304 305 return req, nil 306 } 307 308 func NewObjectTreeRequest(baseUrl, allocationID string, allocationTx string, sig string, path string) (*http.Request, error) { 309 nurl, err := joinUrl(baseUrl, OBJECT_TREE_ENDPOINT, allocationTx) 310 if err != nil { 311 return nil, err 312 } 313 params := url.Values{} 314 params.Add("path", path) 315 //url := fmt.Sprintf("%s%s%s?path=%s", baseUrl, LIST_ENDPOINT, allocation, path) 316 nurl.RawQuery = params.Encode() // Escape Query Parameters 317 req, err := http.NewRequest(http.MethodGet, nurl.String(), nil) 318 if err != nil { 319 return nil, err 320 } 321 322 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 323 return nil, err 324 } 325 326 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 327 328 return req, nil 329 } 330 331 func NewRefsRequest(baseUrl, allocationID, sig, allocationTx, path, pathHash, authToken, offsetPath, updatedDate, offsetDate, fileType, refType string, level, pageLimit int) (*http.Request, error) { 332 nUrl, err := joinUrl(baseUrl, REFS_ENDPOINT, allocationTx) 333 if err != nil { 334 return nil, err 335 } 336 params := url.Values{} 337 params.Add("path", path) 338 params.Add("path_hash", pathHash) 339 params.Add("auth_token", authToken) 340 params.Add("offsetPath", offsetPath) 341 params.Add("pageLimit", strconv.Itoa(pageLimit)) 342 params.Add("updatedDate", updatedDate) 343 params.Add("offsetDate", offsetDate) 344 params.Add("fileType", fileType) 345 params.Add("refType", refType) 346 params.Add("level", strconv.Itoa(level)) 347 nUrl.RawQuery = params.Encode() 348 req, err := http.NewRequest(http.MethodGet, nUrl.String(), nil) 349 if err != nil { 350 return nil, err 351 } 352 353 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 354 355 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 356 return nil, err 357 } 358 359 return req, nil 360 } 361 362 func NewRecentlyAddedRefsRequest(bUrl, allocID, allocTx, sig string, fromDate, offset int64, pageLimit int) (*http.Request, error) { 363 nUrl, err := joinUrl(bUrl, RECENT_REFS_ENDPOINT, allocID) 364 if err != nil { 365 return nil, err 366 } 367 368 params := url.Values{} 369 params.Add("limit", strconv.Itoa(pageLimit)) 370 params.Add("offset", strconv.FormatInt(offset, 10)) 371 params.Add("from-date", strconv.FormatInt(fromDate, 10)) 372 373 nUrl.RawQuery = params.Encode() 374 req, err := http.NewRequest(http.MethodGet, nUrl.String(), nil) 375 if err != nil { 376 return nil, err 377 } 378 379 req.Header.Set(ALLOCATION_ID_HEADER, allocID) 380 381 if err = setClientInfoWithSign(req, sig, allocTx, bUrl); err != nil { 382 return nil, err 383 } 384 385 return req, nil 386 } 387 388 func NewAllocationRequest(baseUrl, allocationID, allocationTx string) (*http.Request, error) { 389 nurl, err := joinUrl(baseUrl, ALLOCATION_ENDPOINT) 390 if err != nil { 391 return nil, err 392 } 393 params := url.Values{} 394 params.Add("id", allocationTx) 395 nurl.RawQuery = params.Encode() // Escape Query Parameters 396 req, err := http.NewRequest(http.MethodGet, nurl.String(), nil) 397 if err != nil { 398 return nil, err 399 } 400 setClientInfo(req) 401 402 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 403 return req, nil 404 } 405 406 func NewCollaboratorRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 407 u, err := joinUrl(baseUrl, COLLABORATOR_ENDPOINT, allocationTx) 408 if err != nil { 409 return nil, err 410 } 411 req, err := http.NewRequest(http.MethodPost, u.String(), body) 412 if err != nil { 413 return nil, err 414 } 415 416 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 417 return nil, err 418 } 419 420 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 421 422 return req, nil 423 } 424 425 func GetCollaboratorsRequest(baseUrl, allocationID, allocationTx, sig string, query *url.Values) (*http.Request, error) { 426 u, err := joinUrl(baseUrl, COLLABORATOR_ENDPOINT, allocationTx) 427 if err != nil { 428 return nil, err 429 } 430 u.RawQuery = query.Encode() 431 req, err := http.NewRequest(http.MethodGet, u.String(), nil) 432 if err != nil { 433 return nil, err 434 } 435 436 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 437 return nil, err 438 } 439 440 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 441 442 return req, nil 443 } 444 445 func DeleteCollaboratorRequest(baseUrl, allocationID, allocationTx, sig string, query *url.Values) (*http.Request, error) { 446 u, err := joinUrl(baseUrl, COLLABORATOR_ENDPOINT, allocationTx) 447 if err != nil { 448 return nil, err 449 } 450 u.RawQuery = query.Encode() 451 452 req, err := http.NewRequest(http.MethodDelete, u.String(), nil) 453 if err != nil { 454 return nil, err 455 } 456 457 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 458 return nil, err 459 } 460 461 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 462 463 return req, nil 464 } 465 466 func NewFileMetaRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 467 u, err := joinUrl(baseUrl, FILE_META_ENDPOINT, allocationTx) 468 if err != nil { 469 return nil, err 470 } 471 req, err := http.NewRequest(http.MethodPost, u.String(), body) 472 if err != nil { 473 return nil, err 474 } 475 476 err = setClientInfoWithSign(req, sig, allocationTx, baseUrl) 477 if err != nil { 478 return nil, err 479 } 480 481 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 482 483 return req, nil 484 } 485 486 func NewFileStatsRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 487 u, err := joinUrl(baseUrl, FILE_STATS_ENDPOINT, allocationTx) 488 if err != nil { 489 return nil, err 490 } 491 req, err := http.NewRequest(http.MethodPost, u.String(), body) 492 if err != nil { 493 return nil, err 494 } 495 496 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 497 return nil, err 498 } 499 500 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 501 502 return req, nil 503 } 504 505 func NewListRequest(baseUrl, allocationID, allocationTx, path, pathHash, auth_token string, list bool, offset, pageLimit int) (*http.Request, error) { 506 nurl, err := joinUrl(baseUrl, LIST_ENDPOINT, allocationTx) 507 if err != nil { 508 return nil, err 509 } 510 params := url.Values{} 511 params.Add("path", path) 512 params.Add("path_hash", pathHash) 513 params.Add("auth_token", auth_token) 514 if list { 515 params.Add("list", "true") 516 } 517 params.Add("offset", strconv.Itoa(offset)) 518 params.Add("limit", strconv.Itoa(pageLimit)) 519 nurl.RawQuery = params.Encode() // Escape Query Parameters 520 req, err := http.NewRequest(http.MethodGet, nurl.String(), nil) 521 if err != nil { 522 return nil, err 523 } 524 setClientInfo(req) 525 526 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 527 528 return req, nil 529 } 530 531 // NewUploadRequestWithMethod create a http request of upload 532 func NewUploadRequestWithMethod(baseURL, allocationID, allocationTx, sig string, body io.Reader, method string) (*http.Request, error) { 533 u, err := joinUrl(baseURL, UPLOAD_ENDPOINT, allocationTx) 534 if err != nil { 535 return nil, err 536 } 537 538 var req *http.Request 539 540 req, err = http.NewRequest(method, u.String(), body) 541 542 if err != nil { 543 return nil, err 544 } 545 546 // set header: X-App-Client-Signature 547 if err := setClientInfoWithSign(req, sig, allocationTx, baseURL); err != nil { 548 return nil, err 549 } 550 551 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 552 553 return req, nil 554 } 555 556 func NewWriteMarkerLockRequest( 557 baseURL, allocationID, allocationTx, sig, connID string) (*http.Request, error) { 558 559 u, err := joinUrl(baseURL, WM_LOCK_ENDPOINT, allocationTx) 560 if err != nil { 561 return nil, err 562 } 563 564 params := url.Values{} 565 params.Add("connection_id", connID) 566 u.RawQuery = params.Encode() // Escape Query Parameters 567 568 req, err := http.NewRequest(http.MethodPost, u.String(), nil) 569 if err != nil { 570 return nil, err 571 } 572 573 err = setClientInfoWithSign(req, sig, allocationTx, baseURL) 574 if err != nil { 575 return nil, err 576 } 577 578 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 579 580 return req, nil 581 } 582 583 func NewWriteMarkerUnLockRequest( 584 baseURL, allocationID, allocationTx, sig, connID, requestTime string) (*http.Request, error) { 585 586 u, err := joinUrl(baseURL, WM_LOCK_ENDPOINT, allocationTx, connID) 587 if err != nil { 588 return nil, err 589 } 590 591 req, err := http.NewRequest(http.MethodDelete, u.String(), nil) 592 if err != nil { 593 return nil, err 594 } 595 596 err = setClientInfoWithSign(req, sig, allocationTx, baseURL) 597 if err != nil { 598 return nil, err 599 } 600 601 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 602 603 return req, nil 604 } 605 606 func NewFastUploadRequest(baseURL, allocationID string, allocationTx string, body []byte, method string) (*fasthttp.Request, error) { 607 u, err := joinUrl(baseURL, UPLOAD_ENDPOINT, allocationTx) 608 if err != nil { 609 return nil, err 610 } 611 612 req := fasthttp.AcquireRequest() 613 614 req.Header.SetMethod(method) 615 req.SetRequestURI(u.String()) 616 req.SetBodyRaw(body) 617 618 // set header: X-App-Client-Signature 619 if err := setFastClientInfoWithSign(req, allocationTx, baseURL); err != nil { 620 return nil, err 621 } 622 623 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 624 return req, nil 625 } 626 627 func setFastClientInfoWithSign(req *fasthttp.Request, allocation, baseURL string) error { 628 req.Header.Set("X-App-Client-ID", client.GetClientID()) 629 req.Header.Set("X-App-Client-Key", client.GetClientPublicKey()) 630 631 sign, err := client.Sign(encryption.Hash(allocation)) 632 if err != nil { 633 return err 634 } 635 req.Header.Set(CLIENT_SIGNATURE_HEADER, sign) 636 hashData := allocation + baseURL 637 sig2, ok := SignCache.Get(hashData) 638 if !ok { 639 sig2, err = client.Sign(encryption.Hash(hashData)) 640 SignCache.Add(hashData, sig2) 641 if err != nil { 642 return err 643 } 644 } 645 req.Header.Set(CLIENT_SIGNATURE_HEADER_V2, sig2) 646 return nil 647 } 648 649 func NewUploadRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader, update bool) (*http.Request, error) { 650 u, err := joinUrl(baseUrl, UPLOAD_ENDPOINT, allocationTx) 651 if err != nil { 652 return nil, err 653 } 654 655 var req *http.Request 656 if update { 657 req, err = http.NewRequest(http.MethodPut, u.String(), body) 658 } else { 659 req, err = http.NewRequest(http.MethodPost, u.String(), body) 660 } 661 if err != nil { 662 return nil, err 663 } 664 665 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 666 return nil, err 667 } 668 669 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 670 671 return req, nil 672 } 673 674 func NewConnectionRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 675 u, err := joinUrl(baseUrl, CREATE_CONNECTION_ENDPOINT, allocationTx) 676 if err != nil { 677 return nil, err 678 } 679 req, err := http.NewRequest(http.MethodPost, u.String(), body) 680 if err != nil { 681 return nil, err 682 } 683 684 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 685 return nil, err 686 } 687 688 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 689 690 return req, nil 691 } 692 693 func NewRenameRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 694 u, err := joinUrl(baseUrl, RENAME_ENDPOINT, allocationTx) 695 if err != nil { 696 return nil, err 697 } 698 699 // url := fmt.Sprintf("%s%s%s", baseUrl, RENAME_ENDPOINT, allocation) 700 req, err := http.NewRequest(http.MethodPost, u.String(), body) 701 if err != nil { 702 return nil, err 703 } 704 705 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 706 return nil, err 707 } 708 709 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 710 711 return req, nil 712 } 713 714 func NewCopyRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 715 u, err := joinUrl(baseUrl, COPY_ENDPOINT, allocationTx) 716 if err != nil { 717 return nil, err 718 } 719 720 req, err := http.NewRequest(http.MethodPost, u.String(), body) 721 if err != nil { 722 return nil, err 723 } 724 725 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 726 return nil, err 727 } 728 729 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 730 731 return req, nil 732 } 733 734 func NewMoveRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 735 u, err := joinUrl(baseUrl, MOVE_ENDPOINT, allocationTx) 736 if err != nil { 737 return nil, err 738 } 739 740 req, err := http.NewRequest(http.MethodPost, u.String(), body) 741 if err != nil { 742 return nil, err 743 } 744 745 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 746 return nil, err 747 } 748 749 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 750 751 return req, nil 752 } 753 754 func NewDownloadRequest(baseUrl, allocationID, allocationTx string) (*http.Request, error) { 755 u, err := joinUrl(baseUrl, DOWNLOAD_ENDPOINT, allocationTx) 756 if err != nil { 757 return nil, err 758 } 759 760 // url := fmt.Sprintf("%s%s%s", baseUrl, DOWNLOAD_ENDPOINT, allocation) 761 req, err := http.NewRequest(http.MethodGet, u.String(), nil) 762 if err != nil { 763 return nil, err 764 } 765 766 sig, err := client.Sign(encryption.Hash(allocationTx)) 767 if err != nil { 768 return nil, err 769 } 770 771 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 772 return nil, err 773 } 774 775 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 776 777 return req, nil 778 } 779 780 func NewFastDownloadRequest(baseUrl, allocationID, allocationTx string) (*fasthttp.Request, error) { 781 u, err := joinUrl(baseUrl, DOWNLOAD_ENDPOINT, allocationTx) 782 if err != nil { 783 return nil, err 784 } 785 786 req := fasthttp.AcquireRequest() 787 788 if err := setFastClientInfoWithSign(req, allocationTx, baseUrl); err != nil { 789 return nil, err 790 } 791 req.SetRequestURI(u.String()) 792 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 793 794 return req, nil 795 } 796 797 func NewRedeemRequest(baseUrl, allocationID, allocationTx string) (*http.Request, error) { 798 u, err := joinUrl(baseUrl, REDEEM_ENDPOINT, allocationTx) 799 if err != nil { 800 return nil, err 801 } 802 803 req, err := http.NewRequest(http.MethodPost, u.String(), nil) 804 if err != nil { 805 return nil, err 806 } 807 setClientInfo(req) 808 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 809 return req, nil 810 } 811 812 func NewDeleteRequest(baseUrl, allocationID, allocationTx, sig string, query *url.Values) (*http.Request, error) { 813 u, err := joinUrl(baseUrl, UPLOAD_ENDPOINT, allocationTx) 814 if err != nil { 815 return nil, err 816 } 817 u.RawQuery = query.Encode() 818 819 req, err := http.NewRequest(http.MethodDelete, u.String(), nil) 820 if err != nil { 821 return nil, err 822 } 823 824 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 825 return nil, err 826 } 827 828 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 829 830 return req, nil 831 } 832 833 func NewCreateDirRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 834 u, err := joinUrl(baseUrl, DIR_ENDPOINT, allocationTx) 835 if err != nil { 836 return nil, err 837 } 838 839 req, err := http.NewRequest(http.MethodPost, u.String(), body) 840 if err != nil { 841 return nil, err 842 } 843 844 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 845 return nil, err 846 } 847 848 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 849 850 return req, nil 851 } 852 853 func NewShareRequest(baseUrl, allocationID, allocationTx, sig string, body io.Reader) (*http.Request, error) { 854 u, err := joinUrl(baseUrl, SHARE_ENDPOINT, allocationTx) 855 if err != nil { 856 return nil, err 857 } 858 859 req, err := http.NewRequest(http.MethodPost, u.String(), body) 860 if err != nil { 861 return nil, err 862 } 863 864 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 865 return nil, err 866 } 867 868 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 869 870 return req, nil 871 } 872 873 func NewRevokeShareRequest(baseUrl, allocationID, allocationTx, sig string, query *url.Values) (*http.Request, error) { 874 u, err := joinUrl(baseUrl, SHARE_ENDPOINT, allocationTx) 875 if err != nil { 876 return nil, err 877 } 878 u.RawQuery = query.Encode() 879 req, err := http.NewRequest(http.MethodDelete, u.String(), nil) 880 if err != nil { 881 return nil, err 882 } 883 884 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 885 return nil, err 886 } 887 888 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 889 890 return req, nil 891 } 892 893 func NewWritemarkerRequest(baseUrl, allocationID, allocationTx, sig string) (*http.Request, error) { 894 895 nurl, err := joinUrl(baseUrl, LATEST_WRITE_MARKER_ENDPOINT, allocationTx) 896 if err != nil { 897 return nil, err 898 } 899 900 req, err := http.NewRequest(http.MethodGet, nurl.String(), nil) 901 if err != nil { 902 return nil, err 903 } 904 905 if err := setClientInfoWithSign(req, sig, allocationTx, baseUrl); err != nil { 906 return nil, err 907 } 908 909 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 910 911 return req, nil 912 } 913 914 func NewRollbackRequest(baseUrl, allocationID string, allocationTx string, body io.Reader) (*http.Request, error) { 915 u, err := joinUrl(baseUrl, ROLLBACK_ENDPOINT, allocationTx) 916 if err != nil { 917 return nil, err 918 } 919 920 req, err := http.NewRequest(http.MethodPost, u.String(), body) 921 if err != nil { 922 return nil, err 923 } 924 setClientInfo(req) 925 926 req.Header.Set(ALLOCATION_ID_HEADER, allocationID) 927 928 return req, nil 929 } 930 931 // MakeSCRestAPICall makes a rest api call to the sharders. 932 // - scAddress is the address of the smart contract 933 // - relativePath is the relative path of the api 934 // - params is the query parameters 935 // - handler is the handler function to handle the response 936 func MakeSCRestAPICall(scAddress string, relativePath string, params map[string]string, handler SCRestAPIHandler) ([]byte, error) { 937 numSharders := len(blockchain.GetSharders()) 938 sharders := blockchain.GetSharders() 939 responses := make(map[int]int) 940 mu := &sync.Mutex{} 941 entityResult := make(map[string][]byte) 942 var retObj []byte 943 maxCount := 0 944 dominant := 200 945 wg := sync.WaitGroup{} 946 947 cfg, err := conf.GetClientConfig() 948 if err != nil { 949 return nil, err 950 } 951 952 for _, sharder := range sharders { 953 wg.Add(1) 954 go func(sharder string) { 955 defer wg.Done() 956 urlString := fmt.Sprintf("%v/%v%v%v", sharder, SC_REST_API_URL, scAddress, relativePath) 957 urlObj, err := url.Parse(urlString) 958 if err != nil { 959 log.Error(err) 960 return 961 } 962 q := urlObj.Query() 963 for k, v := range params { 964 q.Add(k, v) 965 } 966 urlObj.RawQuery = q.Encode() 967 client := &http.Client{Transport: DefaultTransport} 968 response, err := client.Get(urlObj.String()) 969 if err != nil { 970 blockchain.Sharders.Fail(sharder) 971 return 972 } 973 974 defer response.Body.Close() 975 entityBytes, _ := ioutil.ReadAll(response.Body) 976 mu.Lock() 977 if response.StatusCode > http.StatusBadRequest { 978 blockchain.Sharders.Fail(sharder) 979 } else { 980 blockchain.Sharders.Success(sharder) 981 } 982 responses[response.StatusCode]++ 983 if responses[response.StatusCode] > maxCount { 984 maxCount = responses[response.StatusCode] 985 } 986 987 if isCurrentDominantStatus(response.StatusCode, responses, maxCount) { 988 dominant = response.StatusCode 989 retObj = entityBytes 990 } 991 992 entityResult[sharder] = entityBytes 993 blockchain.Sharders.Success(sharder) 994 mu.Unlock() 995 }(sharder) 996 } 997 wg.Wait() 998 999 rate := float32(maxCount*100) / float32(cfg.SharderConsensous) 1000 if rate < consensusThresh { 1001 err = errors.New("consensus_failed", "consensus failed on sharders") 1002 } 1003 1004 if dominant != 200 { 1005 var objmap map[string]json.RawMessage 1006 err := json.Unmarshal(retObj, &objmap) 1007 if err != nil { 1008 return nil, errors.New("", string(retObj)) 1009 } 1010 1011 var parsed string 1012 err = json.Unmarshal(objmap["error"], &parsed) 1013 if err != nil || parsed == "" { 1014 return nil, errors.New("", string(retObj)) 1015 } 1016 1017 return nil, errors.New("", parsed) 1018 } 1019 1020 if handler != nil { 1021 handler(entityResult, numSharders, err) 1022 } 1023 1024 if rate > consensusThresh { 1025 return retObj, nil 1026 } 1027 return nil, err 1028 } 1029 1030 func HttpDo(ctx context.Context, cncl context.CancelFunc, req *http.Request, f func(*http.Response, error) error) error { 1031 // Run the HTTP request in a goroutine and pass the response to f. 1032 c := make(chan error, 1) 1033 go func() { 1034 var err error 1035 // indefinitely try if io.EOF error occurs. As per some research over google 1036 // it occurs when client http tries to send byte stream in connection that is 1037 // closed by the server 1038 for { 1039 var resp *http.Response 1040 resp, err = Client.Do(req.WithContext(ctx)) 1041 if errors.Is(err, io.EOF) { 1042 continue 1043 } 1044 1045 err = f(resp, err) 1046 break 1047 } 1048 c <- err 1049 }() 1050 1051 // TODO: Check cncl context required in any case 1052 // defer cncl() 1053 select { 1054 case <-ctx.Done(): 1055 DefaultTransport.CancelRequest(req) //nolint 1056 <-c // Wait for f to return. 1057 return ctx.Err() 1058 case err := <-c: 1059 return err 1060 } 1061 } 1062 1063 // isCurrentDominantStatus determines whether the current response status is the dominant status among responses. 1064 // 1065 // The dominant status is where the response status is counted the most. 1066 // On tie-breakers, 200 will be selected if included. 1067 // 1068 // Function assumes runningTotalPerStatus can be accessed safely concurrently. 1069 func isCurrentDominantStatus(respStatus int, currentTotalPerStatus map[int]int, currentMax int) bool { 1070 // mark status as dominant if 1071 // - running total for status is the max and response is 200 or 1072 // - running total for status is the max and count for 200 is lower 1073 return currentTotalPerStatus[respStatus] == currentMax && (respStatus == 200 || currentTotalPerStatus[200] < currentMax) 1074 } 1075 1076 func joinUrl(baseURl string, paths ...string) (*url.URL, error) { 1077 u, err := url.Parse(baseURl) 1078 if err != nil { 1079 return nil, err 1080 } 1081 p := path.Join(paths...) 1082 u.Path = path.Join(u.Path, p) 1083 return u, nil 1084 }