Packages
ex_aws
0.5.0
2.7.0
2.6.1
2.6.0
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.1
1.0.0-beta3
1.0.0-beta2
1.0.0-beta1
1.0.0-beta0
0.5.0
0.4.19
0.4.18
0.4.17
0.4.15
0.4.14
0.4.13
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.2
0.1.1
0.1.0
0.0.5
0.0.4
0.0.3
AWS client for Elixir. Currently supports Dynamo, DynamoStreams, EC2, Firehose, Kinesis, KMS, Lambda, RRDS, Route53, S3, SES, SNS, SQS, STS and others.
Current section
Files
Jump to
Current section
Files
lib/ex_aws/s3/impl.ex
defmodule ExAws.S3.Impl do
import ExAws.S3.Utils
alias ExAws.S3.Parsers
@moduledoc false
# Implementation of the AWS S3 API.
#
# See ExAws.S3.Client for usage.
## Buckets
#############
defdelegate stream_objects!(client, bucket), to: ExAws.S3.Lazy
defdelegate stream_objects!(client, bucket, opts), to: ExAws.S3.Lazy
def list_buckets(client, opts \\ []) do
request(client, :get, "", "/", params: opts)
end
def delete_bucket(client, bucket) do
request(client, :delete, bucket, "/")
end
def delete_bucket_cors(client, bucket) do
request(client, :delete, bucket, "/", resource: "cors")
end
def delete_bucket_lifecycle(client, bucket) do
request(client, :delete, bucket, "/", resource: "lifecycle")
end
def delete_bucket_policy(client, bucket) do
request(client, :delete, bucket, "/", resource: "policy")
end
def delete_bucket_replication(client, bucket) do
request(client, :delete, bucket, "/", resource: "replication")
end
def delete_bucket_tagging(client, bucket) do
request(client, :delete, bucket, "/", resource: "tagging")
end
def delete_bucket_website(client, bucket) do
request(client, :delete, bucket, "/", resource: "website")
end
@params [:delimiter, :marker, :prefix, :encoding_type, :max_keys]
def list_objects(client, bucket, opts \\ []) do
params = opts
|> format_and_take(@params)
request(client, :get, bucket, "/", params: params)
|> Parsers.parse_list_objects
end
def list_objects!(client, bucket, opts \\ []) do
{:ok, resp} = list_objects(client, bucket, opts)
resp
end
def get_bucket_acl(client, bucket) do
request(client, :get, bucket, "/", resource: "acl")
end
def get_bucket_cors(client, bucket) do
request(client, :get, bucket, "/", resource: "cors")
end
def get_bucket_lifecycle(client, bucket) do
request(client, :get, bucket, "/", resource: "lifecycle")
end
def get_bucket_policy(client, bucket) do
request(client, :get, bucket, "/", resource: "policy")
end
def get_bucket_location(client, bucket) do
request(client, :get, bucket, "/", resource: "location")
end
def get_bucket_logging(client, bucket) do
request(client, :get, bucket, "/", resource: "logging")
end
def get_bucket_notification(client, bucket) do
request(client, :get, bucket, "/", resource: "notification")
end
def get_bucket_replication(client, bucket) do
request(client, :get, bucket, "/", resource: "replication")
end
def get_bucket_tagging(client, bucket) do
request(client, :get, bucket, "/", resource: "tagging")
end
def get_bucket_object_versions(client, bucket, opts \\ []) do
request(client, :get, bucket, "/", resource: "versions", params: opts)
end
def get_bucket_request_payment(client, bucket) do
request(client, :get, bucket, "/", resource: "requestPayment")
end
def get_bucket_versioning(client, bucket) do
request(client, :get, bucket, "/", resource: "versioning")
end
def get_bucket_website(client, bucket) do
request(client, :get, bucket, "/", resource: "website")
end
def head_bucket(client, bucket) do
request(client, :head, bucket, "/")
end
@params [:delimiter, :encoding_type, :max_uploads, :key_marker, :prefix, :upload_id_marker]
def list_multipart_uploads(client, bucket, opts \\ []) do
params = opts |> format_and_take(@params)
request(client, :get, bucket, "/", resource: "uploads", params: params)
end
def put_bucket(client, bucket, region, opts \\ []) do
headers = opts
|> Enum.into(%{})
|> format_acl_headers
body = """
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<LocationConstraint>#{region}</LocationConstraint>
</CreateBucketConfiguration>
"""
request(client, :put, bucket, "/", body: body, headers: headers)
end
def put_bucket_acl(client, bucket, grants) do
request(client, :put, bucket, "/", headers: format_acl_headers(grants))
end
def put_bucket_cors(client, bucket, cors_rules) do
rules = cors_rules
|> Enum.map(&build_cors_rule/1)
|> IO.iodata_to_binary
body = "<CORSConfiguration>#{rules}</CORSConfiguration>"
content_md5 = :crypto.hash(:md5, body) |> Base.encode64
headers = %{"content-md5" => content_md5}
request(client, :put, bucket, "/",
resource: "cors", body: body, headers: headers)
end
def put_bucket_lifecycle(client, bucket, _livecycle_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_policy(client, bucket, policy) when is_binary(policy) do
request(client, :put, bucket, "/", resource: "policy", body: policy)
end
def put_bucket_policy(client, bucket, policy) do
put_bucket_policy(client, bucket, client.config.json_codec.encode!(policy))
end
def put_bucket_logging(client, bucket, _logging_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_notification(client, bucket, _notification_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_replication(client, bucket, _replication_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_tagging(client, bucket, _tags) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_request_payment(client, bucket, _payer) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_versioning(client, bucket, _version_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
def put_bucket_website(client, bucket, _website_config) do
raise "not yet implemented"
request(client, :put, bucket, "/")
end
## Objects
###########
def delete_object(client, bucket, object, opts \\ []) do
request(client, :delete, bucket, object, headers: opts |> Enum.into(%{}))
end
def delete_object!(client, bucket, object, opts \\ []) do
{:ok, resp} = delete_object(client, bucket, object, opts)
resp
end
def delete_multiple_objects(client, bucket, objects, opts \\ []) do
objects_xml = Enum.map(objects, fn
{key, version} -> ["<Object><Key>", key, "</Key><VersionId>", version, "</VersionId></Object>"]
key -> ["<Object><Key>", key, "</Key></Object>"]
end)
quiet = case opts do
[quiet: true] -> "<Quiet>true</Quiet>"
_ -> ""
end
body = [
~s(<?xml version="1.0" encoding="UTF-8"?>),
quiet,
"<Delete>",
objects_xml,
"</Delete>"
]
content_md5 = :crypto.hash(:md5, body) |> Base.encode64
body_binary = body |> IO.iodata_to_binary
request(client, :post, bucket, "/?delete", body: body_binary, headers: %{"content-md5" => content_md5})
end
def delete_all_objects(client, bucket, objects, opts \\ []) do
do_delete_all(client, bucket, objects, opts, [])
end
defp do_delete_all(_client, _bucket, [], _opts, acc) do
{:ok, Enum.reverse(acc)}
end
defp do_delete_all(client, bucket, objects, opts, acc) do
{objects, rest} = Enum.split(objects, 1000)
with {:ok, result} <- delete_multiple_objects(client, bucket, objects, opts) do
do_delete_all(client, bucket, rest, opts, [result | acc])
end
end
@response_params [:content_type, :content_language, :expires, :cache_control, :content_disposition, :content_encoding]
@request_headers [:range, :if_modified_since, :if_unmodified_since, :if_match, :if_none_match]
def get_object(client, bucket, object, opts \\ []) do
opts = opts |> Enum.into(%{})
response_opts = opts
|> Map.get(:response, %{})
|> format_and_take(@response_params)
|> namespace("response")
headers = opts
|> format_and_take(@request_headers)
headers = opts
|> Map.get(:encryption, %{})
|> build_encryption_headers
|> Map.merge(headers)
request(client, :get, bucket, object, headers: headers, params: response_opts)
end
def get_object!(client, bucket, object, opts \\ []) do
{:ok, resp} = get_object(client, bucket, object, opts)
resp
end
def get_object_acl(client, bucket, object, opts \\ []) do
request(client, :get, bucket, object, resource: "acl", headers: opts |> Enum.into(%{}))
end
def get_object_torrent(client, bucket, object) do
request(client, :get, bucket, object, resource: "torrent")
end
@request_headers [:range, :if_modified_since, :if_unmodified_since, :if_match, :if_none_match]
def head_object(client, bucket, object, opts \\ []) do
opts = opts |> Enum.into(%{})
headers = opts
|> format_and_take(@request_headers)
headers = opts
|> Map.get(:encryption, %{})
|> build_encryption_headers
|> Map.merge(headers)
params = case Map.fetch(opts, :version_id) do
{:ok, id} -> %{"versionId" => id}
_ -> %{}
end
request(client, :head, bucket, object, headers: headers, params: params)
end
def options_object(client, bucket, object, origin, request_method, request_headers \\ []) do
headers = [
{"Origin", origin},
{"Access-Control-Request-Method", request_method},
{"Access-Control-Request-Headers", request_headers |> Enum.join(",")},
]
request(client, :options, bucket, object, headers: headers)
end
def post_object_restore(client, bucket, object, number_of_days, opts \\ []) do
params = case Keyword.fetch(opts, :version_id) do
{:ok, id} -> %{"versionId" => id}
_ -> %{}
end
body = """
<RestoreRequest xmlns="http://s3.amazonaws.com/doc/2006-3-01">
<Days>#{number_of_days}</Days>
</RestoreRequest>
"""
request(client, :post, bucket, object, resource: "restore", params: params, body: body)
end
def put_object(client, bucket, object, body, opts \\ []) do
request(client, :put, bucket, object, body: body, headers: put_object_headers(opts))
end
def put_object!(client, bucket, object, body, opts \\ []) do
{:ok, resp} = put_object(client, bucket, object, body, opts)
resp
end
def put_object_acl(client, bucket, object, acl) do
headers = acl |> Enum.into(%{}) |> format_acl_headers
request(client, :put, bucket, object, headers: headers, resource: "acl")
end
def put_object_acl!(client, bucket, object, acl) do
{:ok, result} = put_object_acl(client, bucket, object, acl)
result
end
@amz_headers ~w(
metadata_directive
copy_source_if_modified_since
copy_source_if_unmodified_since
copy_source_if_match
copy_source_if_none_match
storage_class
website_redirect_location)a
def put_object_copy(client, dest_bucket, dest_object, src_bucket, src_object, opts \\ []) do
opts = opts |> Enum.into(%{})
amz_headers = opts
|> format_and_take(@amz_headers)
|> namespace("x-amz")
source_encryption = opts
|> Map.get(:source_encryption, %{})
|> build_encryption_headers
|> Enum.into(%{}, fn {<<"x-amz", k :: binary>>, v} ->
{"x-amz-copy-source" <> k, v}
end)
destination_encryption = opts
|> Map.get(:destination_encryption, %{})
|> build_encryption_headers
regular_headers = opts
|> Map.delete(:encryption)
|> put_object_headers
headers = regular_headers
|> Map.merge(amz_headers)
|> Map.merge(source_encryption)
|> Map.merge(destination_encryption)
|> Map.put("x-amz-copy-source", URI.encode "/#{src_bucket}/#{src_object}")
request(client, :put, dest_bucket, dest_object, headers: headers)
end
def put_object_copy!(client, dest_bucket, dest_object, src_bucket, src_object, opts \\ []) do
{:ok, resp} = put_object_copy(client, dest_bucket, dest_object, src_bucket, src_object, opts)
resp
end
def initiate_multipart_upload(client, bucket, object, opts \\ []) do
request(client, :post, bucket, object, resource: "uploads", headers: put_object_headers(opts))
|> Parsers.parse_initiate_multipart_upload
end
def upload_part(client, bucket, object, upload_id, part_number, body, _opts \\ []) do
params = %{"uploadId" => upload_id, "partNumber" => part_number}
request(client, :put, bucket, object, params: params, body: body)
end
@amz_headers ~w(
copy_source_if_modified_since
copy_source_if_unmodified_since
copy_source_if_match
copy_source_if_none_match)a
def upload_part_copy(client, dest_bucket, dest_object, src_bucket, src_object, opts \\ []) do
opts = opts |> Enum.into(%{})
source_encryption = opts
|> Map.get(:source_encryption, %{})
|> build_encryption_headers
|> Enum.into(%{}, fn {<<"x-amz", k :: binary>>, v} ->
{"x-amz-copy-source" <> k, v}
end)
destination_encryption = opts
|> Map.get(:destination_encryption, %{})
|> build_encryption_headers
headers = opts
|> format_and_take(@amz_headers)
|> namespace("x-amz")
|> Map.merge(source_encryption)
|> Map.merge(destination_encryption)
headers = case opts do
%{copy_source_range: first..last} -> Map.put(headers, "x-amz-copy-source-range", "bytes=#{first}-#{last}")
_ -> headers
end
|> Map.put("x-amz-copy-source", "/#{src_bucket}/#{src_object}")
request(client, :put, dest_bucket, dest_object, headers: headers)
|> Parsers.parse_upload_part_copy
end
def complete_multipart_upload(client, bucket, object, upload_id, parts) do
parts_xml = parts
|> Enum.map(fn {part_number, etag}->
["<Part>",
"<PartNumber>", Integer.to_string(part_number), "</PartNumber>",
"<ETag>", etag, "</ETag>",
"</Part>"]
end)
body = ["<CompleteMultipartUpload>", parts_xml, "</CompleteMultipartUpload>"]
|> IO.iodata_to_binary
request(client, :post, bucket, object, params: %{"uploadId" => upload_id}, body: body)
|> Parsers.parse_complete_multipart_upload
end
def abort_multipart_upload(client, bucket, object, upload_id) do
request(client, :delete, bucket, object, params: %{"uploadId" => upload_id})
end
def list_parts(client, bucket, object, upload_id, opts \\ []) do
params = opts
|> Enum.into(%{})
|> Map.merge(%{"uploadId" => upload_id})
request(client, :get, bucket, object, params: params)
|> Parsers.parse_list_parts
end
@one_week 60 * 60 * 24 * 7
def presigned_url(client, http_method, bucket, object, opts \\ []) do
expires_in = Keyword.get(opts, :expires_in, 3600)
virtual_host = Keyword.get(opts, :virtual_host, false)
case expires_in > @one_week do
true -> {:error, "expires_in_exceeds_one_week"}
false ->
config = client.config
url = url_to_sign(bucket, object, config, virtual_host)
datetime = :calendar.universal_time
{:ok, ExAws.Auth.presigned_url(http_method, url, client.service, datetime, client.config, expires_in)}
end
end
defp request(%{__struct__: client_module} = client, action, bucket, path, data \\ []) do
client_module.request(client, action, bucket, path, data)
end
defp url_to_sign(bucket, object, config, virtual_host) do
object = ExAws.S3.Request.ensure_slash(object)
case virtual_host do
true -> "#{config[:scheme]}#{bucket}.#{config[:host]}#{object}"
false -> "#{config[:scheme]}#{config[:host]}/#{bucket}#{object}"
end
end
end