package awskit-s3
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
S3 client core — objects, buckets, multipart, and policies
Install
dune-project
Dependency
Authors
Maintainers
Sources
awskit-v0.1.0.tbz
sha256=788e91d57b9eed047bdef011aec476e94588be20e2e2f1b8495cf48b1a90cf0f
sha512=0d441d599f3f3efb766270258bb4d8c9cd660943eb7f90ced0ec6f61a6790f5fb8977ca5cf87f466d84701ee34dbfdf81fe5043b568a2236411f577e698c6d1e
doc/src/awskit-s3/common.ml.html
Source file common.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271module Credentials = Awskit.Credentials module Endpoint = Awskit.Endpoint module Region = Awskit.Region let ( let* ) result f = match result with Ok value -> f value | Error _ as e -> e let result_exn = function | Ok value -> value | Error error -> invalid_arg (Awskit.Error.to_string_hum error) let invalid ?field fmt = Fmt.kstr (fun message -> Error (Awskit.Error.validation ?field message)) fmt let decode fmt = Fmt.kstr Awskit.Error.decode fmt let is_prefix ~prefix value = let prefix_len = String.length prefix in String.length value >= prefix_len && String.sub value 0 prefix_len = prefix let is_suffix ~suffix value = let suffix_len = String.length suffix in let len = String.length value in len >= suffix_len && String.sub value (len - suffix_len) suffix_len = suffix let has_ctl_or_del value = String.exists (fun c -> let code = Char.code c in code < 0x20 || code = 0x7F) value let int_of_string_opt value = try Some (int_of_string value) with Failure _ -> None let int64_of_string_opt value = try Some (Int64.of_string value) with Failure _ -> None let option_map_result f = function | None -> Ok None | Some value -> Result.map Option.some (f value) let ptime_of_string value = match Ptime.of_rfc3339 ~strict:false value with | Ok (time, _, _) -> Some time | Error _ -> None let ptime_to_header value = Ptime.to_rfc3339 value module Xml = struct let el name children = `El ((("", name), []), children) let text name value = el name [ `Data value ] let to_string node = Ezxmlm.to_string [ node ] let root body = try let _, nodes = Ezxmlm.from_string body in match List.find_map (function | `El (((_, name), _), children) -> Some (name, children) | _ -> None) nodes with | Some root -> Ok root | None -> Error (Awskit.Error.decode "empty XML document") with exn -> Error (Awskit.Error.decode (Printexc.to_string exn)) let children name nodes = List.filter_map (function | `El (((_, child_name), _), children) when child_name = name -> Some children | _ -> None) nodes let rec text_content nodes = nodes |> List.map (function | `Data data -> data | `El ((_, _), children) -> text_content children) |> String.concat "" let child name nodes = match children name nodes with [] -> None | x :: _ -> Some x let child_text name nodes = Option.map text_content (child name nodes) let child_texts name nodes = List.map text_content (children name nodes) let decode_root body ~name = match root body with | Error _ as error -> error | Ok (_actual, children) -> Ok children let service_code body = match root body with | Error _ -> None | Ok (_, nodes) -> child_text "Code" nodes let service_message body = match root body with | Error _ -> None | Ok (_, nodes) -> child_text "Message" nodes end module Error = struct type t = Awskit.Error.t let pp = Awskit.Error.pp let equal = Awskit.Error.equal let to_string_hum = Awskit.Error.to_string_hum let service_code = function | Awskit.Error.Service { code; _ } -> code | _ -> None let code_is expected error = match service_code error with | None -> false | Some code -> String.lowercase_ascii code = String.lowercase_ascii expected let is_not_found = Awskit.Error.is_not_found let is_no_such_bucket error = code_is "NoSuchBucket" error let is_no_such_key error = code_is "NoSuchKey" error let is_precondition_failed = function | Awskit.Error.Service { status = 412; _ } -> true | error -> code_is "PreconditionFailed" error let is_conditional_request_conflict error = code_is "ConditionalRequestConflict" error let is_conditional_failure error = is_precondition_failed error || is_conditional_request_conflict error end module Metadata = Metadata module Metadata_headers = struct let prefix = "x-amz-meta-" let of_headers headers = List.filter_map (fun (key, value) -> let lower = String.lowercase_ascii key in if is_prefix ~prefix lower then Some ( String.sub key (String.length prefix) (String.length key - String.length prefix), value ) else None) headers let to_headers metadata = List.map (fun (k, v) -> (prefix ^ k, v)) metadata end module Storage_class = Storage_class module Tag = Tag module Range = Range let validate_header_value ~field value = if value = "" then invalid ~field "%s must be non-empty" field else if has_ctl_or_del value then invalid ~field "%s contains control characters" field else Ok () let validate_metadata metadata = let validate_key key = if key = "" then invalid ~field:"metadata" "metadata key must be non-empty" else if has_ctl_or_del key then invalid ~field:"metadata" "metadata key contains control characters" else if is_prefix ~prefix:Metadata_headers.prefix (String.lowercase_ascii key) then invalid ~field:"metadata" "metadata keys must not include x-amz-meta-" else Ok () in let rec loop = function | [] -> Ok () | (key, value) :: rest -> let* () = validate_key key in let* () = validate_header_value ~field:("metadata " ^ key) value in loop rest in loop metadata let validate_tag (tag : Tag.t) = let* () = validate_header_value ~field:"tag key" tag.key in if has_ctl_or_del tag.value then invalid ~field:"tag value" "tag value contains control characters" else Ok () let = let rec loop = function | [] -> Ok () | tag :: rest -> let* () = validate_tag tag in loop rest in loop tags let validate_bucket bucket = let len = String.length bucket in let is_lower = function 'a' .. 'z' -> true | _ -> false in let is_digit = function '0' .. '9' -> true | _ -> false in let is_alnum c = is_lower c || is_digit c in let is_bucket_char = function '.' | '-' -> true | c -> is_alnum c in let bad_dot_dash = let rec loop i = i + 1 < len && ((bucket.[i] = '.' && bucket.[i + 1] = '.') || (bucket.[i] = '.' && bucket.[i + 1] = '-') || (bucket.[i] = '-' && bucket.[i + 1] = '.') || loop (i + 1)) in loop 0 in let looks_like_ipv4 = match String.split_on_char '.' bucket with | [ a; b; c; d ] -> List.for_all (fun part -> part <> "" && match int_of_string_opt part with | Some n -> n >= 0 && n <= 255 | None -> false) [ a; b; c; d ] | _ -> false in if len < 3 || len > 63 then invalid ~field:"bucket" "bucket must be 3-63 characters" else if not (String.for_all is_bucket_char bucket) then invalid ~field:"bucket" "bucket must contain only lowercase letters, digits, dots, and hyphens" else if not (is_alnum bucket.[0] && is_alnum bucket.[len - 1]) then invalid ~field:"bucket" "bucket must start and end with a lowercase letter or digit" else if has_ctl_or_del bucket then invalid ~field:"bucket" "bucket contains control characters" else if bad_dot_dash then invalid ~field:"bucket" "bucket must not contain adjacent dots or dot-hyphen pairs" else if looks_like_ipv4 then invalid ~field:"bucket" "bucket must not be formatted as an IPv4 address" else if is_prefix ~prefix:"xn--" bucket then invalid ~field:"bucket" "bucket must not start with xn--" else if is_prefix ~prefix:"sthree-" bucket then invalid ~field:"bucket" "bucket must not start with sthree-" else if is_prefix ~prefix:"amzn-s3-demo-" bucket then invalid ~field:"bucket" "bucket must not start with amzn-s3-demo-" else if is_suffix ~suffix:"-s3alias" bucket then invalid ~field:"bucket" "bucket must not end with -s3alias" else if is_suffix ~suffix:"--ol-s3" bucket then invalid ~field:"bucket" "bucket must not end with --ol-s3" else if is_suffix ~suffix:".mrap" bucket then invalid ~field:"bucket" "bucket must not end with .mrap" else if is_suffix ~suffix:"--x-s3" bucket then invalid ~field:"bucket" "bucket must not end with --x-s3" else if is_suffix ~suffix:"--table-s3" bucket then invalid ~field:"bucket" "bucket must not end with --table-s3" else Ok () let validate_key key = if key = "" then invalid ~field:"key" "key must be non-empty" else if has_ctl_or_del key then invalid ~field:"key" "key contains control characters" else Ok () let validate_bucket_key bucket key = let* () = validate_bucket bucket in validate_key key
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>