Skip to content

blake-education/exbuf_plug

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExbufPlug

A small plug to handle decoding protocol buffers.

ExbufPlug is a wrapper around exprotobuf to handle dealing with protobufs over http.

The strategy here is to decode a protocol buffer into binary and send it over http - which this plug will decode into an elixir struct to deal with in your application.

Useful articles

Installation

Add ExbufPlug to your application

mix.deps

defp deps do
  [
    # ...
    {:exbuf_plug, "~> 0.0.1"}
    # ...
  ]
end

config.exs

config :exbuf_plug, ExbufPlug, %{
  list: [
    "TestEvent",
    "BiggerTestEvent"
  ],
  namespace: "ExbufPlug",
  module_name: "Protobufs",
  header_name: "x-protobuf"
}

The items in the configuration allow you to tailor how the decoding behaves.

  • list - The list of protobufs currently supported
  • namespace - The namespace around the protobuf module.
  • module_name - The main module that implements exprotobuf to be used for encoding/decoding protobufs.
  • header_name - The header name to look for to know which protobuf to use for decoding

Given the config above, ExbufPlug will attempt to decode the protobuf using the module ExbufPlug.Protobufs.

A simple example might look like this.

defmodule ExbufPlug.Protobufs do
  use Protobuf, from: Path.expand("./protocol_buffers.proto", __DIR__)
end

Phoenix Controllers

ExbufPlug hooks easily into Phoenix controllers.

The decoded value will assigned to the conn.assigns.protobuf_struct for your use throughout the request.

defmodule MyApp.MyController do
  use MyApp.Web, :controller

  plug ExbufPlug

  def show(conn, _params) do
    conn.assigns.protobuf_struct
  end
end

Multipart support

Multiple messages can be sent in a single request by using multipart form data. The content-type must be set correctly for this to work (either multipart/form-data or multipart/mixed).

The names of the encoded protobuf binary fields is ignored. Just ensure they are different from each other.

In this case, the assigns variable is plural, ie. conn.assigns.protobuf_structs.

All the messages must be the same protobuf, specified in the x-protobuf header.

Small Example

Given the sample protobuf schema we can see a typical flow through the usage.

enum AllowedTitles {
  awesomer = 1;
  sucker = 2;
}

message TestEvent {
  required AllowedTitles title = 1;
  required string name = 2;
  required string desc = 3;
}

We can get the encoded version of this protobuf with the following

# encode protobuf and encode into base64
base64 = Protobuf.TestEvent.new(
  title: :awesomer,
  name: "Bill Nye",
  desc: "The science guy"
)
|> Protobuf.TestEvent.encode
# <<8, 2, 18, 8, 66, 105, 108, 108, 32, 78, 121, 101, 26, 15, 84, 104, 101, 32, 115, 99, 105, 101, 110, 99, 101, 32, 103, 117, 121>>

With this binary, we can post this over HTTP. Imagine some language sending this post to create an event.

# not real code.. :troll:
client = HttpThing.new(host: "http://localhost:4000")
client.post(
  "api/v3/event",
  { body: <<8, 2, 18, 8, 66, 105, 108, 108, 32, 78, 121, 101, 26, 15, 84, 104, 101, 32, 115, 99, 105, 101, 110, 99, 101, 32, 103, 117, 121>> },
  { headers: [
    {"Content-Type": "application/octet-stream"}
    {"x-protobuf": "TestEvent"}
  ]},
)

In elixir it would be better to deal with the protobuf struct, so by adding this plug into any plug app, we can easily deal with pure elixir structs.

defmodule MyApp.MyController do
  use MyApp.Web, :controller

  plug ExbufPlug

  def show(conn, _params) do
    conn.assigns.protobuf_struct == %ExbufPlug.Protobufs.TestEvent{
      title: :sucker,
      name: "Bill Nye",
      desc: "The science guy"
    }
  end
end