aws-sdk 1.6.2 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,7 +26,7 @@ module AWS
26
26
  # * {Amazon S3}[http://aws.amazon.com/s3/]
27
27
  # * {Amazon S3 Documentation}[http://aws.amazon.com/documentation/s3/]
28
28
  #
29
- # == Credentials
29
+ # = Credentials
30
30
  #
31
31
  # You can setup default credentials for all AWS services via
32
32
  # AWS.config:
@@ -34,63 +34,76 @@ module AWS
34
34
  # AWS.config(
35
35
  # :access_key_id => 'YOUR_ACCESS_KEY_ID',
36
36
  # :secret_access_key => 'YOUR_SECRET_ACCESS_KEY')
37
- #
37
+ #
38
38
  # Or you can set them directly on the S3 interface:
39
39
  #
40
40
  # s3 = AWS::S3.new(
41
41
  # :access_key_id => 'YOUR_ACCESS_KEY_ID',
42
42
  # :secret_access_key => 'YOUR_SECRET_ACCESS_KEY')
43
43
  #
44
- # == Buckets Keys and Objects
44
+ # = Buckets
45
45
  #
46
- # S3 stores objects in buckets.
46
+ # Before you can upload files to S3, you need to create a bucket.
47
47
  #
48
- # To create a bucket:
48
+ # s3 = AWS::S3.new
49
+ # bucket = s3.buckets.create('my-bucket')
49
50
  #
50
- # bucket = s3.buckets.create('mybucket')
51
+ # If a bucket already exists, you can get a reference to the bucket.
51
52
  #
52
- # To get a bucket:
53
+ # bucket = s3.buckets['my-bucket'] # no request made
53
54
  #
54
- # bucket = s3.buckets[:mybucket]
55
- # bucket = s3.buckets['mybucket']
55
+ # You can also enumerate all buckets in your account.
56
56
  #
57
- # Listing buckets:
58
- #
59
57
  # s3.buckets.each do |bucket|
60
58
  # puts bucket.name
61
59
  # end
62
60
  #
63
- # See {Bucket} and {BucketCollection} for more information on working
64
- # with S3 buckets.
61
+ # See {BucketCollection} and {Bucket} for more information on working
62
+ # with buckets.
65
63
  #
66
- # == Listing Objects
64
+ # = Objects
67
65
  #
68
- # Enumerating objects in a bucket:
66
+ # Buckets contain objects. Each object in a bucket has a unique key.
69
67
  #
70
- # bucket.objects.each do |object|
71
- # puts object.key #=> no data is fetched from s3, just a list of keys
72
- # end
68
+ # == Getting an Object
69
+ #
70
+ # If the object already exists, you can get a reference to the object.
73
71
  #
74
- # You can limit the list of objects with a prefix, or explore the objects
75
- # in a bucket as a tree. See {ObjectCollection} for more information.
72
+ # # makes no request, returns an AWS::S3::S3Object
73
+ # obj = bucket.objects['key']
76
74
  #
77
- # == Reading and Writing to S3
75
+ # == Reading and Writing an Object
78
76
  #
79
- # Each object in a bucket has a unique key.
77
+ # The example above returns an {S3Object}. You call {S3Object#write} and
78
+ # {S3Object#read} to upload to and download from S3 respectively.
80
79
  #
81
- # photo = s3.buckets['mybucket'].objects['photo.jpg']
80
+ # # streaming upload a file to S3
81
+ # obj.write(Pathname.new('/path/to/file.txt'))
82
82
  #
83
- # Writing to an S3Object:
83
+ # # streaming download from S3 to a file on disk
84
+ # File.open('file.txt', 'w') do |file|
85
+ # obj.read do |chunk|
86
+ # file.write(chunk)
87
+ # end
88
+ # end
89
+ #
90
+ # == Enumerating Objects
84
91
  #
85
- # photo.write(File.read('/some/photo.jpg'))
92
+ # You can enumerate objects in your buckets.
86
93
  #
87
- # Reading from an S3Object:
94
+ # # enumerate ALL objects in the bucket (even if the bucket contains
95
+ # # more than 1k objects)
96
+ # bucket.objects.each do |obj|
97
+ # puts obj.key
98
+ # end
88
99
  #
89
- # File.open("/some/path/on/disk.jpg", "w") do |f|
90
- # f.write(photo.read)
100
+ # # enumerate at most 20 objects with the given prefix
101
+ # bucket.objects.with_prefix('photos/').each(:limit => 20).each do |photo|
102
+ # puts photo.key
91
103
  # end
92
104
  #
93
- # See {S3Object} for more information on reading and writing to S3.
105
+ # See {ObjectCollection} and {S3Object} for more information on working
106
+ # with objects.
94
107
  #
95
108
  class S3
96
109
 
@@ -104,6 +117,8 @@ module AWS
104
117
  autoload :BucketVersionCollection, 'bucket_version_collection'
105
118
  autoload :Client, 'client'
106
119
  autoload :DataOptions, 'data_options'
120
+ autoload :EncryptionUtils, 'encryption_utils'
121
+ autoload :CipherIO, 'cipher_io'
107
122
  autoload :Errors, 'errors'
108
123
  autoload :MultipartUpload, 'multipart_upload'
109
124
  autoload :MultipartUploadCollection, 'multipart_upload_collection'
@@ -14,15 +14,180 @@
14
14
  module AWS
15
15
  class S3
16
16
 
17
- # Represents a single S3 bucket.
17
+ # Represents a bucket in S3.
18
18
  #
19
- # @example Creating a Bucket
20
- #
21
- # bucket = s3.buckets.create('mybucket')
19
+ # = Creating Buckets
22
20
  #
23
- # @example Getting an Existing Bucket
21
+ # You create a bucket by name. Bucket names must be globally unique
22
+ # and must be DNS compatible.
24
23
  #
25
- # bucket = s3.buckets['mybucket']
24
+ # s3 = AWS::S3.new
25
+ # bucket = s3.buckets.create('dns-compat-bucket-name')
26
+ #
27
+ # = Getting a Bucket
28
+ #
29
+ # You can create a reference to a bucket, given its name.
30
+ #
31
+ # bucket = s3.buckets['bucket-name'] # makes no request
32
+ # bucket.exists? #=> returns true/false
33
+ #
34
+ # = Enumerating Buckets
35
+ #
36
+ # The {BucketCollection} class is enumerable.
37
+ #
38
+ # s3.buckets.each do |bucket|
39
+ # puts bucket.name
40
+ # end
41
+ #
42
+ # = Deleting a Bucket
43
+ #
44
+ # You can delete an empty bucket you own.
45
+ #
46
+ # bucket = s3.buckets.create('my-temp-bucket')
47
+ # bucket.objects['abc'].write('xyz')
48
+ #
49
+ # bucket.clear! # deletes all object versions in batches
50
+ # bucket.delete
51
+ #
52
+ # You can alternatively call {#delete!} which will clear
53
+ # the bucket for your first.
54
+ #
55
+ # bucket.delete!
56
+ #
57
+ # = Objects
58
+ #
59
+ # Given a bucket you can access its objects, either by key or by
60
+ # enumeration.
61
+ #
62
+ # bucket.objects['key'] #=> makes no request, returns an S3Object
63
+ #
64
+ # bucket.objects.each do |obj|
65
+ # puts obj.key
66
+ # end
67
+ #
68
+ # See {ObjectCollection} and {S3Object} for more information on working
69
+ # with objects.
70
+ #
71
+ # = Bucket Policies and ACLs
72
+ #
73
+ # You can control access to your bucket and its contents a number
74
+ # of ways. You can specify a bucket ACL (access control list)
75
+ # or a bucket policy.
76
+ #
77
+ # == ACLs
78
+ #
79
+ # ACLs control access to your bucket and its contents via a list of
80
+ # grants and grantees.
81
+ #
82
+ # === Canned ACLs
83
+ #
84
+ # The simplest way to specify an ACL is to use one of Amazon's "canned"
85
+ # ACLs. Amazon accepts the following canned ACLs:
86
+ #
87
+ # * +:private+
88
+ # * +:public_read+
89
+ # * +:public_read_write+
90
+ # * +:authenticated_read+
91
+ # * +:bucket_owner_read+
92
+ # * +:bucket_owner_full_control+
93
+ #
94
+ # You can specify a the ACL at bucket creation or later update a bucket.
95
+ #
96
+ # # at create time, defaults to :private when not specified
97
+ # bucket = s3.buckets.create('name', :acl => :public_read)
98
+ #
99
+ # # replacing an existing bucket ACL
100
+ # bucket.acl = :private
101
+ #
102
+ # === Grants
103
+ #
104
+ # Alternatively you can specify a hash of grants. Each entry in the
105
+ # +:grant+ hash has a grant (key) and a list of grantees (values).
106
+ # Valid grant keys are:
107
+ #
108
+ # * +:grant_read+
109
+ # * +:grant_write+
110
+ # * +:grant_read_acp+
111
+ # * +:grant_write_acp+
112
+ # * +:grant_full_control+
113
+ #
114
+ # Each grantee can be a String, Hash or array of strings or hashes.
115
+ # The following example uses grants to provide public read
116
+ # to everyone while providing full control to a user by email address
117
+ # and to another by their account id (cannonical user id).
118
+ #
119
+ # bucket = s3.buckets.create('name', :grants => {
120
+ # :grant_read => [
121
+ # { :uri => "http://acs.amazonaws.com/groups/global/AllUsers" },
122
+ # ],
123
+ # :grant_full_control => [
124
+ # { :id => 'abc...mno' } # cannonical user id
125
+ # { :email_address => 'foo@bar.com' }, # email address
126
+ # ]
127
+ # })
128
+ #
129
+ # === ACL Object
130
+ #
131
+ # Lastly, you can build an ACL object and use a Ruby DSL to specify grants
132
+ # and grantees. See {ACLObject} for more information.
133
+ #
134
+ # # updating an existing bucket acl using ACLObject
135
+ # bucket.acl.change do |acl|
136
+ # acl.grants.reject! do |g|
137
+ # g.grantee.canonical_user_id != bucket.owner.id
138
+ # end
139
+ # end
140
+ #
141
+ # == Policies
142
+ #
143
+ # You can also work with bucket policies.
144
+ #
145
+ # policy = AWS::S3::Policy.new
146
+ # policy.allow(
147
+ # :actions => [:put_object, :get_object]
148
+ # :resources => [bucket]
149
+ # :principals => :any)
150
+ #
151
+ # bucket.policy = policy
152
+ #
153
+ # See {Core::Policy} and {S3::Policy} for more information on build
154
+ # policy objects.
155
+ #
156
+ # = Versioned Buckets
157
+ #
158
+ # You can enable versioning on a bucket you control. When versioning
159
+ # is enabled, S3 will keep track of each version of each object you
160
+ # write to the bucket (even deletions).
161
+ #
162
+ # bucket.versioning_enabled? #=> false
163
+ # bucket.enable_versioning
164
+ # # there is also a #disable_versioning method
165
+ #
166
+ # obj = bucket.objects['my-obj']
167
+ # obj.write('a')
168
+ # obj.write('b')
169
+ # obj.delete
170
+ # obj.write('c')
171
+ #
172
+ # obj.versions.each do |obj_version|
173
+ # if obj_version.delete_marker?
174
+ # puts obj_version.read
175
+ # else
176
+ # puts "- DELETE MARKER"
177
+ # end
178
+ # end
179
+ #
180
+ # Alternatively you can enumerate all versions of all objects in your
181
+ # bucket.
182
+ #
183
+ # bucket.versions.each do |obj_version|
184
+ # puts obj_version.key + " : " + obj_version.version_id
185
+ # end
186
+ #
187
+ # See {BucketVersionCollection}, {ObjectVersionCollection} and
188
+ # {ObjectVersion} for more information on working with objects in
189
+ # a versioned bucket. Also see the S3 documentation for information
190
+ # on object versioning.
26
191
  #
27
192
  class Bucket
28
193
 
@@ -0,0 +1,119 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class S3
16
+
17
+ # @private
18
+ class CipherIO
19
+
20
+ def initialize cipher, stream, stream_size = nil
21
+
22
+ @stream = stream
23
+ @stream_size = stream_size
24
+ @orig_cipher = cipher.clone
25
+
26
+ reset_cipher
27
+
28
+ # add a #rewind method if the original stream can be rewound
29
+ if @stream.respond_to?(:rewind)
30
+ Core::MetaUtils.extend_method(self, :rewind) do
31
+ reset_cipher
32
+ @stream.rewind
33
+ end
34
+ end
35
+
36
+ # add a #size method if the stream size is known
37
+ if stream_size
38
+ Core::MetaUtils.extend_method(self, :size) do
39
+ EncryptionUtils.get_encrypted_size(@stream_size)
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ # @return [String] Returns the requested number of bytes. If no byte
46
+ # amount is given, it will return the entire body of encrypted data
47
+ def read bytes = nil
48
+ if bytes
49
+ (@eof) ? nil : read_chunk(bytes)
50
+ else
51
+ (@eof) ? "" : read_all()
52
+ end
53
+ end
54
+
55
+ # @return [Boolean] Returns +true+ when the entire stream has been read.
56
+ def eof?
57
+ @eof
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :cipher
63
+
64
+ # Sets the CipherIO in a reset state without having to know anything
65
+ # about the cipher
66
+ def reset_cipher
67
+ @cipher = @orig_cipher.clone
68
+ @eof = false
69
+ @final = nil
70
+ end
71
+
72
+ # @return [String] Returns an encrytped chunk
73
+ def read_chunk bytes
74
+ unless @final
75
+ # If given a number of bytes, read it out and work out encryption
76
+ # issues
77
+ chunk = @stream.read(bytes)
78
+
79
+ # If there is nothing, finish the encryption
80
+ if chunk and chunk.length > 0
81
+ handle_finish(bytes, cipher.update(chunk))
82
+ else
83
+ @eof = true
84
+ cipher.final
85
+ end
86
+ # Read as much as possible if not given a byte size
87
+ else
88
+ @eof = true
89
+ @final
90
+ end
91
+ end
92
+
93
+ # @return [String] Returns the entire encrypted data
94
+ def read_all
95
+ @eof = true
96
+ body = @stream.read()
97
+ data = (body and body.length > 0) ? cipher.update(body) : ""
98
+ data << cipher.final
99
+ end
100
+
101
+ # Figures out how much of the final block goes into the current chunk
102
+ # and adds it.
103
+ # @return [String] Returns the encrypted chunk with possible padding.
104
+ def handle_finish(bytes, chunk)
105
+ free_space = bytes - chunk.size
106
+
107
+ if free_space > 0
108
+ @final = cipher.final
109
+ chunk << @final[0..free_space-1]
110
+ @final = @final[free_space-1..@final.size-1]
111
+ @eof = true unless @final and @final.size > 0
112
+ end
113
+
114
+ chunk
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -295,7 +295,7 @@ module AWS
295
295
 
296
296
  process_response do |response|
297
297
  regex = />(.*)<\/LocationConstraint>/
298
- matches = response.http_response.body.match(regex)
298
+ matches = response.http_response.body.to_s.match(regex)
299
299
  response.data[:location_constraint] = matches ? matches[1] : nil
300
300
  end
301
301
 
@@ -573,10 +573,14 @@ module AWS
573
573
  # * +:private+
574
574
  # * +:public_read+
575
575
  # * ...
576
- # @option options [Symbol] :storage_class+ (:standard)
576
+ # @option options [String] :storage_class+ ('STANDARD')
577
577
  # Controls whether Reduced Redundancy Storage is enabled for
578
- # the object. Valid values are +:standard+ and
579
- # +:reduced_redundancy+.
578
+ # the object. Valid values are 'STANDARD' and
579
+ # 'REDUCED_REDUNDANCY'.
580
+ # @option options [Symbol,String] :server_side_encryption (nil) The
581
+ # algorithm used to encrypt the object on the server side
582
+ # (e.g. :aes256).
583
+ # object on the server side, e.g. +:aes256+)
580
584
  # @option options [String] :cache_control
581
585
  # Can be used to specify caching behavior.
582
586
  # @option options [String] :content_disposition
@@ -610,19 +614,20 @@ module AWS
610
614
  :content_disposition => 'Content-Disposition',
611
615
  :content_encoding => 'Content-Encoding',
612
616
  :content_type => 'Content-Type',
613
- :storage_class => 'x-amz-storage-class',
614
- :server_side_encryption => 'x-amz-server-side-encryption',
615
617
  :expires => 'Expires'
616
618
  }) do
617
619
 
618
- configure_request do |request, options, block|
619
- options[:server_side_encryption] =
620
- options[:server_side_encryption].to_s.upcase if
621
- options[:server_side_encryption].kind_of?(Symbol)
622
- super(request, options)
623
- set_request_data(request, options, block)
620
+ configure_request do |request, options|
621
+
622
+ options = compute_write_options(options)
623
+ set_body_stream_and_content_length(request, options)
624
+
624
625
  request.metadata = options[:metadata]
625
626
  request.storage_class = options[:storage_class]
627
+ request.server_side_encryption = options[:server_side_encryption]
628
+
629
+ super(request, options)
630
+
626
631
  end
627
632
 
628
633
  process_response do |response|
@@ -637,6 +642,7 @@ module AWS
637
642
  end
638
643
 
639
644
  add_sse_to_response(response)
645
+
640
646
  end
641
647
 
642
648
  simulate_response do |response|
@@ -826,8 +832,13 @@ module AWS
826
832
  # @option options [String] :content_disposition
827
833
  # @option options [String] :content_encoding
828
834
  # @option options [String] :content_type
829
- # @option options [String] :storage_class
830
- # @option options [String] :server_side_encryption
835
+ # @option options [String] :storage_class+ ('STANDARD')
836
+ # Controls whether Reduced Redundancy Storage is enabled for
837
+ # the object. Valid values are 'STANDARD' and
838
+ # 'REDUCED_REDUNDANCY'.
839
+ # @option options [Symbol,String] :server_side_encryption (nil) The
840
+ # algorithm used to encrypt the object on the server side
841
+ # (e.g. :aes256).
831
842
  # @option options [String] :expires
832
843
  # @option options [String] :acl A canned ACL (e.g. 'private',
833
844
  # 'public-read', etc). See the S3 API documentation for
@@ -851,23 +862,20 @@ module AWS
851
862
  :content_disposition => 'Content-Disposition',
852
863
  :content_encoding => 'Content-Encoding',
853
864
  :content_type => 'Content-Type',
854
- :storage_class => 'x-amz-storage-class',
855
- :server_side_encryption => 'x-amz-server-side-encryption',
856
865
  :expires => 'Expires'
857
866
  }) do
858
867
 
859
868
  configure_request do |req, options|
860
- options[:server_side_encryption] =
861
- options[:server_side_encryption].to_s.upcase if
862
- options[:server_side_encryption].kind_of?(Symbol)
863
- super(req, options)
864
869
  req.metadata = options[:metadata]
865
870
  req.storage_class = options[:storage_class]
871
+ req.server_side_encryption = options[:server_side_encryption]
872
+ super(req, options)
866
873
  end
867
874
 
868
875
  process_response do |response|
869
876
  add_sse_to_response(response)
870
877
  end
878
+
871
879
  end
872
880
 
873
881
  # @overload list_multipart_uploads(options = {})
@@ -933,26 +941,30 @@ module AWS
933
941
  # @param [Hash] options
934
942
  # @option options [required,String] :bucket_name
935
943
  # @option options [required,String] :key
944
+ # @option options [required,String] :upload_id
945
+ # @option options [required,Integer] :part_number
936
946
  # @option options [required,String,Pathname,File,IO] :data
937
947
  # The data to upload. This can be provided as a string,
938
948
  # a Pathname object, or any object that responds to
939
949
  # +#read+ and +#eof?+ (e.g. IO, File, Tempfile, StringIO, etc).
940
- # @option options [required,String] :upload_id
941
- # @option options [required,Integer] :part_number
942
950
  # @return [Core::Response]
943
951
  object_method(:upload_part, :put,
944
952
  :header_options => {
945
953
  :content_md5 => 'Content-MD5'
946
954
  }) do
947
- configure_request do |request, options, block|
948
- require_upload_id!(options[:upload_id])
949
- validate!("part_number", options[:part_number]) do
950
- "must not be blank" if options[:part_number].to_s.empty?
951
- end
952
- super(request, options)
953
- set_request_data(request, options, block)
955
+ configure_request do |request, options|
956
+
957
+ options = compute_write_options(options)
958
+ set_body_stream_and_content_length(request, options)
959
+
960
+ require_upload_id!(options[:upload_id])
954
961
  request.add_param('uploadId', options[:upload_id])
962
+
963
+ require_part_number!(options[:part_number])
955
964
  request.add_param('partNumber', options[:part_number])
965
+
966
+ super(request, options)
967
+
956
968
  end
957
969
 
958
970
  process_response do |response|
@@ -1051,6 +1063,13 @@ module AWS
1051
1063
  # @option options [String] :acl A canned ACL (e.g. 'private',
1052
1064
  # 'public-read', etc). See the S3 API documentation for
1053
1065
  # a complete list of valid values.
1066
+ # @option options [Symbol,String] :server_side_encryption (nil) The
1067
+ # algorithm used to encrypt the object on the server side
1068
+ # (e.g. :aes256).
1069
+ # @option options [String] :storage_class+ ('STANDARD')
1070
+ # Controls whether Reduced Redundancy Storage is enabled for
1071
+ # the object. Valid values are 'STANDARD' and
1072
+ # 'REDUCED_REDUNDANCY'.
1054
1073
  # @option options [String] :grant_read
1055
1074
  # @option options [String] :grant_write
1056
1075
  # @option options [String] :grant_read_acp
@@ -1067,25 +1086,21 @@ module AWS
1067
1086
  :copy_source => 'x-amz-copy-source',
1068
1087
  :cache_control => 'Cache-Control',
1069
1088
  :metadata_directive => 'x-amz-metadata-directive',
1070
- :storage_class => 'x-amz-storage-class',
1071
- :server_side_encryption => 'x-amz-server-side-encryption',
1072
1089
  :content_type => 'Content-Type',
1073
1090
  }) do
1074
1091
 
1075
1092
  configure_request do |req, options|
1076
- # TODO : validate metadata directive COPY / REPLACE
1077
- # TODO : validate storage class STANDARD / REDUCED_REDUNDANCY
1078
- # TODO : add validations for storage class in other places used
1093
+
1079
1094
  validate!(:copy_source, options[:copy_source]) do
1080
1095
  "may not be blank" if options[:copy_source].to_s.empty?
1081
1096
  end
1097
+
1082
1098
  options = options.merge(:copy_source => escape_path(options[:copy_source]))
1083
- options[:server_side_encryption] =
1084
- options[:server_side_encryption].to_s.upcase if
1085
- options[:server_side_encryption].kind_of?(Symbol)
1086
1099
  super(req, options)
1087
1100
  req.metadata = options[:metadata]
1088
1101
  req.storage_class = options[:storage_class]
1102
+ req.server_side_encryption = options[:server_side_encryption]
1103
+
1089
1104
  if options[:version_id]
1090
1105
  req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
1091
1106
  end
@@ -1130,20 +1145,15 @@ module AWS
1130
1145
  end
1131
1146
  end
1132
1147
 
1133
- def should_retry? response
1148
+ def retryable_error? response
1134
1149
  super or
1135
- response.request_type == :complete_multipart_upload &&
1136
- extract_error_details(response)
1150
+ (response.request_type == :complete_multipart_upload &&
1151
+ extract_error_details(response))
1137
1152
  # complete multipart upload can return an error inside a
1138
1153
  # 200 level response -- this forces us to parse the
1139
1154
  # response for errors every time
1140
1155
  end
1141
1156
 
1142
- def set_request_data request, options, block
1143
- request.body_stream = data_stream_from(options, &block)
1144
- request.headers['Content-Length'] = content_length_from(options)
1145
- end
1146
-
1147
1157
  def new_request
1148
1158
  req = S3::Request.new
1149
1159
  req.force_path_style = config.s3_force_path_style?
@@ -1320,12 +1330,32 @@ module AWS
1320
1330
  end
1321
1331
  end
1322
1332
 
1333
+ def set_body_stream_and_content_length request, options
1334
+
1335
+ unless options[:content_length]
1336
+ msg = "S3 requires a content-length header, unable to determine "
1337
+ msg << "the content length of the data provided, please set "
1338
+ msg << ":content_length"
1339
+ raise ArgumentError, msg
1340
+ end
1341
+
1342
+ request.headers['content-length'] = options[:content_length]
1343
+ request.body_stream = options[:data]
1344
+
1345
+ end
1346
+
1323
1347
  def require_upload_id!(upload_id)
1324
1348
  validate!("upload_id", upload_id) do
1325
1349
  "must not be blank" if upload_id.to_s.empty?
1326
1350
  end
1327
1351
  end
1328
1352
 
1353
+ def require_part_number! part_number
1354
+ validate!("part_number", part_number) do
1355
+ "must not be blank" if part_number.to_s.empty?
1356
+ end
1357
+ end
1358
+
1329
1359
  def validate_parts!(parts)
1330
1360
  validate!("parts", parts) do
1331
1361
  if !parts.kind_of?(Array)