Repository: derekkraan/curl_req
Pull request #45 (2025-01-26 22:48:01)

handle header list correctly
As stated in https://www.rfc-editor.org/rfc/rfc7230#section-3.2.4 we cannot just split the header at `;` because they all have different rules. That's on me, because i interpreted the Req header list as such. But this is only to concatenate values with `,` when the key is the same as specified here: https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2

To parse the cookies i brought in the `plug` dependency. We could skip this if the wouldn't store the cookies in the internal `CurlReq.Request` struct. What are your thought on this?

Edit: I vendored in the code to decode cookies, because it's pretty small and maintanable


Patch:
diff --git a/README.md b/README.md
index 7f07ba6..4b46e9c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# 🥌 CurlReq
+# 🥌 CurlReq  
 
 🥌 🥌 🥌 🥌 🥌 🥌
 
@@ -17,13 +17,12 @@ And you'll have the full curl command.
 ## Usage
 
 ### Req to Curl
-
 ```elixir
 # Turn a Req request into a `curl` command.
 
 iex> Req.new(url: "/fact", base_url: "https://example.com/")
 ...> |> CurlReq.to_curl()
-"curl --compressed -X GET https://example.com/fact"
+"curl --compressed -X GET https://example.com/fact" 
 
 # Or use `CurlReq.inspect/2` to inspect inline.
 
@@ -45,7 +44,6 @@ iex> import CurlReq
 ```
 
 or use `CurlReq.from_curl/1`:
-
 ```elixir
 iex> CurlReq.from_curl("curl https://example.com")
 ...> # |> Req.request!()
@@ -69,40 +67,27 @@ CurlReq parses a bunch of cURL flags and translates them to Req.Request structs
 
 ### Supported Flags
 
-The following flags are supported in all directions (from Req, from cURL, to Req, to cURL)
-
-| Long           | Short | Limitation                     |
-| -------------- | ----- | ------------------------------ |
-| `--header`     | `-H`  |                                |
-| `--request`    | `-X`  |                                |
-| `--data`       | `-d`  | No file interpolation with `@` |
-| `--data_raw`   |       | No file interpolation with `@` |
-| `--data_ascii` |       | No file interpolation with `@` |
-| `--cookie`     | `-b`  |                                |
-| `--head`       | `-I`  |                                |
-| `--form`       | `-F`  |                                |
-| `--location`   | `-L`  |                                |
-| `--user`       | `-u`  | Only as basic auth             |
-| `--compressed` |       |                                |
-| `--proxy`      | `-x`  |                                |
-| `--proxy_user` | `-U`  | Only as basic auth             |
-| `--netrc`      | `-n`  |                                |
-| `--netrc_file` |       |                                |
-| `--insecure`   | `-k`  |                                |
-| `--user_agent` | `-A`  |                                |
-
-### Ignored flags
-
-The following flags are currently ignored because they mostly describe the runtime behaviour and not the request itself.
-
-| Long            | Short |
-| --------------- | ----- |
-| `--verbose`     | `-v`  |
-| `--output`      | `-o`  |
-| `--remote_name` | `-O`  |
-| `--show-error`  | `-S`  |
-| `--silent`      | `-s`  |
-| `--fail`        | `-f`  |
+The follwing flags are supported in all directions (from Req, from cURL, to Req, to cURL)
+
+| Long         | Short | Limitation |
+| ---          | --- | --- |
+| `--header`     | `-H` | |
+| `--request`    | `-X` | |
+| `--data`       | `-d` |  No file interpolation with `@`|
+| `--data_raw`   |      | No file interpolation with `@`|
+| `--data_ascii` |      | No file interpolation with `@`|
+| `--cookie`     | `-b` | |
+| `--head`       | `-I` | |
+| `--form`       | `-F` | |
+| `--location`   | `-L` | |
+| `--user`       | `-u` |  Only as basic auth |
+| `--compressed` |      | |
+| `--proxy`      | `-x` | |
+| `--proxy_user` | `-U` |  Only as basic auth |
+| `--netrc`      | `-n` | |
+| `--netrc_file` |      | |
+| `--insecure`   | `-k` | |
+| `--user_agent` | `-A` | |
 
 ## Installation
 
diff --git a/blend/req_0_4.mix.lock b/blend/req_0_4.mix.lock
index b23b0c9..d433178 100644
--- a/blend/req_0_4.mix.lock
+++ b/blend/req_0_4.mix.lock
@@ -1,6 +1,6 @@
 %{
   "blend": {:hex, :blend, "0.4.1", "d81d39a1b98dddc91dd465d0d6381bc5029c02a9c2018a568f62bb55efd1f64e", [:mix], [], "hexpm", "75c9f391456fa648e973e85c86e5bb4ea9bb1d3750b7b71967e0662e837001ab"},
-  "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"},
+  "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
   "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"},
   "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
   "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"},
@@ -12,7 +12,7 @@
   "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
   "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
   "nimble_ownership": {:hex, :nimble_ownership, "0.3.2", "d4fa4056ade0ae33b5a9eb64554a1b3779689282e37513260125d2d6b32e4874", [:mix], [], "hexpm", "28b9a9f4094fda1aa8ca72f732ff3223eb54aa3eda4fed9022254de2c152b138"},
-  "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
+  "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
   "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
   "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
   "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
diff --git a/lib/curl_req/cookies.ex b/lib/curl_req/cookies.ex
new file mode 100644
index 0000000..50bace4
--- /dev/null
+++ b/lib/curl_req/cookies.ex
@@ -0,0 +1,56 @@
+defmodule CurlReq.Cookies do
+  @moduledoc false
+
+  # Vendored from https://github.com/elixir-plug/plug/blob/main/lib/plug/conn/cookies.ex
+
+  @doc """
+  Decodes the given cookies as given in either a request or response header.
+
+  If a cookie is invalid, it is automatically discarded from the result.
+
+  ## Examples
+
+      iex> decode("key1=value1;key2=value2")
+      %{"key1" => "value1", "key2" => "value2"}
+
+  """
+  def decode(cookie) when is_binary(cookie) do
+    Map.new(decode_kv(cookie, []))
+  end
+
+  defp decode_kv("", acc), do: acc
+  defp decode_kv(<<h, t::binary>>, acc) when h in [?\s, ?\t], do: decode_kv(t, acc)
+  defp decode_kv(kv, acc) when is_binary(kv), do: decode_key(kv, "", acc)
+
+  defp decode_key(<<h, t::binary>>, _key, acc) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f],
+    do: skip_until_cc(t, acc)
+
+  defp decode_key(<<?;, t::binary>>, _key, acc), do: decode_kv(t, acc)
+  defp decode_key(<<?=, t::binary>>, "", acc), do: skip_until_cc(t, acc)
+  defp decode_key(<<?=, t::binary>>, key, acc), do: decode_value(t, "", 0, key, acc)
+  defp decode_key(<<h, t::binary>>, key, acc), do: decode_key(t, <<key::binary, h>>, acc)
+  defp decode_key(<<>>, _key, acc), do: acc
+
+  defp decode_value(<<?;, t::binary>>, value, spaces, key, acc),
+    do: decode_kv(t, [{key, trim_spaces(value, spaces)} | acc])
+
+  defp decode_value(<<?\s, t::binary>>, value, spaces, key, acc),
+    do: decode_value(t, <<value::binary, ?\s>>, spaces + 1, key, acc)
+
+  defp decode_value(<<h, t::binary>>, _value, _spaces, _key, acc)
+       when h in [?\t, ?\r, ?\n, ?\v, ?\f],
+       do: skip_until_cc(t, acc)
+
+  defp decode_value(<<h, t::binary>>, value, _spaces, key, acc),
+    do: decode_value(t, <<value::binary, h>>, 0, key, acc)
+
+  defp decode_value(<<>>, value, spaces, key, acc),
+    do: [{key, trim_spaces(value, spaces)} | acc]
+
+  defp skip_until_cc(<<?;, t::binary>>, acc), do: decode_kv(t, acc)
+  defp skip_until_cc(<<_, t::binary>>, acc), do: skip_until_cc(t, acc)
+  defp skip_until_cc(<<>>, acc), do: acc
+
+  defp trim_spaces(value, 0), do: value
+  defp trim_spaces(value, spaces), do: binary_part(value, 0, byte_size(value) - spaces)
+end
diff --git a/lib/curl_req/curl.ex b/lib/curl_req/curl.ex
index 92f0892..7b13f11 100644
--- a/lib/curl_req/curl.ex
+++ b/lib/curl_req/curl.ex
@@ -288,7 +288,7 @@ defmodule CurlReq.Curl do
     headers =
       for {key, values} <- request.headers, reduce: [] do
         headers ->
-          [headers, header_flag(flag_style, [key, ": ", Enum.intersperse(values, "; ")])]
+          [headers, header_flag(flag_style, [key, ": ", Enum.intersperse(values, ", ")])]
       end
 
     headers =
diff --git a/lib/curl_req/req.ex b/lib/curl_req/req.ex
index 3db24f7..700d3dc 100644
--- a/lib/curl_req/req.ex
+++ b/lib/curl_req/req.ex
@@ -149,10 +149,11 @@ defmodule CurlReq.Req do
     cookies =
       request.cookies
       |> Enum.map(fn {key, val} ->
-        IO.iodata_to_binary([key, "=", val])
+        "#{key}=#{val}"
       end)
+      |> Enum.join("; ")
 
-    req = if cookies != [], do: Req.Request.put_header(req, "cookie", cookies), else: req
+    req = if request.cookies != %{}, do: Req.Request.put_header(req, "cookie", cookies), else: req
 
     proxy =
       if request.proxy do
diff --git a/lib/curl_req/request.ex b/lib/curl_req/request.ex
index 38e68e3..2fab8de 100644
--- a/lib/curl_req/request.ex
+++ b/lib/curl_req/request.ex
@@ -73,42 +73,42 @@ defmodule CurlReq.Request do
   @spec put_header(__MODULE__.t(), String.t(), String.t() | [String.t()]) :: __MODULE__.t()
   def put_header(%__MODULE__{} = request, key, val) when is_binary(val) do
     key = String.downcase(key) |> String.trim()
-    val = String.split(val, ";", trim: true) |> Enum.map(&String.trim/1)
-    put_header(request, key, val)
+    val = String.trim(val)
+    put_header(request, key, [val])
   end
 
   def put_header(%__MODULE__{} = request, key, val) when is_list(val) do
     key = String.downcase(key) |> String.trim()
 
     case {key, val} do
-      {"authorization", ["Bearer " <> token | _]} ->
+      {"authorization", ["Bearer " <> token]} ->
         %{request | auth: {:bearer, token}}
 
-      {"authorization", ["Basic " <> userinfo | _]} ->
+      {"authorization", ["Basic " <> userinfo]} ->
         %{request | auth: {:basic, userinfo}}
 
       {"accept-encoding", [compression | _]}
       when compression in ["gzip", "br", "zstd"] ->
         put_compression(request, String.to_existing_atom(compression))
 
-      {"content-type", ["application/json" | _]} ->
+      {"content-type", ["application/json" <> _]} ->
         %{request | encoding: :json}
 
-      {"content-type", ["application/vnd.api+json" | _]} ->
+      {"content-type", ["application/vnd.api+json" <> _]} ->
         %{request | encoding: :json}
 
-      {"content-type", ["application/x-www-form-urlencoded" | _]} ->
+      {"content-type", ["application/x-www-form-urlencoded" <> _]} ->
         %{request | encoding: :form}
 
-      {"user-agent", [user_agent | _]} ->
+      {"user-agent", [user_agent]} ->
         put_user_agent(request, user_agent)
 
-      {"cookie", cookies} ->
-        for cookie <- cookies, reduce: request do
-          request ->
-            [key, value] = String.split(cookie, "=")
-            put_cookie(request, key, value)
-        end
+      {"cookie", [cookie]} ->
+        cookie
+        |> CurlReq.Cookies.decode()
+        |> Enum.reduce(request, fn {key, val}, request ->
+          put_cookie(request, key, val)
+        end)
 
       {key, val} ->
         headers = Map.put(request.headers, key, val)
diff --git a/mix.lock b/mix.lock
index 3c2ddd4..57864a4 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,6 +1,6 @@
 %{
   "blend": {:hex, :blend, "0.4.1", "d81d39a1b98dddc91dd465d0d6381bc5029c02a9c2018a568f62bb55efd1f64e", [:mix], [], "hexpm", "75c9f391456fa648e973e85c86e5bb4ea9bb1d3750b7b71967e0662e837001ab"},
-  "earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"},
+  "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
   "ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"},
   "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
   "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"},
@@ -11,7 +11,7 @@
   "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
   "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
   "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
-  "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
+  "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
   "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
   "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"},
   "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},


Test patch:
diff --git a/test/curl_req_test.exs b/test/curl_req_test.exs
index 1529806..aab1444 100644
--- a/test/curl_req_test.exs
+++ b/test/curl_req_test.exs
@@ -90,7 +90,7 @@ defmodule CurlReqTest do
     end
 
     test "parameterized header" do
-      assert ~s(curl --compressed -H "my-header: foo; bar=baz" -X GET https://example.com) ==
+      assert ~s(curl --compressed -H "my-header: foo, bar=baz" -X GET https://example.com) ==
                Req.new(url: "https://example.com", headers: %{"my-header" => ["foo", "bar=baz"]})
                |> CurlReq.to_curl()
     end
@@ -340,11 +340,11 @@ defmodule CurlReqTest do
                }
     end
 
-    test "header with parameter" do
-      assert ~CURL(curl -H "my-header: foo; bar=baz" https://example.com) ==
+    test "multiple header" do
+      assert ~CURL(curl -H "my-header: foo, bar=baz" https://example.com) ==
                %Req.Request{
                  url: URI.parse("https://example.com"),
-                 headers: %{"my-header" => ["foo", "bar=baz"]}
+                 headers: %{"my-header" => ["foo, bar=baz"]}
                }
     end
 
@@ -352,12 +352,12 @@ defmodule CurlReqTest do
       request =
         ~CURL(curl --header 'Cookie: TealeafAkaSid=JA-JSAXRCLjKYhjV9IXTzYUbcV1Lnhqf; sapphire=1; visitorId=0184E4601D5A020183FFBB133 80347CE; GuestLocation=33196|25.660|-80.440|FL|US' -X GET https://example.com)
 
-      cookies = request.headers["cookie"]
+      [cookie] = request.headers["cookie"]
 
-      assert "TealeafAkaSid=JA-JSAXRCLjKYhjV9IXTzYUbcV1Lnhqf" in cookies
-      assert "sapphire=1" in cookies
-      assert "visitorId=0184E4601D5A020183FFBB133 80347CE" in cookies
-      assert "GuestLocation=33196|25.660|-80.440|FL|US" in cookies
+      assert String.contains?(cookie, "TealeafAkaSid=JA-JSAXRCLjKYhjV9IXTzYUbcV1Lnhqf")
+      assert String.contains?(cookie, "sapphire=1")
+      assert String.contains?(cookie, "visitorId=0184E4601D5A020183FFBB133 80347CE")
+      assert String.contains?(cookie, "GuestLocation=33196|25.660|-80.440|FL|US")
     end
 
     test "multiple headers with body" do
@@ -417,7 +417,7 @@ defmodule CurlReqTest do
       assert ~CURL(http://example.com -b "name1=value1; name2=value2") ==
                %Req.Request{
                  url: URI.parse("http://example.com"),
-                 headers: %{"cookie" => ["name1=value1", "name2=value2"]}
+                 headers: %{"cookie" => ["name1=value1; name2=value2"]}
                }
     end