Page MenuHomePhorge

No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/majic/plug.ex b/lib/majic/plug.ex
index 24f2395..5e28ae4 100644
--- a/lib/majic/plug.ex
+++ b/lib/majic/plug.ex
@@ -1,141 +1,115 @@
if Code.ensure_loaded?(Plug) do
defmodule Majic.PlugError do
defexception [:message]
end
defmodule Majic.Plug do
@moduledoc """
A `Plug` to automatically set the `content_type` of every `Plug.Upload`.
One of the required option of `pool`, `server` or `once` must be set.
Additional options:
* `fix_extension`, default false: enable use of `Majic.Extension`,
* options for `Majic.Extension`.
To use a majic pool:
```
plug Majic.Plug, pool: MyApp.MajicPool
```
To use a single majic server:
```
plug Majic.Plug, server: MyApp.MajicServer
```
To start a majic process at each file (not recommended):
```
plug Majic.Plug, once: true
```
"""
@behaviour Plug
@impl Plug
def init(opts) do
cond do
Keyword.has_key?(opts, :pool) -> true
Keyword.has_key?(opts, :server) -> true
Keyword.has_key?(opts, :once) -> true
true -> raise(Majic.PlugError, "No server/pool/once option defined")
end
opts
|> Keyword.put_new(:fix_extension, false)
|> Keyword.put_new(:append, false)
|> Keyword.put_new(:subtype_as_extension, false)
end
@impl Plug
def call(conn, opts) do
- collected = collect_uploads([], conn.body_params, [])
-
- Enum.reduce(collected, conn, fn {param_path, upload}, conn ->
- {array_index, param_path} =
- case param_path do
- [index, :array | path] ->
- {index, path}
+ body_params = transform_uploads(conn.body_params, opts)
+ params = merge_params(conn.query_params, body_params)
+ %{conn | body_params: body_params, params: params}
+ end
- path ->
- {nil, path}
- end
+ defp merge_params(query_params, body_params) when is_map(query_params) and is_map(body_params) do
+ Map.merge(query_params, body_params, fn _k, qv, bv ->
+ merge_values(qv, bv)
+ end)
+ end
- param_path = Enum.reverse(param_path)
+ defp merge_values(qv, bv) when is_map(qv) and is_map(bv) do
+ Map.merge(qv, bv, fn _k, qv2, bv2 -> merge_values(qv2, bv2) end)
+ end
- upload =
- case Majic.perform(upload.path, opts) do
- {:ok, magic} -> fix_upload(upload, magic, opts)
- {:error, error} -> raise(Majic.PlugError, "Failed to majic: #{inspect(error)}")
- end
+ defp merge_values(_qv, bv), do: bv
- conn
- |> put_in_if_exists(:params, param_path, upload, array_index)
- |> put_in_if_exists(:body_params, param_path, upload, array_index)
- end)
+ defp transform_uploads(params, opts) when is_map(params) do
+ Map.new(params, fn {k, v} -> {k, transform_upload_value(v, opts)} end)
end
- defp collect_uploads(path, params, acc) do
- Enum.reduce(params, acc, fn value, acc -> collect_upload(path, value, acc) end)
+ defp transform_uploads(params, opts) when is_list(params) do
+ Enum.map(params, &transform_upload_value(&1, opts))
end
- # An upload!
- defp collect_upload(path, {k, %{__struct__: Plug.Upload} = upload}, acc) do
- [{[k | path], upload} | acc]
+ defp transform_upload_value(%{__struct__: Plug.Upload} = upload, opts) do
+ case Majic.perform(upload.path, opts) do
+ {:ok, magic} -> fix_upload(upload, magic, opts)
+ {:error, error} -> raise(Majic.PlugError, "Failed to majic: #{inspect(error)}")
+ end
end
- # Ignore structs.
- defp collect_upload(_path, {_, %{__struct__: _}}, acc) do
- acc
+ defp transform_upload_value(%{__struct__: _} = struct, _opts) do
+ struct
end
- # Nested map.
- defp collect_upload(path, {k, v}, acc) when is_map(v) do
- collect_uploads([k | path], v, acc)
+ defp transform_upload_value(v, opts) when is_map(v) do
+ transform_uploads(v, opts)
end
- defp collect_upload(path, {k, v}, acc) when is_list(v) do
- Enum.reduce(Enum.with_index(v), acc, fn {item, index}, acc ->
- collect_upload([:array, k | path], {index, item}, acc)
- end)
+ defp transform_upload_value(v, opts) when is_list(v) do
+ transform_uploads(v, opts)
end
- defp collect_upload(_path, _, acc) do
- acc
+ defp transform_upload_value(v, _opts) do
+ v
end
defp fix_upload(upload, magic, opts) do
filename =
if Keyword.get(opts, :fix_extension) do
ext_opts = [
append: Keyword.get(opts, :append, false),
subtype_as_extension: Keyword.get(opts, :subtype_as_extension, false)
]
Majic.Extension.fix(upload.filename, magic, ext_opts)
end
%{upload | content_type: magic.mime_type, filename: filename || upload.filename}
end
-
- # put value at path in conn.
- defp put_in_if_exists(conn, key, path, value, nil) do
- if get_in(Map.get(conn, key), path) do
- Map.put(conn, key, put_in(Map.get(conn, key), path, value))
- else
- conn
- end
- end
-
- # change value at index in list at path in conn.
- defp put_in_if_exists(conn, key, path, value, index) do
- if array = get_in(Map.get(conn, key), path) do
- array = List.replace_at(array, index, value)
- Map.put(conn, key, put_in(Map.get(conn, key), path, array))
- else
- conn
- end
- end
end
end
diff --git a/test/majic/plug_test.exs b/test/majic/plug_test.exs
index 43b0ee1..11d3294 100644
--- a/test/majic/plug_test.exs
+++ b/test/majic/plug_test.exs
@@ -1,112 +1,111 @@
defmodule Majic.PlugTest do
use ExUnit.Case, async: true
use Plug.Test
defmodule TestRouter do
use Plug.Router
plug(:match)
plug(:dispatch)
plug(Plug.Parsers,
parsers: [:urlencoded, :multipart],
pass: ["*/*"]
)
# plug Majic.Plug, once: true
post "/" do
send_resp(conn, 200, "Ok")
end
end
setup_all do
Application.ensure_all_started(:plug)
:ok
end
@router_opts TestRouter.init([])
- @tag skip: true
test "convert uploads" do
multipart = """
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"form[makefile]\"; filename*=\"utf-8''mymakefile.txt\"\r
Content-Type: text/plain\r
\r
#{File.read!("Makefile")}\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"form[make][file]\"; filename*=\"utf-8''mymakefile.txt\"\r
Content-Type: text/plain\r
\r
#{File.read!("Makefile")}\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"cat\"; filename*=\"utf-8''cute-cat.jpg\"\r
Content-Type: image/jpg\r
\r
#{File.read!("test/fixtures/cat.webp")}\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"cats[]\"; filename*=\"utf-8''first-cute-cat.jpg\"\r
Content-Type: image/jpg\r
\r
#{File.read!("test/fixtures/cat.webp")}\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"cats[]\"\r
\r
hello i am annoying
\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"cats[]\"; filename*=\"utf-8''second-cute-cat.jpg\"\r
Content-Type: image/jpg\r
\r
#{File.read!("test/fixtures/cat.webp")}\r
------w58EW1cEpjzydSCq\r
Content-Disposition: form-data; name=\"cats[][inception][cat]\"; filename*=\"utf-8''third-cute-cat.jpg\"\r
Content-Type: image/jpg\r
\r
#{File.read!("test/fixtures/cat.webp")}\r
------w58EW1cEpjzydSCq--\r
"""
orig_conn =
conn(:post, "/", multipart)
|> put_req_header("content-type", "multipart/mixed; boundary=----w58EW1cEpjzydSCq")
|> TestRouter.call(@router_opts)
- plug = Majic.Plug.init(once: true)
- plug_no_ext = Majic.Plug.init(once: true, fix_extension: false)
- plug_append_ext = Majic.Plug.init(once: true, fix_extension: true, append: true)
+ plug = Majic.Plug.init(once: true, fix_extension: true, startup_timeout: 5000)
+ plug_no_ext = Majic.Plug.init(once: true, fix_extension: false, startup_timeout: 5000)
+ plug_append_ext = Majic.Plug.init(once: true, fix_extension: true, append: true, startup_timeout: 5000)
conn = Majic.Plug.call(orig_conn, plug)
conn_no_ext = Majic.Plug.call(orig_conn, plug_no_ext)
conn_append_ext = Majic.Plug.call(orig_conn, plug_append_ext)
assert conn.state == :sent
assert conn.status == 200
assert get_in(conn.body_params, ["form", "makefile"]) ==
get_in(conn.params, ["form", "makefile"])
assert get_in(conn.params, ["form", "makefile"]).content_type == "text/x-makefile"
assert get_in(conn.params, ["form", "makefile"]).filename == "mymakefile"
assert get_in(conn_no_ext.params, ["form", "makefile"]).filename == "mymakefile.txt"
assert get_in(conn_append_ext.params, ["form", "makefile"]).filename == "mymakefile"
assert get_in(conn.body_params, ["form", "make", "file"]) ==
get_in(conn.params, ["form", "make", "file"])
assert get_in(conn.params, ["form", "make", "file"]).content_type == "text/x-makefile"
assert get_in(conn.body_params, ["cat"]) == get_in(conn.params, ["cat"])
assert get_in(conn.params, ["cat"]).content_type == "image/webp"
assert get_in(conn.params, ["cat"]).filename == "cute-cat.webp"
assert get_in(conn_no_ext.params, ["cat"]).filename == "cute-cat.jpg"
assert get_in(conn_append_ext.params, ["cat"]).filename == "cute-cat.jpg.webp"
assert Enum.all?(conn.params["cats"], fn
%Plug.Upload{} = upload -> upload.content_type == "image/webp"
%{"inception" => %{"cat" => upload}} -> upload.content_type == "image/webp"
_ -> true
end)
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sun, Aug 9, 10:45 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1722850
Default Alt Text
(9 KB)

Event Timeline