restify 1.15.2 → 2.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/CHANGELOG.md +25 -2
- data/README.md +23 -31
- data/lib/restify/adapter/base.rb +4 -0
- data/lib/restify/adapter/telemetry.rb +54 -0
- data/lib/restify/adapter/typhoeus.rb +21 -3
- data/lib/restify/context.rb +3 -3
- data/lib/restify/error.rb +2 -2
- data/lib/restify/link.rb +4 -4
- data/lib/restify/processors/base/parsing.rb +2 -21
- data/lib/restify/processors/base.rb +1 -1
- data/lib/restify/promise.rb +2 -2
- data/lib/restify/registry.rb +1 -1
- data/lib/restify/relation.rb +45 -17
- data/lib/restify/request.rb +6 -6
- data/lib/restify/timeout.rb +2 -2
- data/lib/restify/version.rb +3 -3
- data/lib/restify.rb +0 -1
- data/spec/restify/cache_spec.rb +16 -12
- data/spec/restify/context_spec.rb +8 -3
- data/spec/restify/error_spec.rb +13 -16
- data/spec/restify/features/head_requests_spec.rb +5 -4
- data/spec/restify/features/request_bodies_spec.rb +8 -8
- data/spec/restify/features/request_errors_spec.rb +2 -2
- data/spec/restify/features/request_headers_spec.rb +3 -6
- data/spec/restify/features/response_errors_spec.rb +1 -1
- data/spec/restify/global_spec.rb +10 -10
- data/spec/restify/processors/base_spec.rb +6 -7
- data/spec/restify/processors/json_spec.rb +21 -62
- data/spec/restify/processors/msgpack_spec.rb +33 -70
- data/spec/restify/promise_spec.rb +31 -31
- data/spec/restify/registry_spec.rb +5 -7
- data/spec/restify/relation_spec.rb +185 -7
- data/spec/restify/resource_spec.rb +47 -53
- data/spec/restify/timeout_spec.rb +3 -3
- data/spec/restify_spec.rb +12 -73
- data/spec/spec_helper.rb +11 -15
- metadata +33 -64
- data/lib/restify/adapter/em.rb +0 -134
- data/lib/restify/adapter/pooled_em.rb +0 -269
@@ -1,269 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'eventmachine'
|
4
|
-
require 'em-http-request'
|
5
|
-
|
6
|
-
module Restify
|
7
|
-
module Adapter
|
8
|
-
class PooledEM < Base
|
9
|
-
include Logging
|
10
|
-
|
11
|
-
# This class maintains a pool of connection objects, grouped by origin,
|
12
|
-
# and ensures limits for total parallel requests and per-origin requests.
|
13
|
-
#
|
14
|
-
# It does so by maintaining a list of already open, reusable connections.
|
15
|
-
# When any of them are checked out for usage, it counts the usages to
|
16
|
-
# prevent constraints being broken.
|
17
|
-
class Pool
|
18
|
-
include Logging
|
19
|
-
|
20
|
-
def initialize(size: 32, per_host: 6, connect_timeout: 2, inactivity_timeout: 10)
|
21
|
-
@size = size
|
22
|
-
@per_host = per_host
|
23
|
-
@connect_timeout = connect_timeout
|
24
|
-
@inactivity_timeout = inactivity_timeout
|
25
|
-
|
26
|
-
@host = Hash.new {|h, k| h[k] = 0 }
|
27
|
-
@available = []
|
28
|
-
@queue = []
|
29
|
-
@used = 0
|
30
|
-
end
|
31
|
-
|
32
|
-
# Request a connection from the pool.
|
33
|
-
#
|
34
|
-
# Attempts to checkout a reusable connection from the pool (or create a
|
35
|
-
# new one). If any of the limits have been reached, the request will be
|
36
|
-
# put onto a queue until other connections are released.
|
37
|
-
#
|
38
|
-
# Returns a Deferrable that succeeds with a connection instance once a
|
39
|
-
# connection has been checked out (usually immediately).
|
40
|
-
#
|
41
|
-
# @return [Deferrable<Request>]
|
42
|
-
#
|
43
|
-
def get(request, timeout: 2)
|
44
|
-
defer = Deferrable.new(request)
|
45
|
-
defer.timeout(timeout, :timeout)
|
46
|
-
defer.errback { @queue.delete(defer) }
|
47
|
-
|
48
|
-
checkout(defer)
|
49
|
-
|
50
|
-
defer
|
51
|
-
end
|
52
|
-
|
53
|
-
# Return a connection to the pool.
|
54
|
-
#
|
55
|
-
# If there are requests in the queue (due to one of the limits having
|
56
|
-
# been reached), they will be given an attempt to use the released
|
57
|
-
# connection.
|
58
|
-
#
|
59
|
-
# If no requests are queued, the connection will be held for reuse by a
|
60
|
-
# subsequent request.
|
61
|
-
#
|
62
|
-
# @return [void]
|
63
|
-
#
|
64
|
-
def release(conn)
|
65
|
-
@available.unshift(conn) if @available.size < @size
|
66
|
-
@used -= 1 if @used.positive?
|
67
|
-
|
68
|
-
logger.debug do
|
69
|
-
"[#{conn.uri}] Released to pool (#{@available.size}/#{@used}/#{size})"
|
70
|
-
end
|
71
|
-
|
72
|
-
checkout(@queue.shift) if @queue.any? # checkout next waiting defer
|
73
|
-
end
|
74
|
-
|
75
|
-
alias << release
|
76
|
-
|
77
|
-
def remove(conn)
|
78
|
-
close(conn)
|
79
|
-
|
80
|
-
logger.debug do
|
81
|
-
"[#{conn.uri}] Removed from pool (#{@available.size}/#{@used}/#{size})"
|
82
|
-
end
|
83
|
-
|
84
|
-
checkout(@queue.shift) if @queue.any? # checkout next waiting defer
|
85
|
-
end
|
86
|
-
|
87
|
-
# Determine the number of connections in the pool.
|
88
|
-
#
|
89
|
-
# This takes into account both reusable (idle) and used connections.
|
90
|
-
#
|
91
|
-
# @return [Integer]
|
92
|
-
#
|
93
|
-
def size
|
94
|
-
@available.size + @used
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def close(conn)
|
100
|
-
@used -= 1 if @used.positive?
|
101
|
-
@host[conn.uri.to_s] -= 1
|
102
|
-
|
103
|
-
conn.close
|
104
|
-
end
|
105
|
-
|
106
|
-
def checkout(defer)
|
107
|
-
origin = defer.request.uri.origin
|
108
|
-
|
109
|
-
if (index = find_reusable_connection(origin))
|
110
|
-
defer.succeed reuse_connection(index, origin)
|
111
|
-
elsif can_build_new_connection?(origin)
|
112
|
-
defer.succeed new_connection(origin)
|
113
|
-
else
|
114
|
-
queue defer
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def find_reusable_connection(origin)
|
119
|
-
@available.find_index {|conn| conn.uri == origin }
|
120
|
-
end
|
121
|
-
|
122
|
-
def reuse_connection(index, origin)
|
123
|
-
@used += 1
|
124
|
-
@available.delete_at(index).tap do
|
125
|
-
logger.debug do
|
126
|
-
"[#{origin}] Take connection from pool " \
|
127
|
-
"(#{@available.size}/#{@used}/#{size})"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def new_connection(origin)
|
133
|
-
# If we have reached the limit, we have to throw away the oldest
|
134
|
-
# reusable connection in order to open a new one
|
135
|
-
close_oldest if size >= @size
|
136
|
-
|
137
|
-
@used += 1
|
138
|
-
new(origin).tap do
|
139
|
-
logger.debug do
|
140
|
-
"[#{origin}] Add new connection to pool " \
|
141
|
-
"(#{@available.size}/#{@used}/#{size})"
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def close_oldest
|
147
|
-
close(@available.pop)
|
148
|
-
|
149
|
-
logger.debug do
|
150
|
-
"[#{origin}] Closed oldest connection in pool " \
|
151
|
-
"(#{@available.size}/#{@used}/#{size})"
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def queue(defer)
|
156
|
-
logger.debug do
|
157
|
-
"[#{origin}] Wait for free slot " \
|
158
|
-
"(#{@available.size}/#{@used}/#{size})"
|
159
|
-
end
|
160
|
-
|
161
|
-
@queue << defer
|
162
|
-
end
|
163
|
-
|
164
|
-
def new(origin)
|
165
|
-
logger.debug do
|
166
|
-
"Connect to '#{origin}' " \
|
167
|
-
"(#{@connect_timeout}/#{@inactivity_timeout})..."
|
168
|
-
end
|
169
|
-
|
170
|
-
@host[origin] += 1
|
171
|
-
|
172
|
-
EventMachine::HttpRequest.new origin,
|
173
|
-
connect_timeout: @connect_timeout,
|
174
|
-
inactivity_timeout: @inactivity_timeout
|
175
|
-
end
|
176
|
-
|
177
|
-
def can_build_new_connection?(origin)
|
178
|
-
return false if @host[origin] >= @per_host
|
179
|
-
|
180
|
-
size < @size || @available.any?
|
181
|
-
end
|
182
|
-
|
183
|
-
class Deferrable
|
184
|
-
include ::EventMachine::Deferrable
|
185
|
-
|
186
|
-
attr_reader :request
|
187
|
-
|
188
|
-
def initialize(request)
|
189
|
-
@request = request
|
190
|
-
end
|
191
|
-
|
192
|
-
def succeed(connection)
|
193
|
-
@connection = connection
|
194
|
-
super
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def initialize(**kwargs)
|
200
|
-
super()
|
201
|
-
@pool = Pool.new(**kwargs)
|
202
|
-
end
|
203
|
-
|
204
|
-
# rubocop:disable Metrics/BlockLength
|
205
|
-
def call_native(request, writer)
|
206
|
-
next_tick do
|
207
|
-
defer = @pool.get(request)
|
208
|
-
|
209
|
-
defer.errback do |error|
|
210
|
-
writer.reject(error)
|
211
|
-
end
|
212
|
-
|
213
|
-
defer.callback do |conn|
|
214
|
-
req = conn.send request.method.downcase,
|
215
|
-
keepalive: true,
|
216
|
-
redirects: 3,
|
217
|
-
path: request.uri.normalized_path,
|
218
|
-
query: request.uri.normalized_query,
|
219
|
-
body: request.body,
|
220
|
-
head: request.headers
|
221
|
-
|
222
|
-
req.callback do
|
223
|
-
writer.fulfill Response.new(
|
224
|
-
request,
|
225
|
-
req.last_effective_url,
|
226
|
-
req.response_header.status,
|
227
|
-
req.response_header,
|
228
|
-
req.response,
|
229
|
-
)
|
230
|
-
|
231
|
-
if req.response_header['CONNECTION'] == 'close'
|
232
|
-
@pool.remove(conn)
|
233
|
-
else
|
234
|
-
@pool << conn
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
req.errback do
|
239
|
-
@pool.remove(conn)
|
240
|
-
writer.reject(req.error)
|
241
|
-
end
|
242
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
243
|
-
@pool.remove(conn)
|
244
|
-
writer.reject(e)
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
# rubocop:enable all
|
249
|
-
|
250
|
-
private
|
251
|
-
|
252
|
-
def next_tick(&block)
|
253
|
-
ensure_running
|
254
|
-
EventMachine.next_tick(&block)
|
255
|
-
end
|
256
|
-
|
257
|
-
def ensure_running
|
258
|
-
return if EventMachine.reactor_running?
|
259
|
-
|
260
|
-
Thread.new do
|
261
|
-
EventMachine.run
|
262
|
-
rescue StandardError => e
|
263
|
-
logger.error(e)
|
264
|
-
raise e
|
265
|
-
end
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|