typhoeus 0.2.4 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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