awsum 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. data/.autotest +1 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +44 -0
  6. data/LICENSE +19 -0
  7. data/README.rdoc +42 -0
  8. data/Rakefile +75 -0
  9. data/awsum.gemspec +199 -0
  10. data/lib/awsum.rb +20 -0
  11. data/lib/awsum/ec2.rb +741 -0
  12. data/lib/awsum/ec2/address.rb +67 -0
  13. data/lib/awsum/ec2/availability_zone.rb +16 -0
  14. data/lib/awsum/ec2/image.rb +62 -0
  15. data/lib/awsum/ec2/instance.rb +57 -0
  16. data/lib/awsum/ec2/keypair.rb +16 -0
  17. data/lib/awsum/ec2/parsers/address_parser.rb +61 -0
  18. data/lib/awsum/ec2/parsers/availability_zone_parser.rb +57 -0
  19. data/lib/awsum/ec2/parsers/image_parser.rb +74 -0
  20. data/lib/awsum/ec2/parsers/instance_parser.rb +90 -0
  21. data/lib/awsum/ec2/parsers/keypair_parser.rb +64 -0
  22. data/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser.rb +34 -0
  23. data/lib/awsum/ec2/parsers/region_parser.rb +56 -0
  24. data/lib/awsum/ec2/parsers/register_image_parser.rb +34 -0
  25. data/lib/awsum/ec2/parsers/reserved_instance_parser.rb +64 -0
  26. data/lib/awsum/ec2/parsers/reserved_instances_offering_parser.rb +63 -0
  27. data/lib/awsum/ec2/parsers/security_group_parser.rb +106 -0
  28. data/lib/awsum/ec2/parsers/snapshot_parser.rb +64 -0
  29. data/lib/awsum/ec2/parsers/volume_parser.rb +77 -0
  30. data/lib/awsum/ec2/region.rb +54 -0
  31. data/lib/awsum/ec2/reserved_instance.rb +24 -0
  32. data/lib/awsum/ec2/reserved_instances_offering.rb +20 -0
  33. data/lib/awsum/ec2/security_group.rb +74 -0
  34. data/lib/awsum/ec2/snapshot.rb +23 -0
  35. data/lib/awsum/ec2/volume.rb +65 -0
  36. data/lib/awsum/error.rb +53 -0
  37. data/lib/awsum/net_fix.rb +100 -0
  38. data/lib/awsum/parser.rb +18 -0
  39. data/lib/awsum/requestable.rb +216 -0
  40. data/lib/awsum/s3.rb +220 -0
  41. data/lib/awsum/s3/bucket.rb +28 -0
  42. data/lib/awsum/s3/headers.rb +24 -0
  43. data/lib/awsum/s3/object.rb +138 -0
  44. data/lib/awsum/s3/parsers/bucket_parser.rb +43 -0
  45. data/lib/awsum/support.rb +94 -0
  46. data/spec/fixtures/ec2/addresses.xml +10 -0
  47. data/spec/fixtures/ec2/allocate_address.xml +5 -0
  48. data/spec/fixtures/ec2/associate_address.xml +5 -0
  49. data/spec/fixtures/ec2/attach_volume.xml +9 -0
  50. data/spec/fixtures/ec2/authorize_ip_access.xml +5 -0
  51. data/spec/fixtures/ec2/authorize_owner_group_access.xml +5 -0
  52. data/spec/fixtures/ec2/authorize_owner_group_access_error.xml +2 -0
  53. data/spec/fixtures/ec2/availability_zones.xml +16 -0
  54. data/spec/fixtures/ec2/available_volume.xml +14 -0
  55. data/spec/fixtures/ec2/create_key_pair.xml +29 -0
  56. data/spec/fixtures/ec2/create_security_group.xml +5 -0
  57. data/spec/fixtures/ec2/create_snapshot.xml +9 -0
  58. data/spec/fixtures/ec2/create_volume.xml +10 -0
  59. data/spec/fixtures/ec2/delete_key_pair.xml +5 -0
  60. data/spec/fixtures/ec2/delete_security_group.xml +5 -0
  61. data/spec/fixtures/ec2/delete_snapshot.xml +5 -0
  62. data/spec/fixtures/ec2/delete_volume.xml +5 -0
  63. data/spec/fixtures/ec2/deregister_image.xml +5 -0
  64. data/spec/fixtures/ec2/detach_volume.xml +9 -0
  65. data/spec/fixtures/ec2/disassociate_address.xml +5 -0
  66. data/spec/fixtures/ec2/image.xml +15 -0
  67. data/spec/fixtures/ec2/images.xml +77 -0
  68. data/spec/fixtures/ec2/instance.xml +36 -0
  69. data/spec/fixtures/ec2/instances.xml +88 -0
  70. data/spec/fixtures/ec2/internal_error.xml +2 -0
  71. data/spec/fixtures/ec2/invalid_amiid_error.xml +2 -0
  72. data/spec/fixtures/ec2/invalid_request_error.xml +2 -0
  73. data/spec/fixtures/ec2/key_pairs.xml +10 -0
  74. data/spec/fixtures/ec2/purchase_reserved_instances_offering.xml +5 -0
  75. data/spec/fixtures/ec2/purchase_reserved_instances_offerings.xml +6 -0
  76. data/spec/fixtures/ec2/regions.xml +14 -0
  77. data/spec/fixtures/ec2/register_image.xml +5 -0
  78. data/spec/fixtures/ec2/release_address.xml +5 -0
  79. data/spec/fixtures/ec2/reserved_instances.xml +18 -0
  80. data/spec/fixtures/ec2/reserved_instances_offering.xml +15 -0
  81. data/spec/fixtures/ec2/reserved_instances_offerings.xml +276 -0
  82. data/spec/fixtures/ec2/revoke_ip_access.xml +5 -0
  83. data/spec/fixtures/ec2/revoke_owner_group_access.xml +5 -0
  84. data/spec/fixtures/ec2/run_instances.xml +30 -0
  85. data/spec/fixtures/ec2/security_groups.xml +159 -0
  86. data/spec/fixtures/ec2/snapshots.xml +13 -0
  87. data/spec/fixtures/ec2/terminate_instances.xml +17 -0
  88. data/spec/fixtures/ec2/unassociated_address.xml +10 -0
  89. data/spec/fixtures/ec2/volumes.xml +23 -0
  90. data/spec/fixtures/errors/invalid_parameter_value.xml +2 -0
  91. data/spec/fixtures/s3/buckets.xml +2 -0
  92. data/spec/fixtures/s3/copy_failure.xml +23 -0
  93. data/spec/fixtures/s3/invalid_request_signature.xml +5 -0
  94. data/spec/fixtures/s3/keys.xml +2 -0
  95. data/spec/lib/awsum/ec2/address_spec.rb +149 -0
  96. data/spec/lib/awsum/ec2/availability_zones_spec.rb +21 -0
  97. data/spec/lib/awsum/ec2/image_spec.rb +92 -0
  98. data/spec/lib/awsum/ec2/instance_spec.rb +125 -0
  99. data/spec/lib/awsum/ec2/keypair_spec.rb +55 -0
  100. data/spec/lib/awsum/ec2/parsers/address_parser_spec.rb +51 -0
  101. data/spec/lib/awsum/ec2/parsers/availability_zone_parser_spec.rb +28 -0
  102. data/spec/lib/awsum/ec2/parsers/image_parser_spec.rb +66 -0
  103. data/spec/lib/awsum/ec2/parsers/instance_parser_spec.rb +75 -0
  104. data/spec/lib/awsum/ec2/parsers/keypair_parser_spec.rb +74 -0
  105. data/spec/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser_spec.rb +14 -0
  106. data/spec/lib/awsum/ec2/parsers/region_parser_spec.rb +27 -0
  107. data/spec/lib/awsum/ec2/parsers/register_image_parser_spec.rb +15 -0
  108. data/spec/lib/awsum/ec2/parsers/reserved_instance_parser_spec.rb +35 -0
  109. data/spec/lib/awsum/ec2/parsers/reserved_instances_offering_parser_spec.rb +32 -0
  110. data/spec/lib/awsum/ec2/parsers/security_group_parser_spec.rb +78 -0
  111. data/spec/lib/awsum/ec2/parsers/snapshot_parser_spec.rb +30 -0
  112. data/spec/lib/awsum/ec2/parsers/volume_parser_spec.rb +35 -0
  113. data/spec/lib/awsum/ec2/region_spec.rb +73 -0
  114. data/spec/lib/awsum/ec2/reserved_instance_spec.rb +61 -0
  115. data/spec/lib/awsum/ec2/reserved_instances_offering_spec.rb +33 -0
  116. data/spec/lib/awsum/ec2/security_group_spec.rb +179 -0
  117. data/spec/lib/awsum/ec2/snapshots_spec.rb +69 -0
  118. data/spec/lib/awsum/ec2/volume_spec.rb +107 -0
  119. data/spec/lib/awsum/ec2_spec.rb +6 -0
  120. data/spec/lib/awsum/error_spec.rb +31 -0
  121. data/spec/lib/awsum/requestable_spec.rb +126 -0
  122. data/spec/lib/awsum/s3/bucket_spec.rb +95 -0
  123. data/spec/lib/awsum/s3/object_spec.rb +128 -0
  124. data/spec/lib/awsum/s3/parsers/bucket_parser_spec.rb +41 -0
  125. data/spec/lib/awsum/s3/parsers/object_parser_spec.rb +41 -0
  126. data/spec/spec_helper.rb +16 -0
  127. metadata +240 -0
@@ -0,0 +1,18 @@
1
+ require 'rexml/document'
2
+
3
+ module Awsum
4
+ class Parser #:nodoc:
5
+ def parse(xml_text)
6
+ REXML::Document.parse_stream(xml_text, self)
7
+ result
8
+ end
9
+
10
+ #--
11
+ #Methods to be overridden by each Parser implementation
12
+ def result ; end
13
+ def tag_start(tag, attributes) ; end
14
+ def text(text) ; end
15
+ def tag_end(tag) ; end
16
+ def xmldecl(*args) ; end
17
+ end
18
+ end
@@ -0,0 +1,216 @@
1
+ require 'base64'
2
+ require 'cgi'
3
+ require 'digest/md5'
4
+ require 'openssl'
5
+ require 'time'
6
+ require 'uri'
7
+ require 'awsum/error'
8
+
9
+ require 'net/https'
10
+ require 'awsum/net_fix'
11
+
12
+ module Awsum
13
+ module Requestable #:nodoc:
14
+
15
+ private
16
+ # Sends a request with query parameters
17
+ #
18
+ # Used for EC2 requests
19
+ def send_query_request(params)
20
+ standard_options = {
21
+ 'AWSAccessKeyId' => @access_key,
22
+ 'Version' => API_VERSION,
23
+ 'Timestamp' => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
24
+ 'SignatureVersion' => SIGNATURE_VERSION,
25
+ 'SignatureMethod' => 'HmacSHA256'
26
+ }
27
+ params = standard_options.merge(params)
28
+
29
+ #Put parameters into query string format
30
+ params_string = params.delete_if{|k,v| v.nil?}.sort{|a,b|
31
+ a[0].to_s <=> b[0].to_s
32
+ }.collect{|key, val|
33
+ "#{CGI::escape(key.to_s)}=#{CGI::escape(val.to_s)}"
34
+ }.join('&')
35
+ params_string.gsub!('+', '%20')
36
+
37
+ #Create request signature
38
+ signature_string = "GET\n#{host}\n/\n#{params_string}"
39
+ signature = sign(signature_string, 'sha256')
40
+
41
+ #Attach signature to query string
42
+ params_string << "&Signature=#{CGI::escape(signature)}"
43
+
44
+ url = "https://#{host}/?#{params_string}"
45
+ response = process_request('GET', url)
46
+ if response.is_a?(Net::HTTPSuccess)
47
+ response
48
+ else
49
+ raise Awsum::Error.new(response)
50
+ end
51
+ end
52
+
53
+ # Sends a full request including headers and possible post data
54
+ #
55
+ # Used for S3 requests
56
+ def send_s3_request(method = 'GET', options = {}, &block)
57
+ bucket = options[:bucket] || ''
58
+ key = options[:key] || ''
59
+ parameters = options[:parameters] || {}
60
+ data = options[:data]
61
+ headers = {}
62
+ (options[:headers] || {}).each { |k,v| headers[k.downcase] = v }
63
+
64
+ #Add the host header
65
+ host_name = "#{bucket}#{bucket.blank? ? '' : '.'}#{host}"
66
+ headers['host'] = host_name
67
+
68
+ # Ensure required headers are in place
69
+ if !data.nil? && headers['content-md5'].nil?
70
+ if data.respond_to?(:read)
71
+ if data.respond_to?(:rewind)
72
+ digest = Digest::MD5.new
73
+ while chunk = data.read(1024 * 1024)
74
+ digest << chunk
75
+ end
76
+ data.rewind
77
+
78
+ headers['content-md5'] = Base64::encode64(digest.digest).gsub(/\n/, '')
79
+ end
80
+ else
81
+ headers['content-md5'] = Base64::encode64(Digest::MD5.digest(data)).gsub(/\n/, '')
82
+ end
83
+ end
84
+ if !data.nil? && headers['content-length'].nil?
85
+ headers['content-length'] = (data.respond_to?(:lstat) ? data.lstat.size : data.size).to_s
86
+ end
87
+ headers['date'] = Time.now.rfc822 if headers['date'].nil?
88
+ headers['content-type'] ||= ''
89
+ if !data.nil?
90
+ headers['expect'] = '100-continue'
91
+ end
92
+
93
+ signature_string = generate_rest_signature_string(method, bucket, key, parameters, headers)
94
+ puts "signature_string: \n#{signature_string}\n\n" if ENV['DEBUG']
95
+
96
+ signature = sign(signature_string)
97
+
98
+ headers['authorization'] = "AWS #{@access_key}:#{signature}"
99
+
100
+ url = "https://#{host_name}#{key[0..0] == '/' ? '' : '/'}#{key}#{parameters.size == 0 ? '' : "?#{parameters.collect{|k,v| v.nil? ? k.to_s : "#{CGI::escape(k.to_s)}=#{CGI::escape(v.to_s)}"}.join('&')}"}"
101
+
102
+ process_request(method, url, headers, data) do |response|
103
+ if response.is_a?(Net::HTTPSuccess)
104
+ if block_given?
105
+ block.call(response)
106
+ else
107
+ response
108
+ end
109
+ else
110
+ raise Awsum::Error.new(response)
111
+ end
112
+ end
113
+ end
114
+
115
+ def generate_rest_signature_string(method, bucket, key, parameters, headers)
116
+ canonicalized_amz_headers = headers.sort{|a,b| a[0].to_s.downcase <=> b[0].to_s.downcase }.collect{|k, v| "#{k.to_s.downcase}:#{(v.is_a?(Array) ? v.join(',') : v).gsub(/\n/, ' ')}" if k =~ /\Ax-amz-/i }.compact.join("\n")
117
+ canonicalized_resource = "#{bucket.blank? ? '' : '/'}#{bucket}#{key[0..0] == '/' ? '' : '/'}#{key}"
118
+ ['acl', 'torrent', 'logging', 'location'].each do |sub_resource|
119
+ if parameters.has_key?(sub_resource)
120
+ canonicalized_resource << "?#{sub_resource}"
121
+ break
122
+ end
123
+ end
124
+
125
+ signature_string = "#{method}\n#{headers['content-md5']}\n#{headers['content-type']}\n#{headers['x-amz-date'].nil? ? headers['date'] : ''}\n#{canonicalized_amz_headers}#{canonicalized_amz_headers.blank? ? '' : "\n"}#{canonicalized_resource}"
126
+ end
127
+
128
+ def generate_s3_signed_request_url(method, bucket, key, expires = nil)
129
+ signature_string = generate_rest_signature_string(method, bucket, key, {}, {'date' => expires})
130
+ signature = sign(signature_string)
131
+ "http://#{bucket}#{bucket.blank? ? '' : '.'}#{host}#{key[0..0] == '/' ? '' : '/'}#{key}?AWSAccessKeyId=#{@access_key}&Signature=#{CGI::escape(signature)}&Expires=#{expires}"
132
+ end
133
+
134
+ def process_request(method, url, headers = {}, data = nil, &block)
135
+ #TODO: Allow secure/non-secure
136
+ uri = URI.parse(url)
137
+ uri.scheme = 'https'
138
+ uri.port = 443
139
+
140
+ #puts uri.to_s
141
+
142
+ Net::HTTP.version_1_1
143
+ con = Net::HTTP.new(uri.host, uri.port)
144
+ con.use_ssl = true
145
+ con.verify_mode = OpenSSL::SSL::VERIFY_NONE
146
+ con.start do |http|
147
+ case method.upcase
148
+ when 'GET'
149
+ request = Net::HTTP::Get.new("#{uri.path}#{"?#{uri.query}" if uri.query}")
150
+ when 'POST'
151
+ request = Net::HTTP::Post.new("#{uri.path}#{"?#{uri.query}" if uri.query}")
152
+ when 'PUT'
153
+ request = Net::HTTP::Put.new("#{uri.path}#{"?#{uri.query}" if uri.query}")
154
+ when 'DELETE'
155
+ request = Net::HTTP::Delete.new("#{uri.path}#{"?#{uri.query}" if uri.query}")
156
+ when 'HEAD'
157
+ request = Net::HTTP::Head.new("#{uri.path}#{"?#{uri.query}" if uri.query}")
158
+ end
159
+ request.initialize_http_header(headers)
160
+
161
+ unless data.nil?
162
+ if data.respond_to?(:read)
163
+ request.body_stream = data
164
+ else
165
+ request.body = data
166
+ end
167
+ end
168
+
169
+ if block_given?
170
+ http.request(request) do |response|
171
+ handle_response response, &block
172
+ end
173
+ else
174
+ response = http.request(request)
175
+ handle_response response
176
+ end
177
+ end
178
+ end
179
+
180
+ def handle_response(response, &block)
181
+ case response
182
+ when Net::HTTPSuccess
183
+ if block_given?
184
+ block.call(response)
185
+ else
186
+ response
187
+ end
188
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPTemporaryRedirect
189
+ new_uri = URI.parse(response['location'])
190
+ uri.host = new_uri.host
191
+ uri.path = "#{new_uri.path}#{uri.path unless uri.path = '/'}"
192
+ process_request(method, uri.to_s, headers, data, &block)
193
+ else
194
+ response
195
+ end
196
+ end
197
+
198
+ # Converts an array of paramters into <param_name>.<num> format
199
+ def array_to_params(arr, param_name)
200
+ arr = [arr] unless arr.is_a?(Array)
201
+ params = {}
202
+ arr.each_with_index do |value,i|
203
+ params["#{param_name}.#{i+1}"] = value
204
+ end
205
+ params
206
+ end
207
+
208
+ # Sign a string with a digest, wrap in HMAC digest and base64 encode
209
+ #
210
+ # ===Returns
211
+ # base64 encoded string with newlines removed
212
+ def sign(string, digest = 'sha1')
213
+ Base64::encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(digest), @secret_key, string)).gsub(/\n/, '')
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,220 @@
1
+ require 'awsum'
2
+ require 'awsum/s3/bucket'
3
+ require 'awsum/s3/object'
4
+ require 'awsum/s3/headers'
5
+
6
+ module Awsum
7
+ # Handles all interaction with Amazon S3
8
+ #
9
+ #--
10
+ # TODO: Change this to S3
11
+ # ==Getting Started
12
+ # Create an Awsum::Ec2 object and begin calling methods on it.
13
+ # require 'rubygems'
14
+ # require 'awsum'
15
+ # ec2 = Awsum::Ec2.new('your access id', 'your secret key')
16
+ # images = ec2.my_images
17
+ # ...
18
+ #
19
+ # All calls to EC2 can be done directly in this class, or through a more
20
+ # object oriented way through the various returned classes
21
+ #
22
+ # ==Examples
23
+ # ec2.image('ami-ABCDEF').run
24
+ #
25
+ # ec2.instance('i-123456789').volumes.each do |vol|
26
+ # vol.create_snapsot
27
+ # end
28
+ #
29
+ # ec2.regions.each do |region|
30
+ # region.use
31
+ # images.each do |img|
32
+ # puts "#{img.id} - #{region.name}"
33
+ # end
34
+ # end
35
+ # end
36
+ #
37
+ # ==Errors
38
+ # All methods will raise an Awsum::Error if an error is returned from Amazon
39
+ #
40
+ # ==Missing Methods
41
+ # If you need any of this functionality, please consider getting involved
42
+ # and help complete this library.
43
+ class S3
44
+ include Awsum::Requestable
45
+
46
+ # Create an new S3 instance
47
+ #
48
+ # The access_key and secret_key are both required to do any meaningful work.
49
+ #
50
+ # If you want to get these keys from environment variables, you can do that
51
+ # in your code as follows:
52
+ # s3 = Awsum::S3.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
53
+ def initialize(access_key = nil, secret_key = nil)
54
+ @access_key = access_key
55
+ @secret_key = secret_key
56
+ end
57
+
58
+ # List all the Bucket(s)
59
+ def buckets
60
+ response = send_s3_request
61
+ parser = Awsum::S3::BucketParser.new(self)
62
+ parser.parse(response.body)
63
+ end
64
+
65
+ def bucket(name)
66
+ Bucket.new(self, name)
67
+ end
68
+
69
+ # Create a new Bucket
70
+ #
71
+ # ===Parameters
72
+ # * <tt>bucket_name</tt> - The name of the new bucket
73
+ # * <tt>location</tt> <i>(optional)</i> - Can be <tt>:default</tt>, <tt>:us</tt> or <tt>:eu</tt>
74
+ def create_bucket(bucket_name, location = :default)
75
+ raise ArgumentError.new('Bucket name cannot be in an ip address style') if bucket_name =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
76
+ raise ArgumentError.new('Bucket name can only have lowercase letters, numbers, periods (.), underscores (_) and dashes (-)') unless bucket_name =~ /^[\w\d][-a-z\d._]+[a-z\d._]$/
77
+ raise ArgumentError.new('Bucket name cannot contain a dash (-) next to a period (.)') if bucket_name =~ /\.-|-\./
78
+ raise ArgumentError.new('Bucket name must be between 3 and 63 characters') if bucket_name.size < 3 || bucket_name.size > 63
79
+
80
+ data = nil
81
+ if location == :eu
82
+ data = '<CreateBucketConfiguration><LocationConstraint>EU</LocationConstraint></CreateBucketConfiguration>'
83
+ end
84
+
85
+ response = send_s3_request('PUT', :bucket => bucket_name, :data => data)
86
+ response.is_a?(Net::HTTPSuccess)
87
+ end
88
+
89
+ def delete_bucket(bucket_name)
90
+ response = send_s3_request('DELETE', :bucket => bucket_name)
91
+ response.is_a?(Net::HTTPSuccess)
92
+ end
93
+
94
+ # List the Key(s) of a Bucket
95
+ #
96
+ # ===Parameters
97
+ # * <tt>bucket_name</tt> - The name of the bucket to search for keys
98
+ # ====Options
99
+ # * <tt>:prefix</tt> - Limits the response to keys which begin with the indicated prefix. You can use prefixes to separate a bucket into different sets of keys in a way similar to how a file system uses folders.
100
+ # * <tt>:marker</tt> - Indicates where in the bucket to begin listing. The list will only include keys that occur lexicographically after marker. This is convenient for pagination: To get the next page of results use the last key of the current page as the marker.
101
+ # * <tt>:max_keys</tt> - The maximum number of keys you'd like to see in the response body. The server might return fewer than this many keys, but will not return more.
102
+ # * <tt>:delimeter</tt> - Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element in the CommonPrefixes collection. These rolled-up keys are not returned elsewhere in the response.
103
+ def keys(bucket_name, options = {})
104
+ paramters = {}
105
+ paramters['prefix'] = options[:prefix] if options[:prefix]
106
+ paramters['marker'] = options[:marker] if options[:marker]
107
+ paramters['max_keys'] = options[:max_keys] if options[:max_keys]
108
+ paramters['prefix'] = options[:prefix] if options[:prefix]
109
+
110
+ response = send_s3_request('GET', :bucket => bucket_name, :paramters => paramters)
111
+ parser = Awsum::S3::ObjectParser.new(self)
112
+ parser.parse(response.body)
113
+ end
114
+
115
+ # Create a new Object in the specified Bucket
116
+ #
117
+ # ===Parameters
118
+ # * <tt>bucket_name</tt> - The name of the Bucket in which to store the Key
119
+ # * <tt>key</tt> - The name/path of the Key to store
120
+ # * <tt>data</tt> - The data to be stored in this Object
121
+ # * <tt>headers</tt> - Standard HTTP headers to be sent along
122
+ # * <tt>meta_headers</tt> - Meta headers to be stored along with the key
123
+ # * <tt>acl</tt> - A canned access policy, can be one of <tt>:private</tt>, <tt>:public_read</tt>, <tt>:public_read_write</tt> or <tt>:authenticated_read</tt>
124
+ def create_object(bucket_name, key, data, headers = {}, meta_headers = {}, acl = :private)
125
+ headers = headers.dup
126
+ meta_headers.each do |k,v|
127
+ headers[k =~ /^x-amz-meta-/i ? k : "x-amz-meta-#{k}"] = v
128
+ end
129
+ headers['x-amz-acl'] = acl.to_s.gsub(/_/, '-')
130
+
131
+ response = send_s3_request('PUT', :bucket => bucket_name, :key => key, :headers => headers, :data => data)
132
+ response.is_a?(Net::HTTPSuccess)
133
+ end
134
+
135
+ # Retrieve the headers for this Object
136
+ #
137
+ # All header methods map directly to the Net::HTTPHeader module
138
+ def object_headers(bucket_name, key)
139
+ response = send_s3_request('HEAD', :bucket => bucket_name, :key => key)
140
+ Headers.new(response)
141
+ end
142
+
143
+ # Retrieve the data stored for the specified Object
144
+ #
145
+ # You can get the data as a single call or add a block to retrieve the data in chunks
146
+ # ==Examples
147
+ # data = s3.object_data('test-bucket', 'key')
148
+ #
149
+ # or
150
+ #
151
+ # s3.object_data('test-bucket', 'key') do |chunk|
152
+ # # handle chunk
153
+ # puts chunk
154
+ # end
155
+ def object_data(bucket_name, key, &block)
156
+ send_s3_request('GET', :bucket => bucket_name, :key => key) do |response|
157
+ if block_given?
158
+ response.read_body &block
159
+ return true
160
+ else
161
+ return response.body
162
+ end
163
+ end
164
+ end
165
+
166
+ # Deletes an Object from a Bucket
167
+ def delete_object(bucket_name, key)
168
+ response = send_s3_request('DELETE', :bucket => bucket_name, :key => key)
169
+ response.is_a?(Net::HTTPSuccess)
170
+ end
171
+
172
+ # Copy the contents of an Object to another key and/or bucket
173
+ #
174
+ # ===Parameters
175
+ # * <tt>source_bucket_name</tt> - The name of the Bucket from which to copy
176
+ # * <tt>source_key</tt> - The name of the Key from which to copy
177
+ # * <tt>destination_bucket_name</tt> - The name of the Bucket to which to copy (Can be nil if copying within the same bucket, or updating header data of existing Key)
178
+ # * <tt>destination_key</tt> - The name of the Key to which to copy (Can be nil if copying to a new bucket with same key, or updating header data of existing Key)
179
+ # * <tt>headers</tt> - If not nil, the headers are replaced with this information
180
+ # * <tt>meta_headers</tt> - If not nil, the meta headers are replaced with this information
181
+ #
182
+ #--
183
+ # TODO: Need to handle copy-if-... headers
184
+ def copy_object(source_bucket_name, source_key, destination_bucket_name = nil, destination_key= nil, headers = nil, meta_headers = nil)
185
+ raise ArgumentError.new('You must include one of destination_bucket_name, destination_key or headers to be replaced') if destination_bucket_name.nil? && destination_key.nil? && headers.nil? && meta_headers.nil?
186
+
187
+ headers = {
188
+ 'x-amz-copy-source' => "/#{source_bucket_name}/#{source_key}",
189
+ 'x-amz-metadata-directive' => (((destination_bucket_name.nil? && destination_key.nil?) || !(headers.nil? || meta_headers.nil?)) ? 'REPLACE' : 'COPY')
190
+ }.merge(headers||{})
191
+ meta_headers.each do |k,v|
192
+ headers[k =~ /^x-amz-meta-/i ? k : "x-amz-meta-#{k}"] = v
193
+ end unless meta_headers.nil?
194
+
195
+ destination_bucket_name ||= source_bucket_name
196
+ destination_key ||= source_key
197
+
198
+ response = send_s3_request('PUT', :bucket => destination_bucket_name, :key => destination_key, :headers => headers, :data => nil)
199
+ if response.is_a?(Net::HTTPSuccess)
200
+ #Check for delayed error (See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectCOPY.html#RESTObjectCOPY_Response)
201
+ response_body = response.body
202
+ if response_body =~ /<Error>/i
203
+ raise Awsum::Error.new(response)
204
+ else
205
+ true
206
+ end
207
+ end
208
+ end
209
+
210
+ #private
211
+ #The host to make all requests against
212
+ def host
213
+ @host ||= 's3.amazonaws.com'
214
+ end
215
+
216
+ def host=(host)
217
+ @host = host
218
+ end
219
+ end
220
+ end