Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion lib/plug/router/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ defmodule Plug.Router.Utils do
including the known parameters.
"""
def build_path_clause(path, guard, context \\ nil) when is_binary(path) do
compiled = :binary.compile_pattern([":", "*"])
compiled = :binary.compile_pattern(["\\:", ":", "*"])

{params, match, guards, post_match} =
path
Expand All @@ -148,6 +148,31 @@ defmodule Plug.Router.Utils do
[] ->
build_path_clause(rest, params, [segment | match], guards, post_match, context, compiled)

[{prefix_size, match_length}] when match_length == 2 ->
suffix_size = byte_size(segment) - prefix_size - 2

<<prefix::binary-size(prefix_size), matched::binary-size(match_length),
suffix::binary-size(suffix_size)>> = segment

case matched do
"\\:" ->
escaped_segment = prefix <> ":" <> suffix

build_path_clause(
rest,
params,
[escaped_segment | match],
guards,
post_match,
context,
compiled
)

_ ->
raise Plug.Router.InvalidSpecError,
"the path segment contains invalid characters, got: " <> inspect(segment)
end

[{prefix_size, _}] ->
suffix_size = byte_size(segment) - prefix_size - 1
<<prefix::binary-size(prefix_size), char, suffix::binary-size(suffix_size)>> = segment
Expand Down
13 changes: 13 additions & 0 deletions test/plug/router/utils_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ defmodule Plug.Router.UtilsTest do
build_path_match("foo/bar:username")
end

test "build match with escaped identifiers" do
assert quote(@opts, do: {[], ["foo", ":id"]}) == build_path_match("/foo/\\:id")
assert quote(@opts, do: {[], ["foo", ":username"]}) == build_path_match("foo/\\:username")

assert quote(@opts, do: {[:id, :post_id], ["foo", id, ":name", post_id]}) ==
build_path_match("/foo/:id/\\:name/:post_id")

assert quote(@opts, do: {[], ["foo", "bar-:id"]}) == build_path_match("/foo/bar-\\:id")

assert quote(@opts, do: {[], ["foo", "bar:batchDelete"]}) ==
build_path_match("foo/bar\\:batchDelete")
end

test "build match only with glob" do
assert quote(@opts, do: {[:bar], bar}) == build_path_match("*bar")
assert quote(@opts, do: {[:glob], glob}) == build_path_match("/*glob")
Expand Down