Skip to content

Commit

Permalink
Do not rely directly on the ranch
Browse files Browse the repository at this point in the history
`bypass` only utilizes a small part of the `ranch` API; we could simply use `:gen_tcp` directly.

And this changes might lower the dependency footprint when `bypass` going to support different HTTP server: PSPDFKit-labs#144
  • Loading branch information
fishtreesugar authored and IceDragon200 committed Oct 17, 2024
1 parent 3f4aac1 commit 78068fc
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 31 deletions.
85 changes: 55 additions & 30 deletions lib/bypass/instance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -319,7 +322,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
Expand Down Expand Up @@ -421,34 +427,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 <stdio.h>
# #include <sys/socket.h>
#
# 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 <stdio.h>
# #include <sys/socket.h>
#
# 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

Expand All @@ -473,4 +485,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
1 change: 0 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ defmodule Bypass.Mixfile do
[
{:plug_cowboy, "~> 2.0"},
{:plug, "~> 1.7"},
{:ranch, "~> 1.7.1"},
{:ex_doc, "> 0.0.0", only: :dev},
{:espec, "~> 1.6", only: [:dev, :test]},
{:mint, "~> 1.1", only: :test},
Expand Down

0 comments on commit 78068fc

Please sign in to comment.