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  }