aws-sdk-s3 1.9.0 → 1.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/aws-sdk-s3/bucket.rb +1 -1
- data/lib/aws-sdk-s3/bucket_notification.rb +3 -2
- data/lib/aws-sdk-s3/client.rb +706 -198
- data/lib/aws-sdk-s3/client_api.rb +171 -2
- data/lib/aws-sdk-s3/customizations/bucket.rb +2 -2
- data/lib/aws-sdk-s3/customizations/object.rb +60 -0
- data/lib/aws-sdk-s3/customizations/object_summary.rb +7 -0
- data/lib/aws-sdk-s3/customizations.rb +1 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +9 -3
- data/lib/aws-sdk-s3/event_streams.rb +56 -0
- data/lib/aws-sdk-s3/file_downloader.rb +10 -9
- data/lib/aws-sdk-s3/file_part.rb +5 -7
- data/lib/aws-sdk-s3/file_uploader.rb +1 -3
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +160 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +1 -1
- data/lib/aws-sdk-s3/object.rb +5 -2
- data/lib/aws-sdk-s3/object_summary.rb +5 -2
- data/lib/aws-sdk-s3/plugins/accelerate.rb +4 -0
- data/lib/aws-sdk-s3/plugins/md5s.rb +1 -1
- data/lib/aws-sdk-s3/plugins/redirects.rb +3 -1
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +5 -1
- data/lib/aws-sdk-s3/presigner.rb +1 -0
- data/lib/aws-sdk-s3/types.rb +866 -129
- data/lib/aws-sdk-s3.rb +2 -1
- metadata +11 -3
@@ -24,15 +24,18 @@ module Aws
|
|
24
24
|
@mode = options[:mode] || "auto"
|
25
25
|
@thread_count = options[:thread_count] || THREAD_COUNT
|
26
26
|
@chunk_size = options[:chunk_size]
|
27
|
-
@
|
28
|
-
|
27
|
+
@params = {
|
28
|
+
bucket: options[:bucket],
|
29
|
+
key: options[:key],
|
30
|
+
}
|
31
|
+
@params[:version_id] = options[:version_id] if options[:version_id]
|
29
32
|
|
30
33
|
case @mode
|
31
34
|
when "auto" then multipart_download
|
32
35
|
when "single_request" then single_request
|
33
36
|
when "get_range"
|
34
37
|
if @chunk_size
|
35
|
-
resp = @client.head_object(
|
38
|
+
resp = @client.head_object(@params)
|
36
39
|
multithreaded_get_by_ranges(construct_chunks(resp.content_length))
|
37
40
|
else
|
38
41
|
msg = "In :get_range mode, :chunk_size must be provided"
|
@@ -48,7 +51,7 @@ module Aws
|
|
48
51
|
private
|
49
52
|
|
50
53
|
def multipart_download
|
51
|
-
resp = @client.head_object(
|
54
|
+
resp = @client.head_object(@params.merge(part_number: 1))
|
52
55
|
count = resp.parts_count
|
53
56
|
if count.nil? || count <= 1
|
54
57
|
resp.content_length < MIN_CHUNK_SIZE ?
|
@@ -56,7 +59,7 @@ module Aws
|
|
56
59
|
multithreaded_get_by_ranges(construct_chunks(resp.content_length))
|
57
60
|
else
|
58
61
|
# partNumber is an option
|
59
|
-
resp = @client.head_object(
|
62
|
+
resp = @client.head_object(@params)
|
60
63
|
resp.content_length < MIN_CHUNK_SIZE ?
|
61
64
|
single_request :
|
62
65
|
compute_mode(resp.content_length, count)
|
@@ -112,9 +115,7 @@ module Aws
|
|
112
115
|
batch.each do |chunk|
|
113
116
|
threads << Thread.new do
|
114
117
|
resp = @client.get_object(
|
115
|
-
|
116
|
-
:key => @key,
|
117
|
-
param.to_sym => chunk
|
118
|
+
@params.merge(param.to_sym => chunk)
|
118
119
|
)
|
119
120
|
write(resp)
|
120
121
|
end
|
@@ -131,7 +132,7 @@ module Aws
|
|
131
132
|
|
132
133
|
def single_request
|
133
134
|
@client.get_object(
|
134
|
-
|
135
|
+
@params.merge(response_target: @path)
|
135
136
|
)
|
136
137
|
end
|
137
138
|
end
|
data/lib/aws-sdk-s3/file_part.rb
CHANGED
@@ -56,14 +56,12 @@ module Aws
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def read_from_file(bytes, output_buffer)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
else
|
63
|
-
data = @file.read(remaining_bytes)
|
64
|
-
end
|
59
|
+
length = [remaining_bytes, *bytes].min
|
60
|
+
data = @file.read(length, output_buffer)
|
61
|
+
|
65
62
|
@position += data ? data.bytesize : 0
|
66
|
-
|
63
|
+
|
64
|
+
data.to_s unless bytes && (data.nil? || data.empty?)
|
67
65
|
end
|
68
66
|
|
69
67
|
def remaining_bytes
|
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'set'
|
3
|
+
require 'tempfile'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module Aws
|
7
|
+
module S3
|
8
|
+
# @api private
|
9
|
+
class MultipartStreamUploader
|
10
|
+
# api private
|
11
|
+
PART_SIZE = 5 * 1024 * 1024 # 5MB
|
12
|
+
|
13
|
+
# api private
|
14
|
+
THREAD_COUNT = 10
|
15
|
+
|
16
|
+
# api private
|
17
|
+
TEMPFILE_PREIX = 'aws-sdk-s3-upload_stream'.freeze
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
CREATE_OPTIONS =
|
21
|
+
Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
UPLOAD_PART_OPTIONS =
|
25
|
+
Set.new(Client.api.operation(:upload_part).input.shape.member_names)
|
26
|
+
|
27
|
+
# @option options [Client] :client
|
28
|
+
def initialize(options = {})
|
29
|
+
@client = options[:client] || Client.new
|
30
|
+
@tempfile = options[:tempfile]
|
31
|
+
@part_size = options[:part_size] || PART_SIZE
|
32
|
+
@thread_count = options[:thread_count] || THREAD_COUNT
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Client]
|
36
|
+
attr_reader :client
|
37
|
+
|
38
|
+
# @option options [required,String] :bucket
|
39
|
+
# @option options [required,String] :key
|
40
|
+
# @return [void]
|
41
|
+
def upload(options = {}, &block)
|
42
|
+
upload_id = initiate_upload(options)
|
43
|
+
parts = upload_parts(upload_id, options, &block)
|
44
|
+
complete_upload(upload_id, parts, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def initiate_upload(options)
|
50
|
+
@client.create_multipart_upload(create_opts(options)).upload_id
|
51
|
+
end
|
52
|
+
|
53
|
+
def complete_upload(upload_id, parts, options)
|
54
|
+
@client.complete_multipart_upload(
|
55
|
+
bucket: options[:bucket],
|
56
|
+
key: options[:key],
|
57
|
+
upload_id: upload_id,
|
58
|
+
multipart_upload: { parts: parts })
|
59
|
+
end
|
60
|
+
|
61
|
+
def upload_parts(upload_id, options, &block)
|
62
|
+
completed = Queue.new
|
63
|
+
errors = IO.pipe do |read_pipe, write_pipe|
|
64
|
+
threads = upload_in_threads(read_pipe, completed, upload_part_opts(options).merge(upload_id: upload_id))
|
65
|
+
block.call(write_pipe)
|
66
|
+
write_pipe.close
|
67
|
+
threads.map(&:value).compact
|
68
|
+
end
|
69
|
+
if errors.empty?
|
70
|
+
Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
|
71
|
+
else
|
72
|
+
abort_upload(upload_id, options, errors)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def abort_upload(upload_id, options, errors)
|
77
|
+
@client.abort_multipart_upload(
|
78
|
+
bucket: options[:bucket],
|
79
|
+
key: options[:key],
|
80
|
+
upload_id: upload_id
|
81
|
+
)
|
82
|
+
msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
|
83
|
+
raise MultipartUploadError.new(msg, errors)
|
84
|
+
rescue MultipartUploadError => error
|
85
|
+
raise error
|
86
|
+
rescue => error
|
87
|
+
msg = "failed to abort multipart upload: #{error.message}"
|
88
|
+
raise MultipartUploadError.new(msg, errors + [error])
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_opts(options)
|
92
|
+
CREATE_OPTIONS.inject({}) do |hash, key|
|
93
|
+
hash[key] = options[key] if options.key?(key)
|
94
|
+
hash
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def upload_part_opts(options)
|
99
|
+
UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
|
100
|
+
hash[key] = options[key] if options.key?(key)
|
101
|
+
hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def read_to_part_body(read_pipe)
|
106
|
+
return if read_pipe.closed?
|
107
|
+
temp_io = @tempfile ? Tempfile.new(TEMPFILE_PREIX) : StringIO.new
|
108
|
+
temp_io.binmode
|
109
|
+
bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
|
110
|
+
temp_io.rewind
|
111
|
+
if bytes_copied == 0
|
112
|
+
if Tempfile === temp_io
|
113
|
+
temp_io.close
|
114
|
+
temp_io.unlink
|
115
|
+
end
|
116
|
+
nil
|
117
|
+
else
|
118
|
+
temp_io
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def upload_in_threads(read_pipe, completed, options)
|
123
|
+
mutex = Mutex.new
|
124
|
+
part_number = 0
|
125
|
+
@thread_count.times.map do
|
126
|
+
thread = Thread.new do
|
127
|
+
begin
|
128
|
+
loop do
|
129
|
+
body, thread_part_number = mutex.synchronize do
|
130
|
+
[read_to_part_body(read_pipe), part_number += 1]
|
131
|
+
end
|
132
|
+
break unless (body || thread_part_number == 1)
|
133
|
+
begin
|
134
|
+
part = options.merge(
|
135
|
+
body: body,
|
136
|
+
part_number: thread_part_number,
|
137
|
+
)
|
138
|
+
resp = @client.upload_part(part)
|
139
|
+
completed << {etag: resp.etag, part_number: part[:part_number]}
|
140
|
+
ensure
|
141
|
+
if Tempfile === body
|
142
|
+
body.close
|
143
|
+
body.unlink
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
nil
|
148
|
+
rescue => error
|
149
|
+
# keep other threads from uploading other parts
|
150
|
+
mutex.synchronize { read_pipe.close_read }
|
151
|
+
error
|
152
|
+
end
|
153
|
+
end
|
154
|
+
thread.abort_on_exception = true
|
155
|
+
thread
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/aws-sdk-s3/object.rb
CHANGED
@@ -866,7 +866,7 @@ module Aws::S3
|
|
866
866
|
# http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
867
867
|
# @option options [String] :tagging
|
868
868
|
# The tag-set for the object. The tag-set must be encoded as URL Query
|
869
|
-
# parameters
|
869
|
+
# parameters. (For example, "Key1=Value1")
|
870
870
|
# @return [Types::PutObjectOutput]
|
871
871
|
def put(options = {})
|
872
872
|
options = options.merge(
|
@@ -898,11 +898,14 @@ module Aws::S3
|
|
898
898
|
# record_delimiter: "RecordDelimiter",
|
899
899
|
# field_delimiter: "FieldDelimiter",
|
900
900
|
# quote_character: "QuoteCharacter",
|
901
|
+
# allow_quoted_record_delimiter: false,
|
901
902
|
# },
|
902
|
-
# compression_type: "NONE", # accepts NONE, GZIP
|
903
|
+
# compression_type: "NONE", # accepts NONE, GZIP, BZIP2
|
903
904
|
# json: {
|
904
905
|
# type: "DOCUMENT", # accepts DOCUMENT, LINES
|
905
906
|
# },
|
907
|
+
# parquet: {
|
908
|
+
# },
|
906
909
|
# },
|
907
910
|
# expression_type: "SQL", # required, accepts SQL
|
908
911
|
# expression: "Expression", # required
|
@@ -709,7 +709,7 @@ module Aws::S3
|
|
709
709
|
# http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
710
710
|
# @option options [String] :tagging
|
711
711
|
# The tag-set for the object. The tag-set must be encoded as URL Query
|
712
|
-
# parameters
|
712
|
+
# parameters. (For example, "Key1=Value1")
|
713
713
|
# @return [Types::PutObjectOutput]
|
714
714
|
def put(options = {})
|
715
715
|
options = options.merge(
|
@@ -741,11 +741,14 @@ module Aws::S3
|
|
741
741
|
# record_delimiter: "RecordDelimiter",
|
742
742
|
# field_delimiter: "FieldDelimiter",
|
743
743
|
# quote_character: "QuoteCharacter",
|
744
|
+
# allow_quoted_record_delimiter: false,
|
744
745
|
# },
|
745
|
-
# compression_type: "NONE", # accepts NONE, GZIP
|
746
|
+
# compression_type: "NONE", # accepts NONE, GZIP, BZIP2
|
746
747
|
# json: {
|
747
748
|
# type: "DOCUMENT", # accepts DOCUMENT, LINES
|
748
749
|
# },
|
750
|
+
# parquet: {
|
751
|
+
# },
|
749
752
|
# },
|
750
753
|
# expression_type: "SQL", # required, accepts SQL
|
751
754
|
# expression: "Expression", # required
|
@@ -60,6 +60,8 @@ each bucket. [Go here for more information](http://docs.aws.amazon.com/AmazonS3
|
|
60
60
|
endpoint.port = 443
|
61
61
|
endpoint.host = "#{bucket_name}.s3-accelerate.amazonaws.com"
|
62
62
|
context.http_request.endpoint = endpoint.to_s
|
63
|
+
# s3 accelerate endpoint doesn't work with 'expect' header
|
64
|
+
context.http_request.headers.delete('expect')
|
63
65
|
end
|
64
66
|
|
65
67
|
def use_combined_accelerate_dualstack_endpoint(context)
|
@@ -70,6 +72,8 @@ each bucket. [Go here for more information](http://docs.aws.amazon.com/AmazonS3
|
|
70
72
|
endpoint.port = 443
|
71
73
|
endpoint.host = "#{bucket_name}.s3-accelerate.dualstack.amazonaws.com"
|
72
74
|
context.http_request.endpoint = endpoint.to_s
|
75
|
+
# s3 accelerate endpoint doesn't work with 'expect' header
|
76
|
+
context.http_request.headers.delete('expect')
|
73
77
|
end
|
74
78
|
|
75
79
|
def validate_bucket_name!(bucket_name)
|
@@ -18,7 +18,9 @@ by Amazon S3.
|
|
18
18
|
response = @handler.call(context)
|
19
19
|
if context.http_response.status_code == 307
|
20
20
|
endpoint = context.http_response.headers['location']
|
21
|
-
context.http_request.endpoint
|
21
|
+
unless context.http_request.endpoint.host.include?('fips')
|
22
|
+
context.http_request.endpoint = endpoint
|
23
|
+
end
|
22
24
|
context.http_response.body.truncate(0)
|
23
25
|
@handler.call(context)
|
24
26
|
else
|
@@ -113,7 +113,7 @@ module Aws
|
|
113
113
|
private
|
114
114
|
|
115
115
|
def handle_region_errors(response)
|
116
|
-
if wrong_sigv4_region?(response)
|
116
|
+
if wrong_sigv4_region?(response) && !fips_region?(response)
|
117
117
|
get_region_and_retry(response.context)
|
118
118
|
else
|
119
119
|
response
|
@@ -133,6 +133,10 @@ module Aws
|
|
133
133
|
S3::BUCKET_REGIONS[context.params[:bucket]] = actual_region
|
134
134
|
end
|
135
135
|
|
136
|
+
def fips_region?(resp)
|
137
|
+
resp.context.http_request.endpoint.host.include?('fips')
|
138
|
+
end
|
139
|
+
|
136
140
|
def wrong_sigv4_region?(resp)
|
137
141
|
resp.context.http_response.status_code == 400 &&
|
138
142
|
(
|
data/lib/aws-sdk-s3/presigner.rb
CHANGED