Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete content-encoding header after decompressing #190

Closed
29 changes: 29 additions & 0 deletions lib/req/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,33 @@ defmodule Req.Response do
when is_binary(key) and is_binary(value) do
%{response | headers: List.keystore(response.headers, key, 0, {key, value})}
end

@doc """
Deletes the header given by `key`

All occurences of the header are delete, in case the header is repeated multiple times.

## Examples

iex> response.headers
[
{"cache-control", "max-age=600"},
{"content-type", "text/html"},
{"Cache-Control", "no-transform"}
]
iex> Req.Response.delete_header(response, "cache-control").headers
[{"content-type", "text/html"}]

"""
def delete_header(%Req.Response{} = response, key) when is_binary(key) do
%Req.Response{
response
| headers:
for(
{name, value} <- response.headers,
String.downcase(name) != String.downcase(key),
do: {name, value}
)
}
end
end
14 changes: 13 additions & 1 deletion lib/req/steps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,10 @@ defmodule Req.Steps do
| zstd | `:ezstd.decompress/1` (if [ezstd] is installed) |
| identity | Returns data as is |

This step updates the following headers to reflect the changes:
- `content-length` is set to the length of the decompressed body
- `content-encoding` is deleted

## Options

* `:raw` - if set to `true`, disables response body decompression. Defaults to `false`.
Expand Down Expand Up @@ -857,7 +861,15 @@ defmodule Req.Steps do

def decompress_body({request, response}) do
compression_algorithms = get_content_encoding_header(response.headers)
{request, update_in(response.body, &decompress_body(&1, compression_algorithms))}
decompressed_body = decompress_body(response.body, compression_algorithms)
decompressed_content_length = decompressed_body |> byte_size() |> to_string()

response =
%Req.Response{response | body: decompressed_body}
|> Req.Response.put_header("content-length", decompressed_content_length)
|> Req.Response.delete_header("content-encoding")

{request, response}
end

defp decompress_body(body, algorithms) do
Expand Down
2 changes: 1 addition & 1 deletion test/req/response_test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Req.ResponseTest do
use ExUnit.Case, async: true
doctest Req.Response, except: [get_header: 2, put_header: 3]
doctest Req.Response, except: [get_header: 2, put_header: 3, delete_header: 2]
end
28 changes: 28 additions & 0 deletions test/req/steps_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,34 @@ defmodule Req.StepsTest do

assert Req.get!(c.url).body == "foo"
end

test "recalculate content-length when decompressing", c do
body = "foo"
gzipped_body = :zlib.gzip(body)

assert byte_size(body) != byte_size(gzipped_body)

Bypass.expect(c.bypass, "GET", "/", fn conn ->
conn
|> Plug.Conn.put_resp_header("content-encoding", "x-gzip")
|> Plug.Conn.put_resp_header("content-length", gzipped_body |> byte_size() |> to_string())
|> Plug.Conn.send_resp(200, gzipped_body)
end)

response = Req.get!(c.url)
[content_length] = Req.Response.get_header(response, "content-length")
assert String.to_integer(content_length) == byte_size(body)
end

test "content-encoding header is deleted after decompress", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
conn
|> Plug.Conn.put_resp_header("content-encoding", "x-gzip")
|> Plug.Conn.send_resp(200, :zlib.gzip("foo"))
end)

refute List.keyfind(Req.get!(c.url).headers, "content-encoding", 0)
end
end

describe "output" do
Expand Down