typhoeus 0.4.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +26 -0
  5. data/CHANGELOG.md +341 -28
  6. data/CONTRIBUTING.md +20 -0
  7. data/Gemfile +31 -2
  8. data/Guardfile +9 -0
  9. data/LICENSE +1 -1
  10. data/README.md +486 -357
  11. data/Rakefile +21 -12
  12. data/UPGRADE.md +55 -0
  13. data/lib/rack/typhoeus/middleware/params_decoder/helper.rb +76 -0
  14. data/lib/rack/typhoeus/middleware/params_decoder.rb +57 -0
  15. data/lib/rack/typhoeus.rb +1 -0
  16. data/lib/typhoeus/adapters/faraday.rb +180 -0
  17. data/lib/typhoeus/cache/dalli.rb +28 -0
  18. data/lib/typhoeus/cache/rails.rb +28 -0
  19. data/lib/typhoeus/cache/redis.rb +35 -0
  20. data/lib/typhoeus/config.rb +69 -0
  21. data/lib/typhoeus/easy_factory.rb +180 -0
  22. data/lib/typhoeus/errors/no_stub.rb +12 -0
  23. data/lib/typhoeus/errors/typhoeus_error.rb +8 -0
  24. data/lib/typhoeus/errors.rb +9 -0
  25. data/lib/typhoeus/expectation.rb +217 -0
  26. data/lib/typhoeus/hydra/addable.rb +23 -0
  27. data/lib/typhoeus/hydra/before.rb +31 -0
  28. data/lib/typhoeus/hydra/block_connection.rb +35 -0
  29. data/lib/typhoeus/hydra/cacheable.rb +15 -0
  30. data/lib/typhoeus/hydra/memoizable.rb +56 -0
  31. data/lib/typhoeus/hydra/queueable.rb +83 -0
  32. data/lib/typhoeus/hydra/runnable.rb +19 -0
  33. data/lib/typhoeus/hydra/stubbable.rb +28 -0
  34. data/lib/typhoeus/hydra.rb +84 -236
  35. data/lib/typhoeus/pool.rb +70 -0
  36. data/lib/typhoeus/railtie.rb +12 -0
  37. data/lib/typhoeus/request/actions.rb +125 -0
  38. data/lib/typhoeus/request/before.rb +30 -0
  39. data/lib/typhoeus/request/block_connection.rb +52 -0
  40. data/lib/typhoeus/request/cacheable.rb +38 -0
  41. data/lib/typhoeus/request/callbacks.rb +151 -0
  42. data/lib/typhoeus/request/marshal.rb +22 -0
  43. data/lib/typhoeus/request/memoizable.rb +38 -0
  44. data/lib/typhoeus/request/operations.rb +40 -0
  45. data/lib/typhoeus/request/responseable.rb +29 -0
  46. data/lib/typhoeus/request/streamable.rb +34 -0
  47. data/lib/typhoeus/request/stubbable.rb +30 -0
  48. data/lib/typhoeus/request.rb +186 -231
  49. data/lib/typhoeus/response/cacheable.rb +14 -0
  50. data/lib/typhoeus/response/header.rb +105 -0
  51. data/lib/typhoeus/response/informations.rb +248 -0
  52. data/lib/typhoeus/response/status.rb +106 -0
  53. data/lib/typhoeus/response.rb +60 -115
  54. data/lib/typhoeus/version.rb +3 -1
  55. data/lib/typhoeus.rb +126 -39
  56. data/perf/profile.rb +14 -0
  57. data/perf/vs_nethttp.rb +64 -0
  58. data/spec/rack/typhoeus/middleware/params_decoder/helper_spec.rb +156 -0
  59. data/spec/rack/typhoeus/middleware/params_decoder_spec.rb +31 -0
  60. data/spec/spec_helper.rb +29 -0
  61. data/spec/support/localhost_server.rb +94 -0
  62. data/spec/support/memory_cache.rb +15 -0
  63. data/spec/support/server.rb +116 -0
  64. data/spec/typhoeus/adapters/faraday_spec.rb +339 -0
  65. data/spec/typhoeus/cache/dalli_spec.rb +41 -0
  66. data/spec/typhoeus/cache/redis_spec.rb +41 -0
  67. data/spec/typhoeus/config_spec.rb +15 -0
  68. data/spec/typhoeus/easy_factory_spec.rb +143 -0
  69. data/spec/typhoeus/errors/no_stub_spec.rb +13 -0
  70. data/spec/typhoeus/expectation_spec.rb +280 -0
  71. data/spec/typhoeus/hydra/addable_spec.rb +22 -0
  72. data/spec/typhoeus/hydra/before_spec.rb +98 -0
  73. data/spec/typhoeus/hydra/block_connection_spec.rb +18 -0
  74. data/spec/typhoeus/hydra/cacheable_spec.rb +88 -0
  75. data/spec/typhoeus/hydra/memoizable_spec.rb +53 -0
  76. data/spec/typhoeus/hydra/queueable_spec.rb +98 -0
  77. data/spec/typhoeus/hydra/runnable_spec.rb +137 -0
  78. data/spec/typhoeus/hydra/stubbable_spec.rb +48 -0
  79. data/spec/typhoeus/hydra_spec.rb +22 -0
  80. data/spec/typhoeus/pool_spec.rb +137 -0
  81. data/spec/typhoeus/request/actions_spec.rb +19 -0
  82. data/spec/typhoeus/request/before_spec.rb +93 -0
  83. data/spec/typhoeus/request/block_connection_spec.rb +75 -0
  84. data/spec/typhoeus/request/cacheable_spec.rb +94 -0
  85. data/spec/typhoeus/request/callbacks_spec.rb +91 -0
  86. data/spec/typhoeus/request/marshal_spec.rb +60 -0
  87. data/spec/typhoeus/request/memoizable_spec.rb +34 -0
  88. data/spec/typhoeus/request/operations_spec.rb +101 -0
  89. data/spec/typhoeus/request/responseable_spec.rb +13 -0
  90. data/spec/typhoeus/request/stubbable_spec.rb +45 -0
  91. data/spec/typhoeus/request_spec.rb +232 -0
  92. data/spec/typhoeus/response/header_spec.rb +147 -0
  93. data/spec/typhoeus/response/informations_spec.rb +283 -0
  94. data/spec/typhoeus/response/status_spec.rb +256 -0
  95. data/spec/typhoeus/response_spec.rb +100 -0
  96. data/spec/typhoeus_spec.rb +105 -0
  97. data/typhoeus.gemspec +25 -0
  98. metadata +146 -158
  99. data/lib/typhoeus/curl.rb +0 -453
  100. data/lib/typhoeus/easy/auth.rb +0 -14
  101. data/lib/typhoeus/easy/callbacks.rb +0 -33
  102. data/lib/typhoeus/easy/ffi_helper.rb +0 -61
  103. data/lib/typhoeus/easy/infos.rb +0 -90
  104. data/lib/typhoeus/easy/options.rb +0 -115
  105. data/lib/typhoeus/easy/proxy.rb +0 -20
  106. data/lib/typhoeus/easy/ssl.rb +0 -82
  107. data/lib/typhoeus/easy.rb +0 -115
  108. data/lib/typhoeus/filter.rb +0 -28
  109. data/lib/typhoeus/form.rb +0 -61
  110. data/lib/typhoeus/header.rb +0 -54
  111. data/lib/typhoeus/hydra/callbacks.rb +0 -24
  112. data/lib/typhoeus/hydra/connect_options.rb +0 -61
  113. data/lib/typhoeus/hydra/stubbing.rb +0 -68
  114. data/lib/typhoeus/hydra_mock.rb +0 -131
  115. data/lib/typhoeus/multi.rb +0 -146
  116. data/lib/typhoeus/param_processor.rb +0 -43
  117. data/lib/typhoeus/remote.rb +0 -306
  118. data/lib/typhoeus/remote_method.rb +0 -108
  119. data/lib/typhoeus/remote_proxy_object.rb +0 -50
  120. data/lib/typhoeus/utils.rb +0 -50
@@ -0,0 +1,180 @@
1
+ require 'set'
2
+
3
+ module Typhoeus
4
+
5
+ # This is a Factory for easies to be used in the hydra.
6
+ # Before an easy is ready to be added to a multi the
7
+ # on_complete callback to be set.
8
+ # This is done by this class.
9
+ #
10
+ # @api private
11
+ class EasyFactory
12
+
13
+ RENAMED_OPTIONS = {
14
+ :auth_method => :httpauth,
15
+ :connect_timeout => :connecttimeout,
16
+ :encoding => :accept_encoding,
17
+ :follow_location => :followlocation,
18
+ :max_redirects => :maxredirs,
19
+ :proxy_type => :proxytype,
20
+ :ssl_cacert => :cainfo,
21
+ :ssl_capath => :capath,
22
+ :ssl_cert => :sslcert,
23
+ :ssl_cert_type => :sslcerttype,
24
+ :ssl_key => :sslkey,
25
+ :ssl_key_password => :keypasswd,
26
+ :ssl_key_type => :sslkeytype,
27
+ :ssl_version => :sslversion,
28
+ }
29
+
30
+ CHANGED_OPTIONS = {
31
+ :disable_ssl_host_verification => :ssl_verifyhost,
32
+ :disable_ssl_peer_verification => :ssl_verifypeer,
33
+ :proxy_auth_method => :proxyauth,
34
+ }
35
+
36
+ REMOVED_OPTIONS = Set.new([:cache_key_basis, :cache_timeout, :user_agent])
37
+
38
+ SANITIZE_IGNORE = Set.new([:method, :cache_ttl, :cache])
39
+ SANITIZE_TIMEOUT = Set.new([:timeout_ms, :connecttimeout_ms])
40
+
41
+ # Returns the request provided.
42
+ #
43
+ # @return [ Typhoeus::Request ]
44
+ attr_reader :request
45
+
46
+ # Returns the hydra provided.
47
+ #
48
+ # @return [ Typhoeus::Hydra ]
49
+ attr_reader :hydra
50
+
51
+ # Create an easy factory.
52
+ #
53
+ # @example Create easy factory.
54
+ # Typhoeus::Hydra::EasyFactory.new(request, hydra)
55
+ #
56
+ # @param [ Request ] request The request to build an easy for.
57
+ # @param [ Hydra ] hydra The hydra to build an easy for.
58
+ def initialize(request, hydra = nil)
59
+ @request = request
60
+ @hydra = hydra
61
+ end
62
+
63
+ # Return the easy in question.
64
+ #
65
+ # @example Return easy.
66
+ # easy_factory.easy
67
+ #
68
+ # @return [ Ethon::Easy ] The easy.
69
+ def easy
70
+ @easy ||= Typhoeus::Pool.get
71
+ end
72
+
73
+ # Fabricated easy.
74
+ #
75
+ # @example Prepared easy.
76
+ # easy_factory.get
77
+ #
78
+ # @return [ Ethon::Easy ] The easy.
79
+ def get
80
+ begin
81
+ easy.http_request(
82
+ request.base_url.to_s,
83
+ request.options.fetch(:method, :get),
84
+ sanitize(request.options)
85
+ )
86
+ rescue Ethon::Errors::InvalidOption => e
87
+ help = provide_help(e.message.match(/:\s(\w+)/)[1])
88
+ raise $!, "#{$!}#{help}", $!.backtrace
89
+ end
90
+ set_callback
91
+ easy
92
+ end
93
+
94
+ private
95
+
96
+ def sanitize(options)
97
+ # set nosignal to true by default
98
+ # this improves thread safety and timeout behavior
99
+ sanitized = {:nosignal => true}
100
+ options.each do |k,v|
101
+ s = k.to_sym
102
+ next if SANITIZE_IGNORE.include?(s)
103
+ if new_option = RENAMED_OPTIONS[k.to_sym]
104
+ warn("Deprecated option #{k}. Please use #{new_option} instead.")
105
+ sanitized[new_option] = v
106
+ # sanitize timeouts
107
+ elsif SANITIZE_TIMEOUT.include?(s)
108
+ if !v.integer?
109
+ warn("Value '#{v}' for option '#{k}' must be integer.")
110
+ end
111
+ sanitized[k] = v.ceil
112
+ else
113
+ sanitized[k] = v
114
+ end
115
+ end
116
+
117
+ sanitize_timeout!(sanitized, :timeout)
118
+ sanitize_timeout!(sanitized, :connecttimeout)
119
+
120
+ sanitized
121
+ end
122
+
123
+ def sanitize_timeout!(options, timeout)
124
+ timeout_ms = :"#{timeout}_ms"
125
+ if options[timeout] && options[timeout].round != options[timeout]
126
+ if !options[timeout_ms]
127
+ options[timeout_ms] = (options[timeout]*1000).ceil
128
+ end
129
+ options[timeout] = options[timeout].ceil
130
+ end
131
+ options
132
+ end
133
+
134
+ # Sets on_complete callback on easy in order to be able to
135
+ # track progress.
136
+ #
137
+ # @example Set callback.
138
+ # easy_factory.set_callback
139
+ #
140
+ # @return [ Ethon::Easy ] The easy.
141
+ def set_callback
142
+ if request.streaming?
143
+ response = nil
144
+ easy.on_headers do |easy|
145
+ response = Response.new(Ethon::Easy::Mirror.from_easy(easy).options)
146
+ request.execute_headers_callbacks(response)
147
+ end
148
+ request.on_body.each do |callback|
149
+ easy.on_body do |chunk, easy|
150
+ callback.call(chunk, response)
151
+ end
152
+ end
153
+ else
154
+ easy.on_headers do |easy|
155
+ request.execute_headers_callbacks(Response.new(Ethon::Easy::Mirror.from_easy(easy).options))
156
+ end
157
+ end
158
+ request.on_progress.each do |callback|
159
+ easy.on_progress do |dltotal, dlnow, ultotal, ulnow, easy|
160
+ callback.call(dltotal, dlnow, ultotal, ulnow, response)
161
+ end
162
+ end
163
+ easy.on_complete do |easy|
164
+ request.finish(Response.new(easy.mirror.options))
165
+ Typhoeus::Pool.release(easy)
166
+ if hydra && !hydra.queued_requests.empty?
167
+ hydra.dequeue_many
168
+ end
169
+ end
170
+ end
171
+
172
+ def provide_help(option)
173
+ if new_option = CHANGED_OPTIONS[option.to_sym]
174
+ "\nPlease try #{new_option} instead of #{option}." if new_option
175
+ elsif REMOVED_OPTIONS.include?(option.to_sym)
176
+ "\nThe option #{option} was removed."
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,12 @@
1
+ module Typhoeus
2
+ module Errors
3
+
4
+ # Raises when block connection is turned on
5
+ # and making a real request.
6
+ class NoStub < TyphoeusError
7
+ def initialize(request)
8
+ super("The connection is blocked and no stub defined: #{request.url}")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Typhoeus
2
+ module Errors
3
+
4
+ # Default typhoeus error class for all custom errors.
5
+ class TyphoeusError < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ require 'typhoeus/errors/typhoeus_error'
2
+ require 'typhoeus/errors/no_stub'
3
+
4
+ module Typhoeus
5
+
6
+ # This namespace contains all errors raised by Typhoeus.
7
+ module Errors
8
+ end
9
+ end
@@ -0,0 +1,217 @@
1
+ module Typhoeus
2
+
3
+ # This class represents an expectation. It is part
4
+ # of the stubbing mechanism. An expectation contains
5
+ # a url and options, like a request. They are compared
6
+ # to the request url and options in order to evaluate
7
+ # whether they match. If that's the case, the attached
8
+ # responses are returned one by one.
9
+ #
10
+ # @example Stub a request and get specified response.
11
+ # expected = Typhoeus::Response.new
12
+ # Typhoeus.stub("www.example.com").and_return(expected)
13
+ #
14
+ # actual = Typhoeus.get("www.example.com")
15
+ # expected == actual
16
+ # #=> true
17
+ #
18
+ # @example Stub a request and get a lazily-constructed response containing data from actual widgets that exist in the system when the stubbed request is made.
19
+ # Typhoeus.stub("www.example.com/widgets") do
20
+ # actual_widgets = Widget.all
21
+ # Typhoeus::Response.new(
22
+ # :body => actual_widgets.inject([]) do |ids, widget|
23
+ # ids << widget.id
24
+ # end.join(",")
25
+ # )
26
+ # end
27
+ #
28
+ # @example Stub a request and get a lazily-constructed response in the format requested.
29
+ # Typhoeus.stub("www.example.com") do |request|
30
+ # accept = (request.options[:headers]||{})['Accept'] || "application/json"
31
+ # format = accept.split(",").first
32
+ # body_obj = { 'things' => [ { 'id' => 'foo' } ] }
33
+ #
34
+ # Typhoeus::Response.new(
35
+ # :headers => {
36
+ # 'Content-Type' => format
37
+ # },
38
+ # :body => SERIALIZERS[format].serialize(body_obj)
39
+ # )
40
+ # end
41
+ class Expectation
42
+
43
+ # @api private
44
+ attr_reader :base_url
45
+
46
+ # @api private
47
+ attr_reader :options
48
+
49
+ # @api private
50
+ attr_reader :from
51
+
52
+ class << self
53
+
54
+ # Returns all expectations.
55
+ #
56
+ # @example Return expectations.
57
+ # Typhoeus::Expectation.all
58
+ #
59
+ # @return [ Array<Typhoeus::Expectation> ] The expectations.
60
+ def all
61
+ @expectations ||= []
62
+ end
63
+
64
+ # Clears expectations. This is handy while
65
+ # testing, and you want to make sure that
66
+ # you don't get canned responses.
67
+ #
68
+ # @example Clear expectations.
69
+ # Typhoeus::Expectation.clear
70
+ def clear
71
+ all.clear
72
+ end
73
+
74
+ # Returns stubbed response matching the
75
+ # provided request.
76
+ #
77
+ # @example Find response
78
+ # Typhoeus::Expectation.response_for(request)
79
+ #
80
+ # @return [ Typhoeus::Response ] The stubbed response from a
81
+ # matching expectation, or nil if no matching expectation
82
+ # is found.
83
+ #
84
+ # @api private
85
+ def response_for(request)
86
+ expectation = find_by(request)
87
+ return nil if expectation.nil?
88
+
89
+ expectation.response(request)
90
+ end
91
+
92
+ # @api private
93
+ def find_by(request)
94
+ all.find do |expectation|
95
+ expectation.matches?(request)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Creates an expectation.
101
+ #
102
+ # @example Create expectation.
103
+ # Typhoeus::Expectation.new(base_url)
104
+ #
105
+ # @return [ Expectation ] The created expectation.
106
+ #
107
+ # @api private
108
+ def initialize(base_url, options = {})
109
+ @base_url = base_url
110
+ @options = options
111
+ @response_counter = 0
112
+ @from = nil
113
+ end
114
+
115
+ # Set from value to mark an expectaion. Useful for
116
+ # other libraries, e.g. WebMock.
117
+ #
118
+ # @example Mark expectation.
119
+ # expectation.from(:webmock)
120
+ #
121
+ # @param [ String ] value Value to set.
122
+ #
123
+ # @return [ Expectation ] Returns self.
124
+ #
125
+ # @api private
126
+ def stubbed_from(value)
127
+ @from = value
128
+ self
129
+ end
130
+
131
+ # Specify what should be returned,
132
+ # when this expectation is hit.
133
+ #
134
+ # @example Add response.
135
+ # expectation.and_return(response)
136
+ #
137
+ # @return [ void ]
138
+ def and_return(response=nil, &block)
139
+ new_response = (response.nil? ? block : response)
140
+ responses.push(*new_response)
141
+ end
142
+
143
+ # Checks whether this expectation matches
144
+ # the provided request.
145
+ #
146
+ # @example Check if request matches.
147
+ # expectation.matches? request
148
+ #
149
+ # @param [ Request ] request The request to check.
150
+ #
151
+ # @return [ Boolean ] True when matches, else false.
152
+ #
153
+ # @api private
154
+ def matches?(request)
155
+ url_match?(request.base_url) && options_match?(request)
156
+ end
157
+
158
+ # Return canned responses.
159
+ #
160
+ # @example Return responses.
161
+ # expectation.responses
162
+ #
163
+ # @return [ Array<Typhoeus::Response> ] The responses.
164
+ #
165
+ # @api private
166
+ def responses
167
+ @responses ||= []
168
+ end
169
+
170
+ # Return the response. When there are
171
+ # multiple responses, they are returned one
172
+ # by one.
173
+ #
174
+ # @example Return response.
175
+ # expectation.response
176
+ #
177
+ # @return [ Response ] The response.
178
+ #
179
+ # @api private
180
+ def response(request)
181
+ response = responses.fetch(@response_counter, responses.last)
182
+ if response.respond_to?(:call)
183
+ response = response.call(request)
184
+ end
185
+ @response_counter += 1
186
+ response.mock = @from || true
187
+ response
188
+ end
189
+
190
+ private
191
+
192
+ # Check whether the options matches the request options.
193
+ # I checks options and original options.
194
+ def options_match?(request)
195
+ (options ? options.all?{ |k,v| request.original_options[k] == v || request.options[k] == v } : true)
196
+ end
197
+
198
+ # Check whether the base_url matches the request url.
199
+ # The base_url can be a string, regex or nil. String and
200
+ # regexp are checked, nil is always true, else false.
201
+ #
202
+ # Nil serves as a placeholder in case you want to match
203
+ # all urls.
204
+ def url_match?(request_url)
205
+ case base_url
206
+ when String
207
+ base_url == request_url
208
+ when Regexp
209
+ base_url === request_url
210
+ when nil
211
+ true
212
+ else
213
+ false
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,23 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module handles the request adding on
5
+ # hydra.
6
+ #
7
+ # @api private
8
+ module Addable
9
+
10
+ # Adds request to multi.
11
+ #
12
+ # @example Add request.
13
+ # hydra.add(request)
14
+ #
15
+ # @param [ Typhoeus::Request ] request to add.
16
+ #
17
+ # @return [ void ]
18
+ def add(request)
19
+ multi.add(EasyFactory.new(request, self).get)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module provides a way to hook into before
5
+ # a request gets queued in hydra. This is very powerful
6
+ # and you should be careful because when you accidently
7
+ # return a falsy value the request won't be executed.
8
+ #
9
+ # @api private
10
+ module Before
11
+
12
+ # Overrride add in order to execute callbacks in
13
+ # Typhoeus.before. Will break and return when a
14
+ # callback returns nil, false or a response. Calls super
15
+ # otherwise.
16
+ #
17
+ # @example Add the request.
18
+ # hydra.add(request)
19
+ def add(request)
20
+ Typhoeus.before.each do |callback|
21
+ value = callback.call(request)
22
+ if value.nil? || value == false || value.is_a?(Response)
23
+ dequeue
24
+ return value
25
+ end
26
+ end
27
+ super
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module handles the blocked connection request mode on
5
+ # the hydra side, where only stubbed requests
6
+ # are allowed.
7
+ # Connection blocking needs to be turned on:
8
+ # Typhoeus.configure do |config|
9
+ # config.block_connection = true
10
+ # end
11
+ #
12
+ # When trying to do real requests a NoStub error
13
+ # is raised.
14
+ #
15
+ # @api private
16
+ module BlockConnection
17
+
18
+ # Overrides add in order to check before if block connection
19
+ # is turned on. If thats the case a NoStub error is
20
+ # raised.
21
+ #
22
+ # @example Add the request.
23
+ # hydra.add(request)
24
+ #
25
+ # @param [ Request ] request The request to enqueue.
26
+ def add(request)
27
+ if request.blocked?
28
+ raise Typhoeus::Errors::NoStub.new(request)
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ module Typhoeus
2
+ class Hydra
3
+ module Cacheable
4
+ def add(request)
5
+ if request.cacheable? && response = request.cached_response
6
+ response.cached = true
7
+ request.finish(response)
8
+ dequeue
9
+ else
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module handles the GET request memoization
5
+ # on the hydra side. Memoization needs to be turned
6
+ # on:
7
+ # Typhoeus.configure do |config|
8
+ # config.memoize = true
9
+ # end
10
+ #
11
+ # @api private
12
+ module Memoizable
13
+
14
+ # Return the memory.
15
+ #
16
+ # @example Return the memory.
17
+ # hydra.memory
18
+ #
19
+ # @return [ Hash ] The memory.
20
+ def memory
21
+ @memory ||= {}
22
+ end
23
+
24
+ # Overrides add in order to check before if request
25
+ # is memoizable and already in memory. If thats the case,
26
+ # super is not called, instead the response is set and
27
+ # the on_complete callback called.
28
+ #
29
+ # @example Add the request.
30
+ # hydra.add(request)
31
+ #
32
+ # @param [ Request ] request The request to add.
33
+ #
34
+ # @return [ Request ] The added request.
35
+ def add(request)
36
+ if request.memoizable? && memory.has_key?(request)
37
+ response = memory[request]
38
+ request.finish(response, true)
39
+ dequeue
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ # Overrides run to make sure the memory is cleared after
46
+ # each run.
47
+ #
48
+ # @example Run hydra.
49
+ # hydra.run
50
+ def run
51
+ super
52
+ memory.clear
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,83 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module handles the request queueing on
5
+ # hydra.
6
+ #
7
+ # @api private
8
+ module Queueable
9
+
10
+ # Return the queued requests.
11
+ #
12
+ # @example Return queued requests.
13
+ # hydra.queued_requests
14
+ #
15
+ # @return [ Array<Typhoeus::Request> ] The queued requests.
16
+ def queued_requests
17
+ @queued_requests ||= []
18
+ end
19
+
20
+ # Abort the current hydra run as good as
21
+ # possible. This means that it only
22
+ # clears the queued requests and can't do
23
+ # anything about already running requests.
24
+ #
25
+ # @example Abort hydra.
26
+ # hydra.abort
27
+ def abort
28
+ queued_requests.clear
29
+ end
30
+
31
+ # Enqueues a request in order to be performed
32
+ # by the hydra. This can even be done while
33
+ # the hydra is running. Also sets hydra on
34
+ # request.
35
+ #
36
+ # @example Queue request.
37
+ # hydra.queue(request)
38
+ def queue(request)
39
+ request.hydra = self
40
+ queued_requests << request
41
+ end
42
+
43
+ # Pushes a request to the front of the queue,
44
+ # to be performed by the hydra. Also sets hydra
45
+ # on request
46
+ #
47
+ # @example Queue reques.
48
+ # hydra.queue_front(request)
49
+ def queue_front(request)
50
+ request.hydra = self
51
+ queued_requests.unshift request
52
+ end
53
+
54
+ # Removes a request from queued_requests and
55
+ # adds it to the hydra in order to be
56
+ # performed next.
57
+ #
58
+ # @example Dequeue request.
59
+ # hydra.dequeue
60
+ #
61
+ # @since 0.6.4
62
+ def dequeue
63
+ add(queued_requests.shift) unless queued_requests.empty?
64
+ end
65
+
66
+ # Removes requests from queued_requests and
67
+ # adds them to the hydra until max_concurrency
68
+ # is reached.
69
+ #
70
+ # @example Dequeue requests.
71
+ # hydra.dequeue_many
72
+ #
73
+ # @since 0.6.8
74
+ def dequeue_many
75
+ number = multi.easy_handles.count
76
+ until number == max_concurrency || queued_requests.empty?
77
+ add(queued_requests.shift)
78
+ number += 1
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ module Typhoeus
2
+ class Hydra
3
+
4
+ # This module contains logic to run a hydra.
5
+ module Runnable
6
+
7
+ # Start the hydra run.
8
+ #
9
+ # @example Start hydra run.
10
+ # hydra.run
11
+ #
12
+ # @return [ Symbol ] Return value from multi.perform.
13
+ def run
14
+ dequeue_many
15
+ multi.perform
16
+ end
17
+ end
18
+ end
19
+ end