xward

xward

Content-encoding gzip auto deflate/gunzip body on incoming requests

I would like to be avoid to receive content-encoding gzip and have transparent gzip/deflate when content reach my controller.

Using
phoenix 1.5.0
elixir 1.10.2
erlang 22.3
jason 1.0
plug_cowboy 2.0
tesla 1.3

Here is some code to show you what I see:

Client side:

defmodule Client do
  use Tesla

  plug(Tesla.Middleware.JSON)
  plug(Tesla.Middleware.Compression)

  def post, do: post("localhost:4000", %{hello: :i_m_fine})
end

Server side:

defmodule Router do
  use MyAppWeb, :router

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/test_notification" do
    pipe_through :api
    post("/", MyAppWeb.TestController, :test)
  end
end

defmodule MyAppWeb.TestController do
  @moduledoc false
  use MyAppWeb, :controller

  def test(conn, _args) do
    conn
    |> send_resp(200, "yeah me too !")
    |> halt()
  end
end

Doing a request:

# in my conn headers
req_headers: [
  {"connection", "keep-alive"},
  {"content-encoding", "gzip"},
  {"content-length", "123"},
  {"content-type", "application/json"},
  {"host", "localhost:4000"}
]

# fail before reaching my controller, fail in Plug.Parser that expect to Json decode

** (Plug.Parsers.ParseError) malformed request, a Jason.DecodeError exception was raised with message "unexpected byte at position 0: 0x1F"
    (plug 1.10.1) lib/plug/parsers/json.ex:88: Plug.Parsers.JSON.decode/2
    (plug 1.10.1) lib/plug/parsers.ex:313: Plug.Parsers.reduce/8
    (my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.plug_builder_call/2
    (my_app 0.1.0) lib/plug/debugger.ex:132: MyAppWeb.Endpoint."call (overridable 3)"/2
    (my_app 0.1.0) lib/my_app_web/endpoint.ex:1: MyAppWeb.Endpoint.call/2
    (phoenix 1.5.3) lib/phoenix/endpoint/cowboy2_handler.ex:65: Phoenix.Endpoint.Cowboy2Handler.init/4

I could add my own little plug in Enpoint.ex that does the feature, but I feel like I miss an obvious option to configure my phoenix endpoint.

Have a nice day everyone !

Marked As Solved

rjk

rjk

Ok got it working, the JSON decoding part is ‘lower’ in the plug pipeline than we thought, you can see it in your endpoint in the parsing bit. That’s also the place where I got it working (early phase code, please refine my code to cope with all other use cases but for now it works).

defmodule PhoenixFailToProcessZipedBodyWeb.Endpoint do
   # ...

  # 1. change your Plug.Parsers options to this, so it accepts
  # our newly created GzipBodyReader that is defined below.
  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    body_reader: {GzipBodyReader, :read_body, []},
    json_decoder: Phoenix.json_library()

    # ...
end

# 2. this is the GzipBodyReader (almost 1:1 from Plug documentation here: 
# https://hexdocs.pm/plug/Plug.Parsers.html#module-custom-body-reader
defmodule GzipBodyReader do
  def read_body(conn, opts) do
    {:ok, body, conn} = Plug.Conn.read_body(conn, opts)
    uncompressed_body = decompress_body(body, "gzip")
    {:ok, uncompressed_body, conn}
  end

  # 3. this part is copied from tesla library decompression part, normally gzip would be your
  # content encoding header so you can also do deflate and pass all others untouched
  # see Teslas code (as linked above in earlier reply) how to handle those cases.
  defp decompress_body(<<31, 139, 8, _::binary>> = body, "gzip"), do: :zlib.gunzip(body)
end

So this seems to work for me.
I also have to point you on a little change that your version of client.ex points to / instead of /create.
(So it actually calls your controller action).

Hope this gets you back on track!
Cheers!

Also Liked

sneako

sneako

I had to implement gzip request handling and found this thread useful. I put together a small library based on the discussion I found here. Right now, the lib only handles content-encoding: gzip, but it can easily be extended to handle more content-encodings.

:zlib.gunzip/1 is great if you know you can trust the input, but if your endpoint is exposed to the internet, you could be vulnerable to a zip bomb attack, so instead I used :zlib.safeInflate/2. I hope someone finds it useful.

https://github.com/sneako/plug_compressed_body_reader

hauleth

hauleth

We had similar problem at Logflare so I have created caisson for that

https://github.com/supabase/plug_caisson

wolf4earth

wolf4earth

Just FYI I found some related issues on plug and cowboy and wanted to share them:

https://github.com/elixir-plug/plug/issues/886
https://github.com/ninenines/cowboy/issues/946

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement