1

I am trying to implement a simple Server (in Go) / Client (in Elixir).

However, I keep getting timeouts on Elixir side before my request reaches the server.

Server

package main

import (
    "fmt"
    "net"
    "net/http"
    "os"
)

func main() {
    socket := "/tmp/ipc_test.sock"
    os.Remove(socket)

    listener, err := net.Listen("unix", socket)
    if err != nil {
        fmt.Printf("Error listening on unix socket: %s\n", err)
        return
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        param := r.URL.Query().Get("param")
        fmt.Printf("%s", param)
        fmt.Fprint(w, param)
    })

    fmt.Printf("Server listening on %s\n", socket)
    http.Serve(listener, nil)
}

Client

defmodule IpcTest do
  def make_request(socket, request) do
    :gen_tcp.send(socket, request)
    {:ok, response} = :gen_tcp.recv(socket, 0)

    :gen_tcp.close(socket)
    response
  end

  def print_responses(responses) do
    responses
    |> Enum.each(&IO.puts/1)
  end

  def run(num_requests) do
    1..num_requests
    |> Enum.map(fn i ->
      request = "/?param=#{i}"

      Task.async(fn ->
        {:ok, socket} =
          :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 0, [:binary, packet: 4, active: false])

        make_request(socket, request)
      end)
    end)
    |> Enum.map(&Task.await/1)
  end
end
> go run src/main.go
Server listening on /tmp/js_runner.sock

As you can see, the server seems to be running fine. However, I am getting the following error in my client.

> iex -S mix
> IpcTest.run 1
** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID<0.237.0>, pid: #PID<0.263.0>, ref: #Reference<0.3740412160.2895708166.190144>}, 5000)
    ** (EXIT) time out
    (elixir 1.14.2) lib/task.ex:830: Task.await/2
    (elixir 1.14.2) lib/enum.ex:1658: Enum."-map/2-lists^map/1-0-"/2
    iex:2: (file)
Moon
  • 33,439
  • 20
  • 81
  • 132

2 Answers2

0

By default, the :gen_tcp.connect/3 function has a timeout value of 5000 milliseconds (5 seconds). Increase to 10:

{:ok, socket} = :gen_tcp.connect({:local, "/tmp/ipc_test.sock"}, 10000, [:

But you should also try stuff like

socat - UNIX-CONNECT:/tmp/ipc_test.sock

Or the other stuff in: https://unix.stackexchange.com/questions/26715/how-can-i-communicate-with-a-unix-domain-socket-via-the-shell-on-debian-squeeze

to see if the socket file is even working for ANY normal linux cli tool.

Andrew Arrow
  • 4,248
  • 9
  • 53
  • 80
  • thanks for the response. `socat - UNIX-CONNECT:/tmp/js_runner.sock` hangs my terminal on Mac. And then on responds with: ``` HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 Connection: close 400 Bad Request ``` So I guess I need to pass in a url somehow? Also, Go server is practically doing nothing! 10 seconds for it seems pretty high and no-go, don't you think? – Moon Jul 13 '23 at 16:15
  • BTW, your example to increase timeout seems to be incorrect since I now immediately get the error `** (FunctionClauseError) no function clause matching in :local_tcp.getserv/1 (kernel 8.5.2) local_tcp.erl:40: :local_tcp.getserv(10000)`. Thanks for looking into this though :) – Moon Jul 13 '23 at 16:20
  • ah i see. is there reason you are using socket file vs. localhost? I've always done this with 127.0.0.1 and simulate a real tcp network connection and not use the file? – Andrew Arrow Jul 13 '23 at 17:31
0

Figured it out! My issue was actually in the Go based server.

Here is the updated code if anyone is interested in:

package main

import (
    "io"
    "log"
    "net"
    "os"
)

const SockAddr = "/tmp/ipc_test.sock"

func echoServer(c net.Conn) {
    log.Printf("Client connected [%s]", c.RemoteAddr().Network())
    io.Copy(c, c)
    c.Close()
}

func main() {
    if err := os.RemoveAll(SockAddr); err != nil {
        log.Fatal(err)
    }

    l, err := net.Listen("unix", SockAddr)
    if err != nil {
        log.Fatal("listen error:", err)
    }
    defer l.Close()

    for {
        conn, err := l.Accept()
        if err != nil {
            log.Fatal("accept error:", err)
        }

        go echoServer(conn)
    }
}

Reference: https://eli.thegreenplace.net/2019/unix-domain-sockets-in-go

Moon
  • 33,439
  • 20
  • 81
  • 132