s3restful 0.2.7
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.
- data/CHANGELOG.md +24 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +13 -0
- data/README.md +230 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/benchmark/right_aws.rb +106 -0
- data/lib/s3restful/aws.rb +65 -0
- data/lib/s3restful/log.rb +40 -0
- data/lib/s3restful/s3/item.rb +136 -0
- data/lib/s3restful/s3/request.rb +111 -0
- data/lib/s3restful/s3.rb +16 -0
- data/lib/s3restful/utils.rb +26 -0
- data/lib/s3restful.rb +23 -0
- data/s3restful.gemspec +69 -0
- data/test/aws_test.rb +41 -0
- data/test/s3/item_test.rb +531 -0
- data/test/s3/request_test.rb +109 -0
- data/test/s3_test.rb +32 -0
- data/test/test_helper.rb +94 -0
- metadata +148 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
module S3restful
|
2
|
+
module S3
|
3
|
+
class Request
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
VALID_HTTP_METHODS = [:head, :get, :aget, :put, :delete]
|
7
|
+
|
8
|
+
attr_accessor :http_method, :url, :options, :response
|
9
|
+
|
10
|
+
def initialize(http_method, url, options = {})
|
11
|
+
@options = {
|
12
|
+
:timeout => 10,
|
13
|
+
:retry_count => 4,
|
14
|
+
:headers => {},
|
15
|
+
:on_error => nil,
|
16
|
+
:on_success => nil,
|
17
|
+
:data => nil,
|
18
|
+
:ssl => {
|
19
|
+
:cert_chain_file => nil,
|
20
|
+
:verify_peer => false
|
21
|
+
}
|
22
|
+
}.update(options)
|
23
|
+
assert_valid_keys(options, :timeout, :on_success, :on_error, :retry_count, :headers, :data, :ssl, :file)
|
24
|
+
@http_method = http_method
|
25
|
+
@url = url
|
26
|
+
|
27
|
+
validate
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute
|
31
|
+
S3restful::Log.debug "Request: #{http_method.to_s.upcase} #{url}"
|
32
|
+
@response = http_class.new(url).send(http_method, :timeout => options[:timeout], :head => options[:headers], :body => options[:data], :ssl => options[:ssl], :file => options[:file])
|
33
|
+
|
34
|
+
@response.errback { error_callback }
|
35
|
+
@response.callback { success_callback }
|
36
|
+
@response
|
37
|
+
end
|
38
|
+
|
39
|
+
def http_class
|
40
|
+
EventMachine::HttpRequest
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def validate
|
46
|
+
raise ArgumentError, "#{http_method} is not a valid HTTP method that #{self.class.name} understands." unless VALID_HTTP_METHODS.include?(http_method)
|
47
|
+
end
|
48
|
+
|
49
|
+
def error_callback
|
50
|
+
S3restful::Log.error "Response error: #{http_method.to_s.upcase} #{url}: #{response.response_header.status rescue ''}"
|
51
|
+
if should_retry?
|
52
|
+
S3restful::Log.info "#{http_method.to_s.upcase} #{url}: retrying after error: status #{response.response_header.status rescue ''}"
|
53
|
+
handle_retry
|
54
|
+
elsif options[:on_error].respond_to?(:call)
|
55
|
+
call_user_error_handler
|
56
|
+
else
|
57
|
+
raise S3restful::Error.new("#{http_method.to_s.upcase} #{url}: Failed reponse! Status code was #{response.response_header.status rescue ''}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def success_callback
|
62
|
+
S3restful::Log.debug "Response success: #{http_method.to_s.upcase} #{url}: #{response.response_header.status rescue ''}"
|
63
|
+
case response.response_header.status
|
64
|
+
when 0, 400, 401, 404, 403, 409, 411, 412, 416, 500, 503
|
65
|
+
if should_retry?
|
66
|
+
S3restful::Log.info "#{http_method.to_s.upcase} #{url}: retrying after: status #{response.response_header.status rescue ''}"
|
67
|
+
handle_retry
|
68
|
+
else
|
69
|
+
S3restful::Log.error "#{http_method.to_s.upcase} #{url}: Re-tried too often - giving up"
|
70
|
+
error_callback
|
71
|
+
end
|
72
|
+
when 300, 301, 303, 304, 307
|
73
|
+
S3restful::Log.info "#{http_method.to_s.upcase} #{url}: being redirected_to: #{response.response_header['LOCATION'] rescue ''}"
|
74
|
+
handle_redirect
|
75
|
+
else
|
76
|
+
call_user_success_handler
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def call_user_success_handler
|
81
|
+
options[:on_success].call(response) if options[:on_success].respond_to?(:call)
|
82
|
+
end
|
83
|
+
|
84
|
+
def call_user_error_handler
|
85
|
+
options[:on_error].call(response) if options[:on_error].respond_to?(:call)
|
86
|
+
end
|
87
|
+
|
88
|
+
def should_retry?
|
89
|
+
options[:retry_count] > 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_retry
|
93
|
+
if should_retry?
|
94
|
+
new_request = self.class.new(http_method, url, options.update(:retry_count => options[:retry_count] - 1 ))
|
95
|
+
new_request.execute
|
96
|
+
else
|
97
|
+
S3restful::Log.error "#{http_method.to_s.upcase} #{url}: Re-tried too often - giving up"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def handle_redirect
|
102
|
+
new_location = response.response_header['LOCATION'] rescue ''
|
103
|
+
raise "Could not find the location to redirect to, empty location header?" if blank?(new_location)
|
104
|
+
|
105
|
+
new_request = self.class.new(http_method, new_location, options)
|
106
|
+
new_request.execute
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/s3restful/s3.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module S3restful
|
2
|
+
module Utils
|
3
|
+
protected
|
4
|
+
|
5
|
+
def symbolize_keys(hash)
|
6
|
+
hash.inject({}) do |h, kv|
|
7
|
+
h[kv[0].to_sym] = kv[1]
|
8
|
+
h
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_valid_keys(hash, *valid_keys)
|
13
|
+
unknown_keys = hash.keys - [valid_keys].flatten
|
14
|
+
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
15
|
+
end
|
16
|
+
|
17
|
+
def present?(obj)
|
18
|
+
!blank?(obj)
|
19
|
+
end
|
20
|
+
|
21
|
+
def blank?(obj)
|
22
|
+
obj.respond_to?(:empty?) ? obj.empty? : !obj
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/s3restful.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'em-http'
|
3
|
+
require 'openssl'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
unless defined?(S3restful)
|
7
|
+
$:<<(File.expand_path(File.dirname(__FILE__) + "/lib"))
|
8
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/utils')
|
9
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/log')
|
10
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/aws')
|
11
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/s3')
|
12
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/s3/request')
|
13
|
+
require File.expand_path(File.dirname(__FILE__) + '/s3restful/s3/item')
|
14
|
+
|
15
|
+
module S3restful
|
16
|
+
MAJOR = 0
|
17
|
+
MINOR = 2
|
18
|
+
PATCH = 7
|
19
|
+
|
20
|
+
VERSION = [MAJOR, MINOR, PATCH].compact.join('.')
|
21
|
+
class Error < RuntimeError; end
|
22
|
+
end
|
23
|
+
end
|
data/s3restful.gemspec
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "s3restful"
|
8
|
+
s.version = "0.2.7"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["João Pereira"]
|
12
|
+
s.date = "2012-06-08"
|
13
|
+
s.description = "An EventMachine based client for S3 compatible APIs"
|
14
|
+
s.email = "joaodrp@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"CHANGELOG.md",
|
21
|
+
"Gemfile",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"benchmark/right_aws.rb",
|
27
|
+
"s3restful.gemspec",
|
28
|
+
"lib/s3restful.rb",
|
29
|
+
"lib/s3restful/aws.rb",
|
30
|
+
"lib/s3restful/log.rb",
|
31
|
+
"lib/s3restful/s3.rb",
|
32
|
+
"lib/s3restful/s3/item.rb",
|
33
|
+
"lib/s3restful/s3/request.rb",
|
34
|
+
"lib/s3restful/utils.rb",
|
35
|
+
"test/aws_test.rb",
|
36
|
+
"test/s3/item_test.rb",
|
37
|
+
"test/s3/request_test.rb",
|
38
|
+
"test/s3_test.rb",
|
39
|
+
"test/test_helper.rb"
|
40
|
+
]
|
41
|
+
s.homepage = "http://github.com/joaodrp/s3-restful"
|
42
|
+
s.licenses = ["BSD"]
|
43
|
+
s.require_paths = ["lib"]
|
44
|
+
s.summary = "An EventMachine based client for S3 compatible APIs"
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
s.add_runtime_dependency(%q<em-http-request>, ["~> 1.0.2"])
|
51
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
53
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
54
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<em-http-request>, ["~> 1.0.2"])
|
57
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
58
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
59
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
60
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<em-http-request>, ["~> 1.0.2"])
|
64
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
65
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
66
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
67
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
68
|
+
end
|
69
|
+
end
|
data/test/aws_test.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class ItemTest < Test::Unit::TestCase
|
4
|
+
context "An S3restful::AWS instance" do
|
5
|
+
|
6
|
+
setup do
|
7
|
+
@aws = S3restful::AWS.new('the-aws-access-key', 'the-aws-secret-key')
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when constructing" do
|
11
|
+
should "require Access Key and Secret Key" do
|
12
|
+
assert_raise(ArgumentError) do
|
13
|
+
S3restful::AWS.new(nil, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
assert_raise(ArgumentError) do
|
17
|
+
S3restful::AWS.new('', '')
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_nothing_raised do
|
21
|
+
S3restful::AWS.new('abc', 'abc')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when signing parameters" do
|
27
|
+
should "return a header hash" do
|
28
|
+
assert_not_nil @aws.sign("GET", '/')['Authorization']
|
29
|
+
end
|
30
|
+
|
31
|
+
should "include the current date" do
|
32
|
+
assert_not_nil @aws.sign("GET", '/')['date']
|
33
|
+
end
|
34
|
+
|
35
|
+
should "keep given headers" do
|
36
|
+
assert_equal 'bar', @aws.sign("GET", '/', {'foo' => 'bar'})['foo']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|