restify 1.10.0 → 1.15.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 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}]"