123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- package imgproxy
- import (
- "context"
- "net"
- "time"
- "github.com/imgproxy/imgproxy/v3/auximageprovider"
- "github.com/imgproxy/imgproxy/v3/cookies"
- "github.com/imgproxy/imgproxy/v3/errorreport"
- "github.com/imgproxy/imgproxy/v3/fetcher"
- healthhandler "github.com/imgproxy/imgproxy/v3/handlers/health"
- landinghandler "github.com/imgproxy/imgproxy/v3/handlers/landing"
- processinghandler "github.com/imgproxy/imgproxy/v3/handlers/processing"
- streamhandler "github.com/imgproxy/imgproxy/v3/handlers/stream"
- "github.com/imgproxy/imgproxy/v3/imagedata"
- "github.com/imgproxy/imgproxy/v3/memory"
- "github.com/imgproxy/imgproxy/v3/monitoring"
- optionsparser "github.com/imgproxy/imgproxy/v3/options/parser"
- "github.com/imgproxy/imgproxy/v3/processing"
- "github.com/imgproxy/imgproxy/v3/security"
- "github.com/imgproxy/imgproxy/v3/server"
- "github.com/imgproxy/imgproxy/v3/workers"
- )
- const (
- faviconPath = "/favicon.ico"
- healthPath = "/health"
- )
- // ImgproxyHandlers holds the handlers for imgproxy
- type ImgproxyHandlers struct {
- Health *healthhandler.Handler
- Landing *landinghandler.Handler
- Processing *processinghandler.Handler
- Stream *streamhandler.Handler
- }
- // Imgproxy holds all the components needed for imgproxy to function
- type Imgproxy struct {
- workers *workers.Workers
- fallbackImage auximageprovider.Provider
- watermarkImage auximageprovider.Provider
- fetcher *fetcher.Fetcher
- imageDataFactory *imagedata.Factory
- handlers ImgproxyHandlers
- security *security.Checker
- optionsParser *optionsparser.Parser
- processor *processing.Processor
- cookies *cookies.Cookies
- monitoring *monitoring.Monitoring
- config *Config
- errorReporter *errorreport.Reporter
- }
- // New creates a new imgproxy instance
- func New(ctx context.Context, config *Config) (*Imgproxy, error) {
- if err := config.Validate(); 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
- }
- workers, err := workers.New(&config.Workers)
- if err != nil {
- return nil, err
- }
- security, err := security.New(&config.Security)
- if err != nil {
- return nil, err
- }
- optionsParser, err := optionsparser.New(&config.OptionsParser)
- if err != nil {
- return nil, err
- }
- processor, err := processing.New(&config.Processing, watermarkImage)
- if err != nil {
- return nil, err
- }
- cookies, err := cookies.New(&config.Cookies)
- if err != nil {
- return nil, err
- }
- monitoring, err := monitoring.New(ctx, &config.Monitoring, config.Workers.WorkersNumber)
- if err != nil {
- return nil, err
- }
- errorReporter, err := errorreport.New(&config.ErrorReport)
- if err != nil {
- return nil, err
- }
- imgproxy := &Imgproxy{
- workers: workers,
- fallbackImage: fallbackImage,
- watermarkImage: watermarkImage,
- fetcher: fetcher,
- imageDataFactory: idf,
- config: config,
- security: security,
- optionsParser: optionsParser,
- processor: processor,
- cookies: cookies,
- monitoring: monitoring,
- errorReporter: errorReporter,
- }
- imgproxy.handlers.Health = healthhandler.New()
- imgproxy.handlers.Landing = landinghandler.New()
- imgproxy.handlers.Stream, err = streamhandler.New(imgproxy, &config.Handlers.Stream)
- if err != nil {
- return nil, err
- }
- imgproxy.handlers.Processing, err = processinghandler.New(
- imgproxy, imgproxy.handlers.Stream, &config.Handlers.Processing,
- )
- if err != nil {
- return nil, err
- }
- return imgproxy, nil
- }
- // BuildRouter sets up the HTTP routes and middleware
- func (i *Imgproxy) BuildRouter() (*server.Router, error) {
- r, err := server.NewRouter(&i.config.Server, i.monitoring, i.errorReporter)
- if err != nil {
- return nil, err
- }
- r.GET("/", i.handlers.Landing.Execute)
- r.GET("", i.handlers.Landing.Execute)
- r.GET(faviconPath, r.NotFoundHandler).Silent()
- r.GET(healthPath, i.handlers.Health.Execute).Silent()
- if i.config.Server.HealthCheckPath != "" {
- r.GET(i.config.Server.HealthCheckPath, i.handlers.Health.Execute).Silent()
- }
- r.GET(
- "/*", i.handlers.Processing.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 := i.monitoring.StartPrometheus(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
- }
- // Close gracefully shuts down the imgproxy instance
- func (i *Imgproxy) Close(ctx context.Context) {
- i.monitoring.Stop(ctx)
- i.errorReporter.Close()
- }
- // 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()
- }
- }
- }
- }
- func (i *Imgproxy) Fetcher() *fetcher.Fetcher {
- return i.fetcher
- }
- func (i *Imgproxy) Workers() *workers.Workers {
- return i.workers
- }
- func (i *Imgproxy) FallbackImage() auximageprovider.Provider {
- return i.fallbackImage
- }
- func (i *Imgproxy) WatermarkImage() auximageprovider.Provider {
- return i.watermarkImage
- }
- func (i *Imgproxy) ImageDataFactory() *imagedata.Factory {
- return i.imageDataFactory
- }
- func (i *Imgproxy) Security() *security.Checker {
- return i.security
- }
- func (i *Imgproxy) OptionsParser() *optionsparser.Parser {
- return i.optionsParser
- }
- func (i *Imgproxy) Processor() *processing.Processor {
- return i.processor
- }
- func (i *Imgproxy) Cookies() *cookies.Cookies {
- return i.cookies
- }
- func (i *Imgproxy) Monitoring() *monitoring.Monitoring {
- return i.monitoring
- }
- func (i *Imgproxy) ErrorReporter() *errorreport.Reporter {
- return i.errorReporter
- }
|