Image.Plug (image_plug v0.2.1)

Copy Markdown View Source

The library's request entry point.

Mount this plug under whatever path your host application uses (e.g. forward "/img", to: Image.Plug, init_opts: [...] for Plug.Router, or plug Image.Plug, [...] from a Phoenix endpoint).

Passthrough

A request whose URL the configured provider does not recognise as an image request is passed through untouched: the plug returns the connection un-halted and unsent so the host application's remaining plugs handle it. This makes plug Image.Plug safe to mount at the root of a Phoenix endpoint ahead of your router — only genuine image URLs are claimed; everything else flows on. A URL that clearly intends an image request but is malformed still produces an error response.

When the plug is mounted as the sole plug of a standalone server (for example a Bandit plug: entry with nothing downstream), an unrecognised URL has nowhere to pass through to; add a fallback route (e.g. a Plug.Router match _ that returns 404) if you need to serve non-image paths from the same endpoint.

Runtime configuration for releases

A Phoenix endpoint calls a plug's init/1 at compile time, so options passed inline to plug Image.Plug, ... — including a provider's :mount path — are frozen into the compiled module and cannot be read from config/runtime.exs or environment variables at boot. To configure the plug at runtime (the usual case for an Elixir release), pass otp_app::

# endpoint.ex
plug Image.Plug, otp_app: :my_app

# config/runtime.exs
config :my_app, Image.Plug,
  provider: {Image.Plug.Provider.Cloudflare, mount: System.fetch_env!("IMAGE_MOUNT")},
  source_resolver: {Image.Plug.SourceResolver.HTTP, []}

In this mode init/1 stores only the app reference; the full configuration is read from the application environment and validated on the first request, then memoized. Inline options given alongside otp_app: act as defaults that the application-environment config overrides per key. Pass :key to override the config key (it defaults to Image.Plug).

Alternatively, without any Image.Plug-specific option, Phoenix's own config :my_app, MyAppWeb.Endpoint, plug_init_mode: :runtime makes the endpoint run every plug's init/1 at boot, after which inline System.get_env/1 calls resolve; note that this applies to the whole endpoint, not just this plug.

Request lifecycle

Each request flows through:

  1. provider.parse/2 — produces either a fully-formed pipeline + source, a variant lookup + source, or a passthrough source.

  2. Variant resolution against the configured Image.Plug.VariantStore. Variant URLs of the form /<account>/<image-id>/<variant-name> are expanded to the stored pipeline; ad-hoc URLs skip this step.

  3. Image.Plug.Pipeline.Normaliser.normalise/1.

  4. source_resolver.load/2 — opens the source as a Vix.Vips.Image, preferring streaming decode.

  5. Image.Plug.Pipeline.Interpreter.execute/2.

  6. Image.Plug.Pipeline.Encoder.encode/3 — produces a streaming body when possible.

  7. The plug pipes the body to the client via Plug.Conn.send_chunked/2 + Plug.Conn.chunk/2. Buffered bytes bodies use Plug.Conn.send_resp/3.

Summary

Types

Deferred configuration produced by init/1 when the :otp_app option is given. The full options are read from the application environment and validated on the first call/2, so that a release can configure the plug from config/runtime.exs.

Functions

Handles a single request end-to-end.

Returns the default telemetry prefix used by the request plug.

Deletes a variant by name.

Fetches a variant by name.

Validates configuration and returns an opaque options struct passed through to every call/2 invocation.

Lists every variant in the store.

Returns the library version as a string.

Types

runtime_config()

@type runtime_config() ::
  {:runtime, otp_app :: atom(), key :: atom(), defaults :: keyword()}

Deferred configuration produced by init/1 when the :otp_app option is given. The full options are read from the application environment and validated on the first call/2, so that a release can configure the plug from config/runtime.exs.

Functions

call(conn, options)

Handles a single request end-to-end.

default_telemetry_prefix()

@spec default_telemetry_prefix() :: [atom(), ...]

Returns the default telemetry prefix used by the request plug.

Returns

  • The list of atoms [:image_plug].

Examples

iex> Image.Plug.default_telemetry_prefix()
[:image_plug]

delete_variant(name, options \\ [])

@spec delete_variant(
  String.t(),
  keyword()
) :: :ok | {:error, :not_found}

Deletes a variant by name.

Returns

  • :ok on success.

  • {:error, :not_found} if the variant does not exist.

get_variant(name, options \\ [])

@spec get_variant(
  String.t(),
  keyword()
) :: {:ok, Image.Plug.Variant.t()} | {:error, :not_found}

Fetches a variant by name.

Returns {:ok, variant} or {:error, :not_found}.

Examples

iex> case Image.Plug.get_variant("public") do
...>   {:ok, variant} -> variant.name
...>   {:error, _} -> nil
...> end
"public"

init(options)

Validates configuration and returns an opaque options struct passed through to every call/2 invocation.

Raises ArgumentError if required configuration is missing or malformed. Configuration errors are programmer errors and must surface at boot time, not per-request.

Runtime configuration

Pass otp_app: :my_app to read the configuration from the application environment (Application.get_env(:my_app, Image.Plug)) on the first request instead of at compile time. This is what lets an Elixir release configure the plug — for example a runtime :mount path — from config/runtime.exs. Any inline options are used as defaults that the application-environment config overrides per key. An optional :key overrides the config key (defaulting to Image.Plug). In this mode configuration errors surface on the first request rather than at boot.

list_variants(options \\ [])

@spec list_variants(keyword()) :: {:ok, [Image.Plug.Variant.t()]}

Lists every variant in the store.

Returns

  • {:ok, [variant]} — the order is store-defined (the default ETS store sorts by name).

put_variant(name_or_variant, definition_or_options \\ [], options \\ [])

@spec put_variant(Image.Plug.Variant.t() | String.t(), term(), keyword()) ::
  {:ok, Image.Plug.Variant.t()} | {:error, term()}

Inserts or updates a variant.

Arguments

  • name_or_variant is either a variant name string or a complete Image.Plug.Variant struct.

  • When the first argument is a name, the second argument is either a Cloudflare-style options string, an Image.Plug.Pipeline struct, or a {provider_module, options_string} tuple.

Options

  • :store{module, options} tuple identifying the store. Defaults to {Image.Plug.VariantStore.ETS, []}.

  • :metadata — arbitrary metadata map stored on the variant.

  • :never_require_signed_urls? — boolean, defaults to false.

Returns

  • {:ok, variant} on success.

  • {:error, reason} on failure (e.g. invalid options string).

version()

@spec version() :: String.t()

Returns the library version as a string.

Returns

  • A semver-like version string such as "0.1.0-dev".

Examples

iex> is_binary(Image.Plug.version())
true