defmodule PlugProbe do @default_path "/probe" @moduledoc """ A plug for responding to HTTP probe requests. This plug responds to `GET` or `HEAD` requests at a specific path (`#{@default_path}` by default), with: * status code: `200` * body: `OK` or `{}` (when `json: true` option is set) ## Options The following options can be used when calling `plug PlugProbe`. * `:path` (string) - specify the path on which `PlugProbe` will be mounted to respond to probe requests. Default to `#{@default_path}`. * `:json` (boolean) - specify whether the response will be an `application/json` response. Default to `false`. ## Examples In general, probe requests are separated from the functionality of the application, because of that, it's better to handle them as light as possible. In order to do that, this plug should be placed near the top of a plug pipeline, then it can match requests early so that subsequent plugs don't have the chance to tamper the connection. For a simple plug pipeline: defmodule DemoServer do use Plug.Builder # Put it before any other plugs plug PlugProbe # ... end For a Phoenix endpoint: defmodule DemoWeb.Endpoint do use Phoenix.Endpoint, otp_app: :demo # Put it before any other plugs plug PlugProbe # ... end Using a custom probe path is easy: defmodule DemoServer do use Plug.Builder # Put it before any other plugs plug PlugProbe, path: "/heartbeat" # ... end """ @behaviour Plug import Plug.Conn def init(opts), do: Keyword.merge([path: @default_path, json: false], opts) def call(%Plug.Conn{} = conn, opts) do expected_path_info = String.split(opts[:path], "/", trim: true) if conn.path_info == expected_path_info and conn.method in ~w(GET HEAD) do conn |> halt() |> response(opts[:json]) else conn end end defp response(conn, false = _json), do: send_resp(conn, 200, "OK") defp response(conn, true = _json), do: conn |> put_resp_content_type("application/json") |> send_resp(200, "{}") end