Current section
Files
Jump to
Current section
Files
lib/aws_secrets_manager/url.ex
defmodule ExSecrets.AwsSecretsManager.Request.Url do
@moduledoc false
@doc """
Builds URL for an operation and a config"
"""
def build(operation, config) do
config
|> Map.take([:scheme, :host, :port])
|> Map.put(:query, query(operation))
|> Map.put(:path, operation.path)
|> normalize_scheme
|> normalize_path(config.normalize_path)
|> convert_port_to_integer
|> (&struct(URI, &1)).()
|> URI.to_string()
|> String.trim_trailing("?")
end
defp query(operation) do
operation
|> Map.get(:params, %{})
|> normalize_params
|> URI.encode_query()
end
defp normalize_scheme(url) do
url |> Map.update(:scheme, "", &String.replace(&1, "://", ""))
end
defp normalize_path(url, false), do: url
defp normalize_path(url, _normalize) do
url |> Map.update(:path, "", &String.replace(&1, ~r/\/{2,}/, "/"))
end
defp convert_port_to_integer(url = %{port: port}) when is_binary(port) do
{port, _} = Integer.parse(port)
put_in(url[:port], port)
end
defp convert_port_to_integer(url), do: url
defp normalize_params(params) when is_map(params) do
params |> Map.delete("") |> Map.delete(nil)
end
defp normalize_params(params), do: params
def sanitize(url, service) when service in ["s3", :s3] do
new_path =
url
|> get_path(service)
|> String.replace_prefix("/", "")
|> uri_encode()
query =
case String.split(url, "?", parts: 2) do
[_] -> nil
[_, ""] -> nil
[_, q] -> q
end
url
|> URI.parse()
|> Map.put(:fragment, nil)
|> Map.put(:path, "/" <> new_path)
|> Map.put(:query, query)
|> URI.to_string()
|> String.replace("+", "%20")
end
def sanitize(url, _), do: String.replace(url, "+", "%20")
def get_path(url, service \\ nil)
# Elixir's URI.parse will treat everything after the # sign
# as a fragment. This is correct, but S3 treats it as part
# of the path of the object.
#
# This will split the URL at the base with the right side
# being the path, except for the query params.
#
# for example:
# `"https://bucket.aws.com/my/path/here+ #3.txt?t=21"`
# https://bucket.aws.com | /my/path/here+ #3.txt?t=21
# ________base__________ | /my/path/here+ #3.txt | t=21
# ________base__________ | /my/path/here+ #3.txt | _params_
#
# After using `uri_encode` it will be `/my/path/here%2B%20%233.txt`
#
def get_path(url, service) when service in ["s3", :s3] do
base =
url
|> URI.parse()
|> Map.put(:path, nil)
|> Map.put(:query, nil)
|> Map.put(:fragment, nil)
|> URI.to_string()
[_base, path_with_params] = String.split(url, base, parts: 2)
[path | _query_params] = String.split(path_with_params, "?", parts: 2)
path
end
def get_path(url, _), do: URI.parse(url).path || "/"
def uri_encode(url), do: URI.encode(url, &valid_path_char?/1)
# Space character
defp valid_path_char?(?\s), do: false
defp valid_path_char?(?/), do: true
defp valid_path_char?(c) do
URI.char_unescaped?(c) && !URI.char_reserved?(c)
end
end