49 lines
1.6 KiB
Go
49 lines
1.6 KiB
Go
package outboundimage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type ObjectReader interface {
|
|
Get(ctx context.Context, key string, maxBytes int64) ([]byte, string, error)
|
|
}
|
|
|
|
func NewStableImageHandler(reader ObjectReader, objectPrefix, routePrefix string, maxBytes int64) (http.Handler, error) {
|
|
objectPrefix = strings.Trim(strings.TrimSpace(objectPrefix), "/")
|
|
routePrefix = "/" + strings.Trim(strings.TrimSpace(routePrefix), "/") + "/"
|
|
if reader == nil || objectPrefix == "" || maxBytes <= 0 {
|
|
return nil, errors.New("outbound image: invalid stable image handler configuration")
|
|
}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
w.Header().Set("Allow", "GET, HEAD")
|
|
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
id := strings.TrimPrefix(r.URL.Path, routePrefix)
|
|
if r.URL.Path == id || strings.Contains(id, "/") || !stableImageIDRE.MatchString(id) {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
data, contentType, err := reader.Get(r.Context(), path.Join(objectPrefix, id), maxBytes)
|
|
if err != nil || len(data) == 0 || imageExtension(contentType) == "" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", strings.TrimSpace(strings.Split(contentType, ";")[0]))
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
|
w.Header().Set("Cache-Control", "public, max-age=86400, immutable")
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
w.WriteHeader(http.StatusOK)
|
|
if r.Method == http.MethodHead {
|
|
return
|
|
}
|
|
_, _ = w.Write(data)
|
|
}), nil
|
|
}
|