typhoeus 0.6.3 → 0.6.4

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.
Files changed (61) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +7 -1
  6. data/CONTRIBUTING.md +16 -0
  7. data/Guardfile +9 -0
  8. data/README.md +303 -1
  9. data/UPGRADE.md +55 -0
  10. data/lib/rack/typhoeus/middleware/params_decoder.rb +1 -1
  11. data/lib/rack/typhoeus/middleware/params_decoder/helper.rb +6 -2
  12. data/lib/typhoeus/adapters/faraday.rb +4 -1
  13. data/lib/typhoeus/easy_factory.rb +36 -15
  14. data/lib/typhoeus/hydra/before.rb +2 -1
  15. data/lib/typhoeus/hydra/cacheable.rb +1 -0
  16. data/lib/typhoeus/hydra/memoizable.rb +1 -0
  17. data/lib/typhoeus/hydra/queueable.rb +15 -3
  18. data/lib/typhoeus/request.rb +6 -2
  19. data/lib/typhoeus/request/marshal.rb +3 -2
  20. data/lib/typhoeus/response/informations.rb +4 -0
  21. data/lib/typhoeus/version.rb +1 -1
  22. data/perf/profile.rb +14 -0
  23. data/perf/vs_nethttp.rb +64 -0
  24. data/spec/rack/typhoeus/middleware/params_decoder/helper_spec.rb +132 -0
  25. data/spec/rack/typhoeus/middleware/params_decoder_spec.rb +31 -0
  26. data/spec/spec_helper.rb +35 -0
  27. data/spec/support/localhost_server.rb +94 -0
  28. data/spec/support/server.rb +108 -0
  29. data/spec/typhoeus/adapters/faraday_spec.rb +245 -0
  30. data/spec/typhoeus/config_spec.rb +15 -0
  31. data/spec/typhoeus/easy_factory_spec.rb +96 -0
  32. data/spec/typhoeus/errors/no_stub_spec.rb +13 -0
  33. data/spec/typhoeus/expectation_spec.rb +273 -0
  34. data/spec/typhoeus/hydra/addable_spec.rb +22 -0
  35. data/spec/typhoeus/hydra/before_spec.rb +97 -0
  36. data/spec/typhoeus/hydra/block_connection_spec.rb +18 -0
  37. data/spec/typhoeus/hydra/cacheable_spec.rb +68 -0
  38. data/spec/typhoeus/hydra/memoizable_spec.rb +53 -0
  39. data/spec/typhoeus/hydra/queueable_spec.rb +34 -0
  40. data/spec/typhoeus/hydra/runnable_spec.rb +155 -0
  41. data/spec/typhoeus/hydra/stubbable_spec.rb +28 -0
  42. data/spec/typhoeus/hydra_spec.rb +26 -0
  43. data/spec/typhoeus/pool_spec.rb +79 -0
  44. data/spec/typhoeus/request/actions_spec.rb +19 -0
  45. data/spec/typhoeus/request/before_spec.rb +92 -0
  46. data/spec/typhoeus/request/block_connection_spec.rb +75 -0
  47. data/spec/typhoeus/request/cacheable_spec.rb +80 -0
  48. data/spec/typhoeus/request/callbacks_spec.rb +91 -0
  49. data/spec/typhoeus/request/marshal_spec.rb +62 -0
  50. data/spec/typhoeus/request/memoizable_spec.rb +34 -0
  51. data/spec/typhoeus/request/operations_spec.rb +70 -0
  52. data/spec/typhoeus/request/responseable_spec.rb +13 -0
  53. data/spec/typhoeus/request/stubbable_spec.rb +27 -0
  54. data/spec/typhoeus/request_spec.rb +131 -0
  55. data/spec/typhoeus/response/header_spec.rb +97 -0
  56. data/spec/typhoeus/response/informations_spec.rb +218 -0
  57. data/spec/typhoeus/response/status_spec.rb +218 -0
  58. data/spec/typhoeus/response_spec.rb +81 -0
  59. data/spec/typhoeus_spec.rb +105 -0
  60. data/typhoeus.gemspec +25 -0
  61. metadata +101 -27
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Rack::Typhoeus::Middleware::ParamsDecoder" do
4
+
5
+ before(:all) do
6
+ require "rack/typhoeus"
7
+ end
8
+
9
+ let(:app) do
10
+ stub
11
+ end
12
+
13
+ let(:env) do
14
+ stub
15
+ end
16
+
17
+ let(:klass) do
18
+ Rack::Typhoeus::Middleware::ParamsDecoder
19
+ end
20
+
21
+ describe "#call" do
22
+ end
23
+
24
+ context "when requesting" do
25
+ let(:response) { Typhoeus.get("localhost:3001", :params => {:x => [:a]}) }
26
+
27
+ it "transforms parameters" do
28
+ expect(response.body).to include("query_hash\":{\"x\":[\"a\"]}")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+ require "typhoeus"
7
+ require "rspec"
8
+
9
+ if defined? require_relative
10
+ require_relative 'support/localhost_server.rb'
11
+ require_relative 'support/server.rb'
12
+ else
13
+ require 'support/localhost_server.rb'
14
+ require 'support/server.rb'
15
+ end
16
+
17
+ RSpec.configure do |config|
18
+ config.order = :rand
19
+
20
+ config.before(:suite) do
21
+ LocalhostServer.new(TESTSERVER.new, 3001)
22
+ end
23
+
24
+ config.after do
25
+ Typhoeus::Pool.clear
26
+ Typhoeus::Expectation.clear
27
+ Typhoeus.before.clear
28
+ Typhoeus.on_complete.clear
29
+ Typhoeus.on_success.clear
30
+ Typhoeus.on_failure.clear
31
+ Typhoeus::Config.verbose = false
32
+ Typhoeus::Config.block_connection = false
33
+ Typhoeus::Config.memoize = false
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ require 'rack'
2
+ require 'rack/handler/webrick'
3
+ require 'net/http'
4
+
5
+ # The code for this is inspired by Capybara's server:
6
+ # http://github.com/jnicklas/capybara/blob/0.3.9/lib/capybara/server.rb
7
+ class LocalhostServer
8
+ READY_MESSAGE = "Server ready"
9
+
10
+ class Identify
11
+ def initialize(app)
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ if env["PATH_INFO"] == "/__identify__"
17
+ [200, {}, [LocalhostServer::READY_MESSAGE]]
18
+ else
19
+ @app.call(env)
20
+ end
21
+ end
22
+ end
23
+
24
+ attr_reader :port
25
+
26
+ def initialize(rack_app, port = nil)
27
+ @port = port || find_available_port
28
+ @rack_app = rack_app
29
+ concurrently { boot }
30
+ wait_until(10, "Boot failed.") { booted? }
31
+ end
32
+
33
+ private
34
+
35
+ def find_available_port
36
+ server = TCPServer.new('127.0.0.1', 0)
37
+ server.addr[1]
38
+ ensure
39
+ server.close if server
40
+ end
41
+
42
+ def boot
43
+ # Use WEBrick since it's part of the ruby standard library and is available on all ruby interpreters.
44
+ options = { :Port => port }
45
+ options.merge!(:AccessLog => [], :Logger => WEBrick::BasicLog.new(StringIO.new)) unless ENV['VERBOSE_SERVER']
46
+ Rack::Handler::WEBrick.run(Identify.new(@rack_app), options)
47
+ end
48
+
49
+ def booted?
50
+ res = ::Net::HTTP.get_response("localhost", '/__identify__', port)
51
+ if res.is_a?(::Net::HTTPSuccess) or res.is_a?(::Net::HTTPRedirection)
52
+ return res.body == READY_MESSAGE
53
+ end
54
+ rescue Errno::ECONNREFUSED, Errno::EBADF
55
+ return false
56
+ end
57
+
58
+ def concurrently
59
+ if should_use_subprocess?
60
+ pid = Process.fork do
61
+ trap(:INT) { ::Rack::Handler::WEBrick.shutdown }
62
+ yield
63
+ exit # manually exit; otherwise this sub-process will re-run the specs that haven't run yet.
64
+ end
65
+
66
+ at_exit do
67
+ Process.kill('INT', pid)
68
+ begin
69
+ Process.wait(pid)
70
+ rescue Errno::ECHILD
71
+ # ignore this error...I think it means the child process has already exited.
72
+ end
73
+ end
74
+ else
75
+ Thread.new { yield }
76
+ end
77
+ end
78
+
79
+ def should_use_subprocess?
80
+ # !ENV['THREADED']
81
+ false
82
+ end
83
+
84
+ def wait_until(timeout, error_message, &block)
85
+ start_time = Time.now
86
+
87
+ while true
88
+ return if yield
89
+ raise TimeoutError.new(error_message) if (Time.now - start_time) > timeout
90
+ sleep(0.05)
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ require 'json'
3
+ require 'zlib'
4
+ require 'sinatra/base'
5
+ require 'rack/typhoeus'
6
+
7
+ TESTSERVER = Sinatra.new do
8
+ set :logging, false
9
+ use Rack::Typhoeus::Middleware::ParamsDecoder
10
+
11
+ fail_count = 0
12
+
13
+ post '/file' do
14
+ {
15
+ 'content-type' => params[:file][:type],
16
+ 'filename' => params[:file][:filename],
17
+ 'content' => params[:file][:tempfile].read,
18
+ 'request-content-type' => request.env['CONTENT_TYPE']
19
+ }.to_json
20
+ end
21
+
22
+ get '/multiple-headers' do
23
+ [200, { 'Set-Cookie' => %w[ foo bar ], 'Content-Type' => 'text/plain' }, ['']]
24
+ end
25
+
26
+ get '/fail/:number' do
27
+ if fail_count >= params[:number].to_i
28
+ "ok"
29
+ else
30
+ fail_count += 1
31
+ error 500, "oh noes!"
32
+ end
33
+ end
34
+
35
+ get '/fail_forever' do
36
+ error 500, "oh noes!"
37
+ end
38
+
39
+ get '/redirect' do
40
+ redirect '/'
41
+ end
42
+
43
+ get '/bad_redirect' do
44
+ redirect '/bad_redirect'
45
+ end
46
+
47
+ get '/auth_basic/:username/:password' do
48
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
49
+ # Check that we've got a basic auth, and that it's credentials match the ones
50
+ # provided in the request
51
+ if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
52
+ # auth is valid - confirm it
53
+ true
54
+ else
55
+ # invalid auth - request the authentication
56
+ response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
57
+ throw(:halt, [401, "Not authorized\n"])
58
+ end
59
+ end
60
+
61
+ get '/auth_ntlm' do
62
+ # we're just checking for the existence if NTLM auth header here. It's validation
63
+ # is too troublesome and really doesn't bother is much, it's up to libcurl to make
64
+ # it valid
65
+ response['WWW-Authenticate'] = 'NTLM'
66
+ is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
67
+ true if is_ntlm_auth
68
+ throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
69
+ end
70
+
71
+ get '/gzipped' do
72
+ req_env = request.env.to_json
73
+ z = Zlib::Deflate.new
74
+ gzipped_env = z.deflate(req_env, Zlib::FINISH)
75
+ z.close
76
+ response['Content-Encoding'] = 'gzip'
77
+ gzipped_env
78
+ end
79
+
80
+ get '/**' do
81
+ sleep params["delay"].to_i if params.has_key?("delay")
82
+ request.env.merge!(:body => request.body.read).to_json
83
+ end
84
+
85
+ head '/**' do
86
+ sleep params["delay"].to_i if params.has_key?("delay")
87
+ end
88
+
89
+ put '/**' do
90
+ request.env.merge!(:body => request.body.read).to_json
91
+ end
92
+
93
+ post '/**' do
94
+ request.env.merge!(:body => request.body.read).to_json
95
+ end
96
+
97
+ delete '/**' do
98
+ request.env.merge!(:body => request.body.read).to_json
99
+ end
100
+
101
+ patch '/**' do
102
+ request.env.merge!(:body => request.body.read).to_json
103
+ end
104
+
105
+ options '/**' do
106
+ request.env.merge!(:body => request.body.read).to_json
107
+ end
108
+ end
@@ -0,0 +1,245 @@
1
+ require 'spec_helper'
2
+ require 'typhoeus/adapters/faraday'
3
+
4
+ describe Faraday::Adapter::Typhoeus do
5
+ let(:base_url) { "http://localhost:3001" }
6
+ let(:adapter) { described_class.new }
7
+ let(:request) { Typhoeus::Request.new(base_url) }
8
+ let(:conn) do
9
+ Faraday.new(:url => base_url) do |faraday|
10
+ faraday.adapter :typhoeus
11
+ end
12
+ end
13
+ let(:response) { conn.get("/") }
14
+
15
+ context "when parallel" do
16
+ it "returns a faraday response" do
17
+ response = nil
18
+ conn.in_parallel { response = conn.get("/") }
19
+ expect(response).to be_a(Faraday::Response)
20
+ end
21
+
22
+ it "succeeds" do
23
+ response = nil
24
+ conn.in_parallel { response = conn.get("/") }
25
+ expect(response.status).to be(200)
26
+ end
27
+ end
28
+
29
+ context "when not parallel" do
30
+ it "returns a faraday response" do
31
+ expect(response).to be_a(Faraday::Response)
32
+ end
33
+
34
+ it "succeeds" do
35
+ expect(response.status).to be(200)
36
+ end
37
+ end
38
+
39
+ describe "#perform_request" do
40
+ let(:env) { {} }
41
+
42
+ context "when body" do
43
+ let(:env) { { :body => double(:read => "body") } }
44
+
45
+ it "reads body" do
46
+ expect(adapter.method(:read_body).call(env)).to eq("body")
47
+ end
48
+ end
49
+
50
+ context "parallel_manager" do
51
+ context "when given" do
52
+ let(:env) { { :parallel_manager => double(:queue => true), :ssl => {}, :request => {} } }
53
+
54
+ it "uses" do
55
+ adapter.method(:perform_request).call(env)
56
+ end
57
+ end
58
+
59
+ context "when not given" do
60
+ let(:env) { { :method => :get, :ssl => {}, :request => {} } }
61
+
62
+ it "falls back to single" do
63
+ Typhoeus::Request.should_receive(:new).and_return(double(:options => {}, :on_complete => [], :run => true))
64
+ adapter.method(:perform_request).call(env)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#request" do
71
+ let(:env) do
72
+ { :url => "url", :method => :get, :body => "body", :request_headers => {}, :ssl => {}, :request => {} }
73
+ end
74
+
75
+ let(:request) { adapter.method(:request).call(env) }
76
+
77
+ it "returns request" do
78
+ expect(request).to be_a(Typhoeus::Request)
79
+ end
80
+
81
+ it "sets url" do
82
+ expect(request.base_url).to eq("url")
83
+ end
84
+
85
+ it "sets http method" do
86
+ expect(request.original_options[:method]).to eq(:get)
87
+ end
88
+
89
+ it "sets body" do
90
+ expect(request.original_options[:body]).to eq("body")
91
+ end
92
+
93
+ it "sets headers" do
94
+ expect(request.original_options[:headers]).to eq({})
95
+ end
96
+
97
+ it "sets on_complete callback" do
98
+ expect(request.on_complete).to have(1).items
99
+ end
100
+ end
101
+
102
+ describe "#configure_socket" do
103
+ let(:env) { { :request => { :bind => { :host => "interface" } } } }
104
+
105
+ before { adapter.method(:configure_socket).call(request, env) }
106
+
107
+ context "when host" do
108
+ it "sets interface" do
109
+ expect(request.options[:interface]).to eq("interface")
110
+ end
111
+ end
112
+ end
113
+
114
+ describe "#configure_timeout" do
115
+ before { adapter.method(:configure_timeout).call(request, env) }
116
+
117
+ context "when timeout" do
118
+ let(:env) { { :request => { :timeout => 1 } } }
119
+
120
+ it "sets timeout_ms" do
121
+ expect(request.options[:timeout_ms]).to eq(1000)
122
+ end
123
+ end
124
+
125
+ context "when open_timeout" do
126
+ let(:env) { { :request => { :open_timeout => 1 } } }
127
+
128
+ it "sets connecttimeout_ms" do
129
+ expect(request.options[:connecttimeout_ms]).to eq(1000)
130
+ end
131
+ end
132
+ end
133
+
134
+ describe "#configure_proxy" do
135
+ before { adapter.method(:configure_proxy).call(request, env) }
136
+
137
+ context "when proxy" do
138
+ let(:env) { { :request => { :proxy => { :uri => double(:host => "localhost", :port => "3001") } } } }
139
+
140
+ it "sets proxy" do
141
+ expect(request.options[:proxy]).to eq("localhost:3001")
142
+ end
143
+
144
+ context "when username and password" do
145
+ let(:env) do
146
+ { :request => { :proxy => {
147
+ :uri => double(:host => :a, :port => :b),
148
+ :username => "a",
149
+ :password => "b"
150
+ } } }
151
+ end
152
+
153
+ it "sets proxyuserpwd" do
154
+ expect(request.options[:proxyuserpwd]).to eq("a:b")
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "#configure_ssl" do
161
+ before { adapter.method(:configure_ssl).call(request, env) }
162
+
163
+ context "when version" do
164
+ let(:env) { { :ssl => { :version => "a" } } }
165
+
166
+ it "sets sslversion" do
167
+ expect(request.options[:sslversion]).to eq("a")
168
+ end
169
+ end
170
+
171
+ context "when client_cert" do
172
+ let(:env) { { :ssl => { :client_cert => "a" } } }
173
+
174
+ it "sets sslcert" do
175
+ expect(request.options[:sslcert]).to eq("a")
176
+ end
177
+ end
178
+
179
+ context "when client_key" do
180
+ let(:env) { { :ssl => { :client_key => "a" } } }
181
+
182
+ it "sets sslkey" do
183
+ expect(request.options[:sslkey]).to eq("a")
184
+ end
185
+ end
186
+
187
+ context "when ca_file" do
188
+ let(:env) { { :ssl => { :ca_file => "a" } } }
189
+
190
+ it "sets cainfo" do
191
+ expect(request.options[:cainfo]).to eq("a")
192
+ end
193
+ end
194
+
195
+ context "when ca_path" do
196
+ let(:env) { { :ssl => { :ca_path => "a" } } }
197
+
198
+ it "sets capath" do
199
+ expect(request.options[:capath]).to eq("a")
200
+ end
201
+ end
202
+
203
+ context "when verify is false" do
204
+ let(:env) { { :ssl => { :verify => false } } }
205
+
206
+ it "sets ssl_verifyhost to 0" do
207
+ expect(request.options[:ssl_verifyhost]).to eq(0)
208
+ end
209
+
210
+ it "sets ssl_verifypeer to false" do
211
+ expect(request.options[:ssl_verifypeer]).to be_false
212
+ end
213
+ end
214
+
215
+ context "when verify is true" do
216
+ let(:env) { { :ssl => { :verify => true } } }
217
+
218
+ it "sets ssl_verifyhost to 2" do
219
+ expect(request.options[:ssl_verifyhost]).to eq(2)
220
+ end
221
+
222
+ it "sets ssl_verifypeer to true" do
223
+ expect(request.options[:ssl_verifypeer]).to be_true
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#parallel?" do
229
+ context "when parallel_manager" do
230
+ let(:env) { { :parallel_manager => true } }
231
+
232
+ it "returns true" do
233
+ expect(adapter.method(:parallel?).call(env)).to be_true
234
+ end
235
+ end
236
+
237
+ context "when no parallel_manager" do
238
+ let(:env) { { :parallel_manager => nil } }
239
+
240
+ it "returns false" do
241
+ expect(adapter.method(:parallel?).call(env)).to be_false
242
+ end
243
+ end
244
+ end
245
+ end