typhoeus 1.6.0 → 1.7.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 +217 -1
- data/README.md +338 -160
- data/UPGRADE.md +29 -0
- data/lib/rack/typhoeus/middleware/params_decoder/helper.rb +67 -14
- data/lib/rack/typhoeus/middleware/params_decoder.rb +8 -19
- data/lib/rack/typhoeus.rb +2 -0
- data/lib/typhoeus/adapters/faraday.rb +89 -28
- data/lib/typhoeus/cache/dalli.rb +2 -0
- data/lib/typhoeus/cache/rails.rb +3 -1
- data/lib/typhoeus/cache/redis.rb +2 -0
- data/lib/typhoeus/config.rb +51 -7
- data/lib/typhoeus/easy_factory.rb +129 -23
- data/lib/typhoeus/errors/invalid_option.rb +23 -0
- data/lib/typhoeus/errors/no_response.rb +18 -0
- data/lib/typhoeus/errors/no_stub.rb +8 -1
- data/lib/typhoeus/errors/typhoeus_error.rb +2 -0
- data/lib/typhoeus/errors.rb +4 -0
- data/lib/typhoeus/expectation.rb +33 -1
- data/lib/typhoeus/hydra/queueable.rb +29 -4
- data/lib/typhoeus/hydra.rb +349 -21
- data/lib/typhoeus/pool.rb +80 -17
- data/lib/typhoeus/railtie.rb +4 -2
- data/lib/typhoeus/request/actions.rb +2 -0
- data/lib/typhoeus/request/callbacks.rb +11 -4
- data/lib/typhoeus/request/marshal.rb +5 -2
- data/lib/typhoeus/request/streamable.rb +2 -0
- data/lib/typhoeus/request.rb +317 -30
- data/lib/typhoeus/response/cacheable.rb +2 -0
- data/lib/typhoeus/response/header.rb +234 -14
- data/lib/typhoeus/response/informations.rb +75 -12
- data/lib/typhoeus/response/status.rb +4 -13
- data/lib/typhoeus/response.rb +35 -0
- data/lib/typhoeus/version.rb +3 -1
- data/lib/typhoeus.rb +89 -26
- data/typhoeus.gemspec +15 -3
- metadata +19 -22
- data/lib/typhoeus/hydra/addable.rb +0 -23
- data/lib/typhoeus/hydra/before.rb +0 -31
- data/lib/typhoeus/hydra/block_connection.rb +0 -35
- data/lib/typhoeus/hydra/cacheable.rb +0 -15
- data/lib/typhoeus/hydra/memoizable.rb +0 -56
- data/lib/typhoeus/hydra/runnable.rb +0 -19
- data/lib/typhoeus/hydra/stubbable.rb +0 -28
- data/lib/typhoeus/request/before.rb +0 -30
- data/lib/typhoeus/request/block_connection.rb +0 -52
- data/lib/typhoeus/request/cacheable.rb +0 -38
- data/lib/typhoeus/request/memoizable.rb +0 -38
- data/lib/typhoeus/request/operations.rb +0 -40
- data/lib/typhoeus/request/responseable.rb +0 -29
- data/lib/typhoeus/request/stubbable.rb +0 -30
data/lib/typhoeus/hydra.rb
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require 'typhoeus/hydra/cacheable'
|
|
4
|
-
require 'typhoeus/hydra/block_connection'
|
|
5
|
-
require 'typhoeus/hydra/memoizable'
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
6
3
|
require 'typhoeus/hydra/queueable'
|
|
7
|
-
require 'typhoeus/hydra/runnable'
|
|
8
|
-
require 'typhoeus/hydra/stubbable'
|
|
9
4
|
|
|
10
5
|
module Typhoeus
|
|
11
6
|
|
|
@@ -39,21 +34,13 @@ module Typhoeus
|
|
|
39
34
|
# @note Callbacks are going to delay the request
|
|
40
35
|
# execution.
|
|
41
36
|
class Hydra
|
|
42
|
-
include Hydra::Addable
|
|
43
|
-
include Hydra::Runnable
|
|
44
|
-
include Hydra::Memoizable
|
|
45
|
-
include Hydra::Cacheable
|
|
46
|
-
include Hydra::BlockConnection
|
|
47
|
-
include Hydra::Stubbable
|
|
48
|
-
include Hydra::Before
|
|
49
37
|
include Hydra::Queueable
|
|
50
38
|
|
|
51
39
|
# @example Set max_concurrency.
|
|
52
40
|
# Typhoeus::Hydra.new(max_concurrency: 20)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
attr_reader :multi
|
|
41
|
+
#
|
|
42
|
+
# @return [ Integer ] The maximum number of concurrent requests.
|
|
43
|
+
attr_reader :max_concurrency
|
|
57
44
|
|
|
58
45
|
class << self
|
|
59
46
|
|
|
@@ -87,9 +74,350 @@ module Typhoeus
|
|
|
87
74
|
# @see http://rubydoc.info/github/typhoeus/ethon/Ethon/Multi#initialize-instance_method
|
|
88
75
|
# Ethon::Multi#initialize
|
|
89
76
|
def initialize(options = {})
|
|
90
|
-
@options = options
|
|
91
|
-
@
|
|
92
|
-
|
|
77
|
+
@options = options.dup
|
|
78
|
+
@multi_creation_failed = false
|
|
79
|
+
self.max_concurrency = @options.fetch(:max_concurrency, 200)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Return the Ethon multi, creating it on first use. Constructing a Multi
|
|
83
|
+
# initializes libcurl, which Ethon 0.19 refuses to do in a process that
|
|
84
|
+
# forked after the parent initialized it. Creating or memoizing a Hydra
|
|
85
|
+
# during boot must therefore not create the native handle yet.
|
|
86
|
+
#
|
|
87
|
+
# @return [ Ethon::Multi ] The multi handle.
|
|
88
|
+
#
|
|
89
|
+
# @api private
|
|
90
|
+
def multi
|
|
91
|
+
@multi ||= build_multi
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Add a request to the hydra.
|
|
95
|
+
#
|
|
96
|
+
# This is the hot path for every request. The checks are unrolled instead
|
|
97
|
+
# of spread over a chain of included modules so a request that uses no
|
|
98
|
+
# optional features takes the shortest route to the multi.
|
|
99
|
+
#
|
|
100
|
+
# @example Add request.
|
|
101
|
+
# hydra.add(request)
|
|
102
|
+
#
|
|
103
|
+
# @param [ Typhoeus::Request ] request The request to add.
|
|
104
|
+
#
|
|
105
|
+
# @return [ Object ] The result of the handling callback or the multi add.
|
|
106
|
+
def add(request)
|
|
107
|
+
request.hydra = self
|
|
108
|
+
|
|
109
|
+
before_callbacks = Typhoeus.before_callbacks_for(request)
|
|
110
|
+
if before_callbacks
|
|
111
|
+
before_callbacks.each do |callback|
|
|
112
|
+
value = callback.call(request)
|
|
113
|
+
if value.is_a?(Response)
|
|
114
|
+
unless request.response
|
|
115
|
+
# A synthetic response is a short circuit like a stub. Mark it
|
|
116
|
+
# so success? works and it is not written to the cache.
|
|
117
|
+
value.mock = true
|
|
118
|
+
request.execute_headers_callbacks(value)
|
|
119
|
+
request.on_body.each { |body_callback| body_callback.call(value.body, value) }
|
|
120
|
+
request.finish(value)
|
|
121
|
+
end
|
|
122
|
+
dequeue_many
|
|
123
|
+
return value
|
|
124
|
+
elsif value.nil? || value == false
|
|
125
|
+
dequeue_many
|
|
126
|
+
return value
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
unless Typhoeus.stubs_disabled?
|
|
132
|
+
if response = Expectation.response_for(request)
|
|
133
|
+
# The stub block already ran this request for real.
|
|
134
|
+
return if response.request.equal?(request)
|
|
135
|
+
|
|
136
|
+
request.execute_headers_callbacks(response)
|
|
137
|
+
request.on_body.each { |callback| callback.call(response.body, response) }
|
|
138
|
+
return request.finish(response)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
raise Typhoeus::Errors::NoStub.new(request) if request.blocked?
|
|
143
|
+
|
|
144
|
+
if request.cacheable? && response = request.cached_response
|
|
145
|
+
request.finish_cached_response(response)
|
|
146
|
+
return dequeue_many
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
if request.memoizable?
|
|
150
|
+
add_memoized(request)
|
|
151
|
+
else
|
|
152
|
+
add_to_multi(request)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Return the memory.
|
|
157
|
+
#
|
|
158
|
+
# @example Get memory.
|
|
159
|
+
# hydra.memory
|
|
160
|
+
#
|
|
161
|
+
# @return [ Hash ] The memory.
|
|
162
|
+
def memory
|
|
163
|
+
@memory ||= {}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Requests that are waiting for an identical request to finish.
|
|
167
|
+
#
|
|
168
|
+
# @return [ Hash ]
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
def pending
|
|
172
|
+
@pending ||= {}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Start the hydra run.
|
|
176
|
+
#
|
|
177
|
+
# @example Start hydra run.
|
|
178
|
+
# hydra.run
|
|
179
|
+
#
|
|
180
|
+
# @return [ Symbol ] Return value from multi.perform.
|
|
181
|
+
def run
|
|
182
|
+
memory.clear
|
|
183
|
+
dequeue_many
|
|
184
|
+
# Nothing reached the multi (all requests were answered by a before
|
|
185
|
+
# callback, a stub, or the cache), so do not initialize libcurl.
|
|
186
|
+
return if !defined?(@multi) || @multi.nil?
|
|
187
|
+
|
|
188
|
+
multi.perform
|
|
189
|
+
rescue Exception => error
|
|
190
|
+
begin
|
|
191
|
+
cleanup_after_error
|
|
192
|
+
reset_multi if multi_handle_unusable?(error)
|
|
193
|
+
rescue StandardError
|
|
194
|
+
# Preserve the original error.
|
|
195
|
+
end
|
|
196
|
+
raise
|
|
197
|
+
ensure
|
|
198
|
+
finish_pending_after_error
|
|
199
|
+
memory.clear
|
|
200
|
+
pending.clear
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Set the maximum number of concurrent requests.
|
|
204
|
+
#
|
|
205
|
+
# @example Set max_concurrency.
|
|
206
|
+
# hydra.max_concurrency = 20
|
|
207
|
+
#
|
|
208
|
+
# @param [ Integer, String ] value The maximum number of requests to run
|
|
209
|
+
# at the same time.
|
|
210
|
+
#
|
|
211
|
+
# @raise [ ArgumentError ] When the value is not greater than zero.
|
|
212
|
+
#
|
|
213
|
+
# @return [ Integer ] The maximum number of concurrent requests.
|
|
214
|
+
def max_concurrency=(value)
|
|
215
|
+
concurrency = Integer(value)
|
|
216
|
+
if concurrency <= 0
|
|
217
|
+
raise ArgumentError, "max_concurrency must be greater than zero"
|
|
218
|
+
end
|
|
219
|
+
@max_concurrency = concurrency
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
private
|
|
223
|
+
|
|
224
|
+
# Add the easy handle for a request to the multi.
|
|
225
|
+
#
|
|
226
|
+
# @param [ Typhoeus::Request ] request The request to add.
|
|
227
|
+
#
|
|
228
|
+
# @return [ Object ] The result of the multi add.
|
|
229
|
+
def add_to_multi(request)
|
|
230
|
+
easy_factory = EasyFactory.new(request, self)
|
|
231
|
+
easy = easy_factory.get
|
|
232
|
+
begin
|
|
233
|
+
multi.add(easy)
|
|
234
|
+
rescue Exception
|
|
235
|
+
easy_factory.release_easy(easy) unless easy_factory.released?
|
|
236
|
+
raise
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Answer a memoized request from memory or attach it to the request that
|
|
241
|
+
# is already in flight.
|
|
242
|
+
#
|
|
243
|
+
# @param [ Typhoeus::Request ] request The request to add.
|
|
244
|
+
#
|
|
245
|
+
# @return [ Object ]
|
|
246
|
+
def add_memoized(request)
|
|
247
|
+
if memory.key?(request)
|
|
248
|
+
finish_request(request, memory[request])
|
|
249
|
+
dequeue_many
|
|
250
|
+
elsif pending.key?(request)
|
|
251
|
+
pending[request] << request
|
|
252
|
+
else
|
|
253
|
+
pending[request] = []
|
|
254
|
+
request.on_complete do |response|
|
|
255
|
+
# Finish from the snapshot taken before the leader's callbacks
|
|
256
|
+
# ran. A callback that changes the leader's response must not
|
|
257
|
+
# change the copy its followers receive. Keep the leader's own
|
|
258
|
+
# callback result as this callback's return value.
|
|
259
|
+
snapshot = memoized_response_for(request) || response
|
|
260
|
+
finish_pending(request, snapshot)
|
|
261
|
+
response.handled_response
|
|
262
|
+
end
|
|
263
|
+
begin
|
|
264
|
+
add_to_multi(request)
|
|
265
|
+
rescue Exception
|
|
266
|
+
pending.delete(request)
|
|
267
|
+
raise
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Finish all requests that waited for the given request.
|
|
273
|
+
#
|
|
274
|
+
# @param [ Typhoeus::Request ] request
|
|
275
|
+
# @param [ Typhoeus::Response ] response
|
|
276
|
+
#
|
|
277
|
+
# @return [ Object ] The response's handled response.
|
|
278
|
+
def finish_pending(request, response)
|
|
279
|
+
requests = delete_pending(request)
|
|
280
|
+
return response.handled_response unless requests
|
|
281
|
+
|
|
282
|
+
error = nil
|
|
283
|
+
requests.each do |pending_request|
|
|
284
|
+
begin
|
|
285
|
+
finish_request(pending_request, response)
|
|
286
|
+
rescue StandardError => e
|
|
287
|
+
error ||= e
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
raise error if error
|
|
291
|
+
|
|
292
|
+
response.handled_response
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Finish a request whose response is already available. Every request gets
|
|
296
|
+
# its own response object and the same header callbacks it would see for
|
|
297
|
+
# a stub or a cache hit.
|
|
298
|
+
#
|
|
299
|
+
# @param [ Typhoeus::Request ] request The waiting request.
|
|
300
|
+
# @param [ Typhoeus::Response ] response The response to copy.
|
|
301
|
+
#
|
|
302
|
+
# @return [ Object ] The response's handled response.
|
|
303
|
+
def finish_request(request, response)
|
|
304
|
+
copy = response.dup
|
|
305
|
+
copy.request = request
|
|
306
|
+
request.execute_headers_callbacks(copy)
|
|
307
|
+
request.finish(copy, true)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Finish followers whose leader produced a response but whose callbacks
|
|
311
|
+
# raised before finish_pending could run.
|
|
312
|
+
#
|
|
313
|
+
# @return [ void ]
|
|
314
|
+
def finish_pending_after_error
|
|
315
|
+
pending.to_a.each do |request, requests|
|
|
316
|
+
response = memoized_response_for(request)
|
|
317
|
+
next unless response
|
|
318
|
+
|
|
319
|
+
requests.each do |pending_request|
|
|
320
|
+
next if pending_request.response
|
|
321
|
+
|
|
322
|
+
begin
|
|
323
|
+
finish_request(pending_request, response)
|
|
324
|
+
rescue StandardError
|
|
325
|
+
# Preserve the original run error.
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Remove the waiting requests without hashing the leader again.
|
|
332
|
+
# Request#hash includes the request options, which a callback may have
|
|
333
|
+
# changed after the entry was added.
|
|
334
|
+
#
|
|
335
|
+
# @param [ Typhoeus::Request ] request The leader request.
|
|
336
|
+
#
|
|
337
|
+
# @return [ Array<Typhoeus::Request>, nil ] The waiting requests.
|
|
338
|
+
def delete_pending(request)
|
|
339
|
+
requests = nil
|
|
340
|
+
pending.delete_if do |pending_request, waiting_requests|
|
|
341
|
+
next false unless pending_request.equal?(request)
|
|
342
|
+
|
|
343
|
+
requests = waiting_requests
|
|
344
|
+
true
|
|
345
|
+
end
|
|
346
|
+
requests
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Find the leader's response by identity for the same reason. A changed
|
|
350
|
+
# option makes the request unusable as a hash key.
|
|
351
|
+
#
|
|
352
|
+
# @param [ Typhoeus::Request ] request The leader request.
|
|
353
|
+
#
|
|
354
|
+
# @return [ Typhoeus::Response, nil ] The memoized response.
|
|
355
|
+
def memoized_response_for(request)
|
|
356
|
+
memory.each_pair do |memoized_request, response|
|
|
357
|
+
return response if memoized_request.equal?(request)
|
|
358
|
+
end
|
|
359
|
+
nil
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Clean up the easies a failed run left behind. Requests that never reached
|
|
363
|
+
# the multi stay queued, so a caller can retry after handling the error.
|
|
364
|
+
#
|
|
365
|
+
# @return [ void ]
|
|
366
|
+
def cleanup_after_error
|
|
367
|
+
if defined?(@multi) && @multi
|
|
368
|
+
multi.easy_handles.dup.each do |easy|
|
|
369
|
+
begin
|
|
370
|
+
multi.delete(easy)
|
|
371
|
+
rescue StandardError
|
|
372
|
+
# The handle may already be unusable.
|
|
373
|
+
end
|
|
374
|
+
begin
|
|
375
|
+
Typhoeus::Pool.release(easy, :cleanup => true)
|
|
376
|
+
rescue StandardError
|
|
377
|
+
# Keep cleaning up the remaining handles.
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
ensure
|
|
382
|
+
# Invalid multi options make every queued request fail the same way.
|
|
383
|
+
queued_requests.clear if @multi_creation_failed
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# A fatal multi_perform error leaves the native multi handle in an
|
|
387
|
+
# unknown state. Replace it so the hydra can run requests again.
|
|
388
|
+
#
|
|
389
|
+
# @return [ void ]
|
|
390
|
+
def reset_multi
|
|
391
|
+
begin
|
|
392
|
+
multi.cleanup if multi.respond_to?(:cleanup)
|
|
393
|
+
rescue StandardError
|
|
394
|
+
# Freeing the old handle is best effort; it is unusable anyway.
|
|
395
|
+
end
|
|
396
|
+
@multi = build_multi
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Ethon 0.19 raises MultiPerform when libcurl reports a fatal error from
|
|
400
|
+
# curl_multi_perform. Earlier versions do not have the class.
|
|
401
|
+
#
|
|
402
|
+
# @param [ Exception ] error The error raised by the run.
|
|
403
|
+
#
|
|
404
|
+
# @return [ Boolean ] Whether the multi handle has to be replaced.
|
|
405
|
+
def multi_handle_unusable?(error)
|
|
406
|
+
defined?(Ethon::Errors::MultiPerform) &&
|
|
407
|
+
error.is_a?(Ethon::Errors::MultiPerform)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Build the multi handle from the options the hydra was created with.
|
|
411
|
+
#
|
|
412
|
+
# @return [ Ethon::Multi ] The new multi handle.
|
|
413
|
+
#
|
|
414
|
+
# @api private
|
|
415
|
+
def build_multi
|
|
416
|
+
@multi_creation_failed = false
|
|
417
|
+
Ethon::Multi.new(@options.reject { |key, _| key == :max_concurrency })
|
|
418
|
+
rescue StandardError
|
|
419
|
+
@multi_creation_failed = true
|
|
420
|
+
raise
|
|
93
421
|
end
|
|
94
422
|
end
|
|
95
423
|
end
|
data/lib/typhoeus/pool.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thread'
|
|
2
4
|
|
|
3
5
|
module Typhoeus
|
|
@@ -16,11 +18,32 @@ module Typhoeus
|
|
|
16
18
|
#
|
|
17
19
|
# @example Release easy.
|
|
18
20
|
# Typhoeus::Pool.release(easy)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
#
|
|
22
|
+
# @example Clean up instead of pooling.
|
|
23
|
+
# Typhoeus::Pool.release(easy, :cleanup => true)
|
|
24
|
+
def self.release(easy, cleanup: false)
|
|
25
|
+
reset_after_fork
|
|
26
|
+
|
|
27
|
+
if cleanup
|
|
28
|
+
easy.cleanup
|
|
29
|
+
return
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
clear_cookies(easy)
|
|
22
33
|
easy.reset
|
|
23
|
-
|
|
34
|
+
|
|
35
|
+
discarded = nil
|
|
36
|
+
@mutex.synchronize do
|
|
37
|
+
if max_size && easies.length >= max_size
|
|
38
|
+
discarded = easy
|
|
39
|
+
else
|
|
40
|
+
easies << easy
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
discarded.cleanup if discarded
|
|
44
|
+
rescue StandardError => e
|
|
45
|
+
# Ethon 0.19+ refuses to reset handles created before a fork.
|
|
46
|
+
raise unless defined?(Ethon::Errors::ForkSafety) && e.is_a?(Ethon::Errors::ForkSafety)
|
|
24
47
|
end
|
|
25
48
|
|
|
26
49
|
# Return an easy from the pool.
|
|
@@ -30,22 +53,34 @@ module Typhoeus
|
|
|
30
53
|
#
|
|
31
54
|
# @return [ Ethon::Easy ] The easy.
|
|
32
55
|
def self.get
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
easies.pop
|
|
36
|
-
else
|
|
37
|
-
# Process has forked. Clear all easies to avoid sockets being
|
|
38
|
-
# shared between processes.
|
|
39
|
-
@pid = Process.pid
|
|
40
|
-
easies.clear
|
|
41
|
-
nil
|
|
42
|
-
end
|
|
43
|
-
end || Ethon::Easy.new
|
|
56
|
+
reset_after_fork
|
|
57
|
+
@mutex.synchronize { easies.pop } || Ethon::Easy.new
|
|
44
58
|
end
|
|
45
59
|
|
|
46
|
-
# Clear the pool
|
|
60
|
+
# Clear the pool and release the native easy handles it owns.
|
|
61
|
+
#
|
|
62
|
+
# @example Clear the pool.
|
|
63
|
+
# Typhoeus::Pool.clear
|
|
64
|
+
#
|
|
65
|
+
# @return [ Array<Ethon::Easy> ] The now empty pool.
|
|
47
66
|
def self.clear
|
|
48
|
-
|
|
67
|
+
reset_after_fork
|
|
68
|
+
cleared = @mutex.synchronize do
|
|
69
|
+
current = easies.dup
|
|
70
|
+
easies.clear
|
|
71
|
+
current
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
cleared.each do |easy|
|
|
75
|
+
begin
|
|
76
|
+
easy.cleanup
|
|
77
|
+
rescue StandardError
|
|
78
|
+
# Clearing the pool is best effort. A handle that cannot be freed
|
|
79
|
+
# must not keep the other pooled handles alive.
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
easies
|
|
49
84
|
end
|
|
50
85
|
|
|
51
86
|
# Use yielded easy, will be released automatically afterwards.
|
|
@@ -63,8 +98,36 @@ module Typhoeus
|
|
|
63
98
|
|
|
64
99
|
private
|
|
65
100
|
|
|
101
|
+
# Drop state inherited from a parent process. The previous mutex may have
|
|
102
|
+
# been locked by a thread that does not exist after the fork, so it is
|
|
103
|
+
# replaced without locking it to avoid a deadlock.
|
|
104
|
+
def self.reset_after_fork
|
|
105
|
+
return if @pid == Process.pid
|
|
106
|
+
|
|
107
|
+
# Replace the lock before publishing the new pid so a thread that sees
|
|
108
|
+
# the new pid cannot pick up the inherited lock.
|
|
109
|
+
@mutex = Mutex.new
|
|
110
|
+
@easies = []
|
|
111
|
+
@pid = Process.pid
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.max_size
|
|
115
|
+
Typhoeus::Config.max_idle_easies if defined?(Typhoeus::Config)
|
|
116
|
+
end
|
|
117
|
+
|
|
66
118
|
def self.easies
|
|
67
119
|
@easies ||= []
|
|
68
120
|
end
|
|
121
|
+
|
|
122
|
+
# Flush cookies to the configured jar and clear them from the handle.
|
|
123
|
+
# libcurl builds without cookie support reject CURLOPT_COOKIELIST; that
|
|
124
|
+
# must not prevent the handle from being recycled.
|
|
125
|
+
def self.clear_cookies(easy)
|
|
126
|
+
easy.cookielist = "flush"
|
|
127
|
+
easy.cookielist = "all"
|
|
128
|
+
rescue StandardError => e
|
|
129
|
+
raise unless defined?(Ethon::Errors::UnsupportedOption) &&
|
|
130
|
+
e.is_a?(Ethon::Errors::UnsupportedOption)
|
|
131
|
+
end
|
|
69
132
|
end
|
|
70
133
|
end
|
data/lib/typhoeus/railtie.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "typhoeus"
|
|
2
4
|
|
|
3
5
|
module Rails
|
|
4
6
|
module Typhoeus
|
|
5
7
|
class Railtie < Rails::Railtie
|
|
6
8
|
# Need to include the Typhoeus middleware.
|
|
7
|
-
initializer "
|
|
8
|
-
app.config.middleware.use
|
|
9
|
+
initializer "typhoeus.params_decoder" do |app|
|
|
10
|
+
app.config.middleware.use Rack::Typhoeus::Middleware::ParamsDecoder
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Typhoeus
|
|
2
4
|
class Request
|
|
3
5
|
|
|
@@ -92,6 +94,9 @@ module Typhoeus
|
|
|
92
94
|
|
|
93
95
|
# Set on_progress callback.
|
|
94
96
|
#
|
|
97
|
+
# The callback is invoked by libcurl while the request transfers data.
|
|
98
|
+
# It is not called when the request completes.
|
|
99
|
+
#
|
|
95
100
|
# @example Set on_progress.
|
|
96
101
|
# request.on_progress do |dltotal, dlnow, ultotal, ulnow|
|
|
97
102
|
# puts "dltotal (#{dltotal}), dlnow (#{dlnow}), ultotal (#{ultotal}), ulnow (#{ulnow})"
|
|
@@ -99,7 +104,8 @@ module Typhoeus
|
|
|
99
104
|
#
|
|
100
105
|
# @param [ Block ] block The block to execute.
|
|
101
106
|
#
|
|
102
|
-
# @yield [
|
|
107
|
+
# @yield [ Integer, Integer, Integer, Integer ] The download total,
|
|
108
|
+
# downloaded bytes, upload total, and uploaded bytes.
|
|
103
109
|
#
|
|
104
110
|
# @return [ Array<Block> ] All on_progress blocks.
|
|
105
111
|
def on_progress(&block)
|
|
@@ -124,8 +130,9 @@ module Typhoeus
|
|
|
124
130
|
end
|
|
125
131
|
|
|
126
132
|
# Execute necessary callback and yields response. This
|
|
127
|
-
#
|
|
128
|
-
#
|
|
133
|
+
# includes on_complete in every case, on_success if successful
|
|
134
|
+
# and on_failure if not. Progress callbacks are invoked by libcurl
|
|
135
|
+
# while the transfer is running and are not called here.
|
|
129
136
|
#
|
|
130
137
|
# @example Execute callbacks.
|
|
131
138
|
# request.execute_callbacks
|
|
@@ -134,7 +141,7 @@ module Typhoeus
|
|
|
134
141
|
#
|
|
135
142
|
# @api private
|
|
136
143
|
def execute_callbacks
|
|
137
|
-
callbacks = Typhoeus.on_complete +
|
|
144
|
+
callbacks = Typhoeus.on_complete + on_complete
|
|
138
145
|
|
|
139
146
|
if response && response.success?
|
|
140
147
|
callbacks += Typhoeus.on_success + on_success
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Typhoeus
|
|
2
4
|
class Request
|
|
3
5
|
|
|
@@ -5,9 +7,10 @@ module Typhoeus
|
|
|
5
7
|
module Marshal
|
|
6
8
|
|
|
7
9
|
# Return the important data needed to serialize this Request, except the
|
|
8
|
-
# request callbacks
|
|
10
|
+
# request callbacks, `hydra`, and the response. The response may contain
|
|
11
|
+
# arbitrary callback results and can be serialized separately.
|
|
9
12
|
def marshal_dump
|
|
10
|
-
unmarshallable = %w(@on_complete @on_success @on_failure @on_progress @on_headers @on_body @hydra)
|
|
13
|
+
unmarshallable = %w(@on_complete @on_success @on_failure @on_progress @on_headers @on_body @before @hydra @response)
|
|
11
14
|
(instance_variables - unmarshallable - unmarshallable.map(&:to_sym)).map do |name|
|
|
12
15
|
[name, instance_variable_get(name)]
|
|
13
16
|
end
|