diff --git a/lib/bypass/instance.ex b/lib/bypass/instance.ex index f8ae2b1..b70054d 100644 --- a/lib/bypass/instance.ex +++ b/lib/bypass/instance.ex @@ -25,7 +25,10 @@ defmodule Bypass.Instance do def init([opts]) do # Get a free port from the OS - case :ranch_tcp.listen(so_reuseport() ++ [ip: listen_ip(), port: Keyword.get(opts, :port, 0)]) do + case :gen_tcp.listen( + Keyword.get(opts, :port, 0), + so_reuseport() ++ [ip: listen_ip()] ++ default_socket_opts() + ) do {:ok, socket} -> {:ok, port} = :inet.port(socket) :erlang.port_close(socket) @@ -340,7 +343,10 @@ defmodule Bypass.Instance do defp do_up(port, ref) do plug_opts = [bypass_instance: self()] - {:ok, socket} = :ranch_tcp.listen(so_reuseport() ++ [ip: listen_ip(), port: port]) + + {:ok, socket} = + :gen_tcp.listen(port, so_reuseport() ++ [ip: listen_ip()] ++ default_socket_opts()) + cowboy_opts = cowboy_opts(port, ref, socket) {:ok, _pid} = Plug.Cowboy.http(Bypass.Plug, plug_opts, cowboy_opts) socket @@ -442,34 +448,40 @@ defmodule Bypass.Instance do [ref: ref, port: port, transport_options: [num_acceptors: 5, socket: socket]] end - # Use raw socket options to set SO_REUSEPORT so we fix {:error, :eaddrinuse} - where the OS errors - # when we attempt to listen on the same port as before, since it's still considered in use. - # - # See https://lwn.net/Articles/542629/ for details on SO_REUSEPORT. - # - # See https://github.com/aetrion/erl-dns/blob/0c8d768/src/erldns_server_sup.erl#L81 for an - # Erlang library using this approach. - # - # We want to do this: - # - # int optval = 1; - # setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); - # - # Use the following C program to find the values on each OS: - # - # #include - # #include - # - # int main() { - # printf("SOL_SOCKET: %d\n", SOL_SOCKET); - # printf("SO_REUSEPORT: %d\n", SO_REUSEPORT); - # return 0; - # } - defp so_reuseport() do - case :os.type() do - {:unix, :linux} -> [{:raw, 1, 15, <<1::32-native>>}] - {:unix, :darwin} -> [{:raw, 65_535, 512, <<1::32-native>>}] - _ -> [] + if System.otp_release() >= "26" do + defp so_reuseport() do + [reuseport: true] + end + else + # Use raw socket options to set SO_REUSEPORT so we fix {:error, :eaddrinuse} - where the OS errors + # when we attempt to listen on the same port as before, since it's still considered in use. + # + # See https://lwn.net/Articles/542629/ for details on SO_REUSEPORT. + # + # See https://github.com/aetrion/erl-dns/blob/0c8d768/src/erldns_server_sup.erl#L81 for an + # Erlang library using this approach. + # + # We want to do this: + # + # int optval = 1; + # setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); + # + # Use the following C program to find the values on each OS: + # + # #include + # #include + # + # int main() { + # printf("SOL_SOCKET: %d\n", SOL_SOCKET); + # printf("SO_REUSEPORT: %d\n", SO_REUSEPORT); + # return 0; + # } + defp so_reuseport() do + case :os.type() do + {:unix, :linux} -> [{:raw, 1, 15, <<1::32-native>>}] + {:unix, :darwin} -> [{:raw, 65_535, 512, <<1::32-native>>}] + _ -> [] + end end end @@ -494,4 +506,17 @@ defmodule Bypass.Instance do end end end + + defp default_socket_opts do + # ref: https://github.com/ninenines/ranch/blob/a8f31f3/src/ranch_tcp.erl#L104-L110 + [ + {:backlog, 1024}, + {:nodelay, true}, + {:send_timeout, 30_000}, + {:send_timeout_close, true}, + :binary, + {:active, false}, + {:packet, :raw} + ] + end end diff --git a/mix.exs b/mix.exs index 9dd16da..d8cf705 100644 --- a/mix.exs +++ b/mix.exs @@ -31,7 +31,6 @@ defmodule Bypass.Mixfile do [ {:plug_cowboy, "~> 2.0"}, {:plug, "~> 1.7"}, - {:ranch, "~> 1.7"}, {:ex_doc, "> 0.0.0", only: :dev}, {:espec, "~> 1.6", only: [:dev, :test]}, {:mint, "~> 1.1", only: :test},