0
Fork 0
mirror of https://github.com/willnorris/imageproxy.git synced 2024-12-16 21:56:43 -05:00
imageproxy/caddy/module.go

166 lines
4.2 KiB
Go

// Package caddy provides ImageProxy as a Caddy module.
package caddy
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
caddy "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/gregjones/httpcache/diskcache"
"github.com/peterbourgon/diskv"
"go.uber.org/zap"
"willnorris.com/go/imageproxy"
)
func init() {
caddy.RegisterModule(ImageProxy{})
httpcaddyfile.RegisterHandlerDirective("imageproxy", parseCaddyfile)
httpcaddyfile.RegisterDirectiveOrder("imageproxy", "after", "reverse_proxy")
}
type ImageProxy struct {
Cache string `json:"cache,omitempty"`
DefaultBaseURL string `json:"default_base_url,omitempty"`
AllowHosts []string `json:"allow_hosts,omitempty"`
DenyHosts []string `json:"deny_hosts,omitempty"`
Referrers []string `json:"referrers,omitempty"`
ContentTypes []string `json:"content_types,omitempty"`
SignatureKeys []string `json:"signature_keys,omitempty"`
Verbose bool `json:"verbose,omitempty"`
logger *zap.Logger
proxy *imageproxy.Proxy
}
// interface guard
var (
_ caddyhttp.MiddlewareHandler = (*ImageProxy)(nil)
)
// CaddyModule returns the Caddy module information.
func (ImageProxy) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.imageproxy",
New: func() caddy.Module { return new(ImageProxy) },
}
}
func (p *ImageProxy) Provision(ctx caddy.Context) error {
p.logger = ctx.Logger()
cache, _ := parseCache(p.Cache)
p.proxy = imageproxy.NewProxy(nil, cache)
p.proxy.DefaultBaseURL, _ = url.Parse(p.DefaultBaseURL)
p.proxy.AllowHosts = p.AllowHosts
p.proxy.DenyHosts = p.DenyHosts
p.proxy.Referrers = p.Referrers
p.proxy.ContentTypes = p.ContentTypes
if len(p.proxy.ContentTypes) == 0 {
p.proxy.ContentTypes = []string{"image/*"}
}
for _, key := range p.SignatureKeys {
p.proxy.SignatureKeys = append(p.proxy.SignatureKeys, []byte(key))
}
p.proxy.Logger = zap.NewStdLog(p.logger)
p.proxy.Verbose = p.Verbose
p.proxy.FollowRedirects = true
return nil
}
func (p *ImageProxy) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
p.proxy.ServeHTTP(w, r)
return nil
}
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
p := new(ImageProxy)
h.Next() // consume the directive name
for nesting := h.Nesting(); h.NextBlock(nesting); {
switch h.Val() {
case "cache":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.Cache = h.Val()
case "default_base_url":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.DefaultBaseURL = h.Val()
case "allow_hosts":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.AllowHosts = append(p.AllowHosts, strings.Split(h.Val(), ",")...)
case "deny_hosts":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.DenyHosts = append(p.DenyHosts, strings.Split(h.Val(), ",")...)
case "referrers":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.Referrers = append(p.Referrers, strings.Split(h.Val(), ",")...)
case "content_types":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.ContentTypes = append(p.ContentTypes, strings.Split(h.Val(), ",")...)
case "signature_key":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.SignatureKeys = append(p.SignatureKeys, h.Val())
case "verbose":
if !h.NextArg() {
return nil, h.ArgErr()
}
p.Verbose, _ = strconv.ParseBool(h.Val())
}
}
return p, nil
}
// parseCache parses c returns the specified Cache implementation.
func parseCache(c string) (imageproxy.Cache, error) {
const defaultMemorySize = 100
if c == "" {
return nil, nil
}
if c == "memory" {
c = fmt.Sprintf("memory:%d", defaultMemorySize)
}
u, err := url.Parse(c)
if err != nil {
return nil, fmt.Errorf("error parsing cache flag: %w", err)
}
switch u.Scheme {
case "file":
return diskCache(u.Path), nil
default:
return diskCache(c), nil
}
}
func diskCache(path string) *diskcache.Cache {
d := diskv.New(diskv.Options{
BasePath: path,
// For file "c0ffee", store file as "c0/ff/c0ffee"
Transform: func(s string) []string { return []string{s[0:2], s[2:4]} },
})
return diskcache.NewWithDiskv(d)
}