aws-sdk 1.6.2 → 1.6.3

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.
@@ -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)