Packages
plug
1.16.1
1.20.3
1.20.2
1.20.1
retired
1.20.0
retired
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.6
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
retired
1.13.0
retired
1.12.1
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
Compose web applications with functions
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/plug/parsers/json.ex
defmodule Plug.Parsers.JSON do
@moduledoc """
Parses JSON request body.
JSON documents that aren't maps (arrays, strings, numbers, etc) are parsed
into a `"_json"` key to allow proper param merging.
An empty request body is parsed as an empty map.
## Options
All options supported by `Plug.Conn.read_body/2` are also supported here.
They are repeated here for convenience:
* `:length` - sets the maximum number of bytes to read from the request,
defaults to 8_000_000 bytes
* `:read_length` - sets the amount of bytes to read at one time from the
underlying socket to fill the chunk, defaults to 1_000_000 bytes
* `:read_timeout` - sets the timeout for each socket read, defaults to
15_000ms
So by default, `Plug.Parsers` will read 1_000_000 bytes at a time from the
socket with an overall limit of 8_000_000 bytes.
The option `:nest_all_json`, when true, specifies all parsed JSON (including maps)
are parsed into a `"_json"` key.
"""
@behaviour Plug.Parsers
@impl true
def init(opts) do
{decoder, opts} = Keyword.pop(opts, :json_decoder)
{body_reader, opts} = Keyword.pop(opts, :body_reader, {Plug.Conn, :read_body, []})
decoder = validate_decoder!(decoder)
{body_reader, decoder, opts}
end
defp validate_decoder!(nil) do
raise ArgumentError, "JSON parser expects a :json_decoder option"
end
defp validate_decoder!({module, fun, args} = mfa)
when is_atom(module) and is_atom(fun) and is_list(args) do
arity = length(args) + 1
if Code.ensure_compiled(module) != {:module, module} do
raise ArgumentError,
"invalid :json_decoder option. The module #{inspect(module)} is not " <>
"loaded and could not be found"
end
if not function_exported?(module, fun, arity) do
raise ArgumentError,
"invalid :json_decoder option. The module #{inspect(module)} must " <>
"implement #{fun}/#{arity}"
end
mfa
end
defp validate_decoder!(decoder) when is_atom(decoder) do
validate_decoder!({decoder, :decode!, []})
end
defp validate_decoder!(decoder) do
raise ArgumentError,
"the :json_decoder option expects a module, or a three-element " <>
"tuple in the form of {module, function, extra_args}, got: #{inspect(decoder)}"
end
@impl true
def parse(conn, "application", subtype, _headers, {{mod, fun, args}, decoder, opts}) do
if subtype == "json" or String.ends_with?(subtype, "+json") do
apply(mod, fun, [conn, opts | args]) |> decode(decoder, opts)
else
{:next, conn}
end
end
def parse(conn, _type, _subtype, _headers, _opts) do
{:next, conn}
end
defp decode({:ok, "", conn}, _decoder, _opts) do
{:ok, %{}, conn}
end
defp decode({:ok, body, conn}, {module, fun, args}, opts) do
nest_all = Keyword.get(opts, :nest_all_json, false)
try do
apply(module, fun, [body | args])
rescue
e -> raise Plug.Parsers.ParseError, exception: e
else
terms when is_map(terms) and not nest_all ->
{:ok, terms, conn}
terms ->
{:ok, %{"_json" => terms}, conn}
end
end
defp decode({:more, _, conn}, _decoder, _opts) do
{:error, :too_large, conn}
end
defp decode({:error, :timeout}, _decoder, _opts) do
raise Plug.TimeoutError
end
defp decode({:error, _}, _decoder, _opts) do
raise Plug.BadRequestError
end
end