awsum 0.5

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