github.com/0chain/gosdk@v1.17.11/zboxcore/sdk/chunked_upload_process_js.go (about)

     1  //go:build js && wasm
     2  // +build js,wasm
     3  
     4  package sdk
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"fmt"
    11  	"net/http"
    12  	"os"
    13  	"sync"
    14  	"sync/atomic"
    15  	"syscall"
    16  	"syscall/js"
    17  	"time"
    18  
    19  	"github.com/0chain/errors"
    20  	thrown "github.com/0chain/errors"
    21  	"github.com/0chain/gosdk/constants"
    22  	"github.com/0chain/gosdk/core/sys"
    23  	"github.com/0chain/gosdk/wasmsdk/jsbridge"
    24  	"github.com/0chain/gosdk/zboxcore/logger"
    25  	"github.com/0chain/gosdk/zboxcore/zboxutil"
    26  	"github.com/hack-pad/go-webworkers/worker"
    27  	"github.com/hack-pad/safejs"
    28  	"github.com/hitenjain14/fasthttp"
    29  	"github.com/valyala/bytebufferpool"
    30  	"golang.org/x/sync/errgroup"
    31  )
    32  
    33  var (
    34  	hasherMap map[string]workerProcess
    35  )
    36  
    37  type workerProcess struct {
    38  	wg     *sync.WaitGroup
    39  	hasher Hasher
    40  }
    41  
    42  type ChunkedUploadFormInfo struct {
    43  	ConnectionID      string
    44  	ChunkSize         int64
    45  	ChunkStartIndex   int
    46  	ChunkEndIndex     int
    47  	IsFinal           bool
    48  	EncryptedKey      string
    49  	EncryptedKeyPoint string
    50  	ShardSize         int64
    51  	HttpMethod        string
    52  	AllocationID      string
    53  	AllocationTx      string
    54  	OnlyHash          bool
    55  }
    56  
    57  // createUploadProgress create a new UploadProgress
    58  func (su *ChunkedUpload) createUploadProgress(connectionId string) {
    59  	if su.progress.ChunkSize <= 0 {
    60  		su.progress = UploadProgress{
    61  			ConnectionID:      connectionId,
    62  			ChunkIndex:        -1,
    63  			ChunkSize:         su.chunkSize,
    64  			EncryptOnUpload:   su.encryptOnUpload,
    65  			EncryptedKeyPoint: su.encryptedKeyPoint,
    66  			ActualSize:        su.fileMeta.ActualSize,
    67  			ChunkNumber:       su.chunkNumber,
    68  		}
    69  	}
    70  	su.progress.Blobbers = make([]*UploadBlobberStatus, su.allocationObj.DataShards+su.allocationObj.ParityShards)
    71  
    72  	for i := 0; i < len(su.progress.Blobbers); i++ {
    73  		su.progress.Blobbers[i] = &UploadBlobberStatus{}
    74  	}
    75  
    76  	su.progress.ID = su.progressID()
    77  	su.saveProgress()
    78  }
    79  
    80  // processUpload process upload fragment to its blobber
    81  func (su *ChunkedUpload) processUpload(chunkStartIndex, chunkEndIndex int,
    82  	fileShards []blobberShards, thumbnailShards blobberShards,
    83  	isFinal bool, uploadLength int64) error {
    84  	if len(fileShards) == 0 {
    85  		return thrown.New("upload_failed", "Upload failed. No data to upload")
    86  	}
    87  
    88  	select {
    89  	case <-su.ctx.Done():
    90  		return context.Cause(su.ctx)
    91  	default:
    92  	}
    93  
    94  	fileMetaJSON, err := json.Marshal(su.fileMeta)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	var (
   100  		pos          uint64
   101  		successCount int
   102  	)
   103  
   104  	formInfo := ChunkedUploadFormInfo{
   105  		ConnectionID:      su.progress.ConnectionID,
   106  		ChunkSize:         su.chunkSize,
   107  		ChunkStartIndex:   chunkStartIndex,
   108  		ChunkEndIndex:     chunkEndIndex,
   109  		IsFinal:           isFinal,
   110  		EncryptedKey:      su.encryptedKey,
   111  		EncryptedKeyPoint: su.progress.EncryptedKeyPoint,
   112  		ShardSize:         su.shardSize,
   113  		HttpMethod:        su.httpMethod,
   114  		AllocationID:      su.allocationObj.ID,
   115  		AllocationTx:      su.allocationObj.Tx,
   116  		OnlyHash:          chunkEndIndex <= su.progress.ChunkIndex,
   117  	}
   118  	formInfoJSON, err := json.Marshal(formInfo)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	//convert json objects to uint8 arrays
   124  	fileMetaUint8 := js.Global().Get("Uint8Array").New(len(fileMetaJSON))
   125  	js.CopyBytesToJS(fileMetaUint8, fileMetaJSON)
   126  	formInfoUint8 := js.Global().Get("Uint8Array").New(len(formInfoJSON))
   127  	js.CopyBytesToJS(formInfoUint8, formInfoJSON)
   128  
   129  	if chunkStartIndex > 0 {
   130  		select {
   131  		case <-su.ctx.Done():
   132  			return context.Cause(su.ctx)
   133  		case su.listenChan <- struct{}{}:
   134  		}
   135  		// index := chunkStartIndex - 1
   136  		// go su.updateProgress(index)
   137  		uploadLength := su.allocationObj.GetChunkReadSize(su.encryptOnUpload) * int64(su.chunkNumber)
   138  		su.progress.UploadLength += uploadLength
   139  		if su.progress.UploadLength > su.fileMeta.ActualSize {
   140  			su.progress.UploadLength = su.fileMeta.ActualSize
   141  		}
   142  		if su.statusCallback != nil {
   143  			su.statusCallback.InProgress(su.allocationObj.ID, su.fileMeta.RemotePath, su.opCode, int(su.progress.UploadLength), nil)
   144  		}
   145  	}
   146  
   147  	for i := su.uploadMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) {
   148  		pos = uint64(i.TrailingZeros())
   149  		blobber := su.blobbers[pos]
   150  		blobber.progress.UploadLength += uploadLength
   151  		var thumbnailChunkData []byte
   152  		worker := jsbridge.GetWorker(blobber.blobber.ID)
   153  		if worker == nil {
   154  			logger.Logger.Error("worker not found for blobber: ", blobber.blobber.Baseurl)
   155  			continue
   156  		}
   157  		if len(thumbnailShards) > 0 {
   158  			thumbnailChunkData = thumbnailShards[pos]
   159  		}
   160  		obj := js.Global().Get("Object").New()
   161  		jsbridge.SetMsgType(&obj, "upload")
   162  		obj.Set("fileMeta", fileMetaUint8)
   163  		if formInfo.OnlyHash && su.progress.UploadMask.And(zboxutil.NewUint128(1).Lsh(pos)).Equals64(0) {
   164  			//check if pos is set in upload mask in progress
   165  			formInfo.OnlyHash = false
   166  			noHashFormInfoJSON, _ := json.Marshal(formInfo)
   167  			noHashFormInfoUint8 := js.Global().Get("Uint8Array").New(len(noHashFormInfoJSON))
   168  			js.CopyBytesToJS(noHashFormInfoUint8, noHashFormInfoJSON)
   169  			obj.Set("formInfo", noHashFormInfoUint8)
   170  			formInfo.OnlyHash = true //reset to true
   171  		} else {
   172  			obj.Set("formInfo", formInfoUint8)
   173  		}
   174  
   175  		if len(thumbnailChunkData) > 0 {
   176  			thumbnailChunkDataUint8 := js.Global().Get("Uint8Array").New(len(thumbnailChunkData))
   177  			js.CopyBytesToJS(thumbnailChunkDataUint8, thumbnailChunkData)
   178  			obj.Set("thumbnailChunkData", thumbnailChunkDataUint8)
   179  			blobber.fileRef.ThumbnailSize = int64(len(thumbnailChunkData))
   180  			blobber.fileRef.ActualThumbnailSize = su.fileMeta.ActualThumbnailSize
   181  			blobber.fileRef.ActualThumbnailHash = su.fileMeta.ActualThumbnailHash
   182  		}
   183  
   184  		dataLen := int64(len(fileShards[pos])-1)*int64(len(fileShards[pos][0])) + int64(len(fileShards[pos][len(fileShards[pos])-1]))
   185  
   186  		fileshardUint8 := js.Global().Get("Uint8Array").New(dataLen)
   187  		offset := 0
   188  		for _, shard := range fileShards[pos] {
   189  			js.CopyBytesToJS(fileshardUint8.Call("subarray", offset, offset+len(shard)), shard)
   190  			offset += len(shard)
   191  		}
   192  		obj.Set("fileShard", fileshardUint8)
   193  		err = worker.PostMessage(safejs.Safe(obj), []safejs.Value{safejs.Safe(fileshardUint8.Get("buffer"))})
   194  		if err == nil {
   195  			successCount++
   196  		} else {
   197  			logger.Logger.Error("error posting message to worker: ", err)
   198  			su.uploadMask = su.uploadMask.And(zboxutil.NewUint128(1).Lsh(pos).Not())
   199  		}
   200  		if isFinal {
   201  			blobber.fileRef.ChunkSize = su.chunkSize
   202  			blobber.fileRef.Size = su.shardUploadedSize
   203  			blobber.fileRef.Path = su.fileMeta.RemotePath
   204  			blobber.fileRef.ActualFileHash = su.fileMeta.ActualHash
   205  			blobber.fileRef.ActualFileSize = su.fileMeta.ActualSize
   206  			blobber.fileRef.EncryptedKey = su.encryptedKey
   207  		}
   208  	}
   209  
   210  	if successCount < su.consensus.consensusThresh {
   211  		su.removeProgress()
   212  		return thrown.New("upload_failed", "Upload failed. Error posting message to worker")
   213  	}
   214  	fileShards = nil
   215  	if isFinal {
   216  		su.uploadWG.Wait()
   217  		select {
   218  		case <-su.ctx.Done():
   219  			return context.Cause(su.ctx)
   220  		default:
   221  		}
   222  		su.progress.UploadLength = su.fileMeta.ActualSize
   223  		if su.statusCallback != nil {
   224  			su.statusCallback.InProgress(su.allocationObj.ID, su.fileMeta.RemotePath, su.opCode, int(su.progress.UploadLength), nil)
   225  		}
   226  	}
   227  
   228  	return nil
   229  }
   230  
   231  type FinalWorkerResult struct {
   232  	FixedMerkleRoot      string
   233  	ValidationRoot       string
   234  	ThumbnailContentHash string
   235  }
   236  
   237  func (su *ChunkedUpload) listen(allEventChan []eventChanWorker, respChan chan error) {
   238  	su.consensus.Reset()
   239  
   240  	var (
   241  		pos      uint64
   242  		errCount int32
   243  		wg       sync.WaitGroup
   244  		wgErrors = make(chan error, len(su.blobbers))
   245  		isFinal  bool
   246  	)
   247  
   248  	for i := su.uploadMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) {
   249  		pos = uint64(i.TrailingZeros())
   250  		wg.Add(1)
   251  		go func(pos uint64) {
   252  			var uploadSuccess bool
   253  			defer func() {
   254  				if !uploadSuccess {
   255  					su.maskMu.Lock()
   256  					su.uploadMask = su.uploadMask.And(zboxutil.NewUint128(1).Lsh(pos).Not())
   257  					su.maskMu.Unlock()
   258  				}
   259  				wg.Done()
   260  			}()
   261  			blobber := su.blobbers[pos]
   262  
   263  			eventChan := allEventChan[pos]
   264  			if eventChan.C == nil {
   265  				errC := atomic.AddInt32(&errCount, 1)
   266  				if errC >= int32(su.consensus.consensusThresh) {
   267  					wgErrors <- thrown.New("upload_failed", "Upload failed. Worker event channel not found")
   268  				}
   269  				return
   270  			}
   271  			for {
   272  				event, ok := <-eventChan.C
   273  				if !ok {
   274  					logger.Logger.Error("chan closed from: ", blobber.blobber.Baseurl)
   275  					errC := atomic.AddInt32(&errCount, 1)
   276  					if errC >= int32(su.consensus.consensusThresh) {
   277  						if su.ctx.Err() != nil {
   278  							wgErrors <- context.Cause(su.ctx)
   279  						} else {
   280  							wgErrors <- thrown.New("upload_failed", "Upload failed. Worker event channel closed")
   281  						}
   282  					}
   283  					return
   284  				}
   285  				msgType, data, err := jsbridge.GetMsgType(event)
   286  				if err != nil {
   287  					errC := atomic.AddInt32(&errCount, 1)
   288  					if errC >= int32(su.consensus.consensusThresh) {
   289  						wgErrors <- errors.Wrap(err, "could not get msgType")
   290  					}
   291  					return
   292  				}
   293  
   294  				switch msgType {
   295  				case "auth":
   296  					if err := su.processWebWorkerAuthRequest(data, eventChan); err != nil {
   297  						errC := atomic.AddInt32(&errCount, 1)
   298  						if errC >= int32(su.consensus.consensusThresh) {
   299  							wgErrors <- err
   300  						}
   301  						return
   302  					}
   303  				case "upload":
   304  					//get error message
   305  					//get final result
   306  					var err error
   307  					isFinal, err = su.processWebWorkerUpload(data, blobber, pos)
   308  					if err != nil {
   309  						errC := atomic.AddInt32(&errCount, 1)
   310  						if errC >= int32(su.consensus.consensusThresh) {
   311  							wgErrors <- err
   312  						}
   313  					} else {
   314  						uploadSuccess = true
   315  					}
   316  					return
   317  				default:
   318  					logger.Logger.Error("unknown msg type: ", msgType)
   319  				}
   320  			}
   321  
   322  		}(pos)
   323  	}
   324  
   325  	wg.Wait()
   326  	close(wgErrors)
   327  	for err := range wgErrors {
   328  		logger.Logger.Error("error from worker: ", err)
   329  		su.ctxCncl(thrown.New("upload_failed", fmt.Sprintf("Upload failed. %s", err)))
   330  		respChan <- err
   331  	}
   332  
   333  	if !su.consensus.isConsensusOk() {
   334  		logger.Logger.Error("consensus not met")
   335  		err := thrown.New("consensus_not_met", fmt.Sprintf("Upload failed File not found for path %s. Required consensus atleast %d, got %d",
   336  			su.fileMeta.RemotePath, su.consensus.consensusThresh, su.consensus.getConsensus()))
   337  		su.ctxCncl(err)
   338  		respChan <- err
   339  	}
   340  	for chunkEndIndex, mask := range su.processMap {
   341  		if mask.CountOnes() >= su.consensus.consensusThresh {
   342  			su.updateProgress(chunkEndIndex, mask)
   343  			delete(su.processMap, chunkEndIndex)
   344  		}
   345  	}
   346  
   347  	if isFinal {
   348  		close(respChan)
   349  	} else {
   350  		respChan <- nil
   351  	}
   352  }
   353  
   354  func (su *ChunkedUpload) processWebWorkerUpload(data *safejs.Value, blobber *ChunkedUploadBlobber, pos uint64) (bool, error) {
   355  	var isFinal bool
   356  	success, err := data.Get("success")
   357  	if err != nil {
   358  		return false, errors.Wrap(err, "could not get 'success' field")
   359  	}
   360  	res, _ := success.Bool()
   361  	if !res {
   362  		errMsg, err := data.Get("error")
   363  		if err != nil {
   364  			return false, errors.Wrap(err, "could not get 'error' field")
   365  		}
   366  
   367  		errMsgStr, _ := errMsg.String()
   368  		return false, fmt.Errorf("%s", errMsgStr)
   369  	}
   370  
   371  	chunkEndIndexObj, _ := data.Get("chunkEndIndex")
   372  	chunkEndIndex, _ := chunkEndIndexObj.Int()
   373  	su.updateChunkProgress(chunkEndIndex, pos)
   374  	finalRequestObject, _ := data.Get("isFinal")
   375  	finalRequest, _ := finalRequestObject.Bool()
   376  	if finalRequest {
   377  		finalResult, err := data.Get("finalResult")
   378  		if err != nil {
   379  			logger.Logger.Error("errorGettingFinalResult")
   380  			return false, errors.Wrap(err, "could not get 'finalResult' field")
   381  		}
   382  
   383  		len, err := finalResult.Length()
   384  		if err != nil {
   385  			logger.Logger.Error("errorGettingFinalResultLength")
   386  			return false, errors.Wrap(err, "could not get 'finalResult' Length")
   387  		}
   388  
   389  		resBuf := make([]byte, len)
   390  		safejs.CopyBytesToGo(resBuf, finalResult)
   391  		var finalResultObj FinalWorkerResult
   392  		err = json.Unmarshal(resBuf, &finalResultObj)
   393  		if err != nil {
   394  			logger.Logger.Error("errorGettingFinalResultUnmarshal")
   395  			return false, errors.Wrap(err, "could not unmarshal 'finalResult' obj")
   396  		}
   397  
   398  		blobber.fileRef.FixedMerkleRoot = finalResultObj.FixedMerkleRoot
   399  		blobber.fileRef.ValidationRoot = finalResultObj.ValidationRoot
   400  		blobber.fileRef.ThumbnailHash = finalResultObj.ThumbnailContentHash
   401  		isFinal = true
   402  	}
   403  
   404  	su.consensus.Done()
   405  	return isFinal, nil
   406  }
   407  
   408  func (su *ChunkedUpload) processWebWorkerAuthRequest(data *safejs.Value, eventChan eventChanWorker) error {
   409  	authMsg, err := jsbridge.ParseEventDataField(data, "msg")
   410  	if err != nil {
   411  		return errors.Wrap(err, "could not parse 'msg' field")
   412  	}
   413  
   414  	rsp, err := sys.AuthCommon(string(authMsg))
   415  	if err != nil {
   416  		return errors.Wrap(err, "chunk upload authCommon failed")
   417  	}
   418  
   419  	if err := jsbridge.PostMessage(jsbridge.GetWorker(eventChan.workerID), jsbridge.MsgTypeAuthRsp,
   420  		map[string]string{
   421  			"data": rsp,
   422  		}); err != nil {
   423  		return errors.Wrap(err, "chunk upload postMessage failed")
   424  	}
   425  
   426  	return nil
   427  }
   428  
   429  func ProcessEventData(data safejs.Value) {
   430  	fileMeta, formInfo, fileShards, thumbnailChunkData, err := parseEventData(data)
   431  	var remotePath string
   432  	if fileMeta != nil {
   433  		remotePath = fileMeta.RemotePath
   434  	}
   435  	if err != nil {
   436  		selfPostMessage(false, false, err.Error(), remotePath, 0, nil)
   437  		return
   438  	}
   439  	wp, ok := hasherMap[fileMeta.RemotePath]
   440  	if !ok {
   441  		wp = workerProcess{
   442  			hasher: CreateHasher(formInfo.ShardSize),
   443  			wg:     &sync.WaitGroup{},
   444  		}
   445  		hasherMap[fileMeta.RemotePath] = wp
   446  	} else if formInfo.ChunkStartIndex == 0 {
   447  		wp.hasher = CreateHasher(formInfo.ShardSize)
   448  		hasherMap[fileMeta.RemotePath] = wp
   449  	}
   450  	if formInfo.IsFinal {
   451  		defer delete(hasherMap, fileMeta.RemotePath)
   452  	}
   453  	formBuilder := CreateChunkedUploadFormBuilder()
   454  	uploadData, err := formBuilder.Build(fileMeta, wp.hasher, formInfo.ConnectionID, formInfo.ChunkSize, formInfo.ChunkStartIndex, formInfo.ChunkEndIndex, formInfo.IsFinal, formInfo.EncryptedKey, formInfo.EncryptedKeyPoint,
   455  		fileShards, thumbnailChunkData, formInfo.ShardSize)
   456  	if err != nil {
   457  		selfPostMessage(false, false, err.Error(), remotePath, formInfo.ChunkEndIndex, nil)
   458  		return
   459  	}
   460  	if formInfo.OnlyHash {
   461  		if formInfo.IsFinal {
   462  			finalResult := &FinalWorkerResult{
   463  				FixedMerkleRoot:      uploadData.formData.FixedMerkleRoot,
   464  				ValidationRoot:       uploadData.formData.ValidationRoot,
   465  				ThumbnailContentHash: uploadData.formData.ThumbnailContentHash,
   466  			}
   467  			selfPostMessage(true, true, "", remotePath, formInfo.ChunkEndIndex, finalResult)
   468  		} else {
   469  			selfPostMessage(true, false, "", remotePath, formInfo.ChunkEndIndex, nil)
   470  		}
   471  		return
   472  	}
   473  	blobberURL := os.Getenv("BLOBBER_URL")
   474  	if !formInfo.IsFinal {
   475  		wp.wg.Add(1)
   476  	}
   477  	go func(blobberData blobberData, remotePath string, wg *sync.WaitGroup) {
   478  		if formInfo.IsFinal && len(blobberData.dataBuffers) > 1 {
   479  			err = sendUploadRequest(blobberData.dataBuffers[:len(blobberData.dataBuffers)-1], blobberData.contentSlice[:len(blobberData.contentSlice)-1], blobberURL, formInfo.AllocationID, formInfo.AllocationTx, formInfo.HttpMethod)
   480  			if err != nil {
   481  				selfPostMessage(false, true, err.Error(), remotePath, formInfo.ChunkEndIndex, nil)
   482  				return
   483  			}
   484  			wg.Wait()
   485  			err = sendUploadRequest(blobberData.dataBuffers[len(blobberData.dataBuffers)-1:], blobberData.contentSlice[len(blobberData.contentSlice)-1:], blobberURL, formInfo.AllocationID, formInfo.AllocationTx, formInfo.HttpMethod)
   486  			if err != nil {
   487  				selfPostMessage(false, true, err.Error(), remotePath, formInfo.ChunkEndIndex, nil)
   488  				return
   489  			}
   490  		} else {
   491  			if formInfo.IsFinal {
   492  				wg.Wait()
   493  			} else {
   494  				defer wg.Done()
   495  			}
   496  			err = sendUploadRequest(blobberData.dataBuffers, blobberData.contentSlice, blobberURL, formInfo.AllocationID, formInfo.AllocationTx, formInfo.HttpMethod)
   497  			if err != nil {
   498  				selfPostMessage(false, formInfo.IsFinal, err.Error(), remotePath, formInfo.ChunkEndIndex, nil)
   499  				return
   500  			}
   501  		}
   502  		if formInfo.IsFinal {
   503  			finalResult := &FinalWorkerResult{
   504  				FixedMerkleRoot:      blobberData.formData.FixedMerkleRoot,
   505  				ValidationRoot:       blobberData.formData.ValidationRoot,
   506  				ThumbnailContentHash: blobberData.formData.ThumbnailContentHash,
   507  			}
   508  			selfPostMessage(true, true, "", remotePath, formInfo.ChunkEndIndex, finalResult)
   509  		} else {
   510  			selfPostMessage(true, false, "", remotePath, formInfo.ChunkEndIndex, nil)
   511  		}
   512  	}(uploadData, remotePath, wp.wg)
   513  
   514  }
   515  
   516  func InitHasherMap() {
   517  	hasherMap = make(map[string]workerProcess)
   518  }
   519  
   520  func selfPostMessage(success, isFinal bool, errMsg, remotePath string, chunkEndIndex int, finalResult *FinalWorkerResult) {
   521  	obj := js.Global().Get("Object").New()
   522  	obj.Set("success", success)
   523  	obj.Set("error", errMsg)
   524  	obj.Set("isFinal", isFinal)
   525  	obj.Set("chunkEndIndex", chunkEndIndex)
   526  	obj.Set("remotePath", remotePath)
   527  	if finalResult != nil {
   528  		finalResultJSON, err := json.Marshal(finalResult)
   529  		if err != nil {
   530  			obj.Set("finalResult", nil)
   531  		} else {
   532  			finalResultUint8 := js.Global().Get("Uint8Array").New(len(finalResultJSON))
   533  			js.CopyBytesToJS(finalResultUint8, finalResultJSON)
   534  			obj.Set("finalResult", finalResultUint8)
   535  		}
   536  	}
   537  
   538  	// msgType is upload
   539  	jsbridge.SetMsgType(&obj, jsbridge.MsgTypeUpload)
   540  
   541  	self := jsbridge.GetSelfWorker()
   542  	self.PostMessage(safejs.Safe(obj), nil) //nolint:errcheck
   543  }
   544  
   545  func parseEventData(data safejs.Value) (*FileMeta, *ChunkedUploadFormInfo, [][]byte, []byte, error) {
   546  
   547  	fileMetaUint8, err := data.Get("fileMeta")
   548  	if err != nil {
   549  		return nil, nil, nil, nil, err
   550  	}
   551  	formInfoUint8, err := data.Get("formInfo")
   552  	if err != nil {
   553  		return nil, nil, nil, nil, err
   554  	}
   555  	fileShardUint8, err := data.Get("fileShard")
   556  	if err != nil {
   557  		return nil, nil, nil, nil, err
   558  	}
   559  	//get fileMetaUint8 length
   560  	fileMetaLen, err := fileMetaUint8.Length()
   561  	if err != nil {
   562  		return nil, nil, nil, nil, err
   563  	}
   564  	fileMetaBytes := make([]byte, fileMetaLen)
   565  	safejs.CopyBytesToGo(fileMetaBytes, fileMetaUint8)
   566  	fileMeta := &FileMeta{}
   567  	err = json.Unmarshal(fileMetaBytes, fileMeta)
   568  	if err != nil {
   569  		return nil, nil, nil, nil, err
   570  	}
   571  	// get formInfoUint8 length
   572  	formInfoLen, err := formInfoUint8.Length()
   573  	if err != nil {
   574  		return nil, nil, nil, nil, err
   575  	}
   576  	formInfoBytes := make([]byte, formInfoLen)
   577  	safejs.CopyBytesToGo(formInfoBytes, formInfoUint8)
   578  	formInfo := &ChunkedUploadFormInfo{}
   579  	err = json.Unmarshal(formInfoBytes, formInfo)
   580  	if err != nil {
   581  		return nil, nil, nil, nil, err
   582  	}
   583  	chunkSize := formInfo.ChunkSize
   584  	if chunkSize == 0 {
   585  		chunkSize = CHUNK_SIZE
   586  	}
   587  	// get fileShardUint8 length
   588  	fileShardLen, err := fileShardUint8.Length()
   589  	if err != nil {
   590  		return nil, nil, nil, nil, err
   591  	}
   592  	buf := make([]byte, fileShardLen)
   593  	safejs.CopyBytesToGo(buf, fileShardUint8)
   594  	fileShards := splitData(buf, int(chunkSize))
   595  
   596  	thumbnailChunkDataUint8, err := data.Get("thumbnailChunkData")
   597  	if err != nil {
   598  		return fileMeta, formInfo, fileShards, nil, nil
   599  	}
   600  	thumbnailChunkDataLen, err := thumbnailChunkDataUint8.Length()
   601  	if err != nil {
   602  		return fileMeta, formInfo, fileShards, nil, nil
   603  	}
   604  	thumbnailChunkData := make([]byte, thumbnailChunkDataLen)
   605  	safejs.CopyBytesToGo(thumbnailChunkData, thumbnailChunkDataUint8)
   606  	return fileMeta, formInfo, fileShards, thumbnailChunkData, nil
   607  }
   608  
   609  func sendUploadRequest(dataBuffers []*bytes.Buffer, contentSlice []string, blobberURL, allocationID, allocationTx, httpMethod string) (err error) {
   610  	eg, _ := errgroup.WithContext(context.TODO())
   611  	for dataInd := 0; dataInd < len(dataBuffers); dataInd++ {
   612  		ind := dataInd
   613  		eg.Go(func() error {
   614  			var (
   615  				shouldContinue bool
   616  			)
   617  			var req *fasthttp.Request
   618  			for i := 0; i < 3; i++ {
   619  				req, err = zboxutil.NewFastUploadRequest(
   620  					blobberURL, allocationID, allocationTx, dataBuffers[ind].Bytes(), httpMethod)
   621  				if err != nil {
   622  					return err
   623  				}
   624  
   625  				req.Header.Add("Content-Type", contentSlice[ind])
   626  				err, shouldContinue = func() (err error, shouldContinue bool) {
   627  					resp := fasthttp.AcquireResponse()
   628  					defer fasthttp.ReleaseResponse(resp)
   629  					err = zboxutil.FastHttpClient.DoTimeout(req, resp, DefaultUploadTimeOut)
   630  					fasthttp.ReleaseRequest(req)
   631  					if err != nil {
   632  						logger.Logger.Error("Upload : ", err)
   633  						if errors.Is(err, fasthttp.ErrConnectionClosed) || errors.Is(err, syscall.EPIPE) || errors.Is(err, fasthttp.ErrDialTimeout) {
   634  							return err, true
   635  						}
   636  						return fmt.Errorf("Error while doing reqeust. Error %s", err), false
   637  					}
   638  
   639  					if resp.StatusCode() == http.StatusOK {
   640  						return
   641  					}
   642  
   643  					respbody := resp.Body()
   644  					if resp.StatusCode() == http.StatusTooManyRequests {
   645  						logger.Logger.Error("Got too many request error")
   646  						var r int
   647  						r, err = zboxutil.GetFastRateLimitValue(resp)
   648  						if err != nil {
   649  							logger.Logger.Error(err)
   650  							return
   651  						}
   652  						time.Sleep(time.Duration(r) * time.Second)
   653  						shouldContinue = true
   654  						return
   655  					}
   656  
   657  					msg := string(respbody)
   658  					logger.Logger.Error(blobberURL,
   659  						" Upload error response: ", resp.StatusCode(),
   660  						"err message: ", msg)
   661  					err = errors.Throw(constants.ErrBadRequest, msg)
   662  					return
   663  				}()
   664  
   665  				if shouldContinue {
   666  					if i == 2 {
   667  						if err != nil {
   668  							logger.Logger.Error("Retry limit exceeded for upload: ", err)
   669  						}
   670  						return errors.Throw(constants.ErrBadRequest, "Retry limit exceeded for upload")
   671  					}
   672  					continue
   673  				}
   674  				buff := &bytebufferpool.ByteBuffer{
   675  					B: dataBuffers[ind].Bytes(),
   676  				}
   677  				formDataPool.Put(buff)
   678  
   679  				if err != nil {
   680  					return err
   681  				}
   682  
   683  				break
   684  			}
   685  			return err
   686  		})
   687  	}
   688  	return eg.Wait()
   689  }
   690  
   691  type eventChanWorker struct {
   692  	C        <-chan worker.MessageEvent
   693  	workerID string
   694  }
   695  
   696  func (su *ChunkedUpload) startProcessor() {
   697  	su.listenChan = make(chan struct{}, su.uploadWorkers)
   698  	su.processMap = make(map[int]zboxutil.Uint128)
   699  	su.uploadWG.Add(1)
   700  	go func() {
   701  		respChan := make(chan error, 1)
   702  		allEventChan := make([]eventChanWorker, len(su.blobbers))
   703  		var pos uint64
   704  		for i := su.uploadMask; !i.Equals64(0); i = i.And(zboxutil.NewUint128(1).Lsh(pos).Not()) {
   705  			pos = uint64(i.TrailingZeros())
   706  			blobber := su.blobbers[pos]
   707  			webWorker := jsbridge.GetWorker(blobber.blobber.ID)
   708  			if webWorker != nil {
   709  				eventChan := make(chan worker.MessageEvent, su.uploadWorkers)
   710  				err := webWorker.SubscribeToEvents(su.fileMeta.RemotePath, eventChan)
   711  				if err != nil {
   712  					logger.Logger.Error("error subscribing to events: ", err)
   713  					su.ctxCncl(thrown.New("upload_failed", "Upload failed. Error subscribing to events"))
   714  					return
   715  				}
   716  				defer webWorker.UnsubscribeToEvents(su.fileMeta.RemotePath)
   717  				allEventChan[pos] = eventChanWorker{
   718  					C:        eventChan,
   719  					workerID: blobber.blobber.ID,
   720  				}
   721  			}
   722  		}
   723  		defer su.uploadWG.Done()
   724  		for {
   725  			go su.listen(allEventChan, respChan)
   726  			select {
   727  			case <-su.ctx.Done():
   728  				return
   729  			case err, ok := <-respChan:
   730  				if !ok || err != nil {
   731  					return
   732  				}
   733  				<-su.listenChan
   734  			}
   735  		}
   736  	}()
   737  }
   738  
   739  func (su *ChunkedUpload) updateChunkProgress(chunkEndIndex int, pos uint64) {
   740  	su.processMapLock.Lock()
   741  	su.processMap[chunkEndIndex] = su.processMap[chunkEndIndex].Or(zboxutil.NewUint128(1).Lsh(pos))
   742  	su.processMapLock.Unlock()
   743  }