Skip to content

Commit

Permalink
fix complexity
Browse files Browse the repository at this point in the history
  • Loading branch information
adnull committed Sep 30, 2024
1 parent 016500b commit dd3fd3d
Showing 1 changed file with 82 additions and 54 deletions.
136 changes: 82 additions & 54 deletions mediaapi/routing/url_preview.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ func makeUrlPreviewHandler(
if err != nil {
activeUrlPreviewRequest.Error = err
} else {
defer resp.Body.Close()
defer func() {
err := resp.Body.Close()
if err != nil {
logger.WithError(err).Error("unable to close response body")
}
}()

var result *types.UrlPreview
var err, err2 error
Expand Down Expand Up @@ -349,66 +354,23 @@ func downloadAndStoreImage(
}

tmpFileName := filepath.Join(string(tmpDir), "content")
// Check if the file is an image.
// Otherwise return an error
file, err := os.Open(string(tmpFileName))
if err != nil {
logger.WithError(err).Error("unable to open file")
return nil, 0, 0, err
}
defer file.Close()

buf := make([]byte, 512)

_, err = file.Read(buf)
fileType, err := detectFileType(tmpFileName, logger)
if err != nil {
logger.WithError(err).Error("unable to read file")
return nil, 0, 0, err
}

fileType := http.DetectContentType(buf)
if !strings.HasPrefix(fileType, "image") {
logger.WithField("contentType", fileType).Debugf("uploaded file is not an image or can not be thumbnailed, not generating thumbnails")
return nil, 0, 0, ErrorUnsupportedContentType
logger.WithError(err).Error("unable to detect file type")
return nil, width, height, err
}
logger.WithField("contentType", fileType).Debug("uploaded file is an image")

// Create a thumbnail from the image
thumbnailPath := tmpFileName + ".thumbnail"

// Check if we have too many thumbnail generators running
// If so, wait up to 30 seconds for one to finish
timeout := time.After(30 * time.Second)
for {
if len(activeThumbnailGeneration.PathToResult) < cfg.MaxThumbnailGenerators {
activeThumbnailGeneration.Lock()
activeThumbnailGeneration.PathToResult[string(hash)] = nil
activeThumbnailGeneration.Unlock()

defer func() {
activeThumbnailGeneration.Lock()
delete(activeThumbnailGeneration.PathToResult, string(hash))
activeThumbnailGeneration.Unlock()
}()

width, height, err = thumbnailer.CreateThumbnailFromFile(types.Path(tmpFileName), types.Path(thumbnailPath), types.ThumbnailSize(cfg.UrlPreviewThumbnailSize), logger)
if err != nil {
if errors.Is(err, thumbnailer.ErrThumbnailTooLarge) {
thumbnailPath = tmpFileName
} else {
logger.WithError(err).Error("unable to create thumbnail")
return nil, 0, 0, err
}
}
break
}

select {
case <-timeout:
logger.Error("timed out waiting for thumbnail generator")
return nil, 0, 0, ErrorTimeoutThumbnailGenerator
default:
time.Sleep(time.Second)
width, height, err = createThumbnail(types.Path(tmpFileName), types.Path(thumbnailPath), types.ThumbnailSize(cfg.UrlPreviewThumbnailSize),
hash, activeThumbnailGeneration, cfg.MaxThumbnailGenerators, logger)
if err != nil {
if errors.Is(err, thumbnailer.ErrThumbnailTooLarge) {
thumbnailPath = tmpFileName
} else {
return nil, width, height, err
}
}

Expand Down Expand Up @@ -462,6 +424,41 @@ func downloadAndStoreImage(
return mediaMetaData, width, height, nil
}

func createThumbnail(src types.Path, dst types.Path, size types.ThumbnailSize, hash types.Base64Hash, activeThumbnailGeneration *types.ActiveThumbnailGeneration, maxThumbnailGenerators int, logger *log.Entry) (int, int, error) {
// Check if we have too many thumbnail generators running
// If so, wait up to 30 seconds for one to finish
timeout := time.After(30 * time.Second)
for {
if len(activeThumbnailGeneration.PathToResult) < maxThumbnailGenerators {

activeThumbnailGeneration.Lock()
activeThumbnailGeneration.PathToResult[string(hash)] = nil
activeThumbnailGeneration.Unlock()

defer func() {
activeThumbnailGeneration.Lock()
delete(activeThumbnailGeneration.PathToResult, string(hash))
activeThumbnailGeneration.Unlock()
}()

width, height, err := thumbnailer.CreateThumbnailFromFile(src, dst, size, logger)
if err != nil {
logger.WithError(err).Error("unable to create thumbnail")
return 0, 0, err
}
return width, height, nil
}

select {
case <-timeout:
logger.Error("timed out waiting for thumbnail generator")
return 0, 0, ErrorTimeoutThumbnailGenerator
default:
time.Sleep(time.Second)
}
}
}

func storeUrlPreviewResponse(ctx context.Context, cfg *config.MediaAPI, db storage.Database, user userapi.Device, hash types.Base64Hash, preview *types.UrlPreview, logger *log.Entry) error {

jsonPreview, err := json.Marshal(preview)
Expand Down Expand Up @@ -532,6 +529,37 @@ func loadUrlPreviewResponse(ctx context.Context, cfg *config.MediaAPI, db storag
return nil, ErrNoMetadataFound
}

func detectFileType(filePath string, logger *log.Entry) (string, error) {
// Check if the file is an image.
// Otherwise return an error
file, err := os.Open(string(filePath))
if err != nil {
logger.WithError(err).Error("unable to open image file")
return "", err
}
defer func() {
err := file.Close()
if err != nil {
logger.WithError(err).Error("unable to close image file")
}
}()

buf := make([]byte, 512)

_, err = file.Read(buf)
if err != nil {
logger.WithError(err).Error("unable to read file")
return "", err
}

fileType := http.DetectContentType(buf)
if !strings.HasPrefix(fileType, "image") {
logger.WithField("contentType", fileType).Debugf("uploaded file is not an image")
return "", ErrorUnsupportedContentType
}
return fileType, nil
}

func getHashFromString(s string) types.Base64Hash {
hasher := sha256.New()
hasher.Write([]byte(s))
Expand Down

0 comments on commit dd3fd3d

Please sign in to comment.