restify 1.10.0 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f44b84123c73ffd08cc8668448b03470c06acaf0f693e844fface91ff44179b5
4
- data.tar.gz: c194b2db48a18a7f234bba20ff4161f98277b1146e6b7bf2572c877c285c3794
3
+ metadata.gz: 241fd43c8e40d2a0c2c839443df03106edf3c8e491cac9338277ff4327a3927f
4
+ data.tar.gz: d8eea64a1de42ee6eb4666feb510669b69482f24c72a0f5c883f3482143751ca
5
5
  SHA512:
6
- metadata.gz: 89de3a2282568b6cd4b58afdf04e2271abf283721a25535e5420a4b1e52e4ed6b3edf894180d6fd15ee010c7692f99942c047fe7baef81d5bd67ff13b756e189
7
- data.tar.gz: e6b18bfd819aec933acdb9fbec820ea7e742918eebc795c77f5f7ba0b927ccae85b876b367df8325d90e2899aad77bcf30f691781419669fd7406a156e197f86
6
+ metadata.gz: 603395d24a3ecad6d35b217ef2d154cd0982f9cd9181769b2a5aa949e134f0f9af1a15cc731f54a2d700e3bd1162b667240420e5669af20b48c66a1ba84fc2c5
7
+ data.tar.gz: 89e64dcd00dde5a7711922619380d2003db5f8c42a7cbc473b19678b6ed251134ffe4b4c94b86afaa7efa7dea030c6d43876e57c00c85ea76523f9f7029f6804
data/CHANGELOG.md CHANGED
@@ -1,91 +1,173 @@
1
1
  # Changelog
2
+ All notable changes to this project will be documented in this file.
2
3
 
3
- ## 1.10.0
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
6
 
5
- * Raise more specific error on a few status codes (#17)
6
- * Complete promises with an empty list (but a list) of dependencies (#18)
7
7
 
8
- ## 1.9.0
9
8
 
10
- * Do not raise error on 3XX responses but return responses
9
+ ## Unreleased
10
+ ---
11
11
 
12
- ## 1.8.0
12
+ ### New
13
13
 
14
- * Add HEAD request method (#16)
14
+ ### Changes
15
15
 
16
- ## 1.7.0
16
+ ### Fixes
17
17
 
18
- * Introduce promise dependency timeouts (#15)
18
+ ### Breaks
19
19
 
20
- ## 1.6.0
21
20
 
22
- * Specify headers on restify clients and individual requests (#14)
21
+ ## 1.15.0 - (2021-07-09)
22
+ ---
23
23
 
24
- ## 1.5.0
24
+ ### New
25
+ * Improve memory usage when running lots of requests with typhoeus adapter
26
+ * Use hydra for synchronous requests
27
+ * Increased thread stability of typhoeus adapter (new internal queuing mechanism)
25
28
 
26
- * Tune typhoeus adapter to be more race-condition resilent
27
- * Add MessagePack processor enabled by default
29
+ ### Changes
30
+ * Use Ruby 2.5 as baseline for testing and linting
31
+ * Add Ruby 3.0 to automated testing
32
+ * Changed timing behavior for multiple requests due to new internal queuing mechanism for the typhoeus adapter
28
33
 
29
- ## 1.4.4
30
34
 
31
- * Fix race condition in typhoeus adapter
32
- * Add `#request` to `NetworkError` to ease debugging
35
+ ## 1.14.0 - (2020-12-15)
33
36
 
34
- ## 1.4.3
37
+ ### New
38
+ * Allow making requests with non-JSON bodies and custom content types (#42)
35
39
 
36
- * Add advanced logging capabilities using logging gem
37
- * Improve compatibility with webmocks returning `nil` as headers
38
40
 
39
- ## 1.4.1
41
+ ## 1.13.0 - (2020-06-12)
42
+ ---
40
43
 
41
- * Fix possible deadlock issues
44
+ ### New
45
+ * typhoeus: Support setting per-request libcurl options on adapter
46
+ * typhoeus: Enable short TCP keepalive probes by default (5s/5s)
42
47
 
43
- ## 1.4.0
44
48
 
45
- * Fix possible concurrency issue with typhoeus adapter
46
- * Add timeout option to requests (only supported by typhoeus adapter)
49
+ ## 1.12.0 - (2020-04-01)
50
+ ---
47
51
 
48
- ## 1.3.1
52
+ ### Added
53
+ * Explicit exception class for HTTP status code 410 (Gone)
49
54
 
50
- * Improve typhoeus adapters initial request queuing
51
- * Disable default pipelining
55
+ ### Changed
52
56
 
53
- ## 1.3.0
57
+ ### Fixed
58
+ * `GatewayError` exception classes introduced in v1.11.0 now properly inherit from `ServerError` (#30)
54
59
 
55
- * Improve typhoeus adapter to better utilize concurrency
56
- * Default to new typhoeus adapter
57
60
 
58
- ## 1.2.1
61
+ ## 1.11.0 - (2019-07-11)
62
+ ### Added
63
+ * Explicit exception classes for HTTP status codes 500, 502, 503, 504
59
64
 
60
- * Fix issue with Ruby 2.2 compatibility
65
+ ## 1.10.0 - 2018-12-11
66
+ ### Changed
67
+ - Raise more specific error on a few status codes (#17)
68
+ - Complete promises with an empty list (but a list) of dependencies (#18)
61
69
 
62
- ## 1.2.0
70
+ ## 1.9.0 - 2018-11-13
71
+ ### Changed
72
+ - Do not raise error on 3XX responses but return responses
63
73
 
64
- * Add experimental PooledEM adapter (#10)
65
- * Improve marshalling of resources
74
+ ## 1.8.0 - 2018-08-22
75
+ ### Added
76
+ - Add HEAD request method (#16)
66
77
 
67
- ## 1.1.0
78
+ ## 1.7.0 - 2018-08-15
79
+ ### Added
80
+ - Introduce promise dependency timeouts (#15)
68
81
 
69
- * Return response body if no processor matches (#7)
70
- * Add shortcuts for creating fulfilled / rejected promises (#6)
82
+ ## 1.6.0 - 2018-08-09
83
+ ### Changed
84
+ - Specify headers on restify clients and individual requests (#14)
71
85
 
72
- ## 1.0.0
86
+ ## 1.5.0 - 2018-07-31
87
+ ### Added
88
+ - Add MessagePack processor enabled by default
73
89
 
74
- * Experimental cache API doing nothing for now
75
- * Use `~> 1.0` of `concurrent-ruby`
90
+ ### Changed
91
+ - Tune typhoeus adapter to be more race-condition resilent
76
92
 
77
- ## 0.5.0
93
+ ## 1.4.4 - 2018-07-13
94
+ ### Added
95
+ - Add `#request` to `NetworkError` to ease debugging
78
96
 
79
- * Add `sync` option to typhoeus adapter
80
- * Add registry for storing entry points
81
- * Make eventmachine based adapter default
97
+ ### Changed
98
+ - Fix race condition in typhoeus adapter
82
99
 
83
- ## 0.4.0
100
+ ## 1.4.3 - 2017-11-15
101
+ ### Added
102
+ - Add advanced logging capabilities using logging gem
84
103
 
85
- * Add method to explicit access resource data
86
- * Drop obligation in favor of simple Concurrent::IVar based promise class.
104
+ ### Changed
105
+ - Improve compatibility with webmocks returning `nil` as headers
106
+
107
+ ## 1.4.1 - 2017-11-15
108
+ ### Changed
109
+ - Fix possible deadlock issues
110
+
111
+ ## 1.4.0 - 2017-11-10
112
+ ### Added
113
+ - Add timeout option to requests (only supported by typhoeus adapter)
114
+
115
+ ### Changed
116
+ - Fix possible concurrency issue with typhoeus adapter
117
+
118
+ ## 1.3.1 - 2017-11-10
119
+ ### Changed
120
+ - Improve typhoeus adapters initial request queuing
121
+ - Disable default pipelining
122
+
123
+ ## 1.3.0 - 2017-11-08
124
+ ### Changed
125
+ - Improve typhoeus adapter to better utilize concurrency
126
+ - Default to new typhoeus adapter
127
+
128
+ ## 1.2.1 - 2017-10-30
129
+ ### Changed
130
+ - Fix issue with Ruby 2.2 compatibility
131
+
132
+ ## 1.2.0 - 2017-10-30
133
+ ### Added
134
+ - Add experimental PooledEM adapter (#10)
135
+
136
+ ### Changed
137
+ - Improve marshaling of resources
138
+
139
+ ## 1.1.0 - 2017-05-12
140
+ ### Added
141
+ - Add shortcuts for creating fulfilled / rejected promises (#6)
142
+
143
+ ### Changed
144
+ - Return response body if no processor matches (#7)
145
+
146
+ ## 1.0.0 - 2016-08-22
147
+ ### Added
148
+ - Experimental cache API doing nothing for now
149
+
150
+ ### Changed
151
+ - Use `~> 1.0` of `concurrent-ruby`
152
+
153
+ ## 0.5.0 - 2016-04-04
154
+ ### Added
155
+ - Add `sync` option to typhoeus adapter
156
+ - Add registry for storing entry points
157
+
158
+ ### Changed
159
+ - Make eventmachine based adapter default
160
+
161
+ ## 0.4.0 - 2016-02-24
162
+ ### Added
163
+ - Add method to explicit access resource data
164
+
165
+ ### Changed
166
+ - Use typhoeus as default adapter
167
+ - `Restify.new` returns relation now instead of resource
168
+
169
+ ### Removed
170
+ - Drop obligation in favor of simple Concurrent::IVar based promise class.
87
171
  Notable changes:
88
- - Returned object us of type `Restify::Promise` now.
89
- - `value` will not raise exception but return `nil` in case of failure. Use `value!` for old behavior.
90
- * Use typhoeus as default adapter
91
- * `Restify.new` returns relation now instead of resource
172
+ - Returned object us of type `Restify::Promise` now.
173
+ - `value` will not raise exception but return `nil` in case of failure. Use `value!` for old behavior.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Restify
2
2
 
3
3
  [![Build Status](https://travis-ci.org/jgraichen/restify.svg?branch=master)](https://travis-ci.org/jgraichen/restify)
4
+ [![Code Quality](https://codebeat.co/badges/18ffe6b7-8239-493a-b5b6-be329b9f275d)](https://codebeat.co/projects/github-com-jgraichen-restify-master)
4
5
 
5
6
  Restify is an hypermedia REST client that does parallel, concurrent and keep-alive requests by default.
6
7
 
@@ -14,20 +15,17 @@ Restify is build upon the following libraries:
14
15
  * [addressable](https://github.com/sporkmonger/addressable)
15
16
  * [typhoeus](https://github.com/typhoeus/typhoeus)
16
17
 
17
- It has optional HTTP adapters using:
18
-
19
- * [em-http-request](https://github.com/igrigorik/em-http-request)
20
-
21
18
  The HTTP adapters are mostly run in a background thread and may not survive mid-application forks.
22
19
 
23
- Included processors can handle:
20
+ Restify includes processors to parse responses and to extract links between resources. The following formats are can be parsed:
24
21
 
25
- * Plain JSON with GitHub-style relations
26
- * MessagePack with GitHub-style relations *(currently experimental)*
22
+ * JSON
23
+ * MessagePack
27
24
 
28
- (Beside HTTP Link header that's always supported)
25
+ Links are extracted from
29
26
 
30
- Restify requires Ruby 2.0+.
27
+ * HTTP Link header
28
+ * Github-style relations in payloads
31
29
 
32
30
  ### Planned features
33
31
 
@@ -105,9 +103,9 @@ commit = repo.rel(:commits).get.value.first
105
103
  And print it:
106
104
 
107
105
  ```ruby
108
- puts "Last commit: #{commit[:sha]}"
109
- puts "By #{commit[:commit][:author][:name]} <#{commit[:commit][:author][:email]}>"
110
- puts "#{commit[:commit][:message]}"
106
+ puts "Last commit: #{commit['sha']}"
107
+ puts "By #{commit['commit']['author']['name']} <#{commit['commit']['author']['email']}>"
108
+ puts "#{commit['commit']['message']}"
111
109
  ```
112
110
 
113
111
  See commented example in main spec [`spec/restify_spec.rb`](https://github.com/jgraichen/restify/blob/master/spec/restify_spec.rb#L100) or in the `examples` directory.
@@ -63,8 +63,8 @@ module Restify
63
63
  query: request.uri.normalized_query,
64
64
  body: request.body,
65
65
  head: request.headers
66
- rescue Exception => err # rubocop:disable RescueException
67
- writer.reject err
66
+ rescue Exception => e # rubocop:disable Lint/RescueException
67
+ writer.reject e
68
68
  requests.shift unless pipeline?
69
69
  return
70
70
  end
@@ -128,12 +128,10 @@ module Restify
128
128
  return if EventMachine.reactor_running?
129
129
 
130
130
  Thread.new do
131
- begin
132
- EventMachine.run {}
133
- rescue StandardError => e
134
- puts "#{self.class} -> #{e}\n#{e.backtrace.join("\n")}"
135
- raise e
136
- end
131
+ EventMachine.run {}
132
+ rescue StandardError => e
133
+ puts "#{self.class} -> #{e}\n#{e.backtrace.join("\n")}"
134
+ raise e
137
135
  end
138
136
  end
139
137
  end
@@ -63,7 +63,7 @@ module Restify
63
63
  #
64
64
  def release(conn)
65
65
  @available.unshift(conn) if @available.size < @size
66
- @used -= 1 if @used > 0
66
+ @used -= 1 if @used.positive?
67
67
 
68
68
  logger.debug do
69
69
  "[#{conn.uri}] Released to pool (#{@available.size}/#{@used}/#{size})"
@@ -97,7 +97,7 @@ module Restify
97
97
  private
98
98
 
99
99
  def close(conn)
100
- @used -= 1 if @used > 0
100
+ @used -= 1 if @used.positive?
101
101
  @host[conn.uri.to_s] -= 1
102
102
 
103
103
  conn.close
@@ -200,9 +200,9 @@ module Restify
200
200
  @pool = Pool.new(**kwargs)
201
201
  end
202
202
 
203
- # rubocop:disable MethodLength
204
- # rubocop:disable AbcSize
205
- # rubocop:disable BlockLength
203
+ # rubocop:disable Metrics/MethodLength
204
+ # rubocop:disable Metrics/AbcSize
205
+ # rubocop:disable Metrics/BlockLength
206
206
  def call_native(request, writer)
207
207
  next_tick do
208
208
  defer = @pool.get(request)
@@ -212,39 +212,37 @@ module Restify
212
212
  end
213
213
 
214
214
  defer.callback do |conn|
215
- begin
216
- req = conn.send request.method.downcase,
217
- keepalive: true,
218
- redirects: 3,
219
- path: request.uri.normalized_path,
220
- query: request.uri.normalized_query,
221
- body: request.body,
222
- head: request.headers
223
-
224
- req.callback do
225
- writer.fulfill Response.new(
226
- request,
227
- req.last_effective_url,
228
- req.response_header.status,
229
- req.response_header,
230
- req.response
231
- )
232
-
233
- if req.response_header['CONNECTION'] == 'close'
234
- @pool.remove(conn)
235
- else
236
- @pool << conn
237
- end
238
- end
239
-
240
- req.errback do
215
+ req = conn.send request.method.downcase,
216
+ keepalive: true,
217
+ redirects: 3,
218
+ path: request.uri.normalized_path,
219
+ query: request.uri.normalized_query,
220
+ body: request.body,
221
+ head: request.headers
222
+
223
+ req.callback do
224
+ writer.fulfill Response.new(
225
+ request,
226
+ req.last_effective_url,
227
+ req.response_header.status,
228
+ req.response_header,
229
+ req.response
230
+ )
231
+
232
+ if req.response_header['CONNECTION'] == 'close'
241
233
  @pool.remove(conn)
242
- writer.reject(req.error)
234
+ else
235
+ @pool << conn
243
236
  end
244
- rescue Exception => ex # rubocop:disable RescueException
237
+ end
238
+
239
+ req.errback do
245
240
  @pool.remove(conn)
246
- writer.reject(ex)
241
+ writer.reject(req.error)
247
242
  end
243
+ rescue Exception => e # rubocop:disable Lint/RescueException
244
+ @pool.remove(conn)
245
+ writer.reject(e)
248
246
  end
249
247
  end
250
248
  end
@@ -261,12 +259,10 @@ module Restify
261
259
  return if EventMachine.reactor_running?
262
260
 
263
261
  Thread.new do
264
- begin
265
- EventMachine.run {}
266
- rescue StandardError => e
267
- logger.error(e)
268
- raise e
269
- end
262
+ EventMachine.run {}
263
+ rescue StandardError => e
264
+ logger.error(e)
265
+ raise e
270
266
  end
271
267
  end
272
268
  end
@@ -16,55 +16,59 @@ module Restify
16
16
  'Transfer-Encoding' => ''
17
17
  }.freeze
18
18
 
19
- def initialize(sync: false, **options)
20
- @sync = sync
21
- @hydra = ::Typhoeus::Hydra.new(**options)
22
- @mutex = Mutex.new
23
- @signal = ConditionVariable.new
19
+ DEFAULT_OPTIONS = {
20
+ followlocation: true,
21
+ tcp_keepalive: true,
22
+ tcp_keepidle: 5,
23
+ tcp_keepintvl: 5
24
+ }.freeze
25
+
26
+ def initialize(sync: false, options: {}, **kwargs)
27
+ @hydra = ::Typhoeus::Hydra.new(**kwargs)
28
+ @mutex = Mutex.new
29
+ @options = DEFAULT_OPTIONS.merge(options)
30
+ @queue = Queue.new
31
+ @sync = sync
32
+ @thread = nil
33
+
34
+ super()
24
35
  end
25
36
 
26
37
  def sync?
27
38
  @sync
28
39
  end
29
40
 
30
- # rubocop:disable AbcSize
31
- # rubocop:disable MethodLength
41
+ # rubocop:disable Metrics/MethodLength
32
42
  def call_native(request, writer)
33
43
  req = convert(request, writer)
34
44
 
35
45
  if sync?
36
- req.run
46
+ @hydra.queue(req)
47
+ @hydra.run
37
48
  else
38
- @mutex.synchronize do
39
- debug 'request:add',
40
- tag: request.object_id,
41
- method: request.method.upcase,
42
- url: request.uri
49
+ debug 'request:add',
50
+ tag: request.object_id,
51
+ method: request.method.upcase,
52
+ url: request.uri
43
53
 
44
- @hydra.queue(req)
45
- @hydra.dequeue_many
54
+ @queue << convert(request, writer)
46
55
 
47
- thread.run unless thread.status
48
- end
49
-
50
- debug 'request:signal', tag: request.object_id
51
-
52
- @signal.signal
56
+ thread.run unless thread.status
53
57
  end
54
58
  end
55
- # rubocop:enable all
59
+ # rubocop:enable Metrics/MethodLength
56
60
 
57
61
  private
58
62
 
59
- # rubocop:disable AbcSize
60
- # rubocop:disable MethodLength
63
+ # rubocop:disable Metrics/AbcSize
64
+ # rubocop:disable Metrics/MethodLength
61
65
  def convert(request, writer)
62
66
  ::Typhoeus::Request.new(
63
67
  request.uri,
68
+ **@options,
64
69
  method: request.method,
65
70
  headers: DEFAULT_HEADERS.merge(request.headers),
66
71
  body: request.body,
67
- followlocation: true,
68
72
  timeout: request.timeout,
69
73
  connecttimeout: request.timeout
70
74
  ).tap do |req|
@@ -81,10 +85,15 @@ module Restify
81
85
  else
82
86
  writer.fulfill convert_back(response, request)
83
87
  end
88
+
89
+ # Add all newly queued requests to active hydra, e.g. requests
90
+ # queued in a completion callback.
91
+ dequeue_all
84
92
  end
85
93
  end
86
94
  end
87
- # rubocop:enable all
95
+ # rubocop:enable Metrics/MethodLength
96
+ # rubocop:enable Metrics/AbcSize
88
97
 
89
98
  def convert_back(response, request)
90
99
  uri = request.uri
@@ -108,38 +117,51 @@ module Restify
108
117
  # Recreate thread if nil or dead
109
118
  debug 'hydra:spawn'
110
119
 
111
- @thread = Thread.new { _loop }
120
+ @thread = Thread.new do
121
+ Thread.current.name = 'Restify/Typhoeus Background'
122
+ run
123
+ end
112
124
  end
113
125
 
114
126
  @thread
115
127
  end
116
128
 
117
- def _loop
118
- Thread.current.name = 'Restify/Typhoeus Background'
119
- loop { _run }
120
- end
129
+ # rubocop:disable Metrics/MethodLength
130
+ def run
131
+ runs = 0
121
132
 
122
- def _ongoing?
123
- @hydra.queued_requests.any? || @hydra.multi.easy_handles.any?
124
- end
133
+ loop do
134
+ if @queue.empty? && runs > 100
135
+ debug 'hydra:gc'
136
+ GC.start(full_mark: false, immediate_sweep: false)
137
+ runs = 0
138
+ end
125
139
 
126
- # rubocop:disable MethodLength
127
- def _run
128
- debug 'hydra:run'
129
- @hydra.run while _ongoing?
130
- debug 'hydra:completed'
140
+ debug 'hydra:pop'
131
141
 
132
- @mutex.synchronize do
133
- return if _ongoing?
142
+ # Wait for next item and add all available requests to hydra
143
+ @hydra.queue @queue.pop
144
+ dequeue_all
145
+
146
+ debug 'hydra:run'
147
+ @hydra.run
148
+ runs += 1
149
+ debug 'hydra:completed'
150
+ rescue StandardError => e
151
+ logger.error(e)
152
+ end
153
+ ensure
154
+ debug 'hydra:exit'
155
+ end
156
+ # rubocop:enable Metrics/MethodLength
134
157
 
135
- debug 'hydra:pause'
136
- @signal.wait(@mutex, 60)
137
- debug 'hydra:resumed'
158
+ def dequeue_all
159
+ loop do
160
+ @hydra.queue @queue.pop(true)
161
+ rescue ThreadError
162
+ break
138
163
  end
139
- rescue StandardError => e
140
- logger.error(e)
141
164
  end
142
- # rubocop:enable all
143
165
 
144
166
  def _log_prefix
145
167
  "[#{object_id}/#{Thread.current.object_id}]"