Need help using Erlang library Quicer in Elixir

I want to use the library GitHub - qzhuyan/quicer which is written in Erlang.
The readme contains a sample snippet:

application:ensure_all_started(quicer),
Port = 4567,
LOptions = [ {certfile, "cert.pem"}
           , {keyfile,  "key.pem"}
           , {alpn, ["sample"]}
           , {peer_bidi_stream_count, 1}
             ],
{ok, L} = quicer:listen(Port, LOptions),
{ok, Conn} = quicer:accept(L, [], 120000),
{ok, Conn} = quicer:handshake(Conn),
{ok, Stm} = quicer:accept_stream(Conn, []),
receive {quic, <<"ping">>, Stm, _Props} -> ok end,
{ok, 4} = quicer:send(Stm, <<"pong">>),
quicer:close_listener(L).

which I translated to the following:

:application.ensure_all_started(:quicer)

    port = 4567
    l_options = [
      {:certfile, "cert.pem"},
      {:keyfile, "key.pem"},
      {:alpn, ["sample"]},
      {:peer_bidi_stream_count, 1}
    ]
    # {:error, :badarg}
    {:ok, listener} = :quicer.listen(port, l_options)
    {:ok, conn} = :quicer.accept(listener, [], 120_000)
    {:ok, conn} = :quicer.handshake(conn)
    {:ok, stm} = :quicer.accept_stream(conn, [])

    receive do
      {:quic, <<"ping">>, stm, _props} -> :ok
    end

    {:ok, 4} = :quicer.send(stm, <<"pong">>)
    :quicer.close_listener(listener)

Something seems to be wrong, as there is a badarg (marked above).
ElixirLS Dialyzer is reporting the following:

The call quicer:listen
         (_port@1 :: 4567,
          _l_options@1 ::
              [{'alpn', [<<_:48>>, ...]} |
               {'certfile', <<_:64>>} |
               {'keyfile', <<_:56>>} |
               {'peer_bidi_stream_count', 1},
               ...]) breaks the contract 
          (listen_on(), listen_opts()) ->
             {'ok', listener_handler()} |
             {'error', 'listener_open_error', atom_reason()} |
             {'error', 'listener_start_error', atom_reason()}

What am I missing?
PS: ChatGPT came to the same translation :slight_smile:

1 Like

I think the problem is the strings. Remember the thing where strings are lists of characters in Erlang?

Try using single quotes in place of double quotes in the translation. eg

{:certfile, 'cert.pem'}
3 Likes

Thanks for the hint, but the error is still the same.
I have also tried String.to_charlist as suggested here: membrane_quic_plugin/server.ex at f50f3658f6a18f5791398ade8102dfc9f08f11fd · mickel8/membrane_quic_plugin · GitHub. That was the only use for that library in Elixir on Github that I could find.
Even an empty list will still raise the same error.

Oh weird. I got slightly nerdsniped and tried in Elixir

:quicer.listen(4567,[certfile: 'cert.pem', keyfile: 'key.pem', alpn: ['sample'], peer_bidi_stream_count: 1])  

I got {:error, :config_error, :tls_error} rather than a bad arg, which seems fair enough as those SSL certificate files and keys didn’t actually exist for me.

Even with incorrect paths, the error is still badarg for me :smiley:
I am using OTP24, do you know if there are any breaking changes since 22 (mentioned as min requirement in quicer readme) ?

Very strange. I can get a badarg by changing (say) the :keyfile value to a binary string. ¯\(ツ)

Seems unlikely. I was using otp 25.

What version of Elixir are you using?

https://hexdocs.pm/elixir/compatibility-and-deprecations.html#compatibility-between-elixir-and-erlang-otp

Looking over the error again, I think it is as simple as not handling error outcomes

Set up a case statement for all three contracts. Right note you only handle the success outcome.

{'ok', listener_handler()} 
 {:'error', 'listener_open_error', atom_reason()} 
 {'error', 'listener_start_error', atom_reason()}

Like in the last ChatGPT Erlang-to-Elixir translation, the robot doesn’t do a good job spotting the difference between binaries and charlists. :man_shrugging:

Dialyzer is complaining about the binaries in your options, since listen_opts is defined as:

alpn() is defined as string() - so charlists

file:filename() is defined in stdlib as… string() - so charlists

1 Like

@BradS2S
Erlang/OTP 24 [erts-12.3.2.7], Elixir (1.14.2),
rlang/OTP 25 [erts-13.1.5], Elixir (1.15.0-dev)

@al2o3cr So then I should use:

:quicer.listen(4567,
  certfile: String.to_charlist("./cert.pem"),
  keyfile: String.to_charlist("./key.pem"),
  ...
)

That also does not work, still badarg. Even the empty list is a badarg. Could the error originate from inside the library/NIF somewhere and just appears to be from the listen call?

There are many places inside of :quicer.listen’s NIF that can return {:error, :badarg}:

Passing an empty list of options results in {:error, :badarg} because of checks for required parameters, for instance the ones on line 302-316.

One other thing that jumps out: the change to add support for certfile and keyfile (instead of just cert and key) isn’t in a released package, it was just added a bit more than a month ago. If you’ve installed quicer from Hex, try using cert and key (the old names) instead.

1 Like

I didn’t realize this was a NIF. There be dragons…

I started using the dependecy from github directly. That resolved the above errors.
in mix.exs: {:quicer, github: "emqx/quic"}

Thanks for the help everyone!