0

Phoenix 1.3
Elixir 1.5.2 compiled with Erlang 20

I have an Android mobile app which sends and receives MsgPack with a HTTP server. Now we are planning to integrate Phoenix framework for some parts. But sending binaries specifically MsgPack is troublesome on Phoenix framework.

How to use custom (de-)serializers like MsgPack in Phoenix?

If it is of any relevance: 1. Using MsgPax library
2. Checked this link on stoiximan.gr

potatoPC
  • 67
  • 1
  • 8
  • First and foremost, the error message: `:eaddrinuse` means that you already have an application listening on the port you are using for local development. Try to find it, or try using another port. Next, why don't you git clone their repo and try to run that. If it works out of the box, then check if there are differences between the code of the tutorial and the code in github. Then either check your code for typos, or either start agin from scratch, copy pasting the relevant code over from straight from Github. – Kevin Johnson Nov 29 '17 at 04:47
  • _“sending binaries specifically MsgPack is troublesome on Phoenix framework”_ oh really? And why is that? – Aleksei Matiushkin Nov 29 '17 at 05:15
  • What is your question? – Aleksei Matiushkin Nov 29 '17 at 05:16
  • @KevinJohnson My previous Phoenix server was running that's the cause of error. – potatoPC Nov 29 '17 at 07:26
  • @mudasobwa when I POST a MsgPack data to a route '/foor/bar' where does it go? Usually, it is stored in `params` but on using `IO.inspect params` it returns an empty map. My question states Binaries but focuses on MsgPack as currently, we are testing out Phoenix. I had hoped to obtain broad view of transferring binaries ranging from text to live video stream while focussing on MsgPack. – potatoPC Nov 29 '17 at 07:37
  • For someone new to this Elixir/Phoenix solutions are scarce to find, thus, the learning becomes tougher. – potatoPC Nov 29 '17 at 07:39
  • Have you tried out the general guidance notes I provided you with besides resolving the `:eaddrinuse` issue? – Kevin Johnson Nov 29 '17 at 18:48

1 Answers1

2

I've used MsgPax to do this, and it works just fine. First add it to your mix.exs file.

To use a custom serializer:

  • Phoenix v1.4+
    configure serlirizer in your Phoenix.Endpoint

    socket "/socket", YourApp.UserSocket,
      websocket: [
        connect_info: [:peer_data, :x_headers, :uri, session: @session_options],
        serializer: [{YourApp.MsgpaxSerializer, "2.0.0"}]
      ]
    
  • Phoenix v1.3 and older
    use Phoenix.Socket.transport macro in your user_socket.ex file, like so:

      defmodule YourApp.UserSocket do
        use Phoenix.Socket
    
        # Your channels here
    
        # Take out whichever version you're not using
        ## Transports for Phoenix 1.2
        transport :websocket, Phoenix.Transports.WebSocket, timeout: 45_000,
                  serializer: YourApp.MsgpaxSerializer
        ## Transports for Phoenix 1.3
        transport :websocket, Phoenix.Transports.WebSocket, timeout: 45_000,
                  serializer: [{YourApp.MsgpaxSerializer, "~> 2.0.0"}]
    
        # Other user socket stuff
      end
    

Then, as according to the docs, your serializer has to have 3 functions: decode!/2, encode!/1, and fastlane!/1. So, copying mostly from phoenix's own implementation, your serializer might look something like this:

defmodule YourApp.MsgpaxSerializer do
  @behaviour Phoenix.Transports.Serializer

  alias Phoenix.Socket.Reply
  alias Phoenix.Socket.Message
  alias Phoenix.Socket.Broadcast

  def fastlane!(%Broadcast{} = msg) do
    msg = %Message{topic: msg.topic, event: msg.event, payload: msg.payload}

    {:socket_push, :binary, encode_to_binary(msg)}
  end

  def encode!(%Reply{} = reply) do
    msg = %Message{
      topic: reply.topic,
      event: "phx_reply",
      ref: reply.ref,
      payload: %{status: reply.status, response: reply.payload}
    }
    {:socket_push, :binary, encode_to_binary(msg)}
  end
  def encode!(%Message{} = msg) do
    {:socket_push, :binary, encode_to_binary(msg)}
  end

  defp encode_to_binary(msg) do
    msg |> Map.from_struct() |> Msgpax.pack!()
  end

  def decode!(message, _opts) do
    message
    |> Msgpax.unpack!()
    |> Phoenix.Socket.Message.from_map!()
  end
end

Finally, if you are encoding structs, you may have to add @derive Msgpax.Packer attribute to the modules containing the structs as detailed in Msgpax's documentation.

Arek Kostrzeba
  • 551
  • 1
  • 7
  • 21
n_i_c_k
  • 1,504
  • 10
  • 18