Skip to content

Commit

Permalink
feat: improve decompression middleware
Browse files Browse the repository at this point in the history
closes #598
  • Loading branch information
yordis committed Aug 10, 2023
1 parent cfee71b commit dd9c39d
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 7 deletions.
4 changes: 2 additions & 2 deletions lib/tesla/middleware.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 12 additions & 1 deletion lib/tesla/middleware/compression.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions lib/tesla/middleware/json.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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 || []
Expand All @@ -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 || []
Expand Down
7 changes: 7 additions & 0 deletions test/support/test_support.ex
Original file line number Diff line number Diff line change
@@ -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
16 changes: 12 additions & 4 deletions test/tesla/middleware/compression_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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}}
Expand All @@ -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
Expand All @@ -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}}
Expand All @@ -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
Expand Down

0 comments on commit dd9c39d

Please sign in to comment.