Packages
plug
1.1.3
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/router/utils.ex
defmodule Plug.Router.InvalidSpecError do
defexception message: "invalid route specification"
end
defmodule Plug.Router.Utils do
@moduledoc false
@doc """
Converts a given method to its connection representation.
The request method is stored in the `Plug.Conn` struct as an uppercase string
(like `"GET"` or `"POST"`). This function converts `method` to that
representation.
## Examples
iex> Plug.Router.Utils.normalize_method(:get)
"GET"
"""
def normalize_method(method) do
method |> to_string |> String.upcase
end
@doc ~S"""
Builds the pattern that will be used to match against the request's host
(provided via the `:host`) option.
If `host` is `nil`, a wildcard match (`_`) will be returned. If `host` ends
with a dot, a match like `"host." <> _` will be returned.
## Examples
iex> Plug.Router.Utils.build_host_match(nil)
{:_, [], Plug.Router.Utils}
iex> Plug.Router.Utils.build_host_match("foo.com")
"foo.com"
iex> Plug.Router.Utils.build_host_match("api.") |> Macro.to_string
"\"api.\" <> _"
"""
def build_host_match(host) do
cond do
is_nil host -> quote do: _
String.last(host) == "." -> quote do: unquote(host) <> _
is_binary host -> host
end
end
@doc """
Generates a representation that will only match routes
according to the given `spec`.
If a non-binary spec is given, it is assumed to be
custom match arguments and they are simply returned.
## Examples
iex> Plug.Router.Utils.build_path_match("/foo/:id")
{[:id], ["foo", {:id, [], nil}]}
"""
def build_path_match(spec, context \\ nil) when is_binary(spec) do
build_path_match split(spec), context, [], []
end
@doc """
Forwards requests to another Plug at a new path.
"""
def forward(%Plug.Conn{path_info: path, script_name: script} = conn, new_path, target, opts) do
{base, ^new_path} = Enum.split(path, length(path) - length(new_path))
conn = %{conn | path_info: new_path, script_name: script ++ base} |> target.call(opts)
%{conn | path_info: path, script_name: script}
end
@doc """
Splits the given path into several segments.
It ignores both leading and trailing slashes in the path.
## Examples
iex> Plug.Router.Utils.split("/foo/bar")
["foo", "bar"]
iex> Plug.Router.Utils.split("/:id/*")
[":id", "*"]
iex> Plug.Router.Utils.split("/foo//*_bar")
["foo", "*_bar"]
"""
def split(bin) do
for segment <- String.split(bin, "/"), segment != "", do: segment
end
## Helpers
# Loops each segment checking for matches.
defp build_path_match([h|t], context, vars, acc) do
handle_segment_match segment_match(h, "", context), t, context, vars, acc
end
defp build_path_match([], _context, vars, acc) do
{vars |> Enum.uniq |> Enum.reverse, Enum.reverse(acc)}
end
# Handle each segment match. They can either be a
# :literal ("foo"), an :identifier (":bar") or a :glob ("*path")
defp handle_segment_match({:literal, literal}, t, context, vars, acc) do
build_path_match t, context, vars, [literal|acc]
end
defp handle_segment_match({:identifier, identifier, expr}, t, context, vars, acc) do
build_path_match t, context, [identifier|vars], [expr|acc]
end
defp handle_segment_match({:glob, _identifier, _expr}, t, _context, _vars, _acc) when t != [] do
raise Plug.Router.InvalidSpecError,
message: "cannot have a *glob followed by other segments"
end
defp handle_segment_match({:glob, identifier, expr}, _t, context, vars, [hs|ts]) do
acc = [{:|, [], [hs, expr]} | ts]
build_path_match([], context, [identifier|vars], acc)
end
defp handle_segment_match({:glob, identifier, expr}, _t, context, vars, _) do
{vars, expr} = build_path_match([], context, [identifier|vars], [expr])
{vars, hd(expr)}
end
# In a given segment, checks if there is a match.
defp segment_match(":" <> argument, buffer, context) do
identifier = binary_to_identifier(":", argument)
expr = quote_if_buffer identifier, buffer, context, fn var ->
quote do: unquote(buffer) <> unquote(var)
end
{:identifier, identifier, expr}
end
defp segment_match("*" <> argument, buffer, context) do
underscore = {:_, [], context}
identifier = binary_to_identifier("*", argument)
expr = quote_if_buffer identifier, buffer, context, fn var ->
quote do: [unquote(buffer) <> unquote(underscore)|unquote(underscore)] = unquote(var)
end
{:glob, identifier, expr}
end
defp segment_match(<<h, t::binary>>, buffer, context) do
segment_match t, buffer <> <<h>>, context
end
defp segment_match(<<>>, buffer, _context) do
{:literal, buffer}
end
defp quote_if_buffer(identifier, "", context, _fun) do
{identifier, [], context}
end
defp quote_if_buffer(identifier, _buffer, context, fun) do
fun.({identifier, [], context})
end
defp binary_to_identifier(prefix, <<letter, _::binary>> = binary)
when letter in ?a..?z or letter == ?_ do
if binary =~ ~r/^\w+$/ do
String.to_atom(binary)
else
raise Plug.Router.InvalidSpecError,
message: "#{prefix}identifier in routes must be made of letters, numbers and underscores"
end
end
defp binary_to_identifier(prefix, _) do
raise Plug.Router.InvalidSpecError,
message: "#{prefix} in routes must be followed by lowercase letters or underscore"
end
end