rest-core 3.6.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/.travis.yml +0 -1
- data/CHANGES.md +28 -0
- data/Gemfile +0 -2
- data/README.md +14 -95
- data/Rakefile +16 -3
- data/example/simple.rb +1 -0
- data/example/use-cases.rb +15 -3
- data/lib/rest-core.rb +38 -75
- data/lib/rest-core/client/universal.rb +3 -1
- data/lib/rest-core/client_oauth1.rb +64 -59
- data/lib/rest-core/event.rb +9 -11
- data/lib/rest-core/middleware/auth_basic.rb +21 -21
- data/lib/rest-core/middleware/bypass.rb +8 -8
- data/lib/rest-core/middleware/cache.rb +94 -90
- data/lib/rest-core/middleware/clash_response.rb +15 -14
- data/lib/rest-core/middleware/common_logger.rb +27 -26
- data/lib/rest-core/middleware/default_headers.rb +8 -8
- data/lib/rest-core/middleware/default_payload.rb +8 -8
- data/lib/rest-core/middleware/default_query.rb +8 -8
- data/lib/rest-core/middleware/default_site.rb +12 -12
- data/lib/rest-core/middleware/defaults.rb +38 -38
- data/lib/rest-core/middleware/error_detector.rb +10 -10
- data/lib/rest-core/middleware/error_detector_http.rb +6 -4
- data/lib/rest-core/middleware/error_handler.rb +14 -14
- data/lib/rest-core/middleware/follow_redirect.rb +28 -27
- data/lib/rest-core/middleware/json_request.rb +13 -11
- data/lib/rest-core/middleware/json_response.rb +29 -28
- data/lib/rest-core/middleware/oauth1_header.rb +84 -83
- data/lib/rest-core/middleware/oauth2_header.rb +27 -25
- data/lib/rest-core/middleware/oauth2_query.rb +15 -15
- data/lib/rest-core/middleware/query_response.rb +14 -14
- data/lib/rest-core/middleware/retry.rb +25 -23
- data/lib/rest-core/middleware/smash_response.rb +15 -14
- data/lib/rest-core/middleware/timeout.rb +18 -19
- data/lib/rest-core/test.rb +1 -18
- data/lib/rest-core/util/clash.rb +38 -37
- data/lib/rest-core/util/config.rb +40 -39
- data/lib/rest-core/util/dalli_extension.rb +11 -10
- data/lib/rest-core/util/hmac.rb +9 -8
- data/lib/rest-core/util/json.rb +55 -54
- data/lib/rest-core/util/parse_link.rb +13 -12
- data/lib/rest-core/util/parse_query.rb +24 -22
- data/lib/rest-core/version.rb +1 -1
- data/rest-core.gemspec +121 -158
- data/test/test_cache.rb +2 -0
- data/test/test_default_payload.rb +1 -1
- data/test/test_error_handler.rb +5 -4
- data/test/test_timeout.rb +9 -8
- data/test/test_universal.rb +8 -0
- metadata +9 -73
- data/lib/rest-core/builder.rb +0 -162
- data/lib/rest-core/client.rb +0 -277
- data/lib/rest-core/client/simple.rb +0 -2
- data/lib/rest-core/engine.rb +0 -36
- data/lib/rest-core/engine/dry.rb +0 -9
- data/lib/rest-core/engine/http-client.rb +0 -41
- data/lib/rest-core/error.rb +0 -5
- data/lib/rest-core/event_source.rb +0 -137
- data/lib/rest-core/middleware.rb +0 -151
- data/lib/rest-core/promise.rb +0 -249
- data/lib/rest-core/thread_pool.rb +0 -131
- data/lib/rest-core/timer.rb +0 -58
- data/lib/rest-core/util/payload.rb +0 -173
- data/test/test_builder.rb +0 -40
- data/test/test_client.rb +0 -177
- data/test/test_event_source.rb +0 -159
- data/test/test_future.rb +0 -16
- data/test/test_httpclient.rb +0 -118
- data/test/test_payload.rb +0 -204
- data/test/test_promise.rb +0 -146
- data/test/test_simple.rb +0 -38
- data/test/test_thread_pool.rb +0 -10
@@ -1,75 +1,80 @@
|
|
1
1
|
|
2
|
-
require '
|
2
|
+
require 'digest/md5'
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
require 'rest-core/util/parse_query'
|
5
|
+
require 'rest-core/util/json'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
module RestCore
|
8
|
+
module ClientOauth1
|
9
|
+
def authorize_url! opts={}
|
10
|
+
self.data = ParseQuery.parse_query(
|
11
|
+
post(request_token_path, {}, {},
|
12
|
+
{:json_response => false}.merge(opts)))
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
authorize_url
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
def authorize_url
|
18
|
+
url(authorize_path, :oauth_token => oauth_token)
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
def authorize! opts={}
|
22
|
+
self.data = ParseQuery.parse_query(
|
23
|
+
post(access_token_path, {}, {},
|
24
|
+
{:json_response => false}.merge(opts)))
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
data['authorized'] = 'true'
|
27
|
+
data
|
28
|
+
end
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
def authorized?
|
31
|
+
!!(oauth_token && oauth_token_secret && data['authorized'])
|
32
|
+
end
|
29
33
|
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
def data_json
|
35
|
+
Json.encode(data.merge('sig' => calculate_sig))
|
36
|
+
end
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
def data_json= json
|
39
|
+
self.data = check_sig_and_return_data(Json.decode(json))
|
40
|
+
rescue Json.const_get(:ParseError)
|
41
|
+
self.data = nil
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
44
|
+
def oauth_token
|
45
|
+
data['oauth_token'] if data.kind_of?(Hash)
|
46
|
+
end
|
47
|
+
def oauth_token= token
|
48
|
+
data['oauth_token'] = token if data.kind_of?(Hash)
|
49
|
+
end
|
50
|
+
def oauth_token_secret
|
51
|
+
data['oauth_token_secret'] if data.kind_of?(Hash)
|
52
|
+
end
|
53
|
+
def oauth_token_secret= secret
|
54
|
+
data['oauth_token_secret'] = secret if data.kind_of?(Hash)
|
55
|
+
end
|
56
|
+
def oauth_callback
|
57
|
+
data['oauth_callback'] if data.kind_of?(Hash)
|
58
|
+
end
|
59
|
+
def oauth_callback= uri
|
60
|
+
data['oauth_callback'] = uri if data.kind_of?(Hash)
|
61
|
+
end
|
58
62
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
private
|
64
|
+
def default_data
|
65
|
+
{}
|
66
|
+
end
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def check_sig_and_return_data hash
|
69
|
+
hash if consumer_secret && hash.kind_of?(Hash) &&
|
70
|
+
calculate_sig(hash) == hash['sig']
|
71
|
+
end
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
def calculate_sig hash=data
|
74
|
+
base = hash.reject{ |(k, _)| k == 'sig' }.sort.map{ |(k, v)|
|
75
|
+
"#{Middleware.escape(k.to_s)}=#{Middleware.escape(v.to_s)}"
|
76
|
+
}.join('&')
|
77
|
+
Digest::MD5.hexdigest("#{Middleware.escape(consumer_secret)}&#{base}")
|
78
|
+
end
|
74
79
|
end
|
75
80
|
end
|
data/lib/rest-core/event.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
|
2
2
|
module RestCore
|
3
|
-
|
4
|
-
RestCore.const_defined?(:EventStruct)
|
5
|
-
|
6
|
-
class Event < EventStruct
|
3
|
+
class Event < Struct.new(:duration, :message)
|
7
4
|
def name; self.class.name[/(?<=::)\w+$/]; end
|
8
5
|
def to_s
|
9
6
|
if duration
|
@@ -13,11 +10,12 @@ module RestCore
|
|
13
10
|
end
|
14
11
|
end
|
15
12
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
|
14
|
+
Event::MultiDone = Class.new(Event)
|
15
|
+
Event::Requested = Class.new(Event)
|
16
|
+
Event::CacheHit = Class.new(Event)
|
17
|
+
Event::CacheCleared = Class.new(Event)
|
18
|
+
Event::Failed = Class.new(Event)
|
19
|
+
Event::WithHeader = Class.new(Event)
|
20
|
+
Event::Retrying = Class.new(Event)
|
23
21
|
end
|
@@ -1,29 +1,29 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
module RestCore
|
3
|
+
class AuthBasic
|
4
|
+
def self.members; [:username, :password]; end
|
5
|
+
include Middleware
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def call env, &k
|
8
|
+
if username(env)
|
9
|
+
if password(env)
|
10
|
+
app.call(env.merge(REQUEST_HEADERS =>
|
11
|
+
auth_basic_header(env).merge(env[REQUEST_HEADERS])), &k)
|
12
|
+
else
|
13
|
+
app.call(log(env, "AuthBasic: username provided: #{username(env)},"\
|
14
|
+
" but password is missing."), &k)
|
15
|
+
end
|
16
|
+
elsif password(env)
|
17
|
+
app.call(log(env, "AuthBasic: password provided: #{password(env)}," \
|
18
|
+
" but username is missing."), &k)
|
13
19
|
else
|
14
|
-
app.call(
|
15
|
-
" but password is missing."), &k)
|
20
|
+
app.call(env, &k)
|
16
21
|
end
|
17
|
-
elsif password(env)
|
18
|
-
app.call(log(env, "AuthBasic: password provided: #{password(env)}," \
|
19
|
-
" but username is missing."), &k)
|
20
|
-
else
|
21
|
-
app.call(env, &k)
|
22
22
|
end
|
23
|
-
end
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def auth_basic_header env
|
25
|
+
{'Authorization' =>
|
26
|
+
"Basic #{["#{username(env)}:#{password(env)}"].pack('m0')}"}
|
27
|
+
end
|
28
28
|
end
|
29
29
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
|
2
|
-
module RestCore; end
|
3
|
-
|
4
2
|
# the simplest middleware
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module RestCore
|
4
|
+
class Bypass
|
5
|
+
def initialize app
|
6
|
+
@app = app
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
def call env, &k
|
10
|
+
@app.call(env, &k)
|
11
|
+
end
|
12
12
|
end
|
13
13
|
end
|
@@ -1,117 +1,121 @@
|
|
1
1
|
|
2
|
-
require 'rest-core/event'
|
3
|
-
require 'rest-core/middleware'
|
4
|
-
|
5
2
|
require 'digest/md5'
|
3
|
+
require 'rest-core/event'
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module RestCore
|
6
|
+
class Cache
|
7
|
+
def self.members; [:cache, :expires_in]; end
|
8
|
+
include Middleware
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
def initialize app, cache, expires_in, &block
|
11
|
+
super(&block)
|
12
|
+
@app, @cache, @expires_in = app, cache, expires_in
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
def call env, &k
|
16
|
+
e = if env['cache.update']
|
17
|
+
cache_clear(env)
|
18
|
+
else
|
19
|
+
env
|
20
|
+
end
|
21
|
+
|
22
|
+
cache_get(e){ |cached|
|
23
|
+
e[TIMER].cancel if e[TIMER]
|
24
|
+
k.call(cached)
|
25
|
+
} || app_call(e, &k)
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
28
|
+
def app_call env
|
29
|
+
app.call(env) do |res|
|
30
|
+
yield(if (res[FAIL] || []).empty?
|
31
|
+
cache_for(res)
|
32
|
+
else
|
33
|
+
res
|
34
|
+
end)
|
35
|
+
end
|
36
36
|
end
|
37
|
-
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
def cache_key env
|
39
|
+
"rest-core:cache:#{Digest::MD5.hexdigest(env['cache.key'] ||
|
40
|
+
cache_key_raw(env))}"
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def cache_get env, &k
|
44
|
+
return unless cache(env)
|
45
|
+
return unless cache_for?(env)
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
uri = request_uri(env)
|
48
|
+
start_time = Time.now
|
49
|
+
return unless data = cache(env)[cache_key(env)]
|
50
|
+
res = log(env.merge(REQUEST_URI => uri),
|
51
|
+
Event::CacheHit.new(Time.now - start_time, uri))
|
52
|
+
data_extract(data, res, k)
|
53
|
+
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
private
|
56
|
+
def cache_key_raw env
|
57
|
+
"#{env[REQUEST_METHOD]}:#{request_uri(env)}:#{header_cache_key(env)}"
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
def cache_clear env
|
61
|
+
return env unless cache(env)
|
62
|
+
return env unless cache_for?(env)
|
64
63
|
|
65
|
-
|
66
|
-
|
64
|
+
cache_store(env, :[]=, nil)
|
65
|
+
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
def cache_for res
|
68
|
+
return res unless cache(res)
|
69
|
+
return res unless cache_for?(res)
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
if expires_in(res).kind_of?(Fixnum) &&
|
72
|
+
cache(res).respond_to?(:store) &&
|
73
|
+
cache(res).method(:store).arity == -3
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
cache_store(res, :store, data_construct(res),
|
76
|
+
:expires_in => expires_in(res))
|
77
|
+
else
|
78
|
+
cache_store(res, :[]= , data_construct(res))
|
79
|
+
end
|
80
80
|
end
|
81
|
-
end
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
|
82
|
+
def cache_store res, msg, value, *args
|
83
|
+
start_time = Time.now
|
84
|
+
cache(res).send(msg, cache_key(res), value, *args)
|
86
85
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
if value
|
87
|
+
res
|
88
|
+
else
|
89
|
+
log(res,
|
90
|
+
Event::CacheCleared.new(Time.now - start_time, request_uri(res)))
|
91
|
+
end
|
92
92
|
end
|
93
|
-
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
94
|
+
def data_construct res
|
95
|
+
"#{ res[RESPONSE_STATUS]}\n" \
|
96
|
+
"#{(res[RESPONSE_HEADERS]||{}).map{|k,v|"#{k}: #{v}\n"}.join}\n\n" \
|
97
|
+
"#{ res[RESPONSE_BODY]}"
|
98
|
+
end
|
100
99
|
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
def data_extract data, res, k
|
101
|
+
_, status, headers, body =
|
102
|
+
data.match(/\A(\d*)\n((?:[^\n]+\n)*)\n\n(.*)\Z/m).to_a
|
104
103
|
|
105
|
-
|
106
|
-
|
107
|
-
|
104
|
+
result = res.merge(RESPONSE_STATUS => status.to_i,
|
105
|
+
RESPONSE_HEADERS =>
|
106
|
+
Hash[(headers||'').scan(/([^:]+): ([^\n]+)\n/)],
|
107
|
+
RESPONSE_BODY => body)
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
!env[DRY] && !env[HIJACK]
|
112
|
-
end
|
109
|
+
result.merge(Promise.claim(result, &k).future_response)
|
110
|
+
end
|
113
111
|
|
114
|
-
|
115
|
-
|
112
|
+
def cache_for? env
|
113
|
+
[:get, :head, :otpions].include?(env[REQUEST_METHOD]) &&
|
114
|
+
!env[DRY] && !env[HIJACK]
|
115
|
+
end
|
116
|
+
|
117
|
+
def header_cache_key env
|
118
|
+
env[REQUEST_HEADERS].sort.map{|(k,v)|"#{k}=#{v}"}.join('&')
|
119
|
+
end
|
116
120
|
end
|
117
121
|
end
|