Powered by AppSignal & Oban Pro

Reverse Proxy Example

examples/reverse_proxy.livemd

Reverse Proxy Example

Mix.install([
  {:req, path: "#{__DIR__}/.."}, # TODO
  {:bandit, "~> 1.0"}
])

Proxy

This example starts a Bandit server that streams request and response bodies using Req.

defmodule Proxy do
  def start_link(upstream_url: upstream_url) do
    with {:ok, pid} <- Bandit.start_link(port: 0, plug: {&forward/2, upstream_url}),
         {:ok, {_ip, port}} <- ThousandIsland.listener_info(pid) do
      {:ok, %{url: URI.new!("http://localhost:#{port}")}}
    end
  end

  defp forward(conn, upstream_url) do
    {:ok, _resp, conn} =
      Req.stream(
        upstream_url,
        conn,
        fn
          data, resp, %{state: :unset} = conn ->
            [content_type] = Req.Response.get_header(resp, "content-type")

            conn =
              conn
              |> Plug.Conn.put_resp_header("content-type", content_type)
              |> Plug.Conn.send_chunked(resp.status)

            {:ok, conn} = Plug.Conn.chunk(conn, data)
            {:cont, conn}

          data, _resp, %{state: :chunked} = conn ->
            {:ok, conn} = Plug.Conn.chunk(conn, data)
            {:cont, conn}
        end,
        method: conn.method,
        body: fn conn ->
          case Plug.Conn.read_body(conn) do
            {:ok, "", conn} ->
              {:done, conn}

            {:ok, data, conn} ->
              {:data, data, conn}

            {:more, data, conn} ->
              {:data, data, conn}
          end
        end,
        raw: true
      )

    conn
  end
end

Try it

{:ok, %{url: url}} = Proxy.start_link(upstream_url: "https://httpbingo.org/anything")

resp = Req.post!(url, body: "Hello, World!")
dbg(resp.body)