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:
parent
677fbb2c97
commit
ff87ec33a7
mediaapi
setup/config
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue