Plug Cowboy - Search dynamically for a free port to run the server

This is how I do it, by passing a 0 port and then fetching the allocated port.

    pid =
      ExUnit.Callbacks.start_link_supervised!(
        {Plug.Cowboy,
         scheme: :http, plug: {MyPlugModule, handler}, options: [port: 0, transport_options: [num_acceptors: 1]]}
      )

    port =
      pid
      |> Supervisor.which_children()
      |> List.keyfind(:ranch_conns_sup, 0)
      |> elem(1)
      |> :sys.get_state()
      |> elem(0)
      |> elem(2)
      |> :ranch_server.get_addr()
      |> elem(1)

Which boils down to this if you know what Plug is doing with the aliases:

    pid =
      ExUnit.Callbacks.start_link_supervised!(
        {Plug.Cowboy,
         scheme: :http, plug: {MyPlugModule, handler}, options: [port: 0, transport_options: [num_acceptors: 1]]}
      )

    port =
      MyPlugModule.HTTP
      |> :ranch_server.get_addr()
      |> elem(1)
2 Likes

If you need to start multiple servers with the same plug handler, my previous post will not work with Cowboy. You will have to build a module on the fly with an unique name that will delegate to your actual handler (MyPlugModule in my previous example).

With Bandit it works well though:

  def mock_http_server(handler) when is_function(handler, 2) do
    pid =
      ExUnit.Callbacks.start_link_supervised!(
        {Bandit,
         plug: {MockablePlug, handler},
         scheme: :http,
         port: 0,
         thousand_island_options: [num_acceptors: 1, num_connections: 1]}
      )

    port =
      pid
      |> Supervisor.which_children()
      |> List.keyfind(:listener, 0)
      |> elem(1)
      |> :sys.get_state()
      |> Map.fetch!(:local_info)
      |> Map.fetch!(:port)

    base = "http://localhost:#{port}"

    Logger.debug("starting http mock server on #{base}/")

    base
  end

You can use to get to the port: Bandit.PhoenixAdapter — Bandit v1.0.0

1 Like