fix handling

This commit is contained in:
2025-11-18 12:08:56 +01:00
parent b49c906d44
commit a2ae2c7b7d
2 changed files with 41 additions and 49 deletions
-18
View File
@@ -52,24 +52,6 @@ func loggingMiddleware(next http.Handler) http.Handler {
}) })
} }
// isAllowedType performs a categorical check for 'image/*', 'video/*', and 'audio/*'.
func isAllowedType(mimeType string, allowGeneric bool) bool {
mimeType = strings.Split(mimeType, ";")[0]
category := strings.Split(mimeType, "/")[0]
switch category {
case "image", "video", "audio":
return true
}
// Allow generic binary streams on the initial HEAD request, forcing the
// more reliable content sniffing to make the final decision.
if allowGeneric && mimeType == "application/octet-stream" {
return true
}
return false
}
// isAllowedDomain checks if a host is in the configured whitelist. // isAllowedDomain checks if a host is in the configured whitelist.
func isAllowedDomain(host string, allowedDomains []string) bool { func isAllowedDomain(host string, allowedDomains []string) bool {
if len(allowedDomains) == 0 { if len(allowedDomains) == 0 {
+40 -30
View File
@@ -166,28 +166,20 @@ func (app *App) handleProxy(w http.ResponseWriter, r *http.Request) {
if headResp.StatusCode != http.StatusOK { if headResp.StatusCode != http.StatusOK {
logger.Warn("Origin server returned non-200 status for HEAD request", "status", headResp.StatusCode) logger.Warn("Origin server returned non-200 status for HEAD request", "status", headResp.StatusCode)
http.Error(w, fmt.Sprintf("Media source returned status: %d", headResp.StatusCode), headResp.StatusCode) w.WriteHeader(headResp.StatusCode)
return return
} }
headerContentType := headResp.Header.Get("Content-Type") headerContentType := headResp.Header.Get("Content-Type")
if !isAllowedType(headerContentType, true) {
logger.Warn("Unsupported content type in header", "content_type", headerContentType)
http.Error(w, fmt.Sprintf("Unsupported content type from header: %s", headerContentType), http.StatusUnsupportedMediaType)
return
}
mediaTypeCategory := strings.Split(headerContentType, "/")[0] mediaTypeCategory := strings.Split(headerContentType, "/")[0]
switch mediaTypeCategory { switch mediaTypeCategory {
case "image": case "image":
logger.Debug("Delegating to image handler") logger.Debug("Delegating to image handler")
app.handleImage(w, r, mediaURL) app.handleImage(w, r, mediaURL)
case "video", "audio":
logger.Debug("Delegating to stream handler")
app.handleStream(w, r, mediaURL)
default: default:
logger.Warn("Media type passed initial checks but is not an image, video, or audio", "category", mediaTypeCategory) logger.Debug("Passing through unhandled content type", "content_type", headerContentType)
http.Error(w, "Unsupported media type", http.StatusUnsupportedMediaType) app.handleStream(w, r, mediaURL)
} }
} }
@@ -217,34 +209,52 @@ func (app *App) handleImage(w http.ResponseWriter, r *http.Request, mediaURL str
mtype := mimetype.Detect(mediaData) mtype := mimetype.Detect(mediaData)
if !strings.HasPrefix(mtype.String(), "image/") { if !strings.HasPrefix(mtype.String(), "image/") {
logger.Warn("Content sniffing detected non-image type after HEAD request", "sniffed_type", mtype.String()) logger.Warn("Content sniffing detected non-image type; passing through", "sniffed_type", mtype.String())
http.Error(w, "Content sniffing detected non-image type", http.StatusUnsupportedMediaType) w.Header().Set("Content-Type", mtype.String())
w.Write(mediaData)
return return
} }
var entryToCache CacheEntry
isAnimated := false
if mtype.Is("image/gif") { if mtype.Is("image/gif") {
var gifErr error isAnimated, _ := isGif(mediaData)
isAnimated, gifErr = isGif(mediaData)
if gifErr != nil {
logger.Warn("Could not determine GIF animation, treating as static", "error", gifErr)
}
}
if isAnimated { if isAnimated {
entryToCache = CacheEntry{ContentType: mtype.String(), Data: mediaData} logger.Debug("Passing through animated GIF")
} else { entryToCache := CacheEntry{ContentType: mtype.String(), Data: mediaData}
optimizedImage, err := optimizeMedia(mediaData, app.Config.DefaultImageQuality) app.Cache.SetWithTTL(mediaURL, entryToCache, int64(len(mediaData)), app.Config.CacheTTL)
if err != nil { w.Header().Set("Content-Type", entryToCache.ContentType)
logger.Error("Failed to process image", "error", err) w.Write(entryToCache.Data)
http.Error(w, "Could not process image", http.StatusInternalServerError)
return return
} }
entryToCache = CacheEntry{ContentType: "image/webp", Data: optimizedImage}
} }
app.Cache.SetWithTTL(mediaURL, entryToCache, int64(len(entryToCache.Data)), app.Config.CacheTTL) if mtype.Is("image/ico") || mtype.Is("image/svg+xml") || mtype.Is("image/x-icon") {
logger.Debug("Passing through unsupported image type", "type", mtype.String())
entryToCache := CacheEntry{ContentType: mtype.String(), Data: mediaData}
app.Cache.SetWithTTL(mediaURL, entryToCache, int64(len(mediaData)), app.Config.CacheTTL)
w.Header().Set("Content-Type", entryToCache.ContentType)
w.Write(entryToCache.Data)
return
}
optimizedImage, err := optimizeMedia(mediaData, app.Config.DefaultImageQuality)
if err != nil || len(optimizedImage) >= len(mediaData) {
if err != nil {
logger.Warn("Could not process image, serving original", "error", err)
} else {
logger.Debug("Optimized image was larger than original, serving original")
}
entryToCache := CacheEntry{ContentType: mtype.String(), Data: mediaData}
app.Cache.SetWithTTL(mediaURL, entryToCache, int64(len(mediaData)), app.Config.CacheTTL)
w.Header().Set("Content-Type", entryToCache.ContentType)
w.Write(entryToCache.Data)
return
}
logger.Debug("Successfully optimized image", "original_size", len(mediaData), "optimized_size", len(optimizedImage))
entryToCache := CacheEntry{ContentType: "image/webp", Data: optimizedImage}
app.Cache.SetWithTTL(mediaURL, entryToCache, int64(len(optimizedImage)), app.Config.CacheTTL)
app.Cache.Wait() app.Cache.Wait()
w.Header().Set("Content-Type", entryToCache.ContentType) w.Header().Set("Content-Type", entryToCache.ContentType)