rest-core 3.6.0 → 4.0.0
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.
- 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
|