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.
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