diff --git a/aws-s3/aws.ml b/aws-s3/aws.ml index 0e77db2..06f7d4e 100644 --- a/aws-s3/aws.ml +++ b/aws-s3/aws.ml @@ -96,7 +96,7 @@ module Make(Io : Types.Io) = struct in Pipe.create_reader ~f:(transfer initial_signature Digestif.SHA256.empty 0 [] None) - let make_request ~(endpoint: Region.endpoint) ?connect_timeout_ms ?(expect=false) ~sink ?(body=Body.Empty) ?(credentials:Credentials.t option) ~headers ~meth ~path ~query () = + let make_request ~(endpoint: Region.endpoint) ?connect_timeout_ms ?(expect=false) ?(unsigned_payload=false) ~sink ?(body=Body.Empty) ?(credentials:Credentials.t option) ~headers ~meth ~path ~query () = let (date, time) = Unix.gettimeofday () |> Time.iso8601_of_time in (* Create headers structure *) @@ -108,8 +108,11 @@ module Make(Io : Types.Io) = struct | (`PUT | `POST), Body.Empty -> Some "0" | _ -> None in + (* A chunked body is signed chunk by chunk, so [unsigned_payload] only has + anything to say about a string body. *) let payload_sha = match body with | Body.Empty -> empty_sha + | Body.String _ when unsigned_payload -> "UNSIGNED-PAYLOAD" | Body.String body -> Authorization.hash_sha256 body |> Authorization.to_hex | Body.Chunked _ -> "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" in diff --git a/aws-s3/aws.mli b/aws-s3/aws.mli index 01214f3..b76eb6d 100644 --- a/aws-s3/aws.mli +++ b/aws-s3/aws.mli @@ -5,6 +5,7 @@ module Make(Io : Types.Io) : sig endpoint:Region.endpoint -> ?connect_timeout_ms:int -> ?expect:bool -> + ?unsigned_payload:bool -> sink:string Io.Pipe.writer -> ?body:Body.Make(Io).t -> ?credentials:Credentials.t -> diff --git a/aws-s3/s3.ml b/aws-s3/s3.ml index bf0ff0b..1f4cd40 100644 --- a/aws-s3/s3.ml +++ b/aws-s3/s3.ml @@ -280,7 +280,7 @@ module Make(Io : Types.Io) = struct Deferred.return (Error (Unknown (code, resp.code))) - let put_common ?credentials ?connect_timeout_ms ?(confirm_requester_pays=false) ~endpoint ?content_type ?content_encoding ?acl ?cache_control ?expect ?(meta_headers=[]) ~bucket ~key ~body () = + let put_common ?credentials ?connect_timeout_ms ?(confirm_requester_pays=false) ?unsigned_payload ~endpoint ?content_type ?content_encoding ?acl ?cache_control ?expect ?(meta_headers=[]) ~bucket ~key ~body () = let path = sprintf "/%s/%s" bucket key in let headers = ( @@ -297,7 +297,7 @@ module Make(Io : Types.Io) = struct in let sink = Body.null () in let cmd () = - Aws.make_request ~endpoint ?expect ?credentials ?connect_timeout_ms ~headers ~meth:`PUT ~path ~sink ~body ~query:[] () + Aws.make_request ~endpoint ?expect ?credentials ?connect_timeout_ms ?unsigned_payload ~headers ~meth:`PUT ~path ~sink ~body ~query:[] () in do_command ~endpoint cmd >>=? fun headers -> @@ -342,9 +342,9 @@ module Make(Io : Types.Io) = struct end (* End streaming module *) - let put ?credentials ?connect_timeout_ms ?confirm_requester_pays ~endpoint ?content_type ?content_encoding ?acl ?cache_control ?expect ?meta_headers ~bucket ~key ~data () = + let put ?credentials ?connect_timeout_ms ?confirm_requester_pays ~endpoint ?unsigned_payload ?content_type ?content_encoding ?acl ?cache_control ?expect ?meta_headers ~bucket ~key ~data () = let body = Body.String data in - put_common ?credentials ?connect_timeout_ms ?confirm_requester_pays ?content_type ?content_encoding ?acl ?cache_control ?expect ?meta_headers ~endpoint ~bucket ~key ~body () + put_common ?credentials ?connect_timeout_ms ?confirm_requester_pays ?unsigned_payload ?content_type ?content_encoding ?acl ?cache_control ?expect ?meta_headers ~endpoint ~bucket ~key ~body () let get ?credentials ?connect_timeout_ms ?confirm_requester_pays ~endpoint ?range ~bucket ~key () = let body, data = string_sink () in diff --git a/aws-s3/s3.mli b/aws-s3/s3.mli index 051736a..949165d 100644 --- a/aws-s3/s3.mli +++ b/aws-s3/s3.mli @@ -82,9 +82,14 @@ module Make(Io : Types.Io) : sig arguments are expected to be a list of key-value pairs, the keys will be prefixed with "x-amz-meta-". @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html#UserMetadata + @param unsigned_payload If true, the request is signed with + UNSIGNED-PAYLOAD instead of the sha256 of [data], which saves a full pass + over the body but leaves the payload out of the signature, so it should + only be used over https. *) val put : - (?content_type:string -> + (?unsigned_payload:bool -> + ?content_type:string -> ?content_encoding:string -> ?acl:string -> ?cache_control:string ->