Skip to content

Commit

Permalink
decompress_body: Support multiple content-encoding headers
Browse files Browse the repository at this point in the history
Closes #191
  • Loading branch information
wojtekmach committed Jun 19, 2023
1 parent ce94146 commit 778d58a
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 40 deletions.
23 changes: 8 additions & 15 deletions lib/req/steps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1487,25 +1487,18 @@ defmodule Req.Steps do
## Utilities

defp get_content_encoding_header(headers) do
if value = get_header(headers, "content-encoding") do
value
|> String.downcase()
|> String.split(",", trim: true)
|> Stream.map(&String.trim/1)
|> Enum.reverse()
else
[]
end
end

defp get_header(headers, name) do
Enum.find_value(headers, nil, fn {key, value} ->
if String.downcase(key) == name do
headers
|> Enum.flat_map(fn {name, value} ->
if String.downcase(name) == "content-encoding" do
value
|> String.downcase()
|> String.split(",", trim: true)
|> Stream.map(&String.trim/1)
else
nil
[]
end
end)
|> Enum.reverse()
end

defp cache_path(cache_dir, request) do
Expand Down
87 changes: 62 additions & 25 deletions test/req/steps_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -246,37 +246,74 @@ defmodule Req.StepsTest do

## Response steps

test "decompress_body: gzip", 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)
describe "decompress_body" do
test "gzip", 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)

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

test "decompress_body: brotli", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
{:ok, body} = :brotli.encode("foo")
test "multiple codecs", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
conn
|> Plug.Conn.put_resp_header("content-encoding", "gzip, deflate")
|> Plug.Conn.send_resp(200, "foo" |> :zlib.gzip() |> :zlib.zip())
end)

conn
|> Plug.Conn.put_resp_header("content-encoding", "br")
|> Plug.Conn.send_resp(200, body)
end)
assert Req.get!(c.url).body == "foo"
end

assert Req.get!(c.url).body == "foo"
end
test "multiple codecs with multiple headers" do
{:ok, listen_socket} = :gen_tcp.listen(0, mode: :binary, active: false)
{:ok, port} = :inet.port(listen_socket)

@tag :capture_log
test "decompress_body: zstd", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
conn
|> Plug.Conn.put_resp_header("content-encoding", "zstd")
|> Plug.Conn.send_resp(200, :ezstd.compress("foo"))
end)
Task.start_link(fn ->
{:ok, socket} = :gen_tcp.accept(listen_socket)
assert {:ok, "GET / HTTP/1.1\r\n" <> _} = :gen_tcp.recv(socket, 0)

assert Req.get!(c.url).body == "foo"
body = "foo" |> :zlib.gzip() |> :zlib.zip()

data = """
HTTP/1.1 200 OK
content-encoding: gzip

This comment has been minimized.

Copy link
@tanguilp

tanguilp Jun 19, 2023

Contributor

I think we could use prepend_resp_headers/2 here instead of launching a web server :)

This comment has been minimized.

Copy link
@wojtekmach

wojtekmach Jun 19, 2023

Author Owner

TIL, thanks!

content-encoding: deflate
content-length: #{byte_size(body)}
#{body}
"""

:ok = :gen_tcp.send(socket, data)
end)

assert Req.get!("http://localhost:#{port}").body == "foo"
end

test "brotli", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
{:ok, body} = :brotli.encode("foo")

conn
|> Plug.Conn.put_resp_header("content-encoding", "br")
|> Plug.Conn.send_resp(200, body)
end)

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

@tag :capture_log
test "zstd", c do
Bypass.expect(c.bypass, "GET", "/", fn conn ->
conn
|> Plug.Conn.put_resp_header("content-encoding", "zstd")
|> Plug.Conn.send_resp(200, :ezstd.compress("foo"))
end)

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

@tag :tmp_dir
Expand Down

0 comments on commit 778d58a

Please sign in to comment.