1
0
Fork 0
mirror of https://github.com/matrix-org/dendrite.git synced 2025-03-28 12:34:27 -05:00

made handler sy-test compatible

This commit is contained in:
Aleksandr Dubovikov 2024-10-01 15:52:39 +02:00
parent 677fbb2c97
commit ff87ec33a7
4 changed files with 74 additions and 60 deletions
mediaapi
setup/config

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/sha256" "crypto/sha256"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -94,6 +95,11 @@ func makeUrlPreviewHandler(
} }
} }
urlParsed, err := url.Parse(pUrl)
if err != nil {
return util.ErrorResponse(ErrorMissingUrl)
}
hash := getHashFromString(pUrl) hash := getHashFromString(pUrl)
// Get for url preview from in-memory cache // Get for url preview from in-memory cache
@ -136,6 +142,7 @@ func makeUrlPreviewHandler(
// we defer caching the url preview response as well as signalling the waiting goroutines // we defer caching the url preview response as well as signalling the waiting goroutines
// about the completion of the request // about the completion of the request
defer func() { defer func() {
urlPreviewCacheItem := &types.UrlPreviewCacheRecord{ urlPreviewCacheItem := &types.UrlPreviewCacheRecord{
Created: time.Now().Unix(), Created: time.Now().Unix(),
} }
@ -164,38 +171,25 @@ func makeUrlPreviewHandler(
if err != nil { if err != nil {
activeUrlPreviewRequest.Error = err activeUrlPreviewRequest.Error = err
} else { } else {
defer func() { defer resp.Body.Close() // nolint: errcheck
err := resp.Body.Close()
if err != nil {
logger.WithError(err).Error("unable to close response body")
}
}()
var result *types.UrlPreview var result *types.UrlPreview
var err, err2 error var err error
var imgUrl *url.URL
var imgReader *http.Response var imgReader *http.Response
var mediaData *types.MediaMetadata var mediaData *types.MediaMetadata
var width, height int var width, height int
if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") { if strings.HasPrefix(resp.Header.Get("Content-Type"), "text/html") {
// The url is a webpage - get data from the meta tags // The url is a webpage - get data from the meta tags
result = getPreviewFromHTML(resp, pUrl) result = getPreviewFromHTML(resp, urlParsed)
if result.ImageUrl != "" { if result.ImageUrl != "" {
// The page has an og:image link // In case of an image in the preview we download it
if imgUrl, err2 = url.Parse(result.ImageUrl); err2 == nil { if imgReader, err = downloadUrl(result.ImageUrl, time.Duration(cfg.UrlPreviewTimeout)*time.Second); err == nil {
imgReader, err2 = downloadUrl(result.ImageUrl, time.Duration(cfg.UrlPreviewTimeout)*time.Second) mediaData, width, height, _ = downloadAndStoreImage("url_preview", req.Context(), imgReader, cfg, device, db, activeThumbnailGeneration, logger)
if err2 == nil {
// Download image and store it as a thumbnail
mediaData, width, height, err2 = downloadAndStoreImage(imgUrl.Path, req.Context(), imgReader, cfg, device, db, activeThumbnailGeneration, logger)
}
} }
// In case of any error in image download // We don't show the original image in the preview
// we don't show the original URL as it is insecure for the room users // as it is insecure for room members
if err2 != nil { result.ImageUrl = ""
result.ImageUrl = ""
}
} }
} else if strings.HasPrefix(resp.Header.Get("Content-Type"), "image/") { } else if strings.HasPrefix(resp.Header.Get("Content-Type"), "image/") {
// The url is an image link // The url is an image link
@ -275,7 +269,10 @@ func checkActivePreviewResponse(activeUrlPreviewRequests *types.ActiveUrlPreview
} }
func downloadUrl(url string, t time.Duration) (*http.Response, error) { func downloadUrl(url string, t time.Duration) (*http.Response, error) {
client := http.Client{Timeout: t} tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := http.Client{Timeout: t, Transport: tr}
resp, err := client.Get(url) resp, err := client.Get(url)
if err != nil { if err != nil {
return nil, err return nil, err
@ -287,15 +284,18 @@ func downloadUrl(url string, t time.Duration) (*http.Response, error) {
return resp, nil return resp, nil
} }
func getPreviewFromHTML(resp *http.Response, url string) *types.UrlPreview { func getPreviewFromHTML(resp *http.Response, urlParsed *url.URL) *types.UrlPreview {
fields := getMetaFieldsFromHTML(resp) fields := getMetaFieldsFromHTML(resp)
preview := &types.UrlPreview{ preview := &types.UrlPreview{
Title: fields["og:title"], Title: fields["og:title"],
Description: fields["og:description"], Description: fields["og:description"],
Type: fields["og:type"],
Url: fields["og:url"],
} }
if fields["og:title"] == "" { if fields["og:title"] == "" {
preview.Title = url preview.Title = urlParsed.String()
} }
if fields["og:image"] != "" { if fields["og:image"] != "" {
preview.ImageUrl = fields["og:image"] preview.ImageUrl = fields["og:image"]
@ -305,14 +305,19 @@ func getPreviewFromHTML(resp *http.Response, url string) *types.UrlPreview {
preview.ImageUrl = fields["og:image:secure_url"] preview.ImageUrl = fields["og:image:secure_url"]
} }
if fields["og:image:width"] != "" { if preview.ImageUrl != "" {
if width, err := strconv.Atoi(fields["og:image:width"]); err == nil { if imgUrl, err := url.Parse(preview.ImageUrl); err == nil {
preview.ImageWidth = width // Use the same scheme and host as the original URL if empty
} if imgUrl.Scheme == "" {
} imgUrl.Scheme = urlParsed.Scheme
if fields["og:image:height"] != "" { }
if height, err := strconv.Atoi(fields["og:image:height"]); err == nil { // Use the same host as the original URL if empty
preview.ImageHeight = height if imgUrl.Host == "" {
imgUrl.Host = urlParsed.Host
}
preview.ImageUrl = imgUrl.String()
} else {
preview.ImageUrl = ""
} }
} }
@ -371,11 +376,11 @@ func downloadAndStoreImage(
if err != nil { if err != nil {
return nil, width, height, err return nil, width, height, err
} }
img, err := thumbnailer.ReadFile(string(filePath)) width, height, err := thumbnailer.GetImageSize(string(filePath))
if err != nil { if err != nil {
return nil, width, height, err return nil, width, height, err
} }
return existingMetadata, img.Bounds().Dx(), img.Bounds().Dy(), nil return existingMetadata, width, height, nil
} }
tmpFileName := filepath.Join(string(tmpDir), "content") tmpFileName := filepath.Join(string(tmpDir), "content")
@ -386,22 +391,34 @@ func downloadAndStoreImage(
} }
logger.WithField("contentType", fileType).Debug("uploaded file is an image") logger.WithField("contentType", fileType).Debug("uploaded file is an image")
// Create a thumbnail from the image var thumbnailPath string
thumbnailPath := tmpFileName + ".thumbnail"
width, height, err = createThumbnail(types.Path(tmpFileName), types.Path(thumbnailPath), types.ThumbnailSize(cfg.UrlPreviewThumbnailSize), if cfg.UrlPreviewThumbnailSize.Width != 0 {
hash, activeThumbnailGeneration, cfg.MaxThumbnailGenerators, logger) // Create a thumbnail from the image
if err != nil { thumbnailPath = tmpFileName + ".thumbnail"
if errors.Is(err, thumbnailer.ErrThumbnailTooLarge) {
// In case the image is smaller than the thumbnail size width, height, err = createThumbnail(types.Path(tmpFileName), types.Path(thumbnailPath), types.ThumbnailSize(cfg.UrlPreviewThumbnailSize),
// we don't create a thumbnail hash, activeThumbnailGeneration, cfg.MaxThumbnailGenerators, logger)
thumbnailPath = tmpFileName if err != nil {
} else { if errors.Is(err, thumbnailer.ErrThumbnailTooLarge) {
// In case the image is smaller than the thumbnail size
// we don't create a thumbnail
thumbnailPath = tmpFileName
} else {
return nil, width, height, err
}
}
} else {
// No thumbnail size specified, use the original image
thumbnailPath = tmpFileName
width, height, err = thumbnailer.GetImageSize(thumbnailPath)
if err != nil {
return nil, width, height, err return nil, width, height, err
} }
} }
thumbnailFileInfo, err := os.Stat(string(thumbnailPath)) thumbnailFileInfo, err := os.Stat(thumbnailPath)
if err != nil { if err != nil {
logger.WithError(err).Error("unable to get thumbnail file info") logger.WithError(err).Error("unable to get thumbnail file info")
return nil, width, height, err return nil, width, height, err
@ -564,12 +581,7 @@ func detectFileType(filePath string, logger *log.Entry) (string, error) {
logger.WithError(err).Error("unable to open image file") logger.WithError(err).Error("unable to open image file")
return "", err return "", err
} }
defer func() { defer file.Close() // nolint: errcheck
err := file.Close()
if err != nil {
logger.WithError(err).Error("unable to close image file")
}
}()
buf := make([]byte, 512) buf := make([]byte, 512)
@ -605,6 +617,8 @@ func getMetaFieldsFromHTML(resp *http.Response) map[string]string {
"og:image:width", "og:image:width",
"og:image:height", "og:image:height",
"og:image:type", "og:image:type",
"og:type",
"og:url",
} }
fieldsMap := make(map[string]bool, len(fieldsToGet)) fieldsMap := make(map[string]bool, len(fieldsToGet))
for _, field := range fieldsToGet { for _, field := range fieldsToGet {

View file

@ -311,6 +311,10 @@ func CreateThumbnailFromFile(
return width, height, nil return width, height, nil
} }
func ReadFile(src string) (image.Image, error) { func GetImageSize(src string) (width int, height int, err error) {
return readFile(src) img, err := readFile(src)
if err != nil {
return 0, 0, err
}
return img.Bounds().Dx(), img.Bounds().Dy(), nil
} }

View file

@ -119,6 +119,8 @@ type UrlPreview struct {
ImageHeight int `json:"og:image:height"` ImageHeight int `json:"og:image:height"`
ImageWidth int `json:"og:image:width"` ImageWidth int `json:"og:image:width"`
Title string `json:"og:title"` Title string `json:"og:title"`
Type string `json:"og:type"`
Url string `json:"og:url"`
} }
type UrlPreviewResult struct { type UrlPreviewResult struct {

View file

@ -75,12 +75,6 @@ func (c *MediaAPI) Defaults(opts DefaultOpts) {
} }
c.BasePath = "./media_store" c.BasePath = "./media_store"
} }
c.UrlPreviewThumbnailSize = ThumbnailSize{
Width: 200,
Height: 200,
ResizeMethod: "scale",
}
} }
func (c *MediaAPI) Verify(configErrs *ConfigErrors) { func (c *MediaAPI) Verify(configErrs *ConfigErrors) {