typhoeus 0.4.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +4 -0
- data/.travis.yml +26 -0
- data/CHANGELOG.md +341 -28
- data/CONTRIBUTING.md +20 -0
- data/Gemfile +31 -2
- data/Guardfile +9 -0
- data/LICENSE +1 -1
- data/README.md +486 -357
- data/Rakefile +21 -12
- data/UPGRADE.md +55 -0
- data/lib/rack/typhoeus/middleware/params_decoder/helper.rb +76 -0
- data/lib/rack/typhoeus/middleware/params_decoder.rb +57 -0
- data/lib/rack/typhoeus.rb +1 -0
- data/lib/typhoeus/adapters/faraday.rb +180 -0
- data/lib/typhoeus/cache/dalli.rb +28 -0
- data/lib/typhoeus/cache/rails.rb +28 -0
- data/lib/typhoeus/cache/redis.rb +35 -0
- data/lib/typhoeus/config.rb +69 -0
- data/lib/typhoeus/easy_factory.rb +180 -0
- data/lib/typhoeus/errors/no_stub.rb +12 -0
- data/lib/typhoeus/errors/typhoeus_error.rb +8 -0
- data/lib/typhoeus/errors.rb +9 -0
- data/lib/typhoeus/expectation.rb +217 -0
- data/lib/typhoeus/hydra/addable.rb +23 -0
- data/lib/typhoeus/hydra/before.rb +31 -0
- data/lib/typhoeus/hydra/block_connection.rb +35 -0
- data/lib/typhoeus/hydra/cacheable.rb +15 -0
- data/lib/typhoeus/hydra/memoizable.rb +56 -0
- data/lib/typhoeus/hydra/queueable.rb +83 -0
- data/lib/typhoeus/hydra/runnable.rb +19 -0
- data/lib/typhoeus/hydra/stubbable.rb +28 -0
- data/lib/typhoeus/hydra.rb +84 -236
- data/lib/typhoeus/pool.rb +70 -0
- data/lib/typhoeus/railtie.rb +12 -0
- data/lib/typhoeus/request/actions.rb +125 -0
- data/lib/typhoeus/request/before.rb +30 -0
- data/lib/typhoeus/request/block_connection.rb +52 -0
- data/lib/typhoeus/request/cacheable.rb +38 -0
- data/lib/typhoeus/request/callbacks.rb +151 -0
- data/lib/typhoeus/request/marshal.rb +22 -0
- data/lib/typhoeus/request/memoizable.rb +38 -0
- data/lib/typhoeus/request/operations.rb +40 -0
- data/lib/typhoeus/request/responseable.rb +29 -0
- data/lib/typhoeus/request/streamable.rb +34 -0
- data/lib/typhoeus/request/stubbable.rb +30 -0
- data/lib/typhoeus/request.rb +186 -231
- data/lib/typhoeus/response/cacheable.rb +14 -0
- data/lib/typhoeus/response/header.rb +105 -0
- data/lib/typhoeus/response/informations.rb +248 -0
- data/lib/typhoeus/response/status.rb +106 -0
- data/lib/typhoeus/response.rb +60 -115
- data/lib/typhoeus/version.rb +3 -1
- data/lib/typhoeus.rb +126 -39
- data/perf/profile.rb +14 -0
- data/perf/vs_nethttp.rb +64 -0
- data/spec/rack/typhoeus/middleware/params_decoder/helper_spec.rb +156 -0
- data/spec/rack/typhoeus/middleware/params_decoder_spec.rb +31 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/localhost_server.rb +94 -0
- data/spec/support/memory_cache.rb +15 -0
- data/spec/support/server.rb +116 -0
- data/spec/typhoeus/adapters/faraday_spec.rb +339 -0
- data/spec/typhoeus/cache/dalli_spec.rb +41 -0
- data/spec/typhoeus/cache/redis_spec.rb +41 -0
- data/spec/typhoeus/config_spec.rb +15 -0
- data/spec/typhoeus/easy_factory_spec.rb +143 -0
- data/spec/typhoeus/errors/no_stub_spec.rb +13 -0
- data/spec/typhoeus/expectation_spec.rb +280 -0
- data/spec/typhoeus/hydra/addable_spec.rb +22 -0
- data/spec/typhoeus/hydra/before_spec.rb +98 -0
- data/spec/typhoeus/hydra/block_connection_spec.rb +18 -0
- data/spec/typhoeus/hydra/cacheable_spec.rb +88 -0
- data/spec/typhoeus/hydra/memoizable_spec.rb +53 -0
- data/spec/typhoeus/hydra/queueable_spec.rb +98 -0
- data/spec/typhoeus/hydra/runnable_spec.rb +137 -0
- data/spec/typhoeus/hydra/stubbable_spec.rb +48 -0
- data/spec/typhoeus/hydra_spec.rb +22 -0
- data/spec/typhoeus/pool_spec.rb +137 -0
- data/spec/typhoeus/request/actions_spec.rb +19 -0
- data/spec/typhoeus/request/before_spec.rb +93 -0
- data/spec/typhoeus/request/block_connection_spec.rb +75 -0
- data/spec/typhoeus/request/cacheable_spec.rb +94 -0
- data/spec/typhoeus/request/callbacks_spec.rb +91 -0
- data/spec/typhoeus/request/marshal_spec.rb +60 -0
- data/spec/typhoeus/request/memoizable_spec.rb +34 -0
- data/spec/typhoeus/request/operations_spec.rb +101 -0
- data/spec/typhoeus/request/responseable_spec.rb +13 -0
- data/spec/typhoeus/request/stubbable_spec.rb +45 -0
- data/spec/typhoeus/request_spec.rb +232 -0
- data/spec/typhoeus/response/header_spec.rb +147 -0
- data/spec/typhoeus/response/informations_spec.rb +283 -0
- data/spec/typhoeus/response/status_spec.rb +256 -0
- data/spec/typhoeus/response_spec.rb +100 -0
- data/spec/typhoeus_spec.rb +105 -0
- data/typhoeus.gemspec +25 -0
- metadata +146 -158
- data/lib/typhoeus/curl.rb +0 -453
- data/lib/typhoeus/easy/auth.rb +0 -14
- data/lib/typhoeus/easy/callbacks.rb +0 -33
- data/lib/typhoeus/easy/ffi_helper.rb +0 -61
- data/lib/typhoeus/easy/infos.rb +0 -90
- data/lib/typhoeus/easy/options.rb +0 -115
- data/lib/typhoeus/easy/proxy.rb +0 -20
- data/lib/typhoeus/easy/ssl.rb +0 -82
- data/lib/typhoeus/easy.rb +0 -115
- data/lib/typhoeus/filter.rb +0 -28
- data/lib/typhoeus/form.rb +0 -61
- data/lib/typhoeus/header.rb +0 -54
- data/lib/typhoeus/hydra/callbacks.rb +0 -24
- data/lib/typhoeus/hydra/connect_options.rb +0 -61
- data/lib/typhoeus/hydra/stubbing.rb +0 -68
- data/lib/typhoeus/hydra_mock.rb +0 -131
- data/lib/typhoeus/multi.rb +0 -146
- data/lib/typhoeus/param_processor.rb +0 -43
- data/lib/typhoeus/remote.rb +0 -306
- data/lib/typhoeus/remote_method.rb +0 -108
- data/lib/typhoeus/remote_proxy_object.rb +0 -50
- data/lib/typhoeus/utils.rb +0 -50
data/lib/typhoeus.rb
CHANGED
@@ -1,56 +1,143 @@
|
|
1
1
|
require 'digest/sha2'
|
2
|
+
require 'ethon'
|
2
3
|
|
3
|
-
require 'typhoeus/
|
4
|
-
require 'typhoeus/
|
5
|
-
require 'typhoeus/
|
6
|
-
require 'typhoeus/
|
7
|
-
require 'typhoeus/form'
|
8
|
-
require 'typhoeus/multi'
|
9
|
-
require 'typhoeus/filter'
|
10
|
-
require 'typhoeus/param_processor'
|
11
|
-
require 'typhoeus/remote'
|
12
|
-
require 'typhoeus/remote_proxy_object'
|
13
|
-
require 'typhoeus/response'
|
14
|
-
require 'typhoeus/request'
|
4
|
+
require 'typhoeus/config'
|
5
|
+
require 'typhoeus/easy_factory'
|
6
|
+
require 'typhoeus/errors'
|
7
|
+
require 'typhoeus/expectation'
|
15
8
|
require 'typhoeus/hydra'
|
16
|
-
require 'typhoeus/
|
9
|
+
require 'typhoeus/pool'
|
10
|
+
require 'typhoeus/request'
|
11
|
+
require 'typhoeus/response'
|
17
12
|
require 'typhoeus/version'
|
18
13
|
|
14
|
+
# If we are using any Rack-based application, then we need the Typhoeus rack
|
15
|
+
# middleware to ensure our app is running properly.
|
16
|
+
if defined?(Rack)
|
17
|
+
require "rack/typhoeus"
|
18
|
+
end
|
19
|
+
|
20
|
+
# If the Redis gem is available, load the redis cache adapter
|
21
|
+
if defined?(Redis)
|
22
|
+
require "typhoeus/cache/redis"
|
23
|
+
end
|
24
|
+
|
25
|
+
# If the Dalli gem is available, load the Dalli cache adapter
|
26
|
+
if defined?(Dalli)
|
27
|
+
require "typhoeus/cache/dalli"
|
28
|
+
end
|
29
|
+
|
30
|
+
# If we are using Rails, load the Rails cache adapter
|
31
|
+
if defined?(Rails)
|
32
|
+
require "typhoeus/cache/rails"
|
33
|
+
end
|
34
|
+
|
35
|
+
# If we are using Rails, then we will include the Typhoeus railtie.
|
36
|
+
# if defined?(Rails)
|
37
|
+
# require "typhoeus/railtie"
|
38
|
+
# end
|
39
|
+
|
40
|
+
# Typhoeus is a HTTP client library based on Ethon which
|
41
|
+
# wraps libcurl. Sitting on top of libcurl makes Typhoeus
|
42
|
+
# very reliable and fast.
|
43
|
+
#
|
44
|
+
# There are some gems using Typhoeus like
|
45
|
+
# {https://github.com/myronmarston/vcr VCR},
|
46
|
+
# {https://github.com/bblimke/webmock WebMock} or
|
47
|
+
# {https://github.com/technoweenie/faraday Faraday}. VCR
|
48
|
+
# and WebMock provide their own adapter whereas
|
49
|
+
# Faraday relies on {Faraday::Adapter::Typhoeus}
|
50
|
+
# since Typhoeus version 0.5.
|
51
|
+
#
|
52
|
+
# @example (see Typhoeus::Request)
|
53
|
+
# @example (see Typhoeus::Hydra)
|
54
|
+
#
|
55
|
+
# @see Typhoeus::Request
|
56
|
+
# @see Typhoeus::Hydra
|
57
|
+
# @see Faraday::Adapter::Typhoeus
|
58
|
+
#
|
59
|
+
# @since 0.5.0
|
19
60
|
module Typhoeus
|
20
|
-
|
21
|
-
|
22
|
-
end
|
61
|
+
extend Request::Actions
|
62
|
+
extend Request::Callbacks::Types
|
23
63
|
|
24
|
-
|
25
|
-
|
26
|
-
easy_object_pool << Typhoeus::Easy.new
|
27
|
-
end
|
28
|
-
end
|
64
|
+
# The default Typhoeus user agent.
|
65
|
+
USER_AGENT = "Typhoeus - https://github.com/typhoeus/typhoeus"
|
29
66
|
|
30
|
-
|
31
|
-
|
32
|
-
|
67
|
+
# Set the Typhoeus configuration options by passing a block.
|
68
|
+
#
|
69
|
+
# @example (see Typhoeus::Config)
|
70
|
+
#
|
71
|
+
# @yield [ Typhoeus::Config ]
|
72
|
+
#
|
73
|
+
# @return [ Typhoeus::Config ] The configuration.
|
74
|
+
#
|
75
|
+
# @see Typhoeus::Config
|
76
|
+
def self.configure
|
77
|
+
yield Config
|
33
78
|
end
|
34
79
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
80
|
+
# Stub out a specific request.
|
81
|
+
#
|
82
|
+
# @example (see Typhoeus::Expectation)
|
83
|
+
#
|
84
|
+
# @param [ String ] base_url The url to stub out.
|
85
|
+
# @param [ Hash ] options The options to stub out.
|
86
|
+
#
|
87
|
+
# @return [ Typhoeus::Expectation ] The expecatation.
|
88
|
+
#
|
89
|
+
# @see Typhoeus::Expectation
|
90
|
+
def self.stub(base_url, options = {}, &block)
|
91
|
+
expectation = Expectation.all.find{ |e| e.base_url == base_url && e.options == options }
|
92
|
+
if expectation.nil?
|
93
|
+
expectation = Expectation.new(base_url, options)
|
94
|
+
Expectation.all << expectation
|
40
95
|
end
|
96
|
+
|
97
|
+
expectation.and_return(&block) unless block.nil?
|
98
|
+
expectation
|
41
99
|
end
|
42
100
|
|
43
|
-
|
44
|
-
|
45
|
-
|
101
|
+
# Add before callbacks.
|
102
|
+
#
|
103
|
+
# @example Add before callback.
|
104
|
+
# Typhoeus.before { |request| p request.base_url }
|
105
|
+
#
|
106
|
+
# @param [ Block ] block The callback.
|
107
|
+
#
|
108
|
+
# @yield [ Typhoeus::Request ]
|
109
|
+
#
|
110
|
+
# @return [ Array<Block> ] All before blocks.
|
111
|
+
def self.before(&block)
|
112
|
+
@before ||= []
|
113
|
+
@before << block if block_given?
|
114
|
+
@before
|
46
115
|
end
|
47
116
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
117
|
+
# Execute given block as if block connection is turned off.
|
118
|
+
# The old block connection state is restored afterwards.
|
119
|
+
#
|
120
|
+
# @example Make a real request, no matter if it's blocked.
|
121
|
+
# Typhoeus::Config.block_connection = true
|
122
|
+
# Typhoeus.get("www.example.com").code
|
123
|
+
# #=> raise Typhoeus::Errors::NoStub
|
124
|
+
#
|
125
|
+
# Typhoeus.with_connection do
|
126
|
+
# Typhoeus.get("www.example.com").code
|
127
|
+
# #=> :ok
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# @yield Yields control to the block after disabling block_connection.
|
131
|
+
# Afterwards, the block_connection is set to its original
|
132
|
+
# value.
|
133
|
+
# @return [ Object ] Returns the return value of the block.
|
134
|
+
#
|
135
|
+
# @see Typhoeus::Config.block_connection
|
136
|
+
def self.with_connection
|
137
|
+
old = Config.block_connection
|
138
|
+
Config.block_connection = false
|
139
|
+
result = yield if block_given?
|
140
|
+
Config.block_connection = old
|
141
|
+
result
|
55
142
|
end
|
56
143
|
end
|
data/perf/profile.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'ruby-prof'
|
3
|
+
|
4
|
+
calls = 50
|
5
|
+
base_url = "http://127.0.0.1:3000/"
|
6
|
+
|
7
|
+
RubyProf.start
|
8
|
+
calls.times do |i|
|
9
|
+
Typhoeus::Request.get(base_url+i.to_s)
|
10
|
+
end
|
11
|
+
result = RubyProf.stop
|
12
|
+
|
13
|
+
printer = RubyProf::FlatPrinter.new(result)
|
14
|
+
printer.print(STDOUT)
|
data/perf/vs_nethttp.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
require 'net/http'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
URL = "http://localhost:300"
|
7
|
+
hydra = Typhoeus::Hydra.new(max_concurrency: 3)
|
8
|
+
|
9
|
+
if defined? require_relative
|
10
|
+
require_relative '../spec/support/localhost_server.rb'
|
11
|
+
require_relative '../spec/support/server.rb'
|
12
|
+
else
|
13
|
+
require '../spec/support/localhost_server.rb'
|
14
|
+
require '../spec/support/server.rb'
|
15
|
+
end
|
16
|
+
LocalhostServer.new(TESTSERVER.new, 3000)
|
17
|
+
LocalhostServer.new(TESTSERVER.new, 3001)
|
18
|
+
LocalhostServer.new(TESTSERVER.new, 3002)
|
19
|
+
|
20
|
+
def url_for(i)
|
21
|
+
"#{URL}#{i%3}/"
|
22
|
+
end
|
23
|
+
|
24
|
+
Benchmark.bm do |bm|
|
25
|
+
|
26
|
+
[1000].each do |calls|
|
27
|
+
puts "[ #{calls} requests ]"
|
28
|
+
|
29
|
+
bm.report("net/http ") do
|
30
|
+
calls.times do |i|
|
31
|
+
uri = URI.parse(url_for(i))
|
32
|
+
Net::HTTP.get_response(uri)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
bm.report("open ") do
|
37
|
+
calls.times do |i|
|
38
|
+
open(url_for(i))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
bm.report("request ") do
|
43
|
+
calls.times do |i|
|
44
|
+
Typhoeus::Request.get(url_for(i))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
bm.report("hydra ") do
|
49
|
+
calls.times do |i|
|
50
|
+
hydra.queue(Typhoeus::Request.new(url_for(i)))
|
51
|
+
end
|
52
|
+
hydra.run
|
53
|
+
end
|
54
|
+
|
55
|
+
bm.report("hydra memoize ") do
|
56
|
+
Typhoeus::Config.memoize = true
|
57
|
+
calls.times do |i|
|
58
|
+
hydra.queue(Typhoeus::Request.new(url_for(i)))
|
59
|
+
end
|
60
|
+
hydra.run
|
61
|
+
Typhoeus::Config.memoize = false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "rack/typhoeus"
|
3
|
+
|
4
|
+
describe "Rack::Typhoeus::Middleware::ParamsDecoder::Helper" do
|
5
|
+
|
6
|
+
let(:klass) do
|
7
|
+
Class.new do
|
8
|
+
include Rack::Typhoeus::Middleware::ParamsDecoder::Helper
|
9
|
+
end.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#decode" do
|
13
|
+
let(:decoded) { klass.decode(params) }
|
14
|
+
let(:params) { { :array => {'0' => :a, '1' => :b } } }
|
15
|
+
|
16
|
+
it "decodes" do
|
17
|
+
expect(decoded[:array]).to match_array([:a, :b])
|
18
|
+
end
|
19
|
+
|
20
|
+
it "doesn't modify" do
|
21
|
+
expect(decoded).to_not be(params)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#decode!" do
|
26
|
+
let(:decoded) { klass.decode!(params) }
|
27
|
+
|
28
|
+
context "when hash" do
|
29
|
+
context "when encoded" do
|
30
|
+
context "when simple" do
|
31
|
+
let(:params) { { :array => {'0' => :a, '1' => :b } } }
|
32
|
+
|
33
|
+
it "decodes" do
|
34
|
+
expect(decoded[:array]).to match_array([:a, :b])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "modifies" do
|
38
|
+
expect(decoded).to eq(params)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when longer and more complex" do
|
43
|
+
let(:params) do
|
44
|
+
{
|
45
|
+
:ids => {
|
46
|
+
"0" => "407304",
|
47
|
+
"1" => "407305",
|
48
|
+
"2" => "407306",
|
49
|
+
"3" => "407307",
|
50
|
+
"4" => "407308",
|
51
|
+
"5" => "407309",
|
52
|
+
"6" => "407310",
|
53
|
+
"7" => "407311",
|
54
|
+
"8" => "407312",
|
55
|
+
"9" => "407313",
|
56
|
+
"10" => "327012"
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "decodes ensuring arrays maintain their original order" do
|
62
|
+
expect(decoded[:ids]).to eq(["407304", "407305", "407306", "407307", "407308", "407309", "407310", "407311", "407312", "407313", "327012"])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when nested" do
|
67
|
+
let(:params) do
|
68
|
+
{ :array => { '0' => 0, '1' => { '0' => 'sub0', '1' => 'sub1' } } }
|
69
|
+
end
|
70
|
+
|
71
|
+
it "decodes" do
|
72
|
+
expect(decoded[:array]).to include(0)
|
73
|
+
expect(decoded[:array].find{|e| e.is_a?(Array)}).to(
|
74
|
+
match_array(['sub0', 'sub1'])
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "modifies" do
|
79
|
+
expect(decoded).to eq(params)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when not encoded" do
|
85
|
+
let(:params) { {:a => :a} }
|
86
|
+
|
87
|
+
it "doesn't modify" do
|
88
|
+
expect(decoded).to be(params)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when no hash" do
|
94
|
+
let(:params) { "a" }
|
95
|
+
|
96
|
+
it "returns self" do
|
97
|
+
expect(decoded).to be(params)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#encoded?" do
|
103
|
+
let(:encoded) { klass.send(:encoded?, params) }
|
104
|
+
|
105
|
+
context "when there is only one key" do
|
106
|
+
context "and its 0" do
|
107
|
+
let(:params){ {'0' => 1} }
|
108
|
+
it 'returns true' do
|
109
|
+
expect(encoded).to be_truthy
|
110
|
+
end
|
111
|
+
end
|
112
|
+
context "and its not 0" do
|
113
|
+
let(:params){ {'some-key' => 1}}
|
114
|
+
it 'returns false' do
|
115
|
+
expect(encoded).to be_falsey
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when keys are ascending numbers starting with zero" do
|
121
|
+
let(:params) { Hash[12.times.map {|i| [i, (i+65).chr]}] }
|
122
|
+
|
123
|
+
it "returns true" do
|
124
|
+
expect(encoded).to be_truthy
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when keys are not ascending numbers starting with zero" do
|
129
|
+
let(:params) { {:a => 1} }
|
130
|
+
|
131
|
+
it "returns false" do
|
132
|
+
expect(encoded).to be_falsey
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "#convert" do
|
138
|
+
let(:converted) { klass.send(:convert, params) }
|
139
|
+
|
140
|
+
context "when encoded" do
|
141
|
+
let(:params) { {'0' => :a, '1' => :b} }
|
142
|
+
|
143
|
+
it "returns values" do
|
144
|
+
expect(converted).to match_array([:a, :b])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when not encoded" do
|
149
|
+
let(:params) { {:a => :a} }
|
150
|
+
|
151
|
+
it "returns unmodified" do
|
152
|
+
expect(converted).to be(params)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -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
|
+
double
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:env) do
|
14
|
+
double
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
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
|
+
Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |f| require f }
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.order = :rand
|
13
|
+
|
14
|
+
config.before(:suite) do
|
15
|
+
LocalhostServer.new(TESTSERVER.new, 3001)
|
16
|
+
end
|
17
|
+
|
18
|
+
config.after do
|
19
|
+
Typhoeus::Pool.clear
|
20
|
+
Typhoeus::Expectation.clear
|
21
|
+
Typhoeus.before.clear
|
22
|
+
Typhoeus.on_complete.clear
|
23
|
+
Typhoeus.on_success.clear
|
24
|
+
Typhoeus.on_failure.clear
|
25
|
+
Typhoeus::Config.verbose = false
|
26
|
+
Typhoeus::Config.block_connection = false
|
27
|
+
Typhoeus::Config.memoize = false
|
28
|
+
end
|
29
|
+
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,116 @@
|
|
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 '/cookies-test' do
|
27
|
+
[200, { 'Set-Cookie' => %w(foo=bar bar=foo), 'Content-Type' => 'text/plain' }, ['']]
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/cookies-test2' do
|
31
|
+
[200, { 'Set-Cookie' => %w(foo2=bar bar2=foo), 'Content-Type' => 'text/plain' }, ['']]
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/fail/:number' do
|
35
|
+
if fail_count >= params[:number].to_i
|
36
|
+
"ok"
|
37
|
+
else
|
38
|
+
fail_count += 1
|
39
|
+
error 500, "oh noes!"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
get '/fail_forever' do
|
44
|
+
error 500, "oh noes!"
|
45
|
+
end
|
46
|
+
|
47
|
+
get '/redirect' do
|
48
|
+
redirect '/'
|
49
|
+
end
|
50
|
+
|
51
|
+
get '/bad_redirect' do
|
52
|
+
redirect '/bad_redirect'
|
53
|
+
end
|
54
|
+
|
55
|
+
get '/auth_basic/:username/:password' do
|
56
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
57
|
+
# Check that we've got a basic auth, and that it's credentials match the ones
|
58
|
+
# provided in the request
|
59
|
+
if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
|
60
|
+
# auth is valid - confirm it
|
61
|
+
true
|
62
|
+
else
|
63
|
+
# invalid auth - request the authentication
|
64
|
+
response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
|
65
|
+
throw(:halt, [401, "Not authorized\n"])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
get '/auth_ntlm' do
|
70
|
+
# we're just checking for the existence if NTLM auth header here. It's validation
|
71
|
+
# is too troublesome and really doesn't bother is much, it's up to libcurl to make
|
72
|
+
# it valid
|
73
|
+
response['WWW-Authenticate'] = 'NTLM'
|
74
|
+
is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
|
75
|
+
true if is_ntlm_auth
|
76
|
+
throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/gzipped' do
|
80
|
+
req_env = request.env.to_json
|
81
|
+
z = Zlib::Deflate.new
|
82
|
+
gzipped_env = z.deflate(req_env, Zlib::FINISH)
|
83
|
+
z.close
|
84
|
+
response['Content-Encoding'] = 'gzip'
|
85
|
+
gzipped_env
|
86
|
+
end
|
87
|
+
|
88
|
+
get '/**' do
|
89
|
+
sleep params["delay"].to_i if params.has_key?("delay")
|
90
|
+
request.env.merge!(:body => request.body.read).to_json
|
91
|
+
end
|
92
|
+
|
93
|
+
head '/**' do
|
94
|
+
sleep params["delay"].to_i if params.has_key?("delay")
|
95
|
+
end
|
96
|
+
|
97
|
+
put '/**' do
|
98
|
+
request.env.merge!(:body => request.body.read).to_json
|
99
|
+
end
|
100
|
+
|
101
|
+
post '/**' do
|
102
|
+
request.env.merge!(:body => request.body.read).to_json
|
103
|
+
end
|
104
|
+
|
105
|
+
delete '/**' do
|
106
|
+
request.env.merge!(:body => request.body.read).to_json
|
107
|
+
end
|
108
|
+
|
109
|
+
patch '/**' do
|
110
|
+
request.env.merge!(:body => request.body.read).to_json
|
111
|
+
end
|
112
|
+
|
113
|
+
options '/**' do
|
114
|
+
request.env.merge!(:body => request.body.read).to_json
|
115
|
+
end
|
116
|
+
end
|