### Common Security Setup Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md Demonstrates a common setup for security-related Plugs in a Plug.Router. ```APIDOC ## Complete Security Setup ### Description This example shows how to configure several security-related Plugs within a Plug.Router, including request ID, logging, SSL with HSTS, session management, and CSRF protection. ### Code Example ```elixir defmodule MyRouter do use Plug.Router plug Plug.RequestId plug Plug.Logger # Security plug Plug.SSL, hsts: 31536000 plug Plug.Session, store: :cookie, key: "_session" plug Plug.CSRFProtection # Routing plug :match plug :dispatch get "/" do send_resp(conn, 200, "Home") end end ``` ``` -------------------------------- ### Example: Using Plug.Head in a Router Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md This example shows how to integrate Plug.Head into a Plug.Router. Requests to GET /users will be handled normally, and HEAD /users requests will be converted to GET and handled by the same route. ```elixir defmodule MyRouter do use Plug.Router plug Plug.Head plug :match plug :dispatch get "/users" do send_resp(conn, 200, "User list") end # HEAD /users is also handled by the same route end ``` -------------------------------- ### Create a Basic GET Connection Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Simulates a simple GET request to the root path. This is the most basic way to start testing a Plug. ```elixir conn = conn(:get, "/") ``` -------------------------------- ### Complete Security Setup with Plug Router Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md This example demonstrates a common setup for a Plug router including security plugs like CSRF protection and session management, followed by routing plugs. ```elixir defmodule MyRouter do use Plug.Router plug Plug.RequestId plug Plug.Logger # Security plug Plug.SSL, hsts: 31536000 plug Plug.Session, store: :cookie, key: "_session" plug Plug.CSRFProtection # Routing plug :match plug :dispatch get "/" do send_resp(conn, 200, "Home") end end ``` -------------------------------- ### Complete Plug Router Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md A comprehensive example of a Plug.Router module defining various routes for user management (GET, POST, PUT, DELETE), including a forward to an admin router and a catch-all 404 handler. ```elixir defmodule MyRouter do use Plug.Router # Plugs that run before matching plug Plug.Logger plug Plug.RequestId # Match and dispatch plug :match plug Plug.Parsers, parsers: [:json] plug :dispatch get "/" do send_resp(conn, 200, "Home") end get "/users" do send_resp(conn, 200, "User list") end get "/users/:id" do send_resp(conn, 200, "User #{id}") end post "/users" do # conn.body_params contains parsed JSON send_resp(conn, 201, "Created") end put "/users/:id" do send_resp(conn, 200, "Updated") end delete "/users/:id" do send_resp(conn, 204, "") end forward "/admin", to: AdminRouter # Catch-all match _ do send_resp(conn, 404, "Not found") end end ``` -------------------------------- ### Basic Plug.Router Example Source: https://github.com/elixir-plug/plug/blob/main/README.md Defines a simple router plug with a GET route for '/hello', a forward to 'UsersRouter', and a catch-all 404 response. ```elixir defmodule MyRouter do use Plug.Router plug :match plug :dispatch get "/hello" do send_resp(conn, 200, "world") end forward "/users", to: UsersRouter match _ do send_resp(conn, 404, "oops") end end ``` -------------------------------- ### Minimal Hello World Plug Example Source: https://github.com/elixir-plug/plug/blob/main/README.md A minimal hello world example using the Cowboy webserver. Module plugs must define init/1 and call/2 functions. call/2 is invoked with the connection and options from init/1. ```elixir Mix.install([:plug, :plug_cowboy]) defmodule MyPlug do import Plug.Conn def init(options) do # initialize options options end def call(conn, _opts) do conn |> put_resp_content_type("text/plain") |> send_resp(200, "Hello world") end end require Logger webserver = {Plug.Cowboy, plug: MyPlug, scheme: :http, options: [port: 4000]} {:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one) Logger.info("Plug now running on localhost:4000") Process.sleep(:infinity) ``` -------------------------------- ### Authentication with Basic Auth Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md Illustrates how to implement basic authentication using Plug.BasicAuth. ```APIDOC ## Authentication with Basic Auth ### Description This example demonstrates how to implement basic authentication by parsing and validating credentials provided in the Authorization header. ### Code Example ```elixir plug :authenticate defp authenticate(conn, _opts) do case Plug.BasicAuth.parse_basic_auth(conn) do {user, pass} -> if validate(user, pass) do assign(conn, :user, user) else Plug.BasicAuth.request_basic_auth(conn) |> halt() end :error -> Plug.BasicAuth.request_basic_auth(conn) |> halt() end end ``` ``` -------------------------------- ### Complete Plug Session Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/06-plug-session.md A comprehensive example demonstrating Plug session management within a Plug.Router. It includes configuration for cookie-based sessions and handlers for login, profile viewing, and logout. ```elixir defmodule MyApp.AuthRouter do use Plug.Router plug Plug.Session, store: :cookie, key: "_my_app_session", signing_salt: "cookie_secret_salt" plug :match plug :dispatch post "/login" do # Parse form data conn = Plug.Conn.fetch_query_params(conn) params = conn.params # Validate credentials case validate_user(params["username"], params["password"]) do {:ok, user} -> conn |> Plug.Conn.fetch_session() |> Plug.Conn.put_session(:user_id, user.id) |> Plug.Conn.put_session(:username, user.username) |> send_resp(200, "Logged in as #{user.username}") :error -> send_resp(conn, 401, "Invalid credentials") end end get "/profile" do conn = Plug.Conn.fetch_session(conn) case Plug.Conn.get_session(conn, :user_id) do nil -> send_resp(conn, 401, "Not logged in") user_id -> user = load_user(user_id) send_resp(conn, 200, "Profile: #{user.username}") end end post "/logout" do conn |> Plug.Conn.fetch_session() |> Plug.Conn.clear_session() |> send_resp(200, "Logged out") end match _ do send_resp(conn, 404, "Not found") end end ``` -------------------------------- ### Example: Logging requests with Plug.Logger Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md This example demonstrates how to use Plug.Logger with a custom log level (`:debug`) in a Plug.Router. All incoming requests will be logged with detailed information. ```elixir defmodule MyRouter do use Plug.Router plug Plug.Logger, log: :debug plug :match plug :dispatch get "/" do send_resp(conn, 200, "Home") end end ``` -------------------------------- ### Create a GET Connection with Query String Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Simulates a GET request with query parameters. The path includes the query string, which can be later parsed into a map. ```elixir conn = conn(:get, "/users?page=2&limit=10") ``` -------------------------------- ### Plug.run/3 Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md Executes a list of plugs, which can be module plugs with options or function plugs, processing the connection through the pipeline. ```elixir conn = Plug.run(conn, [ {Plug.Head, []}, {Plug.Logger, []}, &my_custom_plug/1 ]) ``` -------------------------------- ### Module Plug Examples Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Illustrates how to add module plugs to a pipeline. These reference modules that implement the Plug behaviour. ```elixir plug Plug.Logger plug Plug.RequestId plug MyApp.CustomPlug ``` -------------------------------- ### Plug.Static :at Option Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Demonstrates the usage of the `:at` option in Plug.Static, specifying the request path prefix for serving static assets. ```elixir plug Plug.Static, at: "/public" ``` -------------------------------- ### Basic Static File Server Setup Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Configure Plug.Static to serve files from a specified directory (e.g., '/priv/static') under a given path prefix (e.g., '/public'). Includes a fallback for unmatched requests. ```elixir defmodule MyApp.Router do use Plug.Builder plug Plug.Static, at: "/public", from: :my_app plug :not_found def not_found(conn, _) do send_resp(conn, 404, "Not Found") end end ``` -------------------------------- ### Static Route Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Defines a static route that matches the exact path '/hello'. ```elixir get "/hello" do send_resp(conn, 200, "Hello") end ``` -------------------------------- ### Module Plug Implementation Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/00-index.md An example of a Module Plug that implements the `Plug` behaviour. It includes the `init/1` and `call/2` callbacks. ```elixir defmodule MyPlug do @behaviour Plug def init(opts), do: opts def call(conn, _opts), do: conn end ``` -------------------------------- ### Runtime Pipeline Execution Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md Demonstrates defining a list of plugs and then executing them at runtime using `Plug.run/3`, including an option to log when a plug halts. ```elixir # Define plugs separately plugs = [ {Plug.RequestId, []}, {Plug.Logger, []}, {MyApp.Router, []} ] # Execute at runtime conn = Plug.run(conn, plugs, log_on_halt: :info) ``` -------------------------------- ### Plug.Parsers.UnsupportedMediaTypeError Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/10-error-handling.md An example demonstrating how sending a request with an unsupported content type (e.g., XML without a corresponding parser) will raise an UnsupportedMediaTypeError. ```elixir # Sending XML without parser conn(:post, "/", "...") |> put_req_header("content-type", "application/xml") |> MyRouter.call([]) # Raises UnsupportedMediaTypeError unless :pass option allows it ``` -------------------------------- ### Route Forwarding Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Demonstrates how to forward requests matching a prefix to another router module, stripping the prefix from the path. ```elixir defmodule MainRouter do use Plug.Router plug :match plug :dispatch forward "/admin", to: AdminRouter forward "/api", to: APIRouter match _ do send_resp(conn, 404, "not found") end end defmodule AdminRouter do use Plug.Router plug :match plug :dispatch get "/dashboard" do send_resp(conn, 200, "Admin dashboard") end end ``` -------------------------------- ### Example: Using Plug.RequestId with Plug.Logger Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md This example shows how Plug.RequestId can be used in conjunction with Plug.Logger. Each request will be assigned a unique ID, which will be included in the log output, making it easier to trace requests through the system. ```elixir defmodule MyRouter do use Plug.Router plug Plug.RequestId plug Plug.Logger plug :match plug :dispatch end # Requests automatically get unique IDs logged # GET /users [request_id: "550e8400-e29b-41d4-a716-446655440000"] # Sent 200 [request_id: "550e8400-e29b-41d4-a716-446655440000"] ``` -------------------------------- ### Plug.Conn.AlreadySentError Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/10-error-handling.md An example function that demonstrates how calling send_resp twice will raise an AlreadySentError on the second call. ```elixir defp double_send(conn, _opts) do conn = send_resp(conn, 200, "First") send_resp(conn, 200, "Second") # Raises AlreadySentError end ``` -------------------------------- ### Function Plug Examples Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Shows how to reference functions within the current module as plugs. These functions must accept (conn, opts) and return conn. ```elixir plug :my_function plug :authenticate plug :custom_middleware ``` -------------------------------- ### Plug.Static :from Option Examples Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Illustrates different formats for the `:from` option in Plug.Static, which specifies the source of static assets. Atom format serves from `priv/static/`, string uses a filesystem path, and a tuple combines application name with a custom directory. ```elixir plug Plug.Static, from: :my_app ``` ```elixir plug Plug.Static, from: "priv/static" ``` ```elixir plug Plug.Static, from: {:my_app, "priv/assets"} ``` -------------------------------- ### Function Plug Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/00-index.md A simple function that can be used as a Function Plug. It accepts the connection and options, returning the modified connection. ```elixir def my_plug(conn, _opts), do: conn ``` -------------------------------- ### Application Startup with ETS Session Table Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/06-plug-session.md Example of creating an ETS table for session storage as part of an Elixir application's startup process. ```elixir defmodule MyApp.Application do def start(_type, _args) do # Create ETS table for sessions :ets.new(:session_table, [:named_table, :public, {:read_concurrency, true}]) children = [ # ... other children ... {Plug.Cowboy, plug: MyRouter, scheme: :http, options: [port: 4000]} ] Supervisor.start_link(children, strategy: :one_for_one) end end ``` -------------------------------- ### Test a Plug Endpoint Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/00-index.md Provides an example of how to test a Plug endpoint using Plug.Test. ```elixir test "GET /" do conn = conn(:get, "/") conn = MyApp.Router.call(conn, MyApp.Router.init([])) assert conn.status == 200 assert conn.resp_body == "Home" end ``` -------------------------------- ### Complete Plug.ErrorHandler Example with Plug.Router Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/10-error-handling.md A comprehensive example demonstrating Plug.ErrorHandler with Plug.Router, including handling specific error types like ParseError and NoResultsError. The handle_errors/2 callback logs the error and sends tailored responses. ```elixir defmodule MyApp.Router do use Plug.Router use Plug.ErrorHandler plug :match plug :dispatch get "/" do send_resp(conn, 200, "OK") end post "/users" do case create_user(conn.body_params) do {:ok, user} -> send_resp(conn, 201, "User created") {:error, reason} -> send_resp(conn, 400, "Error: #{reason}") end end defp handle_errors(conn, %{kind: _kind, reason: reason, stack: _stack}) do IO.inspect({reason, __STACKTRACE__}) case reason do %Plug.Parsers.ParseError{} -> send_resp(conn, 400, "Invalid request body") %Ecto.NoResultsError{} -> send_resp(conn, 404, "Not found") _ -> send_resp(conn, 500, "Internal server error: #{inspect(reason)}") end end end ``` -------------------------------- ### Test Basic GET Request Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Simulates a basic GET request to a root path and asserts the response status and body. ```elixir test "simple GET request" do conn = conn(:get, "/") conn = MyRouter.call(conn, MyRouter.init([])) assert conn.status == 200 assert conn.resp_body == "Hello" end ``` -------------------------------- ### Plug.Parsers.ParseError Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/10-error-handling.md An example showing how sending malformed JSON data will result in a ParseError when the content type is set to application/json. ```elixir # Invalid JSON conn(:post, "/", "not valid json") |> put_req_header("content-type", "application/json") |> MyRouter.call([]) # Raises ParseError ``` -------------------------------- ### Anonymous Function Plug Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Demonstrates using an anonymous function directly as a plug for simple, inline operations. ```elixir plug fn conn, _opts -> assign(conn, :time, System.monotonic_time()) end ``` -------------------------------- ### Plug.run/3 Error Handling Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md Illustrates the error raised by `Plug.run/3` when a plug does not return a `Plug.Conn` struct, showing the expected error message. ```elixir ** (RuntimeError) expected module_name to return Plug.Conn, got: :not_a_conn ``` -------------------------------- ### Plug.Conn.NotSentError Example Test Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/10-error-handling.md An example test case demonstrating the expectation that a response must be sent by the router. If no response is sent, the test will fail. ```elixir test "must send response" do conn = conn(:get, "/") conn = MyRouter.call(conn, MyRouter.init([])) # If /route doesn't send response, test fails assert conn.status == 200 end ``` -------------------------------- ### Plug.Upload Struct Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/05-plug-parsers.md Illustrates the structure of a Plug.Upload struct when file uploads are parsed. Shows original filename, temporary file path, and MIME type. ```elixir %Plug.Upload{ filename: "photo.png", # Original filename path: "/tmp/plug-1234/photo", # Temp file path content_type: "image/png" # MIME type } ``` -------------------------------- ### Minimal HTTPS Listener Configuration Source: https://github.com/elixir-plug/plug/blob/main/guides/https.md A basic HTTPS listener setup using Plug.Cowboy. Ensure the certificate and key files are correctly specified. ```elixir Plug.Cowboy.https MyApp.MyPlug, [], port: 8443, cipher_suite: :strong, certfile: "/etc/letsencrypt/live/example.net/cert.pem", keyfile: "/etc/letsencrypt/live/example.net/privkey.pem", cacertfile: "/etc/letsencrypt/live/example.net/chain.pem" ``` -------------------------------- ### Basic Plug.Static Configuration Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Configures Plug.Static to serve files from the '/public' path, sourced from the ':my_app' application. This is a common setup within a Plug.Builder pipeline. ```elixir defmodule MyApp do use Plug.Builder plug Plug.Static, at: "/public", from: :my_app plug :not_found def not_found(conn, _) do send_resp(conn, 404, "Not found") end end ``` -------------------------------- ### Handling File Uploads with Plug.Parsers Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/05-plug-parsers.md Example of a Plug.Router configuration to handle multipart file uploads. It demonstrates accessing the uploaded file's details and copying it to a permanent location. ```elixir defmodule MyRouter do use Plug.Router plug :match plug Plug.Parsers, parsers: [:multipart] plug :dispatch post "/upload" do case conn.body_params do %{"file" => %Plug.Upload{filename: name, path: path}} -> # Copy file to permanent location File.cp!(path, "uploads/#{name}") send_resp(conn, 200, "Uploaded") _ -> send_resp(conn, 400, "Missing file") end end end ``` -------------------------------- ### Module Plug Implementation Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md An example of a module plug that sets the response content type to 'application/json'. It implements the `Plug` behaviour with `init/1` and `call/2`. ```elixir defmodule JSONHeaderPlug do @behaviour Plug import Plug.Conn def init(opts) do opts end def call(conn, _opts) do put_resp_content_type(conn, "application/json") end end ``` -------------------------------- ### Configure Application Supervision Tree for Plug Source: https://github.com/elixir-plug/plug/blob/main/README.md Update your application's Application.ex file to include the Plug.Cowboy child process in the supervision tree. This ensures the web server starts with your application. ```elixir defmodule MyApp.Application do # See https://hexdocs.pm/elixir/Application.html # for more information on OTP Applications @moduledoc false use Application def start(_type, _args) do # List all child processes to be supervised children = [ {Plug.Cowboy, scheme: :http, plug: MyPlug, options: [port: 4001]} ] # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end ``` -------------------------------- ### Plug.forward/4 Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md Forwards a request to a target plug, updating the connection's path and script name to create a nested routing context. ```elixir %{host: "localhost", path_info: ["admin" | rest]} -> Plug.forward(conn, rest, AdminRouter, opts) ``` -------------------------------- ### Define Plug Execution Order Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Plugs are executed in the order they are defined in the Plug.Builder module. This example shows the sequence of plug execution. ```elixir defmodule MyApp do use Plug.Builder plug Plug.RequestId # Runs 1st plug Plug.Logger # Runs 2nd plug :authenticate # Runs 3rd plug :process # Runs 4th end # Execution: RequestId → Logger → authenticate → process ``` -------------------------------- ### Imported Function Plug Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Illustrates referencing an imported function from another module as a plug. Ensure the function is explicitly imported. ```elixir import AnotherModule, only: [middleware_func: 2] plug :middleware_func ``` -------------------------------- ### Parameter Merging Example Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/05-plug-parsers.md Demonstrates how Plug.Parsers merges query parameters and body parameters. Body parameters override query parameters with the same keys. ```elixir # Request: POST /search?q=elixir with JSON body {"q": "erlang"} # conn.query_params => %{"q" => "elixir"} # conn.body_params => %{"q" => "erlang"} # conn.params => %{"q" => "erlang"} (body wins) ``` -------------------------------- ### Manual TLS Configuration with Custom SSL Options Source: https://github.com/elixir-plug/plug/blob/main/guides/https.md Configure HTTPS server with custom TLS versions, ciphers, and other SSL options. This example demonstrates setting specific TLS protocols, AEAD ciphers, and enabling honor_cipher_order. ```elixir Plug.Cowboy.https MyApp.MyPlug, [], port: 8443, certfile: "/etc/letsencrypt/live/example.net/cert.pem", keyfile: "/etc/letsencrypt/live/example.net/privkey.pem", cacertfile: "/etc/letsencrypt/live/example.net/chain.pem", versions: [:"tlsv1.3", :"tlsv1.2"], ciphers: [ # TLS 1.3 Ciphersuites ~c"TLS_AES_256_GCM_SHA384", ~c"TLS_CHACHA20_POLY1305_SHA256", ~c"TLS_AES_128_GCM_SHA256", # Modern TLS 1.2 Ciphersuites ~c"ECDHE-ECDSA-AES128-GCM-SHA256", ~c"ECDHE-RSA-AES128-GCM-SHA256" ], honor_cipher_order: true, sni_fun: &MyPlug.ssl_opts_for_hostname/1 ``` -------------------------------- ### Plug.Conn Struct Example Source: https://github.com/elixir-plug/plug/blob/main/README.md The %Plug.Conn{} struct represents the connection and holds request and response data. You can access and pattern match on its fields. ```elixir %Plug.Conn{ host: "www.example.com", path_info: ["bar", "baz"], ... } ``` -------------------------------- ### Function Plug Implementation Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/01-plug-core.md An example of a function plug that sets the response content type to 'application/json'. It takes the connection and options as arguments. ```elixir def json_header_plug(conn, _opts) do Plug.Conn.put_resp_content_type(conn, "application/json") end ``` -------------------------------- ### Define a Function Plug Source: https://github.com/elixir-plug/plug/blob/main/README.md A function plug directly takes the connection and options as arguments and returns the modified connection. This example sets the response content type and sends a 'Hello world' response. ```elixir def hello_world_plug(conn, _opts) do conn |> put_resp_content_type("text/plain") |> send_resp(200, "Hello world") end ``` -------------------------------- ### Recommended :from Formats for Releases Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Shows recommended formats for the `:from` option in Plug.Static that ensure compatibility with releases and deployments. Atom and tuple formats are preferred over string paths. ```elixir # ✓ Good - works in releases plug Plug.Static, from: :my_app plug Plug.Static, from: {:my_app, "priv/public"} ``` ```elixir # ✗ Avoid - breaks in releases plug Plug.Static, from: "priv/static" ``` -------------------------------- ### HTTP Method Route Definitions Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Illustrates defining routes for different HTTP methods (GET, POST, ANY) with corresponding path patterns and response blocks. ```elixir get "/users" do send_resp(conn, 200, "list users") end post "/users" do send_resp(conn, 201, "user created") end match "/fallback" do send_resp(conn, 404, "not found") end ``` -------------------------------- ### Adding Various Plug Types to a Pipeline Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Demonstrates adding different types of plugs (module, function, anonymous, imported) to a pipeline within a router module. Includes example function definitions for plugs. ```elixir defmodule MyRouter do use Plug.Builder # Module plug with no options plug Plug.Logger # Module plug with options plug Plug.SSL, hsts: true # Function plug (defined in this module) plug :authenticate plug :set_headers # Anonymous function (passed as literal) plug fn conn, _opts -> assign(conn, :start_time, System.monotonic_time()) end # Imported function from another module import AnotherModule, only: [useful_plug: 2] plug :useful_plug get "/" do send_resp(conn, 200, "Home") end defp authenticate(conn, _opts) do if authenticated?(conn) do conn else conn |> send_resp(401, "Unauthorized") |> halt() end end defp set_headers(conn, _opts) do put_resp_header(conn, "x-app-version", "1.0") end end ``` -------------------------------- ### Copy Plug Options to Connection Assigns Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Use `:copy_opts_to_assign` to copy plug initialization options into the connection's assigns. This allows access to the original options within the plug pipeline, for example, via `conn.assigns[:plug_opts]`. ```elixir defmodule MyApp do use Plug.Builder, copy_opts_to_assign: :plug_opts plug Plug.Logger def call(conn, _opts) do # Access original options via conn.assigns opts = conn.assigns[:plug_opts] # ... end end ``` -------------------------------- ### Implement Conditional Plug Execution Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Wrap plugs in functions to enable conditional execution. This example skips parsing for paths starting with 'noparse'. ```elixir defmodule MyRouter do use Plug.Builder plug :match plug :parser plug :dispatch defp parser(%{path_info: ["noparse" | _]} = conn, _opts) do conn # Skip parsing for /noparse paths end defp parser(conn, _opts) do Plug.Parsers.call( conn, Plug.Parsers.init(parsers: [:json, :urlencoded]) ) end end ``` -------------------------------- ### Plug.Head Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md Converts HEAD requests to GET requests, allowing reuse of GET handlers. It changes the connection method to 'GET' and allows the same handler to process both HEAD and GET requests, while suppressing the response body for HEAD requests. ```APIDOC ## Plug.Head ### Description Converts HEAD requests to GET requests, allowing reuse of GET handlers. ### Behavior When a request with method `HEAD` is received: - Changes `conn.method` to `"GET"` - Allows the same handler to process both HEAD and GET - Response body is still sent for GET, suppressed by HTTP adapter for HEAD ### Usage ```elixir plug Plug.Head ``` ### Example ```elixir defmodule MyRouter do use Plug.Router plug Plug.Head plug :match plug :dispatch get "/users" do send_resp(conn, 200, "User list") end # HEAD /users is also handled by the same route end ``` ``` -------------------------------- ### HTTP Method Macros Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Defines routes using HTTP method macros. Each route receives the `conn` variable and returns a `Plug.Conn.t()`. Supported methods include GET, POST, PUT, PATCH, DELETE, OPTIONS, and a general `match` for any method. ```APIDOC ## HTTP Method Macros | Macro | HTTP Method | |-------|-------------| | `get/2` | GET | | `post/2` | POST | | `put/2` | PUT | | `patch/2` | PATCH | | `delete/2` | DELETE | | `options/2` | OPTIONS | | `match/2` | ANY | ### Example: ```elixir get "/users" do send_resp(conn, 200, "list users") end post "/users" do send_resp(conn, 201, "user created") end match "/fallback" do send_resp(conn, 404, "not found") end ``` ``` -------------------------------- ### Router with Middleware and Session Management Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md An example of a Plug router that includes security, session management, error handling, and request parsing middleware. Suitable for defining application routes and their associated middleware. ```elixir defmodule MyApp.Router do use Plug.Builder # Security middleware plug Plug.SSL, hsts: 31536000 plug Plug.RequestId plug Plug.Logger # Session management plug Plug.Session, store: :cookie, key: "_session", signing_salt: "salt" # Error handling plug Plug.Debugger # Dev only plug Plug.ErrorHandler # Request parsing and routing plug :match plug Plug.Parsers, parsers: [:json, :urlencoded] plug :dispatch # Routes get "/" do send_resp(conn, 200, "Home") end post "/users" do send_resp(conn, 201, "Created") end defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack}) do send_resp(conn, 500, "Internal Server Error") end end ``` -------------------------------- ### Handle HEAD requests as GET Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/04-builtin-plugs.md Plug.Head converts HEAD requests to GET requests. This allows you to reuse your GET handlers for HEAD requests, simplifying your routing logic. ```elixir plug Plug.Head ``` -------------------------------- ### Test GET Request Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Simulates a GET request to the root path and asserts the response status and body. ```elixir defmodule MyRouterTest do use ExUnit.Case import Plug.Test test "GET /" do conn = conn(:get, "/") conn = MyRouter.call(conn, MyRouter.init([])) assert conn.state == :sent assert conn.status == 200 assert conn.resp_body == "Home" end test "POST /users with JSON" do conn = conn(:post, "/users", "{\"name\": \"Alice\"}") |> put_req_header("content-type", "application/json") conn = MyRouter.call(conn, MyRouter.init([])) assert conn.status == 201 end end ``` -------------------------------- ### Elixir WebSocket Echo Server with Plug Router Source: https://github.com/elixir-plug/plug/blob/main/README.md This snippet sets up a complete Elixir application for a WebSocket echo server. It includes dependency installation, a router definition using Plug.Router to handle HTTP and WebSocket requests, and a simple EchoServer module for WebSocket message handling. It's designed to be run as a script and requires Bandit and websock_adapter. Accessing the root URL provides JavaScript to interact with the WebSocket. ```elixir Mix.install([:bandit, :websock_adapter]) defmodule EchoServer do def init(options) do {:ok, options} end def handle_in({"ping", [opcode: :text]}, state) do {:reply, :ok, {:text, "pong"}, state} end def terminate(:timeout, state) do {:ok, state} end end defmodule Router do use Plug.Router plug Plug.Logger plug :match plug :dispatch get "/" do send_resp(conn, 200, """ Use the JavaScript console to interact using websockets sock = new WebSocket(\"ws://localhost:4000/websocket\") sock.addEventListener(\"message\", console.log) sock.addEventListener(\"open\", () => sock.send(\"ping\")) """) end get "/websocket" do conn |> WebSockAdapter.upgrade(EchoServer, [], timeout: 60_000) |> halt() end match _ do send_resp(conn, 404, "not found") end end require Logger webserver = {Bandit, plug: Router, scheme: :http, port: 4000} {:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one) Logger.info("Plug now running on localhost:4000") Process.sleep(:infinity) ``` -------------------------------- ### Enable Gzip and Brotli Compression Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Enable serving pre-compressed assets by setting `:gzip` and `:brotli` to `true`. This requires building compressed versions of your assets beforehand. ```elixir plug Plug.Static, at: "/", from: :my_app, gzip: true, brotli: true ``` ```bash gzip -k public/app.js # Creates app.js.gz brotli -k public/app.js # Creates app.js.br ``` ```elixir plug Plug.Static, at: "/", from: :my_app, gzip: true, brotli: true ``` -------------------------------- ### Attach Telemetry Handler for Router Dispatch Start Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/03-plug-router.md Attaches a telemetry handler to capture the :plug_router_dispatch:start event. This is useful for monitoring the beginning of route dispatch. ```elixir :telemetry.attach( "my-router-handler", [:plug, :router_dispatch, :start], fn _event, _measurements, metadata -> IO.inspect(metadata) end, nil ) ``` -------------------------------- ### Request Headers Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Functions for getting, setting, prepending, deleting, and updating request headers. ```APIDOC ## `get_req_header/2` ### Description Gets request header values (may be multiple for same key). ### Signature ```elixir def get_req_header(Plug.Conn.t(), binary) :: [binary] ``` ### Example ```elixir get_req_header(conn, "accept") # => ["application/json"] ``` ``` ```APIDOC ## `put_req_header/3` ### Description Sets a request header (replaces existing). ### Signature ```elixir def put_req_header(Plug.Conn.t(), binary, binary) :: Plug.Conn.t() ``` ### Example ```elixir put_req_header(conn, "authorization", "Bearer token123") ``` ``` ```APIDOC ## `prepend_req_headers/2` ### Description Adds request headers to the front of the header list. ### Signature ```elixir def prepend_req_headers(Plug.Conn.t(), [{binary, binary}]) :: Plug.Conn.t() ``` ``` ```APIDOC ## `delete_req_header/2` ### Description Removes a request header. ### Signature ```elixir def delete_req_header(Plug.Conn.t(), binary) :: Plug.Conn.t() ``` ``` ```APIDOC ## `update_req_header/4` ### Description Updates request header if present, else sets initial value. ### Signature ```elixir def update_req_header(Plug.Conn.t(), binary, binary, (binary -> binary)) :: Plug.Conn.t() ``` ### Example ```elixir update_req_header(conn, "accept", "application/json", &(&1 <> "; charset=utf-8")) ``` ``` -------------------------------- ### Serve Static Files from Different Applications Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Configures Plug.Static to serve files from different applications or directories. This allows for modularity by specifying the `from` option, which can be an application name, a tuple for a specific directory within an application, or a direct filesystem path (not recommended). ```elixir defmodule MyApp.Router do use Plug.Builder # From main app plug Plug.Static, at: "/app", from: :my_app # From plugin plug Plug.Static, at: "/plugin", from: {:plugin_app, "priv/static"} # From filesystem (not recommended) plug Plug.Static, at: "/custom", from: "custom/static" end ``` -------------------------------- ### Get Response Header Value Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Retrieves values for a given response header key. ```elixir def get_resp_header(Plug.Conn.t(), binary) :: [binary] ``` -------------------------------- ### Create a Simple Router Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/00-index.md Defines a basic Plug router with routes for the home page and a 404 handler. ```elixir defmodule MyApp.Router do use Plug.Router plug :match plug :dispatch get "/" do send_resp(conn, 200, "Home") end match _ do send_resp(conn, 404, "Not Found") end end ``` -------------------------------- ### Response Headers Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Functions for getting, setting, prepending, deleting, and updating response headers, including content type. ```APIDOC ## `get_resp_header/2` ### Description Gets response header values. ### Signature ```elixir def get_resp_header(Plug.Conn.t(), binary) :: [binary] ``` ``` ```APIDOC ## `put_resp_header/3` ### Description Sets response header (replaces existing). Validates lowercase keys in test mode. ### Signature ```elixir def put_resp_header(Plug.Conn.t(), binary, binary) :: Plug.Conn.t() ``` ### Example ```elixir put_resp_header(conn, "content-type", "application/json") ``` ``` ```APIDOC ## `prepend_resp_headers/2` ### Description Adds response headers to the front of the list. ### Signature ```elixir def prepend_resp_headers(Plug.Conn.t(), [{binary, binary}]) :: Plug.Conn.t() ``` ``` ```APIDOC ## `delete_resp_header/2` ### Description Removes a response header. ### Signature ```elixir def delete_resp_header(Plug.Conn.t(), binary) :: Plug.Conn.t() ``` ``` ```APIDOC ## `update_resp_header/4` ### Description Updates response header if present, else sets initial value. ### Signature ```elixir def update_resp_header(Plug.Conn.t(), binary, binary, (binary -> binary)) :: Plug.Conn.t() ``` ``` ```APIDOC ## `put_resp_content_type/3` ### Description Sets `content-type` header with optional charset. ### Signature ```elixir def put_resp_content_type(Plug.Conn.t(), binary, binary | nil) :: Plug.Conn.t() ``` ### Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | conn | `Plug.Conn.t()` | — | The connection | | content_type | `binary` | — | MIME type, e.g., `"application/json"` | | charset | `binary | nil` | `"utf-8"` | Character encoding or `nil` for no charset | ### Example ```elixir put_resp_content_type(conn, "application/json") # Sets: "application/json; charset=utf-8" put_resp_content_type(conn, "image/png", nil) # Sets: "image/png" ``` ``` -------------------------------- ### Test Plug.Head in Elixir Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Tests the Plug.Head plug to ensure it correctly converts a HEAD request to a GET request. ```elixir test "head to get conversion" do conn = conn(:head, "/data") conn = Plug.Head.call(conn, []) assert conn.method == "GET" end ``` -------------------------------- ### Basic Plug.Session Usage in a Router Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/06-plug-session.md Demonstrates how to configure Plug.Session with cookie storage and use session functions to set and retrieve user data. ```elixir defmodule MyRouter do use Plug.Router plug Plug.Session, store: :cookie, key: "_my_app_session", signing_salt: "some random salt" plug :match plug :dispatch post "/login" do conn |> Plug.Conn.fetch_session() |> Plug.Conn.put_session(:user_id, 123) |> send_resp(200, "Logged in") end get "/profile" do conn = Plug.Conn.fetch_session(conn) user_id = Plug.Conn.get_session(conn, :user_id) send_resp(conn, 200, "User: #{user_id}") end end ``` -------------------------------- ### Get HTTP Protocol with Plug.Conn Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Use `get_http_protocol/1` to determine the HTTP protocol version used for the connection, returned as an atom. ```elixir def get_http_protocol(Plug.Conn.t()) :: Plug.Conn.Adapter.http_protocol() ``` -------------------------------- ### Create Connection with Full URI Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Simulates a request using a full URI. This allows testing how Plug handles host, port, and scheme parsing. ```elixir conn = conn(:get, "http://example.com:8080/path?foo=bar") ``` -------------------------------- ### Configure Plug Builder Initialization Mode Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/08-plug-builder.md Use `:init_mode` to control when plug options are initialized. Set to `:runtime` for dynamic options that change per request, or `:compile` (default) for static options. ```elixir defmodule MyApp do use Plug.Builder, init_mode: :runtime plug Plug.RequestId # ... end ``` ```elixir # Compile-time initialization defmodule App1 do use Plug.Builder # init_mode: :compile (default) plug Plug.Logger, log: :info plug Plug.SSL, hsts: 31536000 end ``` ```elixir # Runtime initialization defmodule App2 do use Plug.Builder, init_mode: :runtime plug Plug.Logger plug Plug.SSL def init(opts) do # Can inspect opts passed from supervisor opts end end ``` ```elixir # In supervisor children = [ {App2, [log: System.get_env("LOG_LEVEL", "info")]} ] ``` -------------------------------- ### Get Session Value Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/06-plug-session.md Retrieves a value from the session using a specified key. The connection must have session data fetched first. ```elixir def get_session(Plug.Conn.t(), key :: atom | binary) :: term | nil # Example: conn = fetch_session(conn) user_id = get_session(conn, :user_id) ``` -------------------------------- ### Get Local Socket Data with Plug.Conn Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Use `get_sock_data/1` to retrieve information about the local socket, including IP address and port. ```elixir def get_sock_data(Plug.Conn.t()) :: Plug.Conn.Adapter.sock_data() ``` -------------------------------- ### Import Plug.Test Utilities Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/07-plug-test.md Import necessary testing utilities from Plug.Test and Plug.Conn into your test module. This makes functions like `conn/3` and `sent_resp/1` available. ```elixir defmodule MyRouter.Test do use ExUnit.Case, async: true import Plug.Test import Plug.Conn # Now have access to conn/3, sent_resp/1, etc. end ``` -------------------------------- ### Get Peer Connection Data with Plug.Conn Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Use `get_peer_data/1` to retrieve information about the remote peer connection, such as IP address and port. ```elixir def get_peer_data(Plug.Conn.t()) :: Plug.Conn.Adapter.peer_data() ``` -------------------------------- ### Get Request Header Value Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/02-plug-conn.md Retrieves values for a given request header key. Can return multiple values if the key appears more than once. ```elixir def get_req_header(Plug.Conn.t(), binary) :: [binary] ``` ```elixir get_req_header(conn, "accept") # => ["application/json"] ``` -------------------------------- ### Define a Module Plug Source: https://github.com/elixir-plug/plug/blob/main/README.md A module plug must implement an init/1 function for options and a call/2 function to handle the connection. The init/1 function can return false to indicate no initialization is needed. ```elixir defmodule MyPlug do def init([]), do: false def call(conn, _opts), do: conn end ``` -------------------------------- ### Production Configuration for Plug.Static Source: https://github.com/elixir-plug/plug/blob/main/_autodocs/09-plug-static.md Optimize Plug.Static for production by specifying asset types to serve ('only'), enabling compression (gzip, brotli), setting aggressive caching for versioned assets, and adding security headers. ```elixir defmodule MyApp.Router do use Plug.Builder plug Plug.Static, at: "/", from: :my_app, only: ~w( images css js fonts favicon.ico robots.txt sitemap.xml ), gzip: true, brotli: true, cache_control_for_vsn_requests: "public, max-age=31536000, immutable", cache_control_for_etags: "public, max-age=3600", headers: %{ "x-content-type-options" => "nosniff", "x-frame-options" => "DENY", "x-xss-protection" => "1; mode=block" } plug :not_found def not_found(conn, _) do send_resp(conn, 404, "Not found") end end ``` -------------------------------- ### Create New Elixir Project with Supervision Source: https://github.com/elixir-plug/plug/blob/main/README.md Use this command to create a new Elixir project that includes supervision tree support, essential for production deployments. ```shell $ mix new my_app --sup ```