getRemoteFile fetches the remote file and caches it locally A hash map of active remote requests to a struct containing a sync.Cond is used to only download remote files once, regardless of how many download requests are received. Note: The named errorResponse return variable is used in a deferred b
( ctx context.Context, client *gomatrixserverlib.Client, cfg *config.MediaAPI, db storage.Database, activeRemoteRequests *types.ActiveRemoteRequests, activeThumbnailGeneration *types.ActiveThumbnailGeneration, )
| 512 | // regardless of how many download requests are received. |
| 513 | // Note: The named errorResponse return variable is used in a deferred broadcast of the metadata and error response to waiting goroutines. |
| 514 | func (r *downloadRequest) getRemoteFile( |
| 515 | ctx context.Context, |
| 516 | client *gomatrixserverlib.Client, |
| 517 | cfg *config.MediaAPI, |
| 518 | db storage.Database, |
| 519 | activeRemoteRequests *types.ActiveRemoteRequests, |
| 520 | activeThumbnailGeneration *types.ActiveThumbnailGeneration, |
| 521 | ) (errorResponse error) { |
| 522 | // Note: getMediaMetadataFromActiveRequest uses mutexes and conditions from activeRemoteRequests |
| 523 | mediaMetadata, resErr := r.getMediaMetadataFromActiveRequest(activeRemoteRequests) |
| 524 | if resErr != nil { |
| 525 | return resErr |
| 526 | } else if mediaMetadata != nil { |
| 527 | // If we got metadata from an active request, we can respond from the local file |
| 528 | r.MediaMetadata = mediaMetadata |
| 529 | } else { |
| 530 | // Note: This is an active request that MUST broadcastMediaMetadata to wake up waiting goroutines! |
| 531 | // Note: broadcastMediaMetadata uses mutexes and conditions from activeRemoteRequests |
| 532 | defer func() { |
| 533 | // Note: errorResponse is the named return variable so we wrap this in a closure to re-evaluate the arguments at defer-time |
| 534 | if err := recover(); err != nil { |
| 535 | r.broadcastMediaMetadata(activeRemoteRequests, errors.New("paniced")) |
| 536 | panic(err) |
| 537 | } |
| 538 | r.broadcastMediaMetadata(activeRemoteRequests, errorResponse) |
| 539 | }() |
| 540 | |
| 541 | // check if we have a record of the media in our database |
| 542 | mediaMetadata, err := db.GetMediaMetadata( |
| 543 | ctx, r.MediaMetadata.MediaID, r.MediaMetadata.Origin, |
| 544 | ) |
| 545 | if err != nil { |
| 546 | return fmt.Errorf("db.GetMediaMetadata: %w", err) |
| 547 | } |
| 548 | |
| 549 | if mediaMetadata == nil { |
| 550 | // If we do not have a record, we need to fetch the remote file first and then respond from the local file |
| 551 | err := r.fetchRemoteFileAndStoreMetadata( |
| 552 | ctx, client, |
| 553 | cfg.AbsBasePath, cfg.MaxFileSizeBytes, db, |
| 554 | cfg.ThumbnailSizes, activeThumbnailGeneration, |
| 555 | cfg.MaxThumbnailGenerators, |
| 556 | ) |
| 557 | if err != nil { |
| 558 | r.Logger.WithError(err).Errorf("r.fetchRemoteFileAndStoreMetadata: failed to fetch remote file") |
| 559 | return err |
| 560 | } |
| 561 | } else { |
| 562 | // If we have a record, we can respond from the local file |
| 563 | r.MediaMetadata = mediaMetadata |
| 564 | } |
| 565 | } |
| 566 | return nil |
| 567 | } |
| 568 | |
| 569 | func (r *downloadRequest) getMediaMetadataFromActiveRequest(activeRemoteRequests *types.ActiveRemoteRequests) (*types.MediaMetadata, error) { |
| 570 | // Check if there is an active remote request for the file |
no test coverage detected