From 0f798a2295ed7a381689845c5f8ba4a682e99a14 Mon Sep 17 00:00:00 2001 From: Morten Piibeleht Date: Fri, 27 Jan 2023 18:07:17 +1300 Subject: [PATCH] Add response headers to presigned URL v4 --- src/AWSS3.jl | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/src/AWSS3.jl b/src/AWSS3.jl index ea42b7dc..a302fa1e 100644 --- a/src/AWSS3.jl +++ b/src/AWSS3.jl @@ -939,8 +939,13 @@ function _s3_sign_url_v4( path, seconds=3600; verb="GET", - content_type="application/octet-stream", protocol="http", + content_type::Union{AbstractString,Nothing}=nothing, + content_language::Union{AbstractString,Nothing}=nothing, + expires::Union{AbstractString,Nothing}=nothing, + cache_control::Union{AbstractString,Nothing}=nothing, + content_disposition::Union{AbstractString,Nothing}=nothing, + content_encoding::Union{AbstractString,Nothing}=nothing ) path = URIs.escapepath("/$bucket/$path") @@ -986,6 +991,29 @@ function _s3_sign_url_v4( ), ) + # Response header override parameters. It seems like these have to + # be a part of the query parameters, but they are not canonical headers. + # + # We only add them to the query parameters if the corresponding + # keyword argument has been set by the user. + override_query = OrderedDict{String,String}() + for (parameter, value) in ( + "response-content-type" => content_type, + "response-content-language" => content_language, + "response-expires" => expires, + "response-cache-control" => cache_control, + "response-content-disposition" => content_disposition, + "response-content-encoding" => content_encoding + ) + if !isnothing(value) + override_query[parameter] = value + end + end + # It also seems like they have to come after standard query parameters, + # but will need to be alphabetically sorted amongst themselves. + sort!(override_query) + merge!(query, override_query) + canonical_request = string( "$verb\n", "$path\n", @@ -1046,9 +1074,14 @@ function s3_sign_url( path, seconds=3600; verb="GET", - content_type="application/octet-stream", protocol="http", signature_version="v4", + content_type::Union{AbstractString,Nothing}=nothing, + content_language::Union{AbstractString,Nothing}=nothing, + expires::Union{AbstractString,Nothing}=nothing, + cache_control::Union{AbstractString,Nothing}=nothing, + content_disposition::Union{AbstractString,Nothing}=nothing, + content_encoding::Union{AbstractString,Nothing}=nothing ) if signature_version == "v2" _s3_sign_url_v2( @@ -1057,8 +1090,12 @@ function s3_sign_url( path, seconds; verb=verb, - content_type=content_type, - protocol=protocol, + # previously, v2 version set Request-Content-Type to 'application/octet-stream' + # but in v4 it was unset. Hence, to keep backwards compatibility, we still set + # content_type for v2 even if the user is not providing their own value (unlike + # in the v4 case). + content_type=isnothing(content_type) ? "application/octet-stream" : content_type, + protocol=protocol ) elseif signature_version == "v4" _s3_sign_url_v4( @@ -1066,9 +1103,9 @@ function s3_sign_url( bucket, path, seconds; - verb=verb, - content_type=content_type, - protocol=protocol, + verb, protocol, + content_type, content_language, expires, cache_control, content_disposition, + content_encoding ) else throw(ArgumentError("Unknown signature version $signature_version"))