README.md (9487B)
1 # Plug 2 3 [![Build Status](https://github.com/elixir-plug/plug/workflows/CI/badge.svg)](https://github.com/elixir-plug/plug/actions?query=workflow%3A%22CI%22) 4 [![Inline docs](https://inch-ci.org/github/elixir-plug/plug.svg?branch=master)](http://inch-ci.org/github/elixir-plug/plug) 5 6 Plug is: 7 8 1. A specification for composing web applications with functions 9 2. Connection adapters for different web servers in the Erlang VM 10 11 [Documentation for Plug is available online](http://hexdocs.pm/plug/). 12 13 ## Installation 14 15 In order to use Plug, you need a webserver and its bindings for Plug. The Cowboy webserver is the most common one, which can be installed by adding `plug_cowboy` as a dependency to your `mix.exs`: 16 17 ```elixir 18 def deps do 19 [ 20 {:plug_cowboy, "~> 2.0"} 21 ] 22 end 23 ``` 24 25 ## Hello world 26 27 ```elixir 28 Mix.install([:plug, :plug_cowboy]) 29 30 defmodule MyPlug do 31 import Plug.Conn 32 33 def init(options) do 34 # initialize options 35 options 36 end 37 38 def call(conn, _opts) do 39 conn 40 |> put_resp_content_type("text/plain") 41 |> send_resp(200, "Hello world") 42 end 43 end 44 45 require Logger 46 {:ok, _} = Plug.Cowboy.http(MyPlug, []) 47 Logger.info("Plug now running on localhost:4000") 48 ``` 49 50 The snippet above shows a very simple example on how to use Plug. Save that snippet to a file and execute it as `elixir --no-halt hello_world.exs`. Access <http://localhost:4000/> and you should be greeted! 51 52 For now, we have directly started the server in a single file but, for production deployments, you likely want to start it in your supervision tree. See the [Supervised handlers](#supervised-handlers) section next. 53 54 ## Supervised handlers 55 56 On a production system, you likely want to start your Plug pipeline under your application's supervision tree. Start a new Elixir project with the `--sup` flag: 57 58 ```shell 59 $ mix new my_app --sup 60 ``` 61 62 Add both `:plug` and `:plug_cowboy` as dependencies in your `mix.exs`: 63 64 ```elixir 65 def deps do 66 [ 67 {:plug, "~> 1.13"}, 68 {:plug_cowboy, "~> 2.0"} 69 ] 70 end 71 ``` 72 73 Now update `lib/my_app/application.ex` as follows: 74 75 ```elixir 76 defmodule MyApp.Application do 77 # See https://hexdocs.pm/elixir/Application.html 78 # for more information on OTP Applications 79 @moduledoc false 80 81 use Application 82 83 def start(_type, _args) do 84 # List all child processes to be supervised 85 children = [ 86 {Plug.Cowboy, scheme: :http, plug: MyPlug, options: [port: 4001]} 87 ] 88 89 # See https://hexdocs.pm/elixir/Supervisor.html 90 # for other strategies and supported options 91 opts = [strategy: :one_for_one, name: MyApp.Supervisor] 92 Supervisor.start_link(children, opts) 93 end 94 end 95 ``` 96 97 Finally create `lib/my_app/my_plug.ex` with the `MyPlug` module. 98 99 Now run `mix run --no-halt` and it will start your application with a web server running at <http://localhost:4001>. 100 101 ## Supported Versions 102 103 | Branch | Support | 104 |--------|--------------------------| 105 | v1.13 | Bug fixes | 106 | v1.12 | Security patches only | 107 | v1.11 | Security patches only | 108 | v1.10 | Security patches only | 109 | v1.9 | Security patches only | 110 | v1.8 | Security patches only | 111 | v1.7 | Unsupported from 01/2022 | 112 | v1.6 | Unsupported from 01/2022 | 113 | v1.5 | Unsupported from 03/2021 | 114 | v1.4 | Unsupported from 12/2018 | 115 | v1.3 | Unsupported from 12/2018 | 116 | v1.2 | Unsupported from 06/2018 | 117 | v1.1 | Unsupported from 01/2018 | 118 | v1.0 | Unsupported from 05/2017 | 119 120 ## The `Plug.Conn` struct 121 122 In the hello world example, we defined our first plug. What is a plug after all? 123 124 A plug takes two shapes. A function plug receives a connection and a set of options as arguments and returns the connection: 125 126 ```elixir 127 def hello_world_plug(conn, _opts) do 128 conn 129 |> put_resp_content_type("text/plain") 130 |> send_resp(200, "Hello world") 131 end 132 ``` 133 134 A module plug implements an `init/1` function to initialize the options and a `call/2` function which receives the connection and initialized options and returns the connection: 135 136 ```elixir 137 defmodule MyPlug do 138 def init([]), do: false 139 def call(conn, _opts), do: conn 140 end 141 ``` 142 143 As per the specification above, a connection is represented by the `Plug.Conn` struct: 144 145 ```elixir 146 %Plug.Conn{ 147 host: "www.example.com", 148 path_info: ["bar", "baz"], 149 ... 150 } 151 ``` 152 153 Data can be read directly from the connection and also pattern matched on. Manipulating the connection often happens with the use of the functions defined in the `Plug.Conn` module. In our example, both `put_resp_content_type/2` and `send_resp/3` are defined in `Plug.Conn`. 154 155 Remember that, as everything else in Elixir, **a connection is immutable**, so every manipulation returns a new copy of the connection: 156 157 ```elixir 158 conn = put_resp_content_type(conn, "text/plain") 159 conn = send_resp(conn, 200, "ok") 160 conn 161 ``` 162 163 Finally, keep in mind that a connection is a **direct interface to the underlying web server**. When you call `send_resp/3` above, it will immediately send the given status and body back to the client. This makes features like streaming a breeze to work with. 164 165 ## `Plug.Router` 166 167 To write a "router" plug that dispatches based on the path and method of incoming requests, Plug provides `Plug.Router`: 168 169 ```elixir 170 defmodule MyRouter do 171 use Plug.Router 172 173 plug :match 174 plug :dispatch 175 176 get "/hello" do 177 send_resp(conn, 200, "world") 178 end 179 180 forward "/users", to: UsersRouter 181 182 match _ do 183 send_resp(conn, 404, "oops") 184 end 185 end 186 ``` 187 188 The router is a plug. Not only that: it contains its own plug pipeline too. The example above says that when the router is invoked, it will invoke the `:match` plug, represented by a local (imported) `match/2` function, and then call the `:dispatch` plug which will execute the matched code. 189 190 Plug ships with many plugs that you can add to the router plug pipeline, allowing you to plug something before a route matches or before a route is dispatched to. For example, if you want to add logging to the router, just do: 191 192 ```elixir 193 plug Plug.Logger 194 plug :match 195 plug :dispatch 196 ``` 197 198 Note `Plug.Router` compiles all of your routes into a single function and relies on the Erlang VM to optimize the underlying routes into a tree lookup, instead of a linear lookup that would instead match route-per-route. This means route lookups are extremely fast in Plug! 199 200 This also means that a catch all `match` block is recommended to be defined as in the example above, otherwise routing fails with a function clause error (as it would in any regular Elixir function). 201 202 Each route needs to return the connection as per the Plug specification. See the `Plug.Router` docs for more information. 203 204 ## Testing plugs 205 206 Plug ships with a `Plug.Test` module that makes testing your plugs easy. Here is how we can test the router from above (or any other plug): 207 208 ```elixir 209 defmodule MyPlugTest do 210 use ExUnit.Case, async: true 211 use Plug.Test 212 213 @opts MyRouter.init([]) 214 215 test "returns hello world" do 216 # Create a test connection 217 conn = conn(:get, "/hello") 218 219 # Invoke the plug 220 conn = MyRouter.call(conn, @opts) 221 222 # Assert the response and status 223 assert conn.state == :sent 224 assert conn.status == 200 225 assert conn.resp_body == "world" 226 end 227 end 228 ``` 229 230 ## Available plugs 231 232 This project aims to ship with different plugs that can be re-used across applications: 233 234 * `Plug.BasicAuth` - provides Basic HTTP authentication; 235 * `Plug.CSRFProtection` - adds Cross-Site Request Forgery protection to your application. Typically required if you are using `Plug.Session`; 236 * `Plug.Head` - converts HEAD requests to GET requests; 237 * `Plug.Logger` - logs requests; 238 * `Plug.MethodOverride` - overrides a request method with one specified in the request parameters; 239 * `Plug.Parsers` - responsible for parsing the request body given its content-type; 240 * `Plug.RequestId` - sets up a request ID to be used in logs; 241 * `Plug.RewriteOn` - rewrite the request's host/port/protocol from `x-forwarded-*` headers; 242 * `Plug.Session` - handles session management and storage; 243 * `Plug.SSL` - enforces requests through SSL; 244 * `Plug.Static` - serves static files; 245 * `Plug.Telemetry` - instruments the plug pipeline with `:telemetry` events; 246 247 You can go into more details about each of them [in our docs](http://hexdocs.pm/plug/). 248 249 ## Helper modules 250 251 Modules that can be used after you use `Plug.Router` or `Plug.Builder` to help development: 252 253 * `Plug.Debugger` - shows a helpful debugging page every time there is a failure in a request; 254 * `Plug.ErrorHandler` - allows developers to customize error pages in case of crashes instead of sending a blank one; 255 256 ## Contributing 257 258 We welcome everyone to contribute to Plug and help us tackle existing issues! 259 260 Use the [issue tracker][issues] for bug reports or feature requests. Open a [pull request][pulls] when you are ready to contribute. When submitting a pull request you should not update the `CHANGELOG.md`. 261 262 If you are planning to contribute documentation, [please check our best practices for writing documentation][writing-docs]. 263 264 Finally, remember all interactions in our official spaces follow our [Code of Conduct][code-of-conduct]. 265 266 ## License 267 268 Plug source code is released under Apache License 2.0. 269 Check LICENSE file for more information. 270 271 [issues]: https://github.com/elixir-plug/plug/issues 272 [pulls]: https://github.com/elixir-plug/plug/pulls 273 [code-of-conduct]: https://github.com/elixir-lang/elixir/blob/master/CODE_OF_CONDUCT.md 274 [writing-docs]: https://hexdocs.pm/elixir/writing-documentation.html