Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85627751
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Aug 8, 3:33 AM (8 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1722850
Default Alt Text
(9 KB)
Attached To
Mode
R20 majic
Attached
Detach File
Event Timeline
Log In to Comment