typhoeus 0.2.4 → 0.3.2

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.
@@ -1,3 +1,5 @@
1
+ require 'tempfile'
2
+
1
3
  module Typhoeus
2
4
  module Utils
3
5
  # Taken from Rack::Utils, 1.2.1 to remove Rack dependency.
@@ -18,11 +20,10 @@ module Typhoeus
18
20
  when Hash
19
21
  traverse_params_hash(hash[key], result, new_key)
20
22
  when Array
21
- array_key = "#{new_key}[]"
22
23
  hash[key].each do |v|
23
- result[:params] << [array_key, v.to_s]
24
+ result[:params] << [new_key, v.to_s]
24
25
  end
25
- when File
26
+ when File, Tempfile
26
27
  filename = File.basename(hash[key].path)
27
28
  types = MIME::Types.type_for(filename)
28
29
  result[:files] << [
@@ -41,7 +42,7 @@ module Typhoeus
41
42
 
42
43
  def traversal_to_param_string(traversal, escape = true)
43
44
  traversal[:params].collect { |param|
44
- "#{Typhoeus::Utils.escape(param[0])}=#{Typhoeus::Utils.escape(param[1])}"
45
+ escape ? "#{Typhoeus::Utils.escape(param[0])}=#{Typhoeus::Utils.escape(param[1])}" : "#{param[0]}=#{param[1]}"
45
46
  }.join('&')
46
47
  end
47
48
  module_function :traversal_to_param_string
@@ -0,0 +1,3 @@
1
+ module Typhoeus
2
+ VERSION = '0.3.2'
3
+ end
@@ -15,6 +15,10 @@ post '/file' do
15
15
  }.to_json
16
16
  end
17
17
 
18
+ get '/multiple-headers' do
19
+ [200, { 'Set-Cookie' => %w[ foo bar ], 'Content-Type' => 'text/plain' }, ['']]
20
+ end
21
+
18
22
  get '/fail/:number' do
19
23
  if @@fail_count >= params[:number].to_i
20
24
  "ok"
@@ -1,6 +1,6 @@
1
1
  require "rubygems"
2
2
  require 'json'
3
- require "spec"
3
+ require "rspec"
4
4
 
5
5
  # gem install redgreen for colored test output
6
6
  begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
@@ -10,5 +10,5 @@ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
10
10
 
11
11
  require path + '/typhoeus'
12
12
 
13
- Spec::Runner.configure do |config|
14
- end
13
+ RSpec.configure do |config|
14
+ end
@@ -73,13 +73,13 @@ describe Typhoeus::Easy do
73
73
  end
74
74
 
75
75
  it "should allow you to set the user agent" do
76
- easy = Typhoeus::Easy.new
77
- easy.url = "http://localhost:3002"
78
- easy.method = :get
79
- easy.user_agent = "myapp"
80
- easy.perform
81
- easy.response_code.should == 200
82
- JSON.parse(easy.response_body)["HTTP_USER_AGENT"].should == "myapp"
76
+ e = Typhoeus::Easy.new
77
+ e.url = "http://localhost:3002"
78
+ e.method = :get
79
+ e.user_agent = "myapp"
80
+ e.perform
81
+ e.response_code.should == 200
82
+ JSON.parse(e.response_body)["HTTP_USER_AGENT"].should == "myapp"
83
83
  end
84
84
 
85
85
  it "should provide a timeout in milliseconds" do
@@ -110,6 +110,15 @@ describe Typhoeus::Easy do
110
110
  e.perform
111
111
  e.response_code.should == 302
112
112
  end
113
+
114
+ it "should provide the primary IP address that was used to perform the HTTP request" do
115
+ e = Typhoeus::Easy.new
116
+ e.url = "http://localhost:3002"
117
+ e.method = :get
118
+ e.perform
119
+ e.response_code.should == 200
120
+ e.primary_ip.should == "127.0.0.1"
121
+ end
113
122
  end
114
123
 
115
124
  describe "authentication" do
@@ -190,8 +199,7 @@ describe Typhoeus::Easy do
190
199
  :foo => 'bar',
191
200
  :username => ['dbalatero', 'dbalatero2']
192
201
  }
193
-
194
- easy.url.should =~ /\?.*username%5B%5D=dbalatero&username%5B%5D=dbalatero2/
202
+ easy.url.should =~ /\?.*foo=bar&username=dbalatero&username=dbalatero2/
195
203
  end
196
204
  end
197
205
 
@@ -215,6 +223,24 @@ describe Typhoeus::Easy do
215
223
  easy.response_code.should == 200
216
224
  easy.response_body.should include("this is a body!")
217
225
  end
226
+
227
+ it "should be able perform put with empty bodies on the same easy handle" do
228
+ easy = Typhoeus::Easy.new
229
+ easy.url = "http://localhost:3002"
230
+ easy.method = :put
231
+ easy.perform
232
+ easy.response_code.should == 200
233
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "PUT"
234
+
235
+ easy.reset
236
+
237
+ easy.url = "http://localhost:3002"
238
+ easy.method = :put
239
+ easy.perform
240
+ easy.response_code.should == 200
241
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "PUT"
242
+ end
243
+
218
244
  end
219
245
 
220
246
  describe "post" do
@@ -257,7 +283,7 @@ describe Typhoeus::Easy do
257
283
 
258
284
  request = JSON.parse(easy.response_body)
259
285
  request['CONTENT_TYPE'].should == 'application/x-www-form-urlencoded'
260
- request['rack.request.form_vars'].should == 'a=b&c=d&e%5Bf%5D%5Bg%5D=h'
286
+ request['rack.request.form_vars'].should == 'a=b&c=d&e[f][g]=h'
261
287
  end
262
288
 
263
289
  it "should handle a file upload, as multipart" do
@@ -311,6 +337,16 @@ describe Typhoeus::Easy do
311
337
  easy.response_code.should == 200
312
338
  JSON.parse(easy.response_body)["HTTP_ACCEPT_ENCODING"].should == "deflate, gzip"
313
339
  end
340
+
341
+ it "should send valid encoding headers and decode the response after reset" do
342
+ easy = Typhoeus::Easy.new
343
+ easy.reset
344
+ easy.url = "http://localhost:3002/gzipped"
345
+ easy.method = :get
346
+ easy.perform
347
+ easy.response_code.should == 200
348
+ JSON.parse(easy.response_body)["HTTP_ACCEPT_ENCODING"].should == "deflate, gzip"
349
+ end
314
350
 
315
351
  end
316
352
  end
@@ -36,9 +36,9 @@ describe Typhoeus::Form do
36
36
  :name => "John Smith",
37
37
  :age => "29"
38
38
  })
39
- form.should_receive(:formadd_param).with("colors[]", "brown")
40
- form.should_receive(:formadd_param).with("colors[]", "green")
41
- form.should_receive(:formadd_param).with("colors[]", "white")
39
+ form.should_receive(:formadd_param).with("colors", "brown")
40
+ form.should_receive(:formadd_param).with("colors", "green")
41
+ form.should_receive(:formadd_param).with("colors", "white")
42
42
  form.should_receive(:formadd_param).with("name", "John Smith")
43
43
  form.should_receive(:formadd_param).with("age", "29")
44
44
  form.process!
@@ -62,6 +62,16 @@ describe Typhoeus::Form do
62
62
  form.should_receive(:formadd_file).with("text_file", "placeholder.txt", "text/plain", anything)
63
63
  form.process!
64
64
  end
65
+
66
+ it "should handle tempfiles (file subclasses)" do
67
+ tempfile = Tempfile.new('placeholder_temp')
68
+ form = Typhoeus::Form.new(
69
+ :file => tempfile
70
+ )
71
+ form.should_receive(:formadd_file).with("file", File.basename(tempfile.path), "application/octet-stream", anything)
72
+ form.process!
73
+ end
74
+
65
75
  it "should default to 'application/octet-stream' if no content type can be determined" do
66
76
  pending
67
77
  form = Typhoeus::Form.new(
@@ -79,7 +89,7 @@ describe Typhoeus::Form do
79
89
  :name => "John Smith",
80
90
  :age => "29"
81
91
  })
82
- form.to_s.should == "age=29&name=John+Smith"
92
+ form.to_s.should == "age=29&name=John Smith"
83
93
  end
84
94
 
85
95
  it "should handle params that are a hash" do
@@ -92,16 +102,16 @@ describe Typhoeus::Form do
92
102
  :name => "John Smith",
93
103
  :age => "29"
94
104
  })
95
- form.to_s.should == "age=29&attributes%5Beyes%5D=brown&attributes%5Bhair%5D=green&attributes%5Bteeth%5D=white&name=John+Smith"
105
+ form.to_s.should == "age=29&attributes[eyes]=brown&attributes[hair]=green&attributes[teeth]=white&name=John Smith"
96
106
  end
97
107
 
98
- it "should params that have mutliple values" do
108
+ it "should params that have multiple values" do
99
109
  form = Typhoeus::Form.new({
100
110
  :colors => ["brown", "green", "white"],
101
111
  :name => "John Smith",
102
112
  :age => "29"
103
113
  })
104
- form.to_s.should == "age=29&colors%5B%5D=brown&colors%5B%5D=green&colors%5B%5D=white&name=John+Smith"
114
+ form.to_s.should == "age=29&colors=brown&colors=green&colors=white&name=John Smith"
105
115
  end
106
116
  end
107
117
  end
@@ -98,12 +98,12 @@ describe Typhoeus::HydraMock do
98
98
  Typhoeus::Request.new("http://localhost:3000", options.merge(:method => :get))
99
99
  end
100
100
 
101
- def mock(options = {})
101
+ def hydra(options = {})
102
102
  Typhoeus::HydraMock.new("http://localhost:3000", :get, options)
103
103
  end
104
104
 
105
105
  context 'when no :headers option is given' do
106
- subject { mock }
106
+ subject { hydra }
107
107
 
108
108
  it "matches regardless of whether or not the request has headers" do
109
109
  subject.matches?(request(:headers => nil)).should be_true
@@ -114,7 +114,7 @@ describe Typhoeus::HydraMock do
114
114
 
115
115
  [nil, {}].each do |value|
116
116
  context "for :headers => #{value.inspect}" do
117
- subject { mock(:headers => value) }
117
+ subject { hydra(:headers => value) }
118
118
 
119
119
  it "matches when the request has no headers" do
120
120
  subject.matches?(request(:headers => nil)).should be_true
@@ -129,7 +129,7 @@ describe Typhoeus::HydraMock do
129
129
 
130
130
  context 'for :headers => [a hash]' do
131
131
  it 'does not match if the request has no headers' do
132
- m = mock(:headers => { 'A' => 'B', 'C' => 'D' })
132
+ m = hydra(:headers => { 'A' => 'B', 'C' => 'D' })
133
133
 
134
134
  m.matches?(request).should be_false
135
135
  m.matches?(request(:headers => nil)).should be_false
@@ -137,7 +137,7 @@ describe Typhoeus::HydraMock do
137
137
  end
138
138
 
139
139
  it 'does not match if the request lacks any of the given headers' do
140
- mock(
140
+ hydra(
141
141
  :headers => { 'A' => 'B', 'C' => 'D' }
142
142
  ).matches?(request(
143
143
  :headers => { 'A' => 'B' }
@@ -145,7 +145,7 @@ describe Typhoeus::HydraMock do
145
145
  end
146
146
 
147
147
  it 'does not match if any of the specified values are different from the request value' do
148
- mock(
148
+ hydra(
149
149
  :headers => { 'A' => 'B', 'C' => 'D' }
150
150
  ).matches?(request(
151
151
  :headers => { 'A' => 'B', 'C' => 'E' }
@@ -153,39 +153,39 @@ describe Typhoeus::HydraMock do
153
153
  end
154
154
 
155
155
  it 'matches if the given hash is exactly equal to the request headers' do
156
- mock(
156
+ hydra(
157
157
  :headers => { 'A' => 'B', 'C' => 'D' }
158
158
  ).matches?(request(
159
159
  :headers => { 'A' => 'B', 'C' => 'D' }
160
160
  )).should be_true
161
161
  end
162
162
 
163
- it 'matches even if the request has additional headers not specified in the mock' do
164
- mock(
163
+ it 'matches even if the request has additional headers not specified in the hydra' do
164
+ hydra(
165
165
  :headers => { 'A' => 'B', 'C' => 'D' }
166
166
  ).matches?(request(
167
167
  :headers => { 'A' => 'B', 'C' => 'D', 'E' => 'F' }
168
168
  )).should be_true
169
169
  end
170
170
 
171
- it 'matches even if the casing of the header keys is different between the mock and request' do
172
- mock(
171
+ it 'matches even if the casing of the header keys is different between the hydra and request' do
172
+ hydra(
173
173
  :headers => { 'A' => 'B', 'c' => 'D' }
174
174
  ).matches?(request(
175
175
  :headers => { 'a' => 'B', 'C' => 'D' }
176
176
  )).should be_true
177
177
  end
178
178
 
179
- it 'matches if the mocked values are regexes and match the request values' do
180
- mock(
179
+ it 'matches if the hydraed values are regexes and match the request values' do
180
+ hydra(
181
181
  :headers => { 'A' => /foo/, }
182
182
  ).matches?(request(
183
183
  :headers => { 'A' => 'foo bar' }
184
184
  )).should be_true
185
185
  end
186
186
 
187
- it 'does not match if the mocked values are regexes and do not match the request values' do
188
- mock(
187
+ it 'does not match if the hydraed values are regexes and do not match the request values' do
188
+ hydra(
189
189
  :headers => { 'A' => /foo/, }
190
190
  ).matches?(request(
191
191
  :headers => { 'A' => 'bar' }
@@ -194,15 +194,15 @@ describe Typhoeus::HydraMock do
194
194
 
195
195
  context 'when a header is specified as an array' do
196
196
  it 'matches when the request header has the same array' do
197
- mock(
197
+ hydra(
198
198
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
199
199
  ).matches?(request(
200
200
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
201
201
  )).should be_true
202
202
  end
203
203
 
204
- it 'matches when the request header is a single value and the mock array has the same value' do
205
- mock(
204
+ it 'matches when the request header is a single value and the hydra array has the same value' do
205
+ hydra(
206
206
  :headers => { 'Accept' => ['text/html'] }
207
207
  ).matches?(request(
208
208
  :headers => { 'Accept' => 'text/html' }
@@ -210,7 +210,7 @@ describe Typhoeus::HydraMock do
210
210
  end
211
211
 
212
212
  it 'matches even when the request header array is ordered differently' do
213
- mock(
213
+ hydra(
214
214
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
215
215
  ).matches?(request(
216
216
  :headers => { 'Accept' => ['text/plain', 'text/html'] }
@@ -218,7 +218,7 @@ describe Typhoeus::HydraMock do
218
218
  end
219
219
 
220
220
  it 'does not match when the request header array lacks a value' do
221
- mock(
221
+ hydra(
222
222
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
223
223
  ).matches?(request(
224
224
  :headers => { 'Accept' => ['text/plain'] }
@@ -226,7 +226,7 @@ describe Typhoeus::HydraMock do
226
226
  end
227
227
 
228
228
  it 'does not match when the request header array has an extra value' do
229
- mock(
229
+ hydra(
230
230
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
231
231
  ).matches?(request(
232
232
  :headers => { 'Accept' => ['text/html', 'text/plain', 'application/xml'] }
@@ -234,7 +234,7 @@ describe Typhoeus::HydraMock do
234
234
  end
235
235
 
236
236
  it 'does not match when the request header is not an array' do
237
- mock(
237
+ hydra(
238
238
  :headers => { 'Accept' => ['text/html', 'text/plain'] }
239
239
  ).matches?(request(
240
240
  :headers => { 'Accept' => 'text/html' }
@@ -46,6 +46,29 @@ describe Typhoeus::Hydra do
46
46
  second.response.body.should include("second")
47
47
  end
48
48
 
49
+ it "runs queued requests in order of queuing" do
50
+ hydra = Typhoeus::Hydra.new :max_concurrency => 1
51
+ first = Typhoeus::Request.new("http://localhost:3000/first")
52
+ second = Typhoeus::Request.new("http://localhost:3001/second")
53
+ third = Typhoeus::Request.new("http://localhost:3001/third")
54
+ second.on_complete do |response|
55
+ first.response.should_not == nil
56
+ third.response.should == nil
57
+ end
58
+ third.on_complete do |response|
59
+ first.response.should_not == nil
60
+ second.response.should_not == nil
61
+ end
62
+
63
+ hydra.queue first
64
+ hydra.queue second
65
+ hydra.queue third
66
+ hydra.run
67
+ first.response.body.should include("first")
68
+ second.response.body.should include("second")
69
+ third.response.body.should include("third")
70
+ end
71
+
49
72
  it "should store the curl return codes on the reponses" do
50
73
  hydra = Typhoeus::Hydra.new
51
74
  first = Typhoeus::Request.new("http://localhost:3001/?delay=1", :timeout => 100)
@@ -165,6 +188,31 @@ describe Typhoeus::Hydra do
165
188
  @cache.get(second.cache_key).should be_nil
166
189
  end
167
190
 
191
+ it "continues queued requests after a queued cache hit" do
192
+ # Set max_concurrency to 1 so that the second and third requests will end
193
+ # up in the request queue.
194
+ hydra = Typhoeus::Hydra.new :max_concurrency => 1
195
+ hydra.cache_getter do |request|
196
+ @cache.get(request.cache_key) rescue nil
197
+ end
198
+ hydra.cache_setter do |request|
199
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
200
+ end
201
+
202
+ first = Typhoeus::Request.new("http://localhost:3000/first", :params => {:delay => 1})
203
+ second = Typhoeus::Request.new("http://localhost:3000/second", :params => {:delay => 1})
204
+ third = Typhoeus::Request.new("http://localhost:3000/third", :params => {:delay => 1})
205
+ @cache.set(second.cache_key, "second", 60)
206
+ hydra.queue first
207
+ hydra.queue second
208
+ hydra.queue third
209
+ hydra.run
210
+
211
+ first.response.body.should include("first")
212
+ second.response.should == "second"
213
+ third.response.body.should include("third")
214
+ end
215
+
168
216
  it "has a global on_complete" do
169
217
  foo = nil
170
218
  hydra = Typhoeus::Hydra.new
@@ -300,6 +348,34 @@ describe Typhoeus::Hydra::Stubbing do
300
348
 
301
349
  after(:each) do
302
350
  @stub_target.clear_stubs
351
+ @stub_target.stub_finders.clear
352
+ end
353
+
354
+ it 'allows users to register a custom stub finder' do
355
+ @stub_target.register_stub_finder do |request|
356
+ Typhoeus::Response.new :code => 200, :body => "stub for #{request.url.split('/').last}"
357
+ end
358
+
359
+ request = Typhoeus::Request.new("http://localhost:3000/foo")
360
+ returned_response = nil
361
+ request.on_complete { |response| returned_response = response }
362
+ @hydra.queue(request); @hydra.run
363
+
364
+ returned_response.code.should == 200
365
+ returned_response.body.should == "stub for foo"
366
+ end
367
+
368
+ it 'ignores the custom stub finder if it the block returns nil' do
369
+ @stub_target.register_stub_finder { |r| }
370
+
371
+ @stub_target.stub(:get, "http://localhost:3000/foo",
372
+ :headers => { 'user-agent' => 'test'}).
373
+ and_return(@response)
374
+
375
+ @hydra.queue(@request)
376
+ @hydra.run
377
+ @on_complete_handler_called.should be_true
378
+ @response.request.should == @request
303
379
  end
304
380
 
305
381
  it "should provide a stubs accessor" do