Finding the file descriptor for an open long-lived http connection

Hello hivemind,

Is there way to find the file descriptor associated with a long-lived tcp connection in elixir / erlang? I am using the HTTPoison library (and hence :hackney) for http calls. I need to send the file descriptor to a server. The client-server interaction goes like this:

  1. Client opens a long-lived http connection to server (using HTTP GET with Keep-alive header)
  2. The server accepts the connection and waits for a payload from the client
  3. The client finds the file descriptor associated with the connection (which is a path on the client’s local file system in /proc/${PID}/fd)
  4. The client sends the file descriptor to the server.

I couldn’t find any API on HTTPoison or hackney to get connection file descriptors, so I attempted the following:

# start a custom hackney pool with long connection timeout to ensure long-lived connections
:ok = :hackney_pool.start_pool(:my_pool, [timeout: 60000, max_connections: 1])

# Find the PID of `:my_pool`
my_pool_pid =  :hackney_pool.find_pool(:my_pool)

# make http request.
{:ok, _} = HTTPoison.get "http://localhost:8000", [], hackney: [pool: :my_pool]

# List all open ports 
open_tcp_port = 
  :erlang.ports()
   |> Enum.map(fn p -> :erlang.port_info(p, :connected) end)
   |> Enum.find(fn {:connected, p} -> p == my_pool_pid end)
#[
#  name: 'tcp_inet',
#  links: [#PID<0.285.0>],
# id: 288,
#  connected: #PID<0.285.0>,
#  input: 0,
#  output: 68,
#  os_pid: :undefined
#]

# Ask erlang to print out a list of open sockets. Notice that the only open socket has Port=288,
# which is the `id` of `open_tcp_port`.
:inet.i
#Port Module   Recv Sent Owner     Local Address   Foreign Address State        Type
#288  inet_tcp 147  68   <0.285.0> localhost:63617 localhost:irdmi CONNECTED(O) STREAM
#:ok

Question
How can I get from the open_tcp_port and / or the socket information from inet.i to a file descriptor path?

Thanks in advance.

2 Likes

I searched the hackney GitHub repo for fd but I don’t think the results are helpful.

Thanks @dimitarvp. I did the same search but I couldn’t find anything on file descriptors in Hackney or HTTPoison. The best I could get was ports. That’s why I tried getting to the descriptors from ports like I explained in the post.

I found a solution.

# start a custom hackney pool with long connection timeout to ensure long-lived connections
:ok = :hackney_pool.start_pool(:my_pool, [timeout: 60000, max_connections: 1])

# Find the PID of `:my_pool`
my_pool_pid =  :hackney_pool.find_pool(:my_pool)

# make an http request. This should create a long-lived connection if the server supports it
{:ok, _} = HTTPoison.get "http://localhost:8000", [], hackney: [pool: :my_pool]

# Find the open port associated with the connection
connection_port =
  :erlang.ports()
  |> Enum.find(
    fn port ->
       port_info = :erlang.port_info(port)
       port_info[:connected] == my_pool_pid
     end
    )
{:ok, file_descriptor} = :prim_inet.getfd(connection_port)
6 Likes