mirror of
https://github.com/imgproxy/imgproxy.git
synced 2025-10-04 10:29:36 +02:00
171 lines
4.2 KiB
Go
171 lines
4.2 KiB
Go
package imgproxy
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/imgproxy/imgproxy/v3/auximageprovider"
|
|
"github.com/imgproxy/imgproxy/v3/fetcher"
|
|
"github.com/imgproxy/imgproxy/v3/handlers"
|
|
processinghandler "github.com/imgproxy/imgproxy/v3/handlers/processing"
|
|
"github.com/imgproxy/imgproxy/v3/handlers/stream"
|
|
"github.com/imgproxy/imgproxy/v3/headerwriter"
|
|
"github.com/imgproxy/imgproxy/v3/imagedata"
|
|
"github.com/imgproxy/imgproxy/v3/memory"
|
|
"github.com/imgproxy/imgproxy/v3/monitoring/prometheus"
|
|
"github.com/imgproxy/imgproxy/v3/semaphores"
|
|
"github.com/imgproxy/imgproxy/v3/server"
|
|
)
|
|
|
|
const (
|
|
faviconPath = "/favicon.ico"
|
|
healthPath = "/health"
|
|
)
|
|
|
|
// Imgproxy holds all the components needed for imgproxy to function
|
|
type Imgproxy struct {
|
|
HeaderWriter *headerwriter.Writer
|
|
Semaphores *semaphores.Semaphores
|
|
FallbackImage auximageprovider.Provider
|
|
WatermarkImage auximageprovider.Provider
|
|
Fetcher *fetcher.Fetcher
|
|
ProcessingHandler *processinghandler.Handler
|
|
StreamHandler *stream.Handler
|
|
ImageDataFactory *imagedata.Factory
|
|
Config *Config
|
|
}
|
|
|
|
// New creates a new imgproxy instance
|
|
func New(ctx context.Context, config *Config) (*Imgproxy, error) {
|
|
headerWriter, err := headerwriter.New(&config.HeaderWriter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fetcher, err := fetcher.New(&config.Fetcher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
idf := imagedata.NewFactory(fetcher)
|
|
|
|
fallbackImage, err := auximageprovider.NewStaticProvider(ctx, &config.FallbackImage, "fallback", idf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
watermarkImage, err := auximageprovider.NewStaticProvider(ctx, &config.WatermarkImage, "watermark", idf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
semaphores, err := semaphores.New(&config.Semaphores)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
streamHandler, err := stream.New(&config.StreamHandler, headerWriter, fetcher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ph, err := processinghandler.New(
|
|
streamHandler, headerWriter, semaphores, fallbackImage, watermarkImage, idf, &config.ProcessingHandler,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Imgproxy{
|
|
HeaderWriter: headerWriter,
|
|
Semaphores: semaphores,
|
|
FallbackImage: fallbackImage,
|
|
WatermarkImage: watermarkImage,
|
|
Fetcher: fetcher,
|
|
StreamHandler: streamHandler,
|
|
ProcessingHandler: ph,
|
|
ImageDataFactory: idf,
|
|
Config: config,
|
|
}, nil
|
|
}
|
|
|
|
// BuildRouter sets up the HTTP routes and middleware
|
|
func (i *Imgproxy) BuildRouter() (*server.Router, error) {
|
|
r, err := server.NewRouter(&i.Config.Server)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r.GET("/", handlers.LandingHandler)
|
|
r.GET("", handlers.LandingHandler)
|
|
|
|
r.GET(faviconPath, r.NotFoundHandler).Silent()
|
|
r.GET(healthPath, handlers.HealthHandler).Silent()
|
|
if i.Config.Server.HealthCheckPath != "" {
|
|
r.GET(i.Config.Server.HealthCheckPath, handlers.HealthHandler).Silent()
|
|
}
|
|
|
|
r.GET(
|
|
"/*", i.ProcessingHandler.Execute,
|
|
r.WithSecret, r.WithCORS, r.WithPanic, r.WithReportError, r.WithMonitoring,
|
|
)
|
|
|
|
r.HEAD("/*", r.OkHandler, r.WithCORS)
|
|
r.OPTIONS("/*", r.OkHandler, r.WithCORS)
|
|
|
|
return r, nil
|
|
}
|
|
|
|
// Start runs the imgproxy server. This function blocks until the context is cancelled.
|
|
// If hasStarted is not nil, it will be notified with the server address once
|
|
// the server is ready or about to be ready to accept requests.
|
|
func (i *Imgproxy) StartServer(ctx context.Context, hasStarted chan net.Addr) error {
|
|
go i.startMemoryTicker(ctx)
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
if err := prometheus.StartServer(cancel); err != nil {
|
|
return err
|
|
}
|
|
|
|
router, err := i.BuildRouter()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s, err := server.Start(cancel, router)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer s.Shutdown(context.Background())
|
|
|
|
if hasStarted != nil {
|
|
hasStarted <- s.Addr
|
|
close(hasStarted)
|
|
}
|
|
|
|
<-ctx.Done()
|
|
|
|
return nil
|
|
}
|
|
|
|
// startMemoryTicker starts a ticker that periodically frees memory and optionally logs memory stats
|
|
func (i *Imgproxy) startMemoryTicker(ctx context.Context) {
|
|
ticker := time.NewTicker(i.Config.Server.FreeMemoryInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
memory.Free()
|
|
|
|
if i.Config.Server.LogMemStats {
|
|
memory.LogStats()
|
|
}
|
|
}
|
|
}
|
|
}
|