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/hydra_mock.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
module Typhoeus
|
2
|
-
class HydraMock
|
3
|
-
attr_reader :url, :method, :requests, :uri
|
4
|
-
|
5
|
-
def initialize(url, method, options = {})
|
6
|
-
@url = url
|
7
|
-
@uri = URI.parse(url) if url.kind_of?(String)
|
8
|
-
@method = method
|
9
|
-
@requests = []
|
10
|
-
@options = options
|
11
|
-
if @options[:headers]
|
12
|
-
@options[:headers] = Typhoeus::Header.new(@options[:headers])
|
13
|
-
end
|
14
|
-
|
15
|
-
@current_response_index = 0
|
16
|
-
end
|
17
|
-
|
18
|
-
def body
|
19
|
-
@options[:body]
|
20
|
-
end
|
21
|
-
|
22
|
-
def body?
|
23
|
-
@options.has_key?(:body)
|
24
|
-
end
|
25
|
-
|
26
|
-
def headers
|
27
|
-
@options[:headers]
|
28
|
-
end
|
29
|
-
|
30
|
-
def headers?
|
31
|
-
@options.has_key?(:headers)
|
32
|
-
end
|
33
|
-
|
34
|
-
def add_request(request)
|
35
|
-
@requests << request
|
36
|
-
end
|
37
|
-
|
38
|
-
def and_return(val)
|
39
|
-
if val.respond_to?(:each)
|
40
|
-
@responses = val
|
41
|
-
else
|
42
|
-
@responses = [val]
|
43
|
-
end
|
44
|
-
|
45
|
-
# make sure to mark them as a mock.
|
46
|
-
@responses.each { |r| r.mock = true }
|
47
|
-
|
48
|
-
val
|
49
|
-
end
|
50
|
-
|
51
|
-
def response
|
52
|
-
if @current_response_index == (@responses.length - 1)
|
53
|
-
@responses.last
|
54
|
-
else
|
55
|
-
value = @responses[@current_response_index]
|
56
|
-
@current_response_index += 1
|
57
|
-
value
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def matches?(request)
|
62
|
-
if !method_matches?(request) or !url_matches?(request)
|
63
|
-
return false
|
64
|
-
end
|
65
|
-
|
66
|
-
if body?
|
67
|
-
return false unless body_matches?(request)
|
68
|
-
end
|
69
|
-
|
70
|
-
if headers?
|
71
|
-
return false unless headers_match?(request)
|
72
|
-
end
|
73
|
-
|
74
|
-
true
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
def method_matches?(request)
|
79
|
-
self.method == :any or self.method == request.method
|
80
|
-
end
|
81
|
-
|
82
|
-
def url_matches?(request)
|
83
|
-
if url.kind_of?(String)
|
84
|
-
request_uri = URI.parse(request.url)
|
85
|
-
request_uri == self.uri
|
86
|
-
else
|
87
|
-
self.url =~ request.url
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def body_matches?(request)
|
92
|
-
!request.body.nil? && !request.body.empty? && request.body == self.body
|
93
|
-
end
|
94
|
-
|
95
|
-
def headers_match?(request)
|
96
|
-
request_headers = Header.new(request.headers)
|
97
|
-
|
98
|
-
if empty_headers?(self.headers)
|
99
|
-
empty_headers?(request_headers)
|
100
|
-
else
|
101
|
-
return false if empty_headers?(request_headers)
|
102
|
-
|
103
|
-
headers.each do |key, value|
|
104
|
-
return false unless header_value_matches?(value, request_headers[key])
|
105
|
-
end
|
106
|
-
|
107
|
-
true
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def header_value_matches?(mock_value, request_value)
|
112
|
-
mock_arr = mock_value.is_a?(Array) ? mock_value : [mock_value]
|
113
|
-
request_arr = request_value.is_a?(Array) ? request_value : [request_value]
|
114
|
-
|
115
|
-
return false unless mock_arr.size == request_arr.size
|
116
|
-
mock_arr.all? do |value|
|
117
|
-
request_arr.any? { |a| value === a }
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def empty_headers?(headers)
|
122
|
-
# We consider the default User-Agent header to be empty since
|
123
|
-
# Typhoeus always adds that.
|
124
|
-
headers.nil? || headers.empty? || default_typhoeus_headers?(headers)
|
125
|
-
end
|
126
|
-
|
127
|
-
def default_typhoeus_headers?(headers)
|
128
|
-
headers.size == 1 && headers['User-Agent'] == Typhoeus::USER_AGENT
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
data/lib/typhoeus/multi.rb
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
module Typhoeus
|
2
|
-
class Multi
|
3
|
-
attr_reader :easy_handles
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
Curl.init
|
7
|
-
|
8
|
-
@handle = Curl.multi_init
|
9
|
-
@active = 0
|
10
|
-
@running = 0
|
11
|
-
@easy_handles = []
|
12
|
-
|
13
|
-
@timeout = ::FFI::MemoryPointer.new(:long)
|
14
|
-
@timeval = Curl::Timeval.new
|
15
|
-
@fd_read = Curl::FDSet.new
|
16
|
-
@fd_write = Curl::FDSet.new
|
17
|
-
@fd_excep = Curl::FDSet.new
|
18
|
-
@max_fd = ::FFI::MemoryPointer.new(:int)
|
19
|
-
|
20
|
-
ObjectSpace.define_finalizer(self, self.class.finalizer(self))
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.finalizer(multi)
|
24
|
-
proc { Curl.multi_cleanup(multi.handle) }
|
25
|
-
end
|
26
|
-
|
27
|
-
def add(easy)
|
28
|
-
raise "trying to add easy handle twice" if @easy_handles.include?(easy)
|
29
|
-
easy.set_headers() if easy.headers.empty?
|
30
|
-
|
31
|
-
code = Curl.multi_add_handle(@handle, easy.handle)
|
32
|
-
raise RuntimeError.new("An error occured adding the handle: #{code}: #{Curl.multi_strerror(code)}") if code != :call_multi_perform and code != :ok
|
33
|
-
|
34
|
-
do_perform if code == :call_multi_perform
|
35
|
-
|
36
|
-
@active += 1
|
37
|
-
@easy_handles << easy
|
38
|
-
easy
|
39
|
-
end
|
40
|
-
|
41
|
-
def remove(easy)
|
42
|
-
if @easy_handles.include?(easy)
|
43
|
-
@active -= 1
|
44
|
-
Curl.multi_remove_handle(@handle, easy.handle)
|
45
|
-
@easy_handles.delete(easy)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def perform
|
50
|
-
while @active > 0
|
51
|
-
run
|
52
|
-
while @running > 0
|
53
|
-
# get the curl-suggested timeout
|
54
|
-
code = Curl.multi_timeout(@handle, @timeout)
|
55
|
-
raise RuntimeError.new("an error occured getting the timeout: #{code}: #{Curl.multi_strerror(code)}") if code != :ok
|
56
|
-
timeout = @timeout.read_long
|
57
|
-
if timeout == 0 # no delay
|
58
|
-
run
|
59
|
-
next
|
60
|
-
elsif timeout < 0
|
61
|
-
timeout = 1
|
62
|
-
end
|
63
|
-
|
64
|
-
# load the fd sets from the multi handle
|
65
|
-
@fd_read.clear
|
66
|
-
@fd_write.clear
|
67
|
-
@fd_excep.clear
|
68
|
-
code = Curl.multi_fdset(@handle, @fd_read, @fd_write, @fd_excep, @max_fd)
|
69
|
-
raise RuntimeError.new("an error occured getting the fdset: #{code}: #{Curl.multi_strerror(code)}") if code != :ok
|
70
|
-
|
71
|
-
max_fd = @max_fd.read_int
|
72
|
-
if max_fd == -1
|
73
|
-
# curl is doing something special so let it run for a moment
|
74
|
-
sleep(0.001)
|
75
|
-
else
|
76
|
-
@timeval[:sec] = timeout / 1000
|
77
|
-
@timeval[:usec] = (timeout * 1000) % 1000000
|
78
|
-
|
79
|
-
code = Curl.select(max_fd + 1, @fd_read, @fd_write, @fd_excep, @timeval)
|
80
|
-
raise RuntimeError.new("error on thread select: #{::FFI.errno}") if code < 0
|
81
|
-
end
|
82
|
-
|
83
|
-
run
|
84
|
-
end
|
85
|
-
end
|
86
|
-
reset_easy_handles
|
87
|
-
end
|
88
|
-
|
89
|
-
def fire_and_forget
|
90
|
-
run
|
91
|
-
end
|
92
|
-
|
93
|
-
# check for finished easy handles and remove from the multi handle
|
94
|
-
def read_info
|
95
|
-
msgs_left = ::FFI::MemoryPointer.new(:int)
|
96
|
-
while not (msg = Curl.multi_info_read(@handle, msgs_left)).null?
|
97
|
-
next if msg[:code] != :done
|
98
|
-
|
99
|
-
easy = @easy_handles.find {|easy| easy.handle == msg[:easy_handle] }
|
100
|
-
next if not easy
|
101
|
-
|
102
|
-
response_code = ::FFI::MemoryPointer.new(:long)
|
103
|
-
response_code.write_long(-1)
|
104
|
-
Curl.easy_getinfo(easy.handle, :response_code, response_code)
|
105
|
-
response_code = response_code.read_long
|
106
|
-
remove(easy)
|
107
|
-
|
108
|
-
easy.curl_return_code = msg[:data][:code]
|
109
|
-
if easy.curl_return_code != 0 then easy.failure
|
110
|
-
elsif (200..299).member?(response_code) or response_code == 0 then easy.success
|
111
|
-
else easy.failure
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def cleanup
|
117
|
-
Curl.multi_cleanup(@handle)
|
118
|
-
@active = 0
|
119
|
-
@running = 0
|
120
|
-
@easy_handles = []
|
121
|
-
end
|
122
|
-
|
123
|
-
def reset_easy_handles
|
124
|
-
@easy_handles.dup.each do |easy|
|
125
|
-
remove(easy)
|
126
|
-
yield easy if block_given?
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
private
|
131
|
-
|
132
|
-
# called by perform and fire_and_forget
|
133
|
-
def run
|
134
|
-
begin code = do_perform end while code == :call_multi_perform
|
135
|
-
raise RuntimeError.new("an error occured while running perform: #{code}: #{Curl.multi_strerror(code)}") if code != :ok
|
136
|
-
read_info
|
137
|
-
end
|
138
|
-
|
139
|
-
def do_perform
|
140
|
-
running = ::FFI::MemoryPointer.new(:int)
|
141
|
-
code = Curl.multi_perform(@handle, running)
|
142
|
-
@running = running.read_int
|
143
|
-
code
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
|
3
|
-
module Typhoeus
|
4
|
-
class ParamProcessor
|
5
|
-
class << self
|
6
|
-
def traverse_params_hash(hash, result = nil, current_key = nil)
|
7
|
-
result ||= { :files => [], :params => [] }
|
8
|
-
|
9
|
-
hash.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |key|
|
10
|
-
new_key = (current_key ? "#{current_key}[#{key}]" : key).to_s
|
11
|
-
current_value = hash[key]
|
12
|
-
process_value current_value, :result => result, :new_key => new_key
|
13
|
-
end
|
14
|
-
result
|
15
|
-
end
|
16
|
-
|
17
|
-
def process_value(current_value, options)
|
18
|
-
result = options[:result]
|
19
|
-
new_key = options[:new_key]
|
20
|
-
|
21
|
-
case current_value
|
22
|
-
when Hash
|
23
|
-
traverse_params_hash(current_value, result, new_key)
|
24
|
-
when Array
|
25
|
-
current_value.each do |v|
|
26
|
-
result[:params] << [new_key, v.to_s]
|
27
|
-
end
|
28
|
-
when File, Tempfile
|
29
|
-
filename = File.basename(current_value.path)
|
30
|
-
types = MIME::Types.type_for(filename)
|
31
|
-
result[:files] << [
|
32
|
-
new_key,
|
33
|
-
filename,
|
34
|
-
types.empty? ? 'application/octet-stream' : types[0].to_s,
|
35
|
-
File.expand_path(current_value.path)
|
36
|
-
]
|
37
|
-
else
|
38
|
-
result[:params] << [new_key, current_value.to_s]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/lib/typhoeus/remote.rb
DELETED
@@ -1,306 +0,0 @@
|
|
1
|
-
module Typhoeus
|
2
|
-
USER_AGENT = "Typhoeus - http://github.com/dbalatero/typhoeus/tree/master"
|
3
|
-
|
4
|
-
def self.included(base)
|
5
|
-
base.extend ClassMethods
|
6
|
-
end
|
7
|
-
|
8
|
-
class MockExpectedError < StandardError; end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def allow_net_connect
|
12
|
-
@allow_net_connect = true if @allow_net_connect.nil?
|
13
|
-
@allow_net_connect
|
14
|
-
end
|
15
|
-
|
16
|
-
def allow_net_connect=(value)
|
17
|
-
@allow_net_connect = value
|
18
|
-
end
|
19
|
-
|
20
|
-
def mock(method, args = {})
|
21
|
-
@remote_mocks ||= {}
|
22
|
-
@remote_mocks[method] ||= {}
|
23
|
-
args[:code] ||= 200
|
24
|
-
args[:body] ||= ""
|
25
|
-
args[:headers] ||= ""
|
26
|
-
args[:time] ||= 0
|
27
|
-
url = args.delete(:url)
|
28
|
-
url ||= :catch_all
|
29
|
-
params = args.delete(:params)
|
30
|
-
|
31
|
-
key = mock_key_for(url, params)
|
32
|
-
|
33
|
-
@remote_mocks[method][key] = args
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns a key for a given URL and passed in
|
37
|
-
# set of Typhoeus options to be used to store/retrieve
|
38
|
-
# a corresponding mock.
|
39
|
-
def mock_key_for(url, params = nil)
|
40
|
-
if url == :catch_all
|
41
|
-
url
|
42
|
-
else
|
43
|
-
key = url
|
44
|
-
if params and !params.empty?
|
45
|
-
key += flatten_and_sort_hash(params).to_s
|
46
|
-
end
|
47
|
-
key
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def flatten_and_sort_hash(params)
|
52
|
-
params = params.dup
|
53
|
-
|
54
|
-
# Flatten any sub-hashes to a single string.
|
55
|
-
params.keys.each do |key|
|
56
|
-
if params[key].is_a?(Hash)
|
57
|
-
params[key] = params[key].sort_by { |k, v| k.to_s.downcase }.to_s
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
params.sort_by { |k, v| k.to_s.downcase }
|
62
|
-
end
|
63
|
-
|
64
|
-
def get_mock(method, url, options)
|
65
|
-
return nil unless @remote_mocks
|
66
|
-
if @remote_mocks.has_key? method
|
67
|
-
extra_response_args = { :requested_http_method => method,
|
68
|
-
:requested_url => url,
|
69
|
-
:start_time => Time.now }
|
70
|
-
mock_key = mock_key_for(url, options[:params])
|
71
|
-
if @remote_mocks[method].has_key? mock_key
|
72
|
-
get_mock_and_run_handlers(method,
|
73
|
-
@remote_mocks[method][mock_key].merge(
|
74
|
-
extra_response_args),
|
75
|
-
options)
|
76
|
-
elsif @remote_mocks[method].has_key? :catch_all
|
77
|
-
get_mock_and_run_handlers(method,
|
78
|
-
@remote_mocks[method][:catch_all].merge(
|
79
|
-
extra_response_args),
|
80
|
-
options)
|
81
|
-
else
|
82
|
-
nil
|
83
|
-
end
|
84
|
-
else
|
85
|
-
nil
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def enforce_allow_net_connect!(http_verb, url, params = nil)
|
90
|
-
if !allow_net_connect
|
91
|
-
message = "Real HTTP connections are disabled. Unregistered request: " <<
|
92
|
-
"#{http_verb.to_s.upcase} #{url}\n" <<
|
93
|
-
" Try: mock(:#{http_verb}, :url => \"#{url}\""
|
94
|
-
if params
|
95
|
-
message << ",\n :params => #{params.inspect}"
|
96
|
-
end
|
97
|
-
|
98
|
-
message << ")"
|
99
|
-
|
100
|
-
raise MockExpectedError, message
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def check_expected_headers!(response_args, options)
|
105
|
-
missing_headers = {}
|
106
|
-
|
107
|
-
response_args[:expected_headers].each do |key, value|
|
108
|
-
if options[:headers].nil?
|
109
|
-
missing_headers[key] = [value, nil]
|
110
|
-
elsif ((options[:headers][key] && value != :anything) &&
|
111
|
-
options[:headers][key] != value)
|
112
|
-
|
113
|
-
missing_headers[key] = [value, options[:headers][key]]
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
unless missing_headers.empty?
|
118
|
-
raise headers_error_summary(response_args, options, missing_headers, 'expected')
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def check_unexpected_headers!(response_args, options)
|
123
|
-
bad_headers = {}
|
124
|
-
response_args[:unexpected_headers].each do |key, value|
|
125
|
-
if (options[:headers][key] && value == :anything) ||
|
126
|
-
(options[:headers][key] == value)
|
127
|
-
bad_headers[key] = [value, options[:headers][key]]
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
unless bad_headers.empty?
|
132
|
-
raise headers_error_summary(response_args, options, bad_headers, 'did not expect')
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def headers_error_summary(response_args, options, missing_headers, lead_in)
|
137
|
-
error = "#{lead_in} the following headers: #{response_args[:expected_headers].inspect}, but received: #{options[:headers].inspect}\n\n"
|
138
|
-
error << "Differences:\n"
|
139
|
-
error << "------------\n"
|
140
|
-
missing_headers.each do |key, values|
|
141
|
-
error << " - #{key}: #{lead_in} #{values[0].inspect}, got #{values[1].inspect}\n"
|
142
|
-
end
|
143
|
-
|
144
|
-
error
|
145
|
-
end
|
146
|
-
private :headers_error_summary
|
147
|
-
|
148
|
-
def get_mock_and_run_handlers(method, response_args, options)
|
149
|
-
response = Response.new(response_args)
|
150
|
-
|
151
|
-
if response_args.has_key? :expected_body
|
152
|
-
raise "#{method} expected body of \"#{response_args[:expected_body]}\" but received #{options[:body]}" if response_args[:expected_body] != options[:body]
|
153
|
-
end
|
154
|
-
|
155
|
-
if response_args.has_key? :expected_headers
|
156
|
-
check_expected_headers!(response_args, options)
|
157
|
-
end
|
158
|
-
|
159
|
-
if response_args.has_key? :unexpected_headers
|
160
|
-
check_unexpected_headers!(response_args, options)
|
161
|
-
end
|
162
|
-
|
163
|
-
if response.code >= 200 && response.code < 300 && options.has_key?(:on_success)
|
164
|
-
response = options[:on_success].call(response)
|
165
|
-
elsif options.has_key?(:on_failure)
|
166
|
-
response = options[:on_failure].call(response)
|
167
|
-
end
|
168
|
-
|
169
|
-
encode_nil_response(response)
|
170
|
-
end
|
171
|
-
|
172
|
-
[:get, :post, :put, :delete].each do |method|
|
173
|
-
line = __LINE__ + 2 # get any errors on the correct line num
|
174
|
-
code = <<-SRC
|
175
|
-
def #{method.to_s}(url, options = {})
|
176
|
-
mock_object = get_mock(:#{method.to_s}, url, options)
|
177
|
-
unless mock_object.nil?
|
178
|
-
decode_nil_response(mock_object)
|
179
|
-
else
|
180
|
-
enforce_allow_net_connect!(:#{method.to_s}, url, options[:params])
|
181
|
-
remote_proxy_object(url, :#{method.to_s}, options)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
SRC
|
185
|
-
module_eval(code, "./lib/typhoeus/remote.rb", line)
|
186
|
-
end
|
187
|
-
|
188
|
-
def remote_proxy_object(url, method, options)
|
189
|
-
easy = Typhoeus.get_easy_object
|
190
|
-
|
191
|
-
easy.url = url
|
192
|
-
easy.method = method
|
193
|
-
easy.headers = options[:headers] if options.has_key?(:headers)
|
194
|
-
easy.headers["User-Agent"] = (options[:user_agent] || Typhoeus::USER_AGENT)
|
195
|
-
easy.params = options[:params] if options[:params]
|
196
|
-
easy.request_body = options[:body] if options[:body]
|
197
|
-
easy.timeout = options[:timeout] if options[:timeout]
|
198
|
-
easy.set_headers
|
199
|
-
|
200
|
-
proxy = Typhoeus::RemoteProxyObject.new(clear_memoized_proxy_objects, easy, options)
|
201
|
-
set_memoized_proxy_object(method, url, options, proxy)
|
202
|
-
end
|
203
|
-
|
204
|
-
def remote_defaults(options)
|
205
|
-
@remote_defaults ||= {}
|
206
|
-
@remote_defaults.merge!(options) if options
|
207
|
-
@remote_defaults
|
208
|
-
end
|
209
|
-
|
210
|
-
# If we get subclassed, make sure that child inherits the remote defaults
|
211
|
-
# of the parent class.
|
212
|
-
def inherited(child)
|
213
|
-
child.__send__(:remote_defaults, @remote_defaults)
|
214
|
-
end
|
215
|
-
|
216
|
-
def call_remote_method(method_name, args)
|
217
|
-
m = @remote_methods[method_name]
|
218
|
-
|
219
|
-
base_uri = args.delete(:base_uri) || m.base_uri || ""
|
220
|
-
|
221
|
-
if args.has_key? :path
|
222
|
-
path = args.delete(:path)
|
223
|
-
else
|
224
|
-
path = m.interpolate_path_with_arguments(args)
|
225
|
-
end
|
226
|
-
path ||= ""
|
227
|
-
|
228
|
-
http_method = m.http_method
|
229
|
-
url = base_uri + path
|
230
|
-
options = m.merge_options(args)
|
231
|
-
|
232
|
-
# proxy_object = memoized_proxy_object(http_method, url, options)
|
233
|
-
# return proxy_object unless proxy_object.nil?
|
234
|
-
#
|
235
|
-
# if m.cache_responses?
|
236
|
-
# object = @cache.get(get_memcache_response_key(method_name, args))
|
237
|
-
# if object
|
238
|
-
# set_memoized_proxy_object(http_method, url, options, object)
|
239
|
-
# return object
|
240
|
-
# end
|
241
|
-
# end
|
242
|
-
|
243
|
-
proxy = memoized_proxy_object(http_method, url, options)
|
244
|
-
unless proxy
|
245
|
-
if m.cache_responses?
|
246
|
-
options[:cache] = @cache
|
247
|
-
options[:cache_key] = get_memcache_response_key(method_name, args)
|
248
|
-
options[:cache_timeout] = m.cache_ttl
|
249
|
-
end
|
250
|
-
proxy = send(http_method, url, options)
|
251
|
-
end
|
252
|
-
proxy
|
253
|
-
end
|
254
|
-
|
255
|
-
def set_memoized_proxy_object(http_method, url, options, object)
|
256
|
-
@memoized_proxy_objects ||= {}
|
257
|
-
@memoized_proxy_objects["#{http_method}_#{url}_#{options.to_s}"] = object
|
258
|
-
end
|
259
|
-
|
260
|
-
def memoized_proxy_object(http_method, url, options)
|
261
|
-
@memoized_proxy_objects ||= {}
|
262
|
-
@memoized_proxy_objects["#{http_method}_#{url}_#{options.to_s}"]
|
263
|
-
end
|
264
|
-
|
265
|
-
def clear_memoized_proxy_objects
|
266
|
-
lambda { @memoized_proxy_objects = {} }
|
267
|
-
end
|
268
|
-
|
269
|
-
def get_memcache_response_key(remote_method_name, args)
|
270
|
-
result = "#{remote_method_name.to_s}-#{args.to_s}"
|
271
|
-
(Digest::SHA2.new << result).to_s
|
272
|
-
end
|
273
|
-
|
274
|
-
def cache=(cache)
|
275
|
-
@cache = cache
|
276
|
-
end
|
277
|
-
|
278
|
-
def define_remote_method(name, args = {})
|
279
|
-
@remote_defaults ||= {}
|
280
|
-
args[:method] ||= @remote_defaults[:method]
|
281
|
-
args[:on_success] ||= @remote_defaults[:on_success]
|
282
|
-
args[:on_failure] ||= @remote_defaults[:on_failure]
|
283
|
-
args[:base_uri] ||= @remote_defaults[:base_uri]
|
284
|
-
args[:path] ||= @remote_defaults[:path]
|
285
|
-
m = RemoteMethod.new(args)
|
286
|
-
|
287
|
-
@remote_methods ||= {}
|
288
|
-
@remote_methods[name] = m
|
289
|
-
|
290
|
-
class_eval <<-SRC
|
291
|
-
def self.#{name.to_s}(args = {})
|
292
|
-
call_remote_method(:#{name.to_s}, args)
|
293
|
-
end
|
294
|
-
SRC
|
295
|
-
end
|
296
|
-
|
297
|
-
private
|
298
|
-
def encode_nil_response(response)
|
299
|
-
response == nil ? :__nil__ : response
|
300
|
-
end
|
301
|
-
|
302
|
-
def decode_nil_response(response)
|
303
|
-
response == :__nil__ ? nil : response
|
304
|
-
end
|
305
|
-
end # ClassMethods
|
306
|
-
end
|