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 }