rewrite_on.ex (2439B)
1 defmodule Plug.RewriteOn do 2 @moduledoc """ 3 A plug to rewrite the request's host/port/protocol from `x-forwarded-*` headers. 4 5 If your Plug application is behind a proxy that handles HTTPS, you may 6 need to tell Plug to parse the proper protocol from the `x-forwarded-*` 7 header. 8 9 plug Plug.RewriteOn, [:x_forwarded_host, :x_forwarded_port, :x_forwarded_proto] 10 11 The supported values are: 12 13 * `:x_forwarded_host` - to override the host based on on the "x-forwarded-host" header 14 * `:x_forwarded_port` - to override the port based on on the "x-forwarded-port" header 15 * `:x_forwarded_proto` - to override the protocol based on on the "x-forwarded-proto" header 16 17 Since rewriting the scheme based on `x-forwarded-*` headers can open up 18 security vulnerabilities, only use this plug if: 19 20 * your app is behind a proxy 21 * your proxy strips the given `x-forwarded-*` headers from all incoming requests 22 * your proxy sets the `x-forwarded-*` headers and sends it to Plug 23 """ 24 @behaviour Plug 25 26 import Plug.Conn, only: [get_req_header: 2] 27 28 @impl true 29 def init(header), do: List.wrap(header) 30 31 @impl true 32 def call(conn, [:x_forwarded_proto | rewrite_on]) do 33 conn 34 |> put_scheme(get_req_header(conn, "x-forwarded-proto")) 35 |> call(rewrite_on) 36 end 37 38 def call(conn, [:x_forwarded_port | rewrite_on]) do 39 conn 40 |> put_port(get_req_header(conn, "x-forwarded-port")) 41 |> call(rewrite_on) 42 end 43 44 def call(conn, [:x_forwarded_host | rewrite_on]) do 45 conn 46 |> put_host(get_req_header(conn, "x-forwarded-host")) 47 |> call(rewrite_on) 48 end 49 50 def call(_conn, [other | _rewrite_on]) do 51 raise "unknown rewrite: #{inspect(other)}" 52 end 53 54 def call(conn, []) do 55 conn 56 end 57 58 defp put_scheme(%{scheme: :http, port: 80} = conn, ["https"]), 59 do: %{conn | scheme: :https, port: 443} 60 61 defp put_scheme(conn, ["https"]), 62 do: %{conn | scheme: :https} 63 64 defp put_scheme(%{scheme: :https, port: 443} = conn, ["http"]), 65 do: %{conn | scheme: :http, port: 80} 66 67 defp put_scheme(conn, ["http"]), 68 do: %{conn | scheme: :http} 69 70 defp put_scheme(conn, _scheme), 71 do: conn 72 73 defp put_host(conn, [proper_host]), 74 do: %{conn | host: proper_host} 75 76 defp put_host(conn, _), 77 do: conn 78 79 defp put_port(conn, headers) do 80 with [header] <- headers, 81 {port, ""} <- Integer.parse(header) do 82 %{conn | port: port} 83 else 84 _ -> conn 85 end 86 end 87 end