feat: Add ability to start and stop HTTP listeners. (#1)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://code.harton.nz/bivouac/wayfarer/pulls/1 Co-authored-by: James Harton <james@harton.nz> Co-committed-by: James Harton <james@harton.nz>
This commit is contained in:
parent
cc9e8978ff
commit
20c5ce6f44
13 changed files with 405 additions and 17 deletions
|
@ -7,3 +7,6 @@ config :git_ops,
|
|||
manage_mix_version?: true,
|
||||
version_tag_prefix: "v",
|
||||
manage_readme_version: "README.md"
|
||||
|
||||
config :wayfarer,
|
||||
start_listeners?: config_env() != :test
|
||||
|
|
|
@ -2,17 +2,4 @@ defmodule Wayfarer do
|
|||
@moduledoc """
|
||||
Documentation for `Wayfarer`.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Hello world.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Wayfarer.hello()
|
||||
:world
|
||||
|
||||
"""
|
||||
def hello do
|
||||
:world
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,15 @@ defmodule Wayfarer.Application do
|
|||
@spec start(any, any) :: {:error, any} | {:ok, pid}
|
||||
def start(_type, _args) do
|
||||
[]
|
||||
|> start_listeners?()
|
||||
|> Supervisor.start_link(strategy: :one_for_one, name: Wayfarer.Supervisor)
|
||||
end
|
||||
|
||||
defp start_listeners?(children) do
|
||||
if Application.get_env(:wayfarer, :start_listeners?, true) do
|
||||
Enum.concat(children, [Wayfarer.Listener.Supervisor])
|
||||
else
|
||||
children
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
23
lib/wayfarer/listener.ex
Normal file
23
lib/wayfarer/listener.ex
Normal file
|
@ -0,0 +1,23 @@
|
|||
defmodule Wayfarer.Listener do
|
||||
@moduledoc """
|
||||
Manage HTTP listeners.
|
||||
"""
|
||||
|
||||
alias Wayfarer.Listener.DynamicSupervisor, as: ListenerSupervisor
|
||||
alias Wayfarer.Listener.Registry, as: ListenerRegistry
|
||||
alias Wayfarer.Listener.Server
|
||||
|
||||
@doc """
|
||||
Start listener.
|
||||
"""
|
||||
@spec start_listener(Server.options()) :: Supervisor.on_start_child()
|
||||
def start_listener(options),
|
||||
do: DynamicSupervisor.start_child(ListenerSupervisor, {Server, options})
|
||||
|
||||
@doc """
|
||||
Stop listener
|
||||
"""
|
||||
@spec stop_listener(:inet.socket_address(), :inet.port_number()) :: :ok
|
||||
def stop_listener(ip, port),
|
||||
do: GenServer.stop({:via, Registry, {ListenerRegistry, {ip, port}}}, :normal)
|
||||
end
|
21
lib/wayfarer/listener/plug.ex
Normal file
21
lib/wayfarer/listener/plug.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule Wayfarer.Listener.Plug do
|
||||
@moduledoc """
|
||||
Plug pipeline to handle inbound HTTP connections.
|
||||
"""
|
||||
import Plug.Conn
|
||||
@behaviour Plug
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def call(conn, _opts) do
|
||||
conn
|
||||
|> put_resp_content_type("text/plain")
|
||||
|> send_resp(502, "Bad Gateway")
|
||||
end
|
||||
end
|
86
lib/wayfarer/listener/server.ex
Normal file
86
lib/wayfarer/listener/server.ex
Normal file
|
@ -0,0 +1,86 @@
|
|||
defmodule Wayfarer.Listener.Server do
|
||||
@moduledoc """
|
||||
A GenServer which manages the state of each listener.
|
||||
"""
|
||||
|
||||
alias Wayfarer.Listener.Plug
|
||||
alias Wayfarer.Listener.Registry, as: ListenerRegistry
|
||||
|
||||
use GenServer, restart: :transient
|
||||
require Logger
|
||||
|
||||
# How long to wait for connections to drain when shutting down a listener.
|
||||
@drain_timeout :timer.seconds(60)
|
||||
|
||||
@type options :: [
|
||||
scheme: :http | :https,
|
||||
port: :inet.port_number(),
|
||||
ip: :inet.socket_address(),
|
||||
keyfile: binary(),
|
||||
certfile: binary(),
|
||||
otp_app: binary() | atom(),
|
||||
cipher_suite: :strong | :compatible,
|
||||
display_plug: module(),
|
||||
startup_log: Logger.level() | false,
|
||||
thousand_island_options: ThousandIsland.options(),
|
||||
http_1_options: Bandit.http_1_options(),
|
||||
http_2_options: Bandit.http_2_options(),
|
||||
websocket_options: Bandit.websocket_options()
|
||||
]
|
||||
|
||||
@doc false
|
||||
@spec start_link(options) :: GenServer.on_start()
|
||||
def start_link(options), do: GenServer.start_link(__MODULE__, options)
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def init(options) do
|
||||
options =
|
||||
options
|
||||
|> Keyword.put(:plug, Plug)
|
||||
|> Keyword.put(:startup_log, false)
|
||||
|> Keyword.update(
|
||||
:thousand_island_options,
|
||||
[shutdown_timeout: @drain_timeout],
|
||||
&Keyword.put_new(&1, :shutdown_timeout, @drain_timeout)
|
||||
)
|
||||
|
||||
with {:ok, scheme} <- fetch_required_option(options, :scheme),
|
||||
{:ok, pid} <- Bandit.start_link(options),
|
||||
{:ok, %{address: addr, port: port}} <- ThousandIsland.listener_info(pid),
|
||||
{:ok, _pid} <- Registry.register(ListenerRegistry, {addr, port}, pid) do
|
||||
listen_url = listen_url(scheme, addr, port)
|
||||
version = Application.spec(:wayfarer)[:vsn]
|
||||
Logger.info("Started Wayfarer v#{version} listener on #{listen_url}")
|
||||
|
||||
{:ok, %{server: pid, options: options, addr: addr}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def terminate(:normal, %{server: server}) do
|
||||
GenServer.stop(server, :normal)
|
||||
end
|
||||
|
||||
defp fetch_required_option(options, option) do
|
||||
case Keyword.fetch(options, option) do
|
||||
{:ok, value} -> {:ok, value}
|
||||
:error -> {:error, {:required_option, option}}
|
||||
end
|
||||
end
|
||||
|
||||
defp listen_url(scheme, {:local, socket_path}, _), do: "#{scheme}:#{socket_path}"
|
||||
|
||||
defp listen_url(scheme, address, port) when tuple_size(address) == 4 do
|
||||
"#{scheme}://#{:inet.ntoa(address)}:#{port}"
|
||||
|> URI.new!()
|
||||
|> to_string()
|
||||
end
|
||||
|
||||
defp listen_url(scheme, address, port) when tuple_size(address) == 8 do
|
||||
"#{scheme}://[#{:inet.ntoa(address)}]:#{port}"
|
||||
|> URI.new!()
|
||||
|> to_string()
|
||||
end
|
||||
end
|
21
lib/wayfarer/listener/supervisor.ex
Normal file
21
lib/wayfarer/listener/supervisor.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule Wayfarer.Listener.Supervisor do
|
||||
@moduledoc """
|
||||
Supervisor for HTTP listeners.
|
||||
"""
|
||||
|
||||
use Supervisor
|
||||
|
||||
@doc false
|
||||
@spec start_link(any) :: Supervisor.on_start()
|
||||
def start_link(arg), do: Supervisor.start_link(__MODULE__, arg)
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def init(_arg) do
|
||||
[
|
||||
{Registry, keys: :unique, name: Wayfarer.Listener.Registry},
|
||||
{DynamicSupervisor, name: Wayfarer.Listener.DynamicSupervisor}
|
||||
]
|
||||
|> Supervisor.init(strategy: :one_for_one)
|
||||
end
|
||||
end
|
7
mix.exs
7
mix.exs
|
@ -55,6 +55,12 @@ defmodule Wayfarer.MixProject do
|
|||
opts = [only: ~w[dev test]a, runtime: false]
|
||||
|
||||
[
|
||||
{:bandit, "~> 0.7"},
|
||||
{:mint, "~> 1.5"},
|
||||
{:nimble_options, "~> 1.0"},
|
||||
{:plug, "~> 1.15"},
|
||||
{:websock, "~> 0.5"},
|
||||
|
||||
# Dev/test
|
||||
{:credo, "~> 1.7", opts},
|
||||
{:dialyxir, "~> 1.3", opts},
|
||||
|
@ -63,6 +69,7 @@ defmodule Wayfarer.MixProject do
|
|||
{:ex_check, "~> 0.15", opts},
|
||||
{:ex_doc, ">= 0.0.0", opts},
|
||||
{:faker, "~> 0.17", opts},
|
||||
{:finch, "~> 0.16", opts},
|
||||
{:git_ops, "~> 2.6", opts},
|
||||
{:mix_audit, "~> 2.1", opts}
|
||||
]
|
||||
|
|
13
mix.lock
13
mix.lock
|
@ -1,5 +1,7 @@
|
|||
%{
|
||||
"bandit": {:hex, :bandit, "0.7.7", "48456d09022607a312cf723a91992236aeaffe4af50615e6e2d2e383fb6bef10", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 0.6.7", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "772f0a32632c2ce41026d85e24b13a469151bb8cea1891e597fb38fde103640a"},
|
||||
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
|
||||
"castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"},
|
||||
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
|
||||
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"},
|
||||
|
@ -11,14 +13,25 @@
|
|||
"ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"},
|
||||
"faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"},
|
||||
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
|
||||
"git_ops": {:hex, :git_ops, "2.6.0", "e0791ee1cf5db03f2c61b7ebd70e2e95cba2bb9b9793011f26609f22c0900087", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "b98fca849b18aaf490f4ac7d1dd8c6c469b0cc3e6632562d366cab095e666ffe"},
|
||||
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
|
||||
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
|
||||
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
|
||||
"mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"},
|
||||
"mix_audit": {:hex, :mix_audit, "2.1.1", "653aa6d8f291fc4b017aa82bdb79a4017903902ebba57960ef199cbbc8c008a1", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "541990c3ab3a7bb8c4aaa2ce2732a4ae160ad6237e5dcd5ad1564f4f85354db1"},
|
||||
"nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"},
|
||||
"plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
|
||||
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||
"thousand_island": {:hex, :thousand_island, "0.6.7", "3a91a7e362ca407036c6691e8a4f6e01ac8e901db3598875863a149279ac8571", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "541a5cb26b88adf8d8180b6b96a90f09566b4aad7a6b3608dcac969648cf6765"},
|
||||
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
|
||||
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
|
||||
}
|
||||
|
|
30
test/support/test.cert
Normal file
30
test/support/test.cert
Normal file
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFOzCCAyOgAwIBAgIUdOavKT2HoubHQ61WxIFEIXO+/fIwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjMxMDEzMjMxMTI4WhcNMzMx
|
||||
MDEwMjMxMTI4WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTCCAiIwDQYJKoZIhvcN
|
||||
AQEBBQADggIPADCCAgoCggIBAKWgA8rt/3qP7L4JqCKj/oRdNm9iUSOP2TfzTH+4
|
||||
V2ho9XgE3MZJXqcnKvtIPmAHv2bFIR+jiYmv58cvB+vjRHvl4g88WX0Kbj2VGAFI
|
||||
VYFJ2lAsuIsxucjoD/6nSoW3uZcWAvipBQsEe7D70IcyXjh6ZwWxabywL3tp9Kjb
|
||||
ZvB0IwKrBWrqESYRSZtfrh/g//qp1uZJnJf+GaAJ9Mgn0rV8t1IbKOZjDRG8wNm6
|
||||
8fuywdu3HYj9x9zHPO0NyxQzOY1tDlcIVXKAvlJbaKP5vYXu/fdRS3t4Xh///CHh
|
||||
EjoRwymHly9g1hDmQXKe1ZAqudGjqhKG+jTidROuYj25yLpIsi8Lx1ZEQj1vMJMK
|
||||
pWv+k/Rb2m4a0lvkGPlLAGd7aTakNqAwyEbNKo9T2E/tpggZCQ/72ltW+vFfpMue
|
||||
gLiJG6CdWH+6iCi99flKApD/I+vBuFRjWYtls8XptmnPHh657WO8J1dGzvhW/W2a
|
||||
SyWdrKmFsXuDmMlXdoqyNlVnyA3Zq07FlMlHZcqhxFXF9kDAhixbIWUXk8O8Cjqk
|
||||
7dkTtY/bWBe4jsTigOClPseeTY9h/1zvluJmTycJzH1weWtYrGah1RTX7QcRt5H6
|
||||
CiD3hYBGj+0EW9z6R3JqgmfodRUpQRLiWPxDlNly7H0D8c+2pB0mKymF6DRk+S9P
|
||||
ODCZAgMBAAGjgYAwfjAdBgNVHQ4EFgQU+YZ+OGNddicX5fWAYGfr+iLNV8IwHwYD
|
||||
VR0jBBgwFoAU+YZ+OGNddicX5fWAYGfr+iLNV8IwDwYDVR0TAQH/BAUwAwEB/zAr
|
||||
BgNVHREEJDAiggtleGFtcGxlLmNvbYINKi5leGFtcGxlLmNvbYcEfwAAATANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEALoEobHweztlJaYElHlMKK6ZaWYqXE6BZN4zOuItubqzt
|
||||
rYVYkpM+Lamu8RCqw+Hizj77OzE5TiLNRFZX/Y27uXFRVw7PT8+IkdVfgfEjIwvR
|
||||
CCWFmFdTA0qD7mNnZjRUMH8WGllbXHdrZ4WPu3pNWV2op1/nGjSF7+QKez4f7BYK
|
||||
vcA3N7Vw/w+NE0PLI707RpKdwnbAtqFI/8Rus5K6HEU8lGh4vErQSkYLpxfWfqgH
|
||||
BPzuA75fpzzx3DPQ6Xo4BN2KwyZFHX8vdZ4aQxFq4aH4ITUuirdJIVLMnM0ghI3O
|
||||
ZaRdZ6FhV9tSDBrX4pNZSgAFHtDiEGsYZF4gS81G9NUNxGN5zXK1s5KCtvCACKAm
|
||||
OC0+9zWFNQSK/0KEwVnv7TDsApTa3Fu60xnRLwmcuK1erx8Pa76ae72CVbG4iKgK
|
||||
msvWCuzUnM3wZqm7rUD8GJMeOJg8aSMJn8qLz/+vr23o8DUxUdPyZPnb/0wkZ88C
|
||||
IwWPPXz3XgVnUZpLK6KtxPbGyDJlDZ9EzY/avtepMbBYiWB0UfibqiW/ZwwFx0rd
|
||||
BWamBzZeWjUc7ayJ+4rYpZs4IToPl9sJEiAJnrLlDe5ezJ2tHYHtxBLQZ5XjF4vR
|
||||
MP5QDhLCMUXqnq9EG5m7bNhm/paHw/OdvhyYwE+HkyzJXJidPnf44Vts+DDGv6o=
|
||||
-----END CERTIFICATE-----
|
52
test/support/test.key
Normal file
52
test/support/test.key
Normal file
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
HvfyQy8D6gQkufWaRINIMmNukIEFlg==
|
||||
-----END PRIVATE KEY-----
|
139
test/wayfarer/listener_test.exs
Normal file
139
test/wayfarer/listener_test.exs
Normal file
|
@ -0,0 +1,139 @@
|
|||
defmodule Wayfarer.ListenerTest do
|
||||
@moduledoc false
|
||||
use ExUnit.Case, async: false
|
||||
alias Wayfarer.Listener
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
setup do
|
||||
start_supervised!(Wayfarer.Listener.Supervisor)
|
||||
|
||||
start_supervised!(
|
||||
{Finch,
|
||||
name: :test_client,
|
||||
pools: %{
|
||||
default: [
|
||||
conn_opts: [
|
||||
transport_opts: [
|
||||
verify: :verify_none
|
||||
]
|
||||
]
|
||||
]
|
||||
}}
|
||||
)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "start_listener/1" do
|
||||
test "it returns an error when the scheme option is missing" do
|
||||
assert {:error, {:required_option, :scheme}} = Listener.start_listener([])
|
||||
end
|
||||
|
||||
test "it returns an error when an option is incorrect" do
|
||||
assert {:error, _} = Listener.start_listener(scheme: "Marty McFly", port: random_port())
|
||||
end
|
||||
|
||||
test "it can start an HTTP listener" do
|
||||
port = random_port()
|
||||
|
||||
assert {:ok, _pid} =
|
||||
Listener.start_listener(
|
||||
scheme: :http,
|
||||
ip: {127, 0, 0, 1},
|
||||
port: port
|
||||
)
|
||||
|
||||
assert {:ok, %{status: 502}} =
|
||||
:get
|
||||
|> Finch.build("http://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
end
|
||||
|
||||
test "it can start an HTTPS listener" do
|
||||
port = random_port()
|
||||
|
||||
certfile = Path.join(__DIR__, "../support/test.cert")
|
||||
keyfile = Path.join(__DIR__, "../support/test.key")
|
||||
|
||||
assert {:ok, _pid} =
|
||||
Listener.start_listener(
|
||||
scheme: :https,
|
||||
ip: {127, 0, 0, 1},
|
||||
port: port,
|
||||
certfile: certfile,
|
||||
keyfile: keyfile
|
||||
)
|
||||
|
||||
assert {:ok, %{status: 502}} =
|
||||
:get
|
||||
|> Finch.build("https://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
end
|
||||
|
||||
test "it restarts listeners when they crash" do
|
||||
port = random_port()
|
||||
|
||||
assert {:ok, _pid} =
|
||||
Listener.start_listener(
|
||||
scheme: :http,
|
||||
ip: {127, 0, 0, 1},
|
||||
port: port
|
||||
)
|
||||
|
||||
# It's up
|
||||
assert {:ok, %{status: 502}} =
|
||||
:get
|
||||
|> Finch.build("http://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
|
||||
# Crash it
|
||||
capture_log(fn ->
|
||||
[{_, pid}] = Registry.lookup(Listener.Registry, {{127, 0, 0, 1}, port})
|
||||
Process.exit(pid, :kill)
|
||||
end)
|
||||
|
||||
# It's up again
|
||||
assert {:ok, %{status: 502}} =
|
||||
:get
|
||||
|> Finch.build("http://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
end
|
||||
end
|
||||
|
||||
describe "stop_listener/2" do
|
||||
test "it can shut down a listener" do
|
||||
port = random_port()
|
||||
|
||||
assert {:ok, pid} =
|
||||
Listener.start_listener(
|
||||
scheme: :http,
|
||||
ip: {127, 0, 0, 1},
|
||||
port: port
|
||||
)
|
||||
|
||||
assert {:ok, %{status: 502}} =
|
||||
:get
|
||||
|> Finch.build("http://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
|
||||
Listener.stop_listener({127, 0, 0, 1}, port)
|
||||
|
||||
wait_until_dead(pid)
|
||||
|
||||
assert {:error, _} =
|
||||
:get
|
||||
|> Finch.build("http://127.0.0.1:#{port}/")
|
||||
|> Finch.request(:test_client)
|
||||
end
|
||||
end
|
||||
|
||||
defp random_port, do: :rand.uniform(0xFFFF - 1000) + 1000
|
||||
|
||||
defp wait_until_dead(pid) do
|
||||
if Process.alive?(pid) do
|
||||
wait_until_dead(pid)
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,5 @@
|
|||
defmodule WayfarerTest do
|
||||
@moduledoc false
|
||||
use ExUnit.Case
|
||||
doctest Wayfarer
|
||||
|
||||
test "greets the world" do
|
||||
assert Wayfarer.hello() == :world
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue