ruby-atmos-pure 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ # This does nothing except signal that the chosen XML parser to use is REXML
@@ -0,0 +1,221 @@
1
+ module Atmos
2
+ class Request # :nodoc:
3
+
4
+ def initialize(options = {})
5
+ valid = [:store, :default_tag].freeze
6
+ invalid = options.keys - valid
7
+ raise Atmos::Exceptions::ArgumentException,
8
+ "Unrecognized options: #{invalid.inspect}" if (!invalid.empty?)
9
+ Atmos::LOG.debug("Request.initialize: options: #{options.inspect}")
10
+
11
+ @baseurl = options[:store].uri
12
+ @uid = options[:store].uid
13
+ @secret = options[:store].secret
14
+ @http = options[:store].http
15
+ @tag = options[:default_tag]
16
+ end
17
+
18
+
19
+ #
20
+ # If there is no namespace option provided, we're using the object interface,
21
+ # otherwise we're using the namespace interface. Determine which it is, so we know
22
+ # which URI to use.
23
+ #
24
+ # If aoid is nil, we're creating a new object regardless.
25
+ #
26
+ def get_uri(action, options)
27
+
28
+ if (options.has_key?(:namespace) && action.has_key?(:namespace_uri))
29
+
30
+ # only two actions need namespace, so if the action doesn't have the
31
+ # :namespace_url key, we use the regular :object_url.
32
+ ns = (options[:namespace].start_with?('/')) ? options[:namespace][1..-1] : options[:namespace]
33
+ uri = action[:namespace_uri].sub(/:namespace/, ns)
34
+
35
+ else
36
+
37
+ uri = action[:object_uri]
38
+
39
+ # only need the following check/substution if the uri requires an id
40
+ if (!uri.index(':id').nil?)
41
+
42
+ if (options.has_key?(:id) && !options[:id].nil?)
43
+ uri = uri.sub(/:id/, options[:id])
44
+ else
45
+ raise Atmos::Exceptions::ArgumentException, "An id is required for this action. This is an internal error."
46
+ end
47
+ end
48
+ end
49
+
50
+ Atmos::LOG.info("uri: #{uri}")
51
+ uri
52
+ end
53
+
54
+
55
+
56
+ def do(actionname, options = {}, &block)
57
+ verbs = [:head, :get, :put, :post, :delete].freeze
58
+ Atmos::LOG.info("do #{actionname} options: #{options.inspect}")
59
+ raise Atmos::Exceptions::InternalLibraryException,
60
+ "Invalid REST action: #{actionname}" if (REST[actionname].nil?)
61
+
62
+ action = REST[actionname]
63
+ uri = get_uri(action, options)
64
+ url = URI::join(@baseurl.to_s, uri.to_s)
65
+ verb = action[:verb]
66
+
67
+ request = (verbs.include?(verb)) ? Net::HTTP.const_get(verb.to_s.capitalize).new(uri) : nil
68
+ raise Atmos::Exceptions::AtmosException,
69
+ "Couldn't create Net::HTTP request object for #{verb}" if (request.nil?)
70
+
71
+ headers = {}
72
+ headers['Date'] = Time.now().httpdate()
73
+ headers['x-emc-date'] = Time.now().httpdate()
74
+ headers['x-emc-uid'] = @uid
75
+
76
+ if (!action[:required_headers].nil?)
77
+ action[:required_headers].each do |header|
78
+ case header
79
+ when 'Content-Type' then
80
+ headers[header] = (!options[:mimetype].nil?) ? options[:mimetype] : 'binary/octet-stream'
81
+ when 'Content-Length' then
82
+ headers[header] = (!options[:length].nil?) ? options[:length] : 0
83
+ when 'x-emc-tags' then
84
+ headers[header] = (!options[header].nil?) ? options[header] : ""
85
+ else
86
+ if (options[header])
87
+ headers[header] = options[header]
88
+ else
89
+ raise "Value not supplied for required header: '#{header}'"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ if (!action[:optional_headers].nil?)
96
+ action[:optional_headers].each do |header|
97
+ if (header.eql?('Range') && options.has_key?('Range') && !options['Range'].nil?)
98
+ r = options['Range']
99
+ options.delete('Range')
100
+ #headers[header] = (r.kind_of?(Range)) ? "Bytes=#{r.first}-#{r.last}" : "Bytes=#{r.to_s}"
101
+ if (r.kind_of?(Range))
102
+ headers[header] = "Bytes=#{r.first}-#{r.last}"
103
+ Atmos::LOG.info("Request.initialize: given Range object: #{headers[header]}")
104
+ else
105
+ Atmos::LOG.info("Request.initialize: given range string: #{headers[header]}")
106
+ headers[header] = "Bytes=#{r.to_s}"
107
+ end
108
+ if ((!options[:data].nil?) and ((r.end-r.begin+1) != options[:data].length))
109
+ raise Atmos::Exceptions::ArgumentException, "The range length (#{r.end - r.begin + 1}) doesn't match the data length (#{options[:data].length})."
110
+ end
111
+ end
112
+ key = (action[:header2sym] && action[:header2sym][header]) ? action[:header2sym][header] : header
113
+
114
+ if (!options[key].nil?)
115
+ if (options[key].kind_of?(Hash))
116
+ headers[header] = ""
117
+ options[key].each do |key,val|
118
+ headers[header] += "#{key}=#{val}, "
119
+ end
120
+ headers[header].chop!
121
+ headers[header].chop!
122
+ elsif (options[key].kind_of?(Array))
123
+ headers[header] = ""
124
+ options[key].each do |val|
125
+ headers[header] += "#{val}, "
126
+ end
127
+ headers[header].chop!
128
+ headers[header].chop!
129
+ elsif (options[key].respond_to?(:to_s))
130
+ headers[header] = options[key]
131
+ else
132
+ raise "value for optional header '#{header}' id bad type: #{options[header].class}"
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ if (!@tag.nil? && !action[:optional_headers].nil? && action[:optional_headers].include?('x-emc-listable-meta'))
139
+ if (headers.has_key?('x-emc-listable-meta'))
140
+ headers['x-emc-listable-meta'] += ", #{@tag}=atmos-ruby-default"
141
+ else
142
+ headers['x-emc-listable-meta'] = "#{@tag}=atmos-ruby-default"
143
+ end
144
+ end
145
+
146
+ headers['x-emc-signature'] = calculate_signature(action[:verb], @secret, url, headers)
147
+
148
+ if (options[:data])
149
+ Atmos::LOG.info("there is data: [#{options[:data]}]")
150
+ if (options[:data].respond_to?(:read))
151
+ request.body_stream = options[:data]
152
+ else
153
+ request.body = options[:data]
154
+ end
155
+ headers['Content-Length'] =
156
+ options[:data].respond_to?(:lstat) ? options[:data].stat.size : options[:data].size
157
+ Atmos::LOG.info("set Content-Length to: #{headers['Content-Length']}\n")
158
+
159
+ end
160
+
161
+ headers.each do |key,val|
162
+ request[key] = val
163
+ end
164
+
165
+ @http.set_debug_output($stdout) if (Atmos::LOG.level == Log4r::DEBUG)
166
+
167
+
168
+ #
169
+ # so this is weird. because some of these atmos request may be
170
+ # insanely long, we need to allow a mechanism for our API layer
171
+ # to read progressively, not only all at once
172
+ #
173
+ if (block)
174
+ @http.request(request) do |response|
175
+ block.call(response)
176
+ end
177
+ return
178
+ end
179
+
180
+ response = @http.request(request)
181
+ Atmos::Parser::response_check_action_error(actionname, response)
182
+
183
+ Atmos::Response.new(response, actionname)
184
+ end
185
+
186
+
187
+ def calculate_signature(verb, secret, url, headers)
188
+ headers_copy = headers.clone
189
+
190
+ # normalize emc headers
191
+ headers_copy.each do |header, value|
192
+ if (header.start_with?('x-emc-'))
193
+ headers.delete(header)
194
+ headers[header.to_s.strip.downcase] = value.to_s.strip.sub(/\s+/, ' ').sub(/\n/, '')
195
+ end
196
+ end
197
+
198
+ # string together all emc headers, no newline at end
199
+ emc = ""
200
+ headers.keys.sort.each do |header|
201
+ if (header.start_with?('x-emc-'))
202
+ emc += "#{header}:#{headers[header]}\n"
203
+ end
204
+ end
205
+ emc.chomp!
206
+
207
+ hashstring = verb.to_s.upcase+"\n"
208
+ ['Content-Type', 'Range', 'Date'].each do |key|
209
+ hashstring += headers[key] if (!headers[key].nil?)
210
+ hashstring += "\n"
211
+ end
212
+
213
+ hashstring += url.path
214
+ hashstring += '?'+url.query if (!url.query.nil?)
215
+ hashstring += "\n"+ emc
216
+
217
+ Atmos::LOG.debug("calculate_signature: hashstring: #{hashstring}")
218
+ Base64.encode64(HMAC::SHA1.digest(Base64.decode64(secret), hashstring)).chomp()
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,116 @@
1
+ module Atmos
2
+ class Response # :nodoc:
3
+ attr_reader :http_response
4
+
5
+ def initialize(response, action)
6
+ @http_response = response
7
+ @action = action
8
+
9
+ if (response.kind_of?(Net::HTTPServerError))
10
+ raise Atmos::Exceptions::ServerException, response.message
11
+ elsif (response.kind_of?(Net::HTTPClientError))
12
+
13
+ raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
14
+
15
+ Atmos::LOG.debug("#{response.class}")
16
+ Atmos::LOG.debug("#{response.body}")
17
+
18
+ Atmos::Parser::response_check_action_error(@action, response)
19
+
20
+ elsif (!response.kind_of?(Net::HTTPSuccess))
21
+ raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
22
+ end
23
+
24
+ end
25
+
26
+ def each(header)
27
+ if (!self[header].nil?)
28
+ self[header].split(',').each do |elt|
29
+ if (!elt.index('=').nil?)
30
+ key,val = elt.split('=')
31
+ yield key.strip, val.strip
32
+ else
33
+ yield elt.strip
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def body
40
+ @http_response.body
41
+ end
42
+
43
+ def method_missing(sym, *args)
44
+ Atmos::LOG.info("method missing: #{sym}")
45
+ header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
46
+ if (REST[@action][:return_headers].include?(header))
47
+ Atmos::LOG.info("header: #{header}")
48
+ rv = Atmos::Util.header2hash(header, @http_response[header])
49
+ Atmos::LOG.debug("rv: #{rv.inspect}")
50
+ return rv
51
+ else
52
+ return super.method_missing(sym, *args)
53
+ end
54
+ end
55
+
56
+ def delta
57
+ if (REST[@action][:return_headers].include?('x-emc-delta'))
58
+ @http_response['x-emc-delta']
59
+ else
60
+ raise "A #{@action} request doesn't return a delta."
61
+ end
62
+ end
63
+
64
+ def policy
65
+ if (REST[@action][:return_headers].include?('x-emc-policy'))
66
+ @http_response['x-emc-policy']
67
+ else
68
+ raise "A #{@action} request doesn't return a policy description."
69
+ end
70
+ end
71
+
72
+ def id
73
+ if (REST[@action][:return_headers].include?('location'))
74
+ @http_response['location'][@http_response['location'].rindex('/')+1..-1]
75
+ else
76
+ raise "A #{@action} request doesn't return an id."
77
+ end
78
+ end
79
+
80
+ def headers
81
+ headers = {}
82
+ @http_response.each do |header, value|
83
+ headers[header] = value
84
+ end
85
+ headers
86
+ end
87
+
88
+ def [](header)
89
+ @http_response[header]
90
+ end
91
+
92
+ end
93
+
94
+
95
+
96
+
97
+
98
+
99
+ # while (response.kind_of?(Net::HTTPRedirection))
100
+
101
+ #From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
102
+ # rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
103
+
104
+ # puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
105
+
106
+ # if rurl.start_with?('/')
107
+ # puts("Got a relative redirect url: #{options[:url]}")
108
+ # options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
109
+ # end
110
+
111
+ # options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
112
+ # response = self.generic_request(options)
113
+ # end
114
+ # raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
115
+
116
+ end
data/lib/atmos/rest.rb ADDED
@@ -0,0 +1,145 @@
1
+ module Atmos
2
+ REST = {
3
+ :listable_tags => { :verb => :get,
4
+ :object_uri => '/rest/objects?listabletags',
5
+ :required_headers => ['x-emc-tags'],
6
+ :optional_headers => ['x-emc-token'],
7
+ :http_response => 200,
8
+ :return_headers => ['x-emc-listable-tags'],
9
+ },
10
+ :set_group_acl => { :verb => :post,
11
+ :object_uri => '/rest/objects/:id?acl',
12
+ :required_headers => ['x-emc-groupacl'],
13
+ :http_response => 200,
14
+ },
15
+ :set_user_acl => { :verb => :post,
16
+ :object_uri => '/rest/objects/:id?acl',
17
+ :required_headers => ['x-emc-useracl'],
18
+ :http_response => 200,
19
+ },
20
+ :set_metadata => { :verb => :post,
21
+ :object_uri => '/rest/objects/:id?metadata/user',
22
+ :http_response => 200,
23
+ :required_headers => ['x-emc-meta'],
24
+ },
25
+ :set_listable_metadata => { :verb => :post,
26
+ :object_uri => '/rest/objects/:id?metadata/user',
27
+ :http_response => 200,
28
+ :required_headers => ['x-emc-listable-meta'],
29
+ },
30
+ :delete_metadata => { :verb => :delete,
31
+ :object_uri => '/rest/objects/:id?metadata/user',
32
+ :http_response => 204,
33
+ :required_headers => ['x-emc-tags'],
34
+ },
35
+ :read_object => { :verb => :get,
36
+ :object_uri => '/rest/objects/:id',
37
+ :http_response => 200,
38
+ :optional_headers => ['Range'],
39
+ },
40
+ :update_object => { :verb => :put,
41
+ :object_uri => '/rest/objects/:id',
42
+ :http_response => 200,
43
+ :required_headers => ['Content-Type', 'Content-Length'],
44
+ :optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
45
+ },
46
+ :trunc_object => { :verb => :put,
47
+ :object_uri => '/rest/objects/:id',
48
+ :http_response => 200,
49
+ :required_headers => ['Content-Length'],
50
+ :optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
51
+ },
52
+ :get_service_info => { :verb => :get,
53
+ :object_uri => '/rest/service',
54
+ :response => :xml,
55
+ :http_response => 200,
56
+ },
57
+
58
+ :list_objects => { :verb => :get,
59
+ :object_uri => '/rest/objects',
60
+ :response => :xml,
61
+ :http_response => 200,
62
+ :required_headers => ['x-emc-tags', 'Content-Type'],
63
+ :optional_headers => ['x-emc-include-meta', 'x-emc-tags'],
64
+ :errors => { '1003' => { :class => Atmos::Exceptions::ArgumentException ,
65
+ :message => "No such listable tag found." } },
66
+ },
67
+
68
+ :create_object => { :verb => :post,
69
+ :namespace_uri => '/rest/namespace/:namespace',
70
+ :object_uri => '/rest/objects',
71
+ :http_response => 201,
72
+ :optional_headers => ['x-emc-wschecksum', 'x-emc-useracl', 'x-emc-groupacl', 'x-emc-meta', 'x-emc-listable-meta'],
73
+ :required_headers => ['Content-Type', 'Content-Length'],
74
+ :return_headers => ['x-emc-delta', 'x-emc-policy', 'location'],
75
+ :header2sym => {'x-emc-wschecksum' => :checksum,
76
+ 'x-emc-useracl' => :user_acl,
77
+ 'x-emc-groupacl' => :group_acl,
78
+ 'x-emc-meta' => :metadata,
79
+ 'x-emc-listable-meta' => :listable_metadata},
80
+ },
81
+
82
+ :get_object_info => { :verb => :get,
83
+ :namespace_uri => '/rest/namespace/:namespace?info',
84
+ :object_uri => '/rest/objects/:id?info',
85
+ :http_response => 200,
86
+ :return_headers => ['x-emc-policy'],
87
+ },
88
+ :delete_object => { :verb => :delete,
89
+ :object_uri => '/rest/objects/:id',
90
+ :http_response => 204,
91
+ :required_headers => ['Content-Type'],
92
+ :return_headers => ['x-emc-delta', 'x-emc-policy'],
93
+ :errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
94
+ :message => "Object not found to delete." } },
95
+ },
96
+
97
+ :list_system_metadata=> { :verb => :get,
98
+ :object_uri => '/rest/objects/:id?metadata/system',
99
+ :http_response => 200,
100
+ :return_headers => ['x-emc-meta'],
101
+ :errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
102
+ :message => "Object not found to delete." } },
103
+ },
104
+ :list_metadata => { :verb => :get,
105
+ :object_uri => '/rest/objects/:id?metadata/user',
106
+ :http_response => 200,
107
+ :required_headers => ['Content-Type'],
108
+ :return_headers => ['x-emc-meta', 'x-emc-listable-meta'],
109
+ },
110
+
111
+ :list_tags => { :verb => :get,
112
+ :object_uri => '/rest/objects/:id?metadata/tags',
113
+ :http_response => 200,
114
+ :return_headers => ['x-emc-tags', 'x-emc-listable-tags'],
115
+ },
116
+
117
+ :list_acl => { :verb => :get,
118
+ :object_uri => '/rest/objects/:id?acl',
119
+ :http_response => 200,
120
+ :return_headers => ['x-emc-useracl', 'x-emc-groupacl'],
121
+ },
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+ :delete_version => { :verb => :delete,
130
+ :object_uri => '/rest/objects/:id?versions',
131
+ :http_response => 204,
132
+ },
133
+ :create_version => { :verb => :post,
134
+ :object_uri => '/rest/objects/:id?versions' ,
135
+ :http_response => 201,
136
+ },
137
+ :list_versions => { :verb => :get,
138
+ :object_uri => '/rest/objects/:id?versions',
139
+ :http_response => 200},
140
+ :restore_version => { :verb => :put,
141
+ :object_uri => '/rest/objects/:id?versions',
142
+ :http_response => 200},
143
+ } # :nodoc:
144
+
145
+ end