Packages

Bridge between unix socket and tcp port. You can redirect UNIX socket (e.g. Docker API) to TCP port.

Current section

Files

Jump to
socket_tcp_bridge lib socket_tcp_bridge.ex
Raw

lib/socket_tcp_bridge.ex

defmodule SocketTcpBridge do
use GenServer
@moduledoc """
This is GenServer module without any public API. You can just start process with UNIX socket path and port:
```elixir
{:ok, pid} = SocketTcpBridge.start_link("/var/run/docker.sock", 8000)
```
and you can connect to docker using port 8000.
Please remember to run above process using Supervisor on production!
**Important:** Library use `socat` which need to be installed in your system.
"""
@doc """
Starts listener on given TCP port bound to UNIX socket.
"""
@spec start_link(bitstring, integer) :: {atom, pid}
def start_link(unix_socket, bind_port) when is_bitstring(unix_socket) and is_number(bind_port) do
GenServer.start_link(__MODULE__, %{unix_socket: unix_socket, bind_port: bind_port})
end
def init(%{unix_socket: unix_socket, bind_port: bind_port}) do
with {:ok, socat_filename} <- socat_exec_path(),
{:ok, port} <- run_socat(socat_filename, unix_socket, bind_port)
do
Port.connect(port, self())
{:ok, %{port: port}}
else
err -> {:error, err}
end
end
def handle_call(_request, _from, state) do
{:reply, :ok, state}
end
def handle_info({_port, {:exit_status, status}}, state) do
{:stop, {:port_exit, status}, state}
end
def terminate(_reason, %{port: port}) do
try do
Port.close(port)
rescue
_ ->
end
end
defp socat_exec_path do
case System.find_executable("socat") do
false -> {:error, "'socat' executable not found"}
filename -> {:ok, filename}
end
end
defp run_socat(socat_filename, unix_socket, bind_port) do
args = [
socat_filename,
"tcp-listen:#{bind_port},reuseaddr,bind=127.0.0.1,fork",
unix_socket
]
port = Port.open({:spawn_executable, :erlsh.fdlink_executable()}, [:stream, :exit_status, args: args])
{:ok, port}
end
end