From f1986162160f631618f97b207ec83b5f45c27bf4 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Sat, 13 Jan 2018 19:58:35 -0700 Subject: [PATCH] Add a resource handler for url previews Adds #13 --- .../services/url_service/resource_handler.go | 130 ++++++++++++++++++ .../services/url_service/url_service.go | 60 +------- 2 files changed, 132 insertions(+), 58 deletions(-) create mode 100644 src/github.com/turt2live/matrix-media-repo/services/url_service/resource_handler.go diff --git a/src/github.com/turt2live/matrix-media-repo/services/url_service/resource_handler.go b/src/github.com/turt2live/matrix-media-repo/services/url_service/resource_handler.go new file mode 100644 index 00000000..c907a9b8 --- /dev/null +++ b/src/github.com/turt2live/matrix-media-repo/services/url_service/resource_handler.go @@ -0,0 +1,130 @@ +package url_service + +import ( + "context" + "fmt" + "sync" + + "github.com/disintegration/imaging" + "github.com/sirupsen/logrus" + "github.com/turt2live/matrix-media-repo/config" + "github.com/turt2live/matrix-media-repo/resource_handler" + "github.com/turt2live/matrix-media-repo/services/media_service" + "github.com/turt2live/matrix-media-repo/types" + "github.com/turt2live/matrix-media-repo/util" + "github.com/turt2live/matrix-media-repo/util/errs" +) + +type urlResourceHandler struct { + resourceHandler *resource_handler.ResourceHandler +} + +type urlPreviewRequest struct { + urlStr string + forUserId string + onHost string +} + +type urlPreviewResponse struct { + preview *types.UrlPreview + err error +} + +var resHandlerInstance *urlResourceHandler +var resHandlerSingletonLock = &sync.Once{} + +func getResourceHandler() (*urlResourceHandler) { + if resHandlerInstance == nil { + resHandlerSingletonLock.Do(func() { + handler, err := resource_handler.New(config.Get().UrlPreviews.NumWorkers, urlPreviewWorkFn) + if err != nil { + panic(err) + } + + resHandlerInstance = &urlResourceHandler{handler} + }) + } + + return resHandlerInstance +} + +func urlPreviewWorkFn(request *resource_handler.WorkRequest) interface{} { + info := request.Metadata.(*urlPreviewRequest) + log := logrus.WithFields(logrus.Fields{ + "worker_requestId": request.Id, + "worker_url": info.urlStr, + "worker_previewer": "OpenGraph", + }) + log.Info("Processing url preview request") + + ctx := context.TODO() // TODO: Should we use a real context? + + svc := New(ctx, log) // url_service (us) + previewer := NewOpenGraphPreviewer(ctx, log) + preview, err := previewer.GeneratePreview(info.urlStr) + if err != nil { + if err == errs.ErrMediaNotFound { + svc.store.InsertPreviewError(info.urlStr, errs.ErrCodeNotFound) + } else { + svc.store.InsertPreviewError(info.urlStr, errs.ErrCodeUnknown) + } + return &urlPreviewResponse{err:err} + } + + result := &types.UrlPreview{ + Url: preview.Url, + SiteName: preview.SiteName, + Type: preview.Type, + Description: preview.Description, + Title: preview.Title, + } + + // Store the thumbnail, if there is one + mediaSvc := media_service.New(ctx, log) + if preview.Image != nil && !mediaSvc.IsTooLarge(preview.Image.ContentLength, preview.Image.ContentLengthHeader) { + // UploadMedia will close the read stream for the thumbnail + media, err := mediaSvc.UploadMedia(preview.Image.Data, preview.Image.ContentType, preview.Image.Filename, info.forUserId, info.onHost) + if err != nil { + log.Warn("Non-fatal error storing preview thumbnail: " + err.Error()) + } else { + img, err := imaging.Open(media.Location) + if err != nil { + log.Warn("Non-fatal error getting thumbnail dimensions: " + err.Error()) + } else { + result.ImageMxc = media.MxcUri() + result.ImageType = media.ContentType + result.ImageSize = media.SizeBytes + result.ImageWidth = img.Bounds().Max.X + result.ImageHeight = img.Bounds().Max.Y + } + } + } + + dbRecord := &types.CachedUrlPreview{ + Preview: result, + SearchUrl: info.urlStr, + ErrorCode: "", + FetchedTs: util.NowMillis(), + } + err = svc.store.InsertPreview(dbRecord) + if err != nil { + log.Warn("Error caching URL preview: " + err.Error()) + // Non-fatal: Just report it and move on. The worst that happens is we re-cache it. + } + + return &urlPreviewResponse{preview:result} +} + +func (h *urlResourceHandler) GeneratePreview(urlStr string, forUserId string, onHost string) chan *urlPreviewResponse { + resultChan := make(chan *urlPreviewResponse) + go func() { + reqId := fmt.Sprintf("preview_%s", urlStr) // don't put the user id or host in the ID string + result := <-h.resourceHandler.GetResource(reqId, &urlPreviewRequest{ + urlStr:urlStr, + forUserId:forUserId, + onHost:onHost, + }) + resultChan <- result.(*urlPreviewResponse) + }() + return resultChan +} diff --git a/src/github.com/turt2live/matrix-media-repo/services/url_service/url_service.go b/src/github.com/turt2live/matrix-media-repo/services/url_service/url_service.go index 40844aca..889dde98 100644 --- a/src/github.com/turt2live/matrix-media-repo/services/url_service/url_service.go +++ b/src/github.com/turt2live/matrix-media-repo/services/url_service/url_service.go @@ -7,11 +7,9 @@ import ( "net" "net/url" - "github.com/disintegration/imaging" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/turt2live/matrix-media-repo/config" - "github.com/turt2live/matrix-media-repo/services/media_service" "github.com/turt2live/matrix-media-repo/storage" "github.com/turt2live/matrix-media-repo/storage/stores" "github.com/turt2live/matrix-media-repo/types" @@ -106,62 +104,8 @@ func (s *urlService) GetPreview(urlStr string, onHost string, forUserId string, return nil, errs.ErrHostBlacklisted } - s.log = s.log.WithFields(logrus.Fields{ - "previewer": "OpenGraph", - }) - - previewer := NewOpenGraphPreviewer(s.ctx, s.log) - preview, err := previewer.GeneratePreview(urlStr) - if err != nil { - if err == errs.ErrMediaNotFound { - s.store.InsertPreviewError(urlStr, errs.ErrCodeNotFound) - } else { - s.store.InsertPreviewError(urlStr, errs.ErrCodeUnknown) - } - return nil, err - } - - result := &types.UrlPreview{ - Url: preview.Url, - SiteName: preview.SiteName, - Type: preview.Type, - Description: preview.Description, - Title: preview.Title, - } - - // Store the thumbnail, if there is one - mediaSvc := media_service.New(s.ctx, s.log) - if preview.Image != nil && !mediaSvc.IsTooLarge(preview.Image.ContentLength, preview.Image.ContentLengthHeader) { - // UploadMedia will close the read stream for the thumbnail - media, err := mediaSvc.UploadMedia(preview.Image.Data, preview.Image.ContentType, preview.Image.Filename, forUserId, onHost) - if err != nil { - s.log.Warn("Non-fatal error storing preview thumbnail: " + err.Error()) - } else { - img, err := imaging.Open(media.Location) - if err != nil { - s.log.Warn("Non-fatal error getting thumbnail dimensions: " + err.Error()) - } else { - result.ImageMxc = media.MxcUri() - result.ImageType = media.ContentType - result.ImageSize = media.SizeBytes - result.ImageWidth = img.Bounds().Max.X - result.ImageHeight = img.Bounds().Max.Y - } - } - } - - dbRecord := &types.CachedUrlPreview{ - Preview: result, - SearchUrl: urlStr, - ErrorCode: "", - FetchedTs: util.NowMillis(), - } - err = s.store.InsertPreview(dbRecord) - if err != nil { - s.log.Warn("Error caching URL preview: " + err.Error()) - } - - return result, nil + result := <-getResourceHandler().GeneratePreview(urlStr, forUserId, onHost) + return result.preview, result.err } func isAllowed(ip net.IP, allowed []string, disallowed []string, log *logrus.Entry) bool { -- GitLab