From dd9c39d1b24a4893e7eacbbd8a3a26f9c85b5a81 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Lazo Date: Thu, 10 Aug 2023 16:07:09 -0400 Subject: [PATCH] feat: improve decompression middleware closes #598 --- lib/tesla/middleware.ex | 4 ++-- lib/tesla/middleware/compression.ex | 13 ++++++++++++- lib/tesla/middleware/json.ex | 4 ++++ test/support/test_support.ex | 7 +++++++ test/tesla/middleware/compression_test.exs | 16 ++++++++++++---- 5 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 test/support/test_support.ex diff --git a/lib/tesla/middleware.ex b/lib/tesla/middleware.ex index 660c26ae..4650d3e5 100644 --- a/lib/tesla/middleware.ex +++ b/lib/tesla/middleware.ex @@ -16,11 +16,11 @@ defmodule Tesla.Middleware do Tesla.client([{Tesla.Middleware.BaseUrl, "https://example.com"}]) ## Ordering - + The order in which middleware is defined matters. Note that the order when _sending_ the request matches the order the middleware was defined in, but the order when _receiving_ the response is reversed. - + For example, `Tesla.Middleware.DecompressResponse` must come _after_ `Tesla.Middleware.JSON`, otherwise the response isn't decompressed before it reaches the JSON parser. diff --git a/lib/tesla/middleware/compression.ex b/lib/tesla/middleware/compression.ex index c2cb24ad..1a6276ae 100644 --- a/lib/tesla/middleware/compression.ex +++ b/lib/tesla/middleware/compression.ex @@ -61,8 +61,19 @@ defmodule Tesla.Middleware.Compression do def decompress({:error, reason}), do: {:error, reason} def decompress(env) do + body = decompress_body(env.body, Tesla.get_header(env, "content-encoding")) + + content_length = + body + |> byte_size() + |> to_string() + env - |> Tesla.put_body(decompress_body(env.body, Tesla.get_header(env, "content-encoding"))) + |> Tesla.put_body(body) + # what should happen if the content-encoding is not supported and the body + # is not decompressed? + |> Tesla.delete_header("content-encoding") + |> Tesla.put_header("content-length", content_length) end defp decompress_body(<<31, 139, 8, _::binary>> = body, "gzip"), do: :zlib.gunzip(body) diff --git a/lib/tesla/middleware/json.ex b/lib/tesla/middleware/json.ex index 61095b93..b0e481f8 100644 --- a/lib/tesla/middleware/json.ex +++ b/lib/tesla/middleware/json.ex @@ -164,6 +164,8 @@ defmodule Tesla.Middleware.DecodeJson do """ @moduledoc since: "1.8.0" + @behaviour Tesla.Middleware + @impl Tesla.Middleware def call(env, next, opts) do opts = opts || [] @@ -180,6 +182,8 @@ defmodule Tesla.Middleware.EncodeJson do """ @moduledoc since: "1.8.0" + @behaviour Tesla.Middleware + @impl Tesla.Middleware def call(env, next, opts) do opts = opts || [] diff --git a/test/support/test_support.ex b/test/support/test_support.ex new file mode 100644 index 00000000..cdd0f17d --- /dev/null +++ b/test/support/test_support.ex @@ -0,0 +1,7 @@ +defmodule TestSupport do + def gzip_headers(env) do + env.headers + |> Enum.map_join("|", fn {key, value} -> "#{key}: #{value}" end) + |> :zlib.gzip() + end +end diff --git a/test/tesla/middleware/compression_test.exs b/test/tesla/middleware/compression_test.exs index a82ecfe7..0c5686f5 100644 --- a/test/tesla/middleware/compression_test.exs +++ b/test/tesla/middleware/compression_test.exs @@ -67,8 +67,14 @@ defmodule Tesla.Middleware.CompressionTest do end end + test "decompress response body update the headers correctly" do + assert {:ok, env} = CompressionResponseClient.get("/response-gzip") + assert env.headers == [{"content-type", "text/plain"}, {"content-length", "17"}] + end + test "decompress response body (gzip)" do assert {:ok, env} = CompressionResponseClient.get("/response-gzip") + assert env.headers == [{"content-type", "text/plain"}, {"content-length", "17"}] assert env.body == "decompressed gzip" end @@ -114,7 +120,8 @@ defmodule Tesla.Middleware.CompressionTest do {status, headers, body} = case env.url do "/" -> - {200, [{"content-type", "text/plain"}, {"content-encoding", "gzip"}], env.headers} + {200, [{"content-type", "text/plain"}, {"content-encoding", "gzip"}], + TestSupport.gzip_headers(env)} end {:ok, %{env | status: status, headers: headers, body: body}} @@ -123,7 +130,7 @@ defmodule Tesla.Middleware.CompressionTest do test "Compression headers" do assert {:ok, env} = CompressionHeadersClient.get("/") - assert env.body == [{"accept-encoding", "gzip, deflate"}] + assert env.body == "accept-encoding: gzip, deflate" end defmodule DecompressResponseHeadersClient do @@ -135,7 +142,8 @@ defmodule Tesla.Middleware.CompressionTest do {status, headers, body} = case env.url do "/" -> - {200, [{"content-type", "text/plain"}, {"content-encoding", "gzip"}], env.headers} + {200, [{"content-type", "text/plain"}, {"content-encoding", "gzip"}], + TestSupport.gzip_headers(env)} end {:ok, %{env | status: status, headers: headers, body: body}} @@ -144,7 +152,7 @@ defmodule Tesla.Middleware.CompressionTest do test "Decompress response headers" do assert {:ok, env} = DecompressResponseHeadersClient.get("/") - assert env.body == [{"accept-encoding", "gzip, deflate"}] + assert env.body == "accept-encoding: gzip, deflate" end defmodule CompressRequestHeadersClient do