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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +3 -0
  3. data/.travis.yml +0 -1
  4. data/CHANGES.md +28 -0
  5. data/Gemfile +0 -2
  6. data/README.md +14 -95
  7. data/Rakefile +16 -3
  8. data/example/simple.rb +1 -0
  9. data/example/use-cases.rb +15 -3
  10. data/lib/rest-core.rb +38 -75
  11. data/lib/rest-core/client/universal.rb +3 -1
  12. data/lib/rest-core/client_oauth1.rb +64 -59
  13. data/lib/rest-core/event.rb +9 -11
  14. data/lib/rest-core/middleware/auth_basic.rb +21 -21
  15. data/lib/rest-core/middleware/bypass.rb +8 -8
  16. data/lib/rest-core/middleware/cache.rb +94 -90
  17. data/lib/rest-core/middleware/clash_response.rb +15 -14
  18. data/lib/rest-core/middleware/common_logger.rb +27 -26
  19. data/lib/rest-core/middleware/default_headers.rb +8 -8
  20. data/lib/rest-core/middleware/default_payload.rb +8 -8
  21. data/lib/rest-core/middleware/default_query.rb +8 -8
  22. data/lib/rest-core/middleware/default_site.rb +12 -12
  23. data/lib/rest-core/middleware/defaults.rb +38 -38
  24. data/lib/rest-core/middleware/error_detector.rb +10 -10
  25. data/lib/rest-core/middleware/error_detector_http.rb +6 -4
  26. data/lib/rest-core/middleware/error_handler.rb +14 -14
  27. data/lib/rest-core/middleware/follow_redirect.rb +28 -27
  28. data/lib/rest-core/middleware/json_request.rb +13 -11
  29. data/lib/rest-core/middleware/json_response.rb +29 -28
  30. data/lib/rest-core/middleware/oauth1_header.rb +84 -83
  31. data/lib/rest-core/middleware/oauth2_header.rb +27 -25
  32. data/lib/rest-core/middleware/oauth2_query.rb +15 -15
  33. data/lib/rest-core/middleware/query_response.rb +14 -14
  34. data/lib/rest-core/middleware/retry.rb +25 -23
  35. data/lib/rest-core/middleware/smash_response.rb +15 -14
  36. data/lib/rest-core/middleware/timeout.rb +18 -19
  37. data/lib/rest-core/test.rb +1 -18
  38. data/lib/rest-core/util/clash.rb +38 -37
  39. data/lib/rest-core/util/config.rb +40 -39
  40. data/lib/rest-core/util/dalli_extension.rb +11 -10
  41. data/lib/rest-core/util/hmac.rb +9 -8
  42. data/lib/rest-core/util/json.rb +55 -54
  43. data/lib/rest-core/util/parse_link.rb +13 -12
  44. data/lib/rest-core/util/parse_query.rb +24 -22
  45. data/lib/rest-core/version.rb +1 -1
  46. data/rest-core.gemspec +121 -158
  47. data/test/test_cache.rb +2 -0
  48. data/test/test_default_payload.rb +1 -1
  49. data/test/test_error_handler.rb +5 -4
  50. data/test/test_timeout.rb +9 -8
  51. data/test/test_universal.rb +8 -0
  52. metadata +9 -73
  53. data/lib/rest-core/builder.rb +0 -162
  54. data/lib/rest-core/client.rb +0 -277
  55. data/lib/rest-core/client/simple.rb +0 -2
  56. data/lib/rest-core/engine.rb +0 -36
  57. data/lib/rest-core/engine/dry.rb +0 -9
  58. data/lib/rest-core/engine/http-client.rb +0 -41
  59. data/lib/rest-core/error.rb +0 -5
  60. data/lib/rest-core/event_source.rb +0 -137
  61. data/lib/rest-core/middleware.rb +0 -151
  62. data/lib/rest-core/promise.rb +0 -249
  63. data/lib/rest-core/thread_pool.rb +0 -131
  64. data/lib/rest-core/timer.rb +0 -58
  65. data/lib/rest-core/util/payload.rb +0 -173
  66. data/test/test_builder.rb +0 -40
  67. data/test/test_client.rb +0 -177
  68. data/test/test_event_source.rb +0 -159
  69. data/test/test_future.rb +0 -16
  70. data/test/test_httpclient.rb +0 -118
  71. data/test/test_payload.rb +0 -204
  72. data/test/test_promise.rb +0 -146
  73. data/test/test_simple.rb +0 -38
  74. data/test/test_thread_pool.rb +0 -10
@@ -1,39 +1,40 @@
1
1
 
2
- require 'rest-core/middleware'
3
2
  require 'rest-core/util/json'
4
3
 
5
- class RestCore::JsonResponse
6
- def self.members; [:json_response]; end
7
- include RestCore::Middleware
4
+ module RestCore
5
+ class JsonResponse
6
+ def self.members; [:json_response]; end
7
+ include Middleware
8
8
 
9
- class ParseError < Json.const_get(:ParseError)
10
- attr_reader :cause, :body
11
- def initialize cause, body
12
- msg = cause.message.force_encoding('utf-8')
13
- super("#{msg}\nOriginal text: #{body}")
14
- @cause, @body = cause, body
9
+ class ParseError < Json.const_get(:ParseError)
10
+ attr_reader :cause, :body
11
+ def initialize cause, body
12
+ msg = cause.message.force_encoding('utf-8')
13
+ super("#{msg}\nOriginal text: #{body}")
14
+ @cause, @body = cause, body
15
+ end
15
16
  end
16
- end
17
17
 
18
- JSON_RESPONSE_HEADER = {'Accept' => 'application/json'}.freeze
18
+ JSON_RESPONSE_HEADER = {'Accept' => 'application/json'}.freeze
19
19
 
20
- def call env, &k
21
- return app.call(env, &k) if env[DRY]
22
- return app.call(env, &k) unless json_response(env)
20
+ def call env, &k
21
+ return app.call(env, &k) if env[DRY]
22
+ return app.call(env, &k) unless json_response(env)
23
23
 
24
- app.call(env.merge(REQUEST_HEADERS =>
25
- JSON_RESPONSE_HEADER.merge(env[REQUEST_HEADERS]||{}))){ |response|
26
- yield(process(response))
27
- }
28
- end
24
+ app.call(env.merge(REQUEST_HEADERS =>
25
+ JSON_RESPONSE_HEADER.merge(env[REQUEST_HEADERS]||{}))){ |response|
26
+ yield(process(response))
27
+ }
28
+ end
29
29
 
30
- def process response
31
- # StackExchange returns the problematic BOM! in UTF-8, so we need to
32
- # strip it or it would break JSON parsers (i.e. yajl-ruby and json)
33
- body = response[RESPONSE_BODY].to_s.sub(/\A\xEF\xBB\xBF/, '')
34
- response.merge(RESPONSE_BODY => Json.decode("[#{body}]").first)
35
- # [this].first is not needed for yajl-ruby
36
- rescue Json.const_get(:ParseError) => error
37
- fail(response, ParseError.new(error, body))
30
+ def process response
31
+ # StackExchange returns the problematic BOM! in UTF-8, so we need to
32
+ # strip it or it would break JSON parsers (i.e. yajl-ruby and json)
33
+ body = response[RESPONSE_BODY].to_s.sub(/\A\xEF\xBB\xBF/, '')
34
+ response.merge(RESPONSE_BODY => Json.decode("[#{body}]").first)
35
+ # [this].first is not needed for yajl-ruby
36
+ rescue Json.const_get(:ParseError) => error
37
+ fail(response, ParseError.new(error, body))
38
+ end
38
39
  end
39
40
  end
@@ -1,100 +1,101 @@
1
1
 
2
- require 'rest-core/middleware'
2
+ require 'openssl'
3
+ require 'rest-core/event'
3
4
  require 'rest-core/util/hmac'
4
5
 
5
- require 'uri'
6
- require 'openssl'
6
+ module RestCore
7
+ # http://tools.ietf.org/html/rfc5849
8
+ class Oauth1Header
9
+ def self.members
10
+ [:request_token_path, :access_token_path, :authorize_path,
11
+ :consumer_key, :consumer_secret,
12
+ :oauth_callback, :oauth_verifier,
13
+ :oauth_token, :oauth_token_secret, :data]
14
+ end
15
+ include Middleware
7
16
 
8
- # http://tools.ietf.org/html/rfc5849
9
- class RestCore::Oauth1Header
10
- def self.members
11
- [:request_token_path, :access_token_path, :authorize_path,
12
- :consumer_key, :consumer_secret,
13
- :oauth_callback, :oauth_verifier,
14
- :oauth_token, :oauth_token_secret, :data]
15
- end
16
- include RestCore::Middleware
17
- def call env, &k
18
- start_time = Time.now
19
- headers = {'Authorization' => oauth_header(env)}.
20
- merge(env[REQUEST_HEADERS])
17
+ def call env, &k
18
+ start_time = Time.now
19
+ headers = {'Authorization' => oauth_header(env)}.
20
+ merge(env[REQUEST_HEADERS])
21
21
 
22
- event = Event::WithHeader.new(Time.now - start_time,
23
- "Authorization: #{headers['Authorization']}")
22
+ event = Event::WithHeader.new(Time.now - start_time,
23
+ "Authorization: #{headers['Authorization']}")
24
24
 
25
- app.call(log(env.merge(REQUEST_HEADERS => headers), event), &k)
26
- end
25
+ app.call(log(env.merge(REQUEST_HEADERS => headers), event), &k)
26
+ end
27
27
 
28
- def oauth_header env
29
- oauth = attach_signature(env,
30
- 'oauth_consumer_key' => consumer_key(env),
31
- 'oauth_signature_method' => 'HMAC-SHA1',
32
- 'oauth_timestamp' => Time.now.to_i.to_s,
33
- 'oauth_nonce' => nonce,
34
- 'oauth_version' => '1.0',
35
- 'oauth_callback' => oauth_callback(env),
36
- 'oauth_verifier' => oauth_verifier(env),
37
- 'oauth_token' => oauth_token(env)).
38
- map{ |(k, v)| "#{k}=\"#{escape(v)}\"" }.join(', ')
39
-
40
- "OAuth #{oauth}"
41
- end
28
+ def oauth_header env
29
+ oauth = attach_signature(env,
30
+ 'oauth_consumer_key' => consumer_key(env),
31
+ 'oauth_signature_method' => 'HMAC-SHA1',
32
+ 'oauth_timestamp' => Time.now.to_i.to_s,
33
+ 'oauth_nonce' => nonce,
34
+ 'oauth_version' => '1.0',
35
+ 'oauth_callback' => oauth_callback(env),
36
+ 'oauth_verifier' => oauth_verifier(env),
37
+ 'oauth_token' => oauth_token(env)).
38
+ map{ |(k, v)| "#{k}=\"#{escape(v)}\"" }.join(', ')
39
+
40
+ "OAuth #{oauth}"
41
+ end
42
42
 
43
- def attach_signature env, oauth_params
44
- params = reject_blank(oauth_params)
45
- params.merge('oauth_signature' => signature(env, params))
46
- end
43
+ def attach_signature env, oauth_params
44
+ params = reject_blank(oauth_params)
45
+ params.merge('oauth_signature' => signature(env, params))
46
+ end
47
47
 
48
- def signature env, params
49
- [Hmac.sha1("#{consumer_secret(env)}&#{oauth_token_secret(env)}",
50
- base_string(env, params))].pack('m0')
51
- end
48
+ def signature env, params
49
+ [Hmac.sha1("#{consumer_secret(env)}&#{oauth_token_secret(env)}",
50
+ base_string(env, params))].pack('m0')
51
+ end
52
52
 
53
- def base_string env, oauth_params
54
- method = env[REQUEST_METHOD].to_s.upcase
55
- base_uri = env[REQUEST_PATH]
56
- payload = payload_params(env)
57
- query = reject_blank(env[REQUEST_QUERY])
58
- params = reject_blank(oauth_params.merge(query.merge(payload))).
59
- to_a.sort.map{ |(k, v)|
60
- "#{escape(k.to_s)}=#{escape(v.to_s)}"}.join('&')
53
+ def base_string env, oauth_params
54
+ method = env[REQUEST_METHOD].to_s.upcase
55
+ base_uri = env[REQUEST_PATH]
56
+ payload = payload_params(env)
57
+ query = reject_blank(env[REQUEST_QUERY])
58
+ params = reject_blank(oauth_params.merge(query.merge(payload))).
59
+ to_a.sort.map{ |(k, v)|
60
+ "#{escape(k.to_s)}=#{escape(v.to_s)}"}.join('&')
61
61
 
62
- "#{method}&#{escape(base_uri)}&#{escape(params)}"
63
- end
62
+ "#{method}&#{escape(base_uri)}&#{escape(params)}"
63
+ end
64
64
 
65
- def nonce
66
- [OpenSSL::Random.random_bytes(32)].pack('m0').tr("+/=", '')
67
- end
65
+ def nonce
66
+ [OpenSSL::Random.random_bytes(32)].pack('m0').tr("+/=", '')
67
+ end
68
68
 
69
- # according to OAuth 1.0a spec, only:
70
- # Content-Type: application/x-www-form-urlencoded
71
- # should take payload as a part of the base_string
72
- def payload_params env
73
- # if we already specified Content-Type and which is not
74
- # application/x-www-form-urlencoded, then we should not
75
- # take payload as a part of the base_string
76
- if env[REQUEST_HEADERS].kind_of?(Hash) &&
77
- env[REQUEST_HEADERS]['Content-Type'] &&
78
- env[REQUEST_HEADERS]['Content-Type'] !=
79
- 'application/x-www-form-urlencoded'
80
- {}
81
-
82
- # if it contains any binary data,
83
- # then it shouldn't be application/x-www-form-urlencoded either
84
- # the Content-Type header would be handled in our HTTP client
85
- elsif contain_binary?(env[REQUEST_PAYLOAD])
86
- {}
87
-
88
- # so the Content-Type header must be application/x-www-form-urlencoded
89
- else
90
- reject_blank(env[REQUEST_PAYLOAD])
69
+ # according to OAuth 1.0a spec, only:
70
+ # Content-Type: application/x-www-form-urlencoded
71
+ # should take payload as a part of the base_string
72
+ def payload_params env
73
+ # if we already specified Content-Type and which is not
74
+ # application/x-www-form-urlencoded, then we should not
75
+ # take payload as a part of the base_string
76
+ if env[REQUEST_HEADERS].kind_of?(Hash) &&
77
+ env[REQUEST_HEADERS]['Content-Type'] &&
78
+ env[REQUEST_HEADERS]['Content-Type'] !=
79
+ 'application/x-www-form-urlencoded'
80
+ {}
81
+
82
+ # if it contains any binary data,
83
+ # then it shouldn't be application/x-www-form-urlencoded either
84
+ # the Content-Type header would be handled in our HTTP client
85
+ elsif contain_binary?(env[REQUEST_PAYLOAD])
86
+ {}
87
+
88
+ # so the Content-Type header must be application/x-www-form-urlencoded
89
+ else
90
+ reject_blank(env[REQUEST_PAYLOAD])
91
+ end
91
92
  end
92
- end
93
93
 
94
- def reject_blank params
95
- params.reject{ |k, v| v.nil? || v == false ||
96
- (v.respond_to?(:strip) &&
97
- v.respond_to?(:empty) &&
98
- v.strip.empty? == true) }
94
+ def reject_blank params
95
+ params.reject{ |k, v| v.nil? || v == false ||
96
+ (v.respond_to?(:strip) &&
97
+ v.respond_to?(:empty) &&
98
+ v.strip.empty? == true) }
99
+ end
99
100
  end
100
101
  end
@@ -1,33 +1,35 @@
1
1
 
2
- require 'rest-core/middleware'
2
+ require 'rest-core/event'
3
3
 
4
- # http://tools.ietf.org/html/rfc6749
5
- class RestCore::Oauth2Header
6
- def self.members; [:access_token_type, :access_token]; end
7
- include RestCore::Middleware
4
+ module RestCore
5
+ # http://tools.ietf.org/html/rfc6749
6
+ class Oauth2Header
7
+ def self.members; [:access_token_type, :access_token]; end
8
+ include Middleware
8
9
 
9
- def call env, &k
10
- start_time = Time.now
11
- headers = build_headers(env)
12
- auth = headers['Authorization']
13
- event = Event::WithHeader.new(Time.now - start_time,
14
- "Authorization: #{auth}") if auth
10
+ def call env, &k
11
+ start_time = Time.now
12
+ headers = build_headers(env)
13
+ auth = headers['Authorization']
14
+ event = Event::WithHeader.new(Time.now - start_time,
15
+ "Authorization: #{auth}") if auth
15
16
 
16
- app.call(log(env.merge(REQUEST_HEADERS => headers), event), &k)
17
- end
17
+ app.call(log(env.merge(REQUEST_HEADERS => headers), event), &k)
18
+ end
18
19
 
19
- def build_headers env
20
- auth = case token = access_token(env)
21
- when String
22
- token
23
- when Hash
24
- token.map{ |(k, v)| "#{k}=\"#{v}\"" }.join(', ')
25
- end
20
+ def build_headers env
21
+ auth = case token = access_token(env)
22
+ when String
23
+ token
24
+ when Hash
25
+ token.map{ |(k, v)| "#{k}=\"#{v}\"" }.join(', ')
26
+ end
26
27
 
27
- if auth
28
- {'Authorization' => "#{access_token_type(env)} #{auth}"}
29
- else
30
- {}
31
- end.merge(env[REQUEST_HEADERS])
28
+ if auth
29
+ {'Authorization' => "#{access_token_type(env)} #{auth}"}
30
+ else
31
+ {}
32
+ end.merge(env[REQUEST_HEADERS])
33
+ end
32
34
  end
33
35
  end
@@ -1,20 +1,20 @@
1
1
 
2
- require 'rest-core/middleware'
2
+ module RestCore
3
+ # http://tools.ietf.org/html/rfc6749
4
+ class Oauth2Query
5
+ def self.members; [:access_token]; end
6
+ include Middleware
3
7
 
4
- # http://tools.ietf.org/html/rfc6749
5
- class RestCore::Oauth2Query
6
- def self.members; [:access_token]; end
7
- include RestCore::Middleware
8
+ def call env, &k
9
+ local = if access_token(env)
10
+ env.merge(REQUEST_QUERY =>
11
+ {'access_token' => access_token(env)}.
12
+ merge(env[REQUEST_QUERY]))
13
+ else
14
+ env
15
+ end
8
16
 
9
- def call env, &k
10
- local = if access_token(env)
11
- env.merge(REQUEST_QUERY =>
12
- {'access_token' => access_token(env)}.
13
- merge(env[REQUEST_QUERY]))
14
- else
15
- env
16
- end
17
-
18
- app.call(local, &k)
17
+ app.call(local, &k)
18
+ end
19
19
  end
20
20
  end
@@ -1,23 +1,23 @@
1
1
 
2
- require 'rest-core/middleware'
3
2
  require 'rest-core/util/parse_query'
4
3
 
5
- class RestCore::QueryResponse
6
- def self.members; [:query_response]; end
7
- include RestCore::Middleware
4
+ module RestCore
5
+ class QueryResponse
6
+ def self.members; [:query_response]; end
7
+ include Middleware
8
8
 
9
- QUERY_RESPONSE_HEADER =
10
- {'Accept' => 'application/x-www-form-urlencoded'}.freeze
9
+ QUERY_RESPONSE_HEADER =
10
+ {'Accept' => 'application/x-www-form-urlencoded'}.freeze
11
11
 
12
- def call env, &k
13
- return app.call(env, &k) if env[DRY]
14
- return app.call(env, &k) unless query_response(env)
12
+ def call env, &k
13
+ return app.call(env, &k) if env[DRY]
14
+ return app.call(env, &k) unless query_response(env)
15
15
 
16
- headers = QUERY_RESPONSE_HEADER.merge(env[REQUEST_HEADERS]||{})
17
- app.call(env.merge(REQUEST_HEADERS => headers)) do |response|
18
- body = ParseQuery.parse_query(response[RESPONSE_BODY])
19
- yield(response.merge(RESPONSE_BODY => body))
16
+ headers = QUERY_RESPONSE_HEADER.merge(env[REQUEST_HEADERS]||{})
17
+ app.call(env.merge(REQUEST_HEADERS => headers)) do |response|
18
+ body = ParseQuery.parse_query(response[RESPONSE_BODY])
19
+ yield(response.merge(RESPONSE_BODY => body))
20
+ end
20
21
  end
21
22
  end
22
23
  end
23
-
@@ -1,33 +1,35 @@
1
1
 
2
- require 'rest-core/middleware'
2
+ require 'rest-core/event'
3
3
 
4
- class RestCore::Retry
5
- def self.members; [:max_retries, :retry_exceptions]; end
6
- include RestCore::Middleware
4
+ module RestCore
5
+ class Retry
6
+ def self.members; [:max_retries, :retry_exceptions]; end
7
+ include Middleware
7
8
 
8
- DefaultRetryExceptions = [IOError, SystemCallError]
9
+ DefaultRetryExceptions = [IOError, SystemCallError]
9
10
 
10
- def call env, &k
11
- if env[DRY]
12
- app.call(env, &k)
13
- else
14
- app.call(env){ |res| process(res, k) }
11
+ def call env, &k
12
+ if env[DRY]
13
+ app.call(env, &k)
14
+ else
15
+ app.call(env){ |res| process(res, k) }
16
+ end
15
17
  end
16
- end
17
18
 
18
- def process res, k
19
- times = max_retries(res)
20
- return k.call(res) if times <= 0
21
- errors = retry_exceptions(res) || DefaultRetryExceptions
19
+ def process res, k
20
+ times = max_retries(res)
21
+ return k.call(res) if times <= 0
22
+ errors = retry_exceptions(res) || DefaultRetryExceptions
22
23
 
23
- if idx = res[FAIL].index{ |f| errors.find{ |e| f.kind_of?(e) } }
24
- err = res[FAIL].delete_at(idx)
25
- error_callback(res, err)
26
- env = res.merge('max_retries' => times - 1)
27
- give_promise(call(log(
28
- env, Event::Retrying.new(nil, "(#{times}) for: #{err.inspect}")), &k))
29
- else
30
- k.call(res)
24
+ if idx = res[FAIL].index{ |f| errors.find{ |e| f.kind_of?(e) } }
25
+ err = res[FAIL].delete_at(idx)
26
+ error_callback(res, err)
27
+ env = res.merge('max_retries' => times - 1)
28
+ event = Event::Retrying.new(nil, "(#{times}) for: #{err.inspect}")
29
+ give_promise(call(log(env, event), &k))
30
+ else
31
+ k.call(res)
32
+ end
31
33
  end
32
34
  end
33
35
  end