typhoeus 0.1.6 → 0.2.0

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 (44) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG.markdown +31 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +30 -0
  5. data/README.textile +333 -0
  6. data/Rakefile +38 -0
  7. data/VERSION +1 -0
  8. data/benchmarks/profile.rb +25 -0
  9. data/benchmarks/vs_nethttp.rb +35 -0
  10. data/examples/twitter.rb +21 -0
  11. data/ext/typhoeus/.gitignore +6 -0
  12. data/ext/typhoeus/extconf.rb +3 -2
  13. data/ext/typhoeus/native.h +7 -0
  14. data/ext/typhoeus/typhoeus_multi.c +1 -2
  15. data/lib/typhoeus/.gitignore +1 -0
  16. data/lib/typhoeus/easy.rb +172 -42
  17. data/lib/typhoeus/hydra/callbacks.rb +24 -0
  18. data/lib/typhoeus/hydra/connect_options.rb +45 -0
  19. data/lib/typhoeus/hydra/stubbing.rb +52 -0
  20. data/lib/typhoeus/hydra.rb +85 -59
  21. data/lib/typhoeus/hydra_mock.rb +131 -0
  22. data/lib/typhoeus/multi.rb +4 -3
  23. data/lib/typhoeus/normalized_header_hash.rb +58 -0
  24. data/lib/typhoeus/remote_proxy_object.rb +6 -6
  25. data/lib/typhoeus/request.rb +118 -31
  26. data/lib/typhoeus/response.rb +61 -4
  27. data/lib/typhoeus/service.rb +20 -0
  28. data/lib/typhoeus/utils.rb +24 -0
  29. data/lib/typhoeus.rb +11 -8
  30. data/profilers/valgrind.rb +24 -0
  31. data/spec/fixtures/result_set.xml +60 -0
  32. data/spec/servers/app.rb +48 -1
  33. data/spec/spec_helper.rb +1 -1
  34. data/spec/typhoeus/easy_spec.rb +126 -4
  35. data/spec/typhoeus/hydra_mock_spec.rb +300 -0
  36. data/spec/typhoeus/hydra_spec.rb +456 -0
  37. data/spec/typhoeus/normalized_header_hash_spec.rb +41 -0
  38. data/spec/typhoeus/remote_spec.rb +2 -2
  39. data/spec/typhoeus/request_spec.rb +247 -0
  40. data/spec/typhoeus/response_spec.rb +104 -1
  41. data/spec/typhoeus/utils_spec.rb +22 -0
  42. data/typhoeus.gemspec +123 -0
  43. metadata +145 -34
  44. data/ext/typhoeus/Makefile +0 -157
@@ -0,0 +1,456 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ # some of these tests assume that you have some local services running.
4
+ # ruby spec/servers/app.rb -p 3000
5
+ # ruby spec/servers/app.rb -p 3001
6
+ # ruby spec/servers/app.rb -p 3002
7
+ describe Typhoeus::Hydra do
8
+ before(:all) do
9
+ cache_class = Class.new do
10
+ def initialize
11
+ @cache = {}
12
+ end
13
+ def get(key)
14
+ @cache[key]
15
+ end
16
+ def set(key, object, timeout = 0)
17
+ @cache[key] = object
18
+ end
19
+ end
20
+ @cache = cache_class.new
21
+ end
22
+
23
+ it "has a singleton" do
24
+ Typhoeus::Hydra.hydra.should be_a Typhoeus::Hydra
25
+ end
26
+
27
+ it "has a setter for the singleton" do
28
+ Typhoeus::Hydra.hydra = :foo
29
+ Typhoeus::Hydra.hydra.should == :foo
30
+ Typhoeus::Hydra.hydra = Typhoeus::Hydra.new
31
+ end
32
+
33
+ it "queues a request" do
34
+ hydra = Typhoeus::Hydra.new
35
+ hydra.queue Typhoeus::Request.new("http://localhost:3000")
36
+ end
37
+
38
+ it "runs a batch of requests" do
39
+ hydra = Typhoeus::Hydra.new
40
+ first = Typhoeus::Request.new("http://localhost:3000/first")
41
+ second = Typhoeus::Request.new("http://localhost:3001/second")
42
+ hydra.queue first
43
+ hydra.queue second
44
+ hydra.run
45
+ first.response.body.should include("first")
46
+ second.response.body.should include("second")
47
+ end
48
+
49
+ it "has a cache_setter proc" do
50
+ hydra = Typhoeus::Hydra.new
51
+ hydra.cache_setter do |request|
52
+ # @cache.set(request.cache_key, request.response, request.cache_timeout)
53
+ end
54
+ end
55
+
56
+ it "has a cache_getter" do
57
+ hydra = Typhoeus::Hydra.new
58
+ hydra.cache_getter do |request|
59
+ # @cache.get(request.cache_key) rescue nil
60
+ end
61
+ end
62
+
63
+ it "memoizes GET reqeusts" do
64
+ hydra = Typhoeus::Hydra.new
65
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
66
+ second = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
67
+ hydra.queue first
68
+ hydra.queue second
69
+ start_time = Time.now
70
+ hydra.run
71
+ first.response.body.should include("foo")
72
+ first.handled_response.body.should include("foo")
73
+ first.response.should == second.response
74
+ first.handled_response.should == second.handled_response
75
+ (Time.now - start_time).should < 1.2 # if it had run twice it would be ~ 2 seconds
76
+ end
77
+
78
+ it "can turn off memoization for GET requests" do
79
+ hydra = Typhoeus::Hydra.new
80
+ hydra.disable_memoization
81
+ first = Typhoeus::Request.new("http://localhost:3000/foo")
82
+ second = Typhoeus::Request.new("http://localhost:3000/foo")
83
+ hydra.queue first
84
+ hydra.queue second
85
+ hydra.run
86
+ first.response.body.should include("foo")
87
+ first.response.object_id.should_not == second.response.object_id
88
+ end
89
+
90
+ it "pulls GETs from cache" do
91
+ hydra = Typhoeus::Hydra.new
92
+ start_time = Time.now
93
+ hydra.cache_getter do |request|
94
+ @cache.get(request.cache_key) rescue nil
95
+ end
96
+ hydra.cache_setter do |request|
97
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
98
+ end
99
+
100
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
101
+ @cache.set(first.cache_key, :foo, 60)
102
+ hydra.queue first
103
+ hydra.run
104
+ (Time.now - start_time).should < 0.1
105
+ first.response.should == :foo
106
+ end
107
+
108
+ it "sets GET responses to cache when the request has a cache_timeout value" do
109
+ hydra = Typhoeus::Hydra.new
110
+ hydra.cache_getter do |request|
111
+ @cache.get(request.cache_key) rescue nil
112
+ end
113
+ hydra.cache_setter do |request|
114
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
115
+ end
116
+
117
+ first = Typhoeus::Request.new("http://localhost:3000/first", :cache_timeout => 0)
118
+ second = Typhoeus::Request.new("http://localhost:3000/second")
119
+ hydra.queue first
120
+ hydra.queue second
121
+ hydra.run
122
+ first.response.body.should include("first")
123
+ @cache.get(first.cache_key).should == first.response
124
+ @cache.get(second.cache_key).should be_nil
125
+ end
126
+
127
+ it "has a global on_complete" do
128
+ foo = nil
129
+ hydra = Typhoeus::Hydra.new
130
+ hydra.on_complete do |response|
131
+ foo = :called
132
+ end
133
+
134
+ first = Typhoeus::Request.new("http://localhost:3000/first")
135
+ hydra.queue first
136
+ hydra.run
137
+ first.response.body.should include("first")
138
+ foo.should == :called
139
+ end
140
+
141
+ it "has a global on_complete setter" do
142
+ foo = nil
143
+ hydra = Typhoeus::Hydra.new
144
+ proc = Proc.new {|response| foo = :called}
145
+ hydra.on_complete = proc
146
+
147
+ first = Typhoeus::Request.new("http://localhost:3000/first")
148
+ hydra.queue first
149
+ hydra.run
150
+ first.response.body.should include("first")
151
+ foo.should == :called
152
+ end
153
+
154
+ it "should reuse connections from the pool for a host"
155
+
156
+ it "should queue up requests while others are running" do
157
+ hydra = Typhoeus::Hydra.new
158
+
159
+ start_time = Time.now
160
+ @responses = []
161
+
162
+ request = Typhoeus::Request.new("http://localhost:3000/first", :params => {:delay => 1})
163
+ request.on_complete do |response|
164
+ @responses << response
165
+ response.body.should include("first")
166
+ end
167
+
168
+ request.after_complete do |object|
169
+ second_request = Typhoeus::Request.new("http://localhost:3001/second", :params => {:delay => 2})
170
+ second_request.on_complete do |response|
171
+ @responses << response
172
+ response.body.should include("second")
173
+ end
174
+ hydra.queue second_request
175
+ end
176
+ hydra.queue request
177
+
178
+ third_request = Typhoeus::Request.new("http://localhost:3002/third", :params => {:delay => 3})
179
+ third_request.on_complete do |response|
180
+ @responses << response
181
+ response.body.should include("third")
182
+ end
183
+ hydra.queue third_request
184
+
185
+ hydra.run
186
+ @responses.size.should == 3
187
+ (Time.now - start_time).should < 3.3
188
+ end
189
+
190
+ it "should fire and forget" do
191
+ # this test is totally hacky. I have no clue how to make it verify. I just look at the test servers
192
+ # to verify that stuff is running
193
+ hydra = Typhoeus::Hydra.new
194
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
195
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=2")
196
+ hydra.queue first
197
+ hydra.queue second
198
+ hydra.fire_and_forget
199
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=3")
200
+ hydra.queue third
201
+ hydra.fire_and_forget
202
+ sleep 3 # have to do this or future tests may break.
203
+ end
204
+
205
+ it "should take the maximum number of concurrent requests as an argument" do
206
+ hydra = Typhoeus::Hydra.new(:max_concurrency => 2)
207
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
208
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=1")
209
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=1")
210
+ hydra.queue first
211
+ hydra.queue second
212
+ hydra.queue third
213
+
214
+ start_time = Time.now
215
+ hydra.run
216
+ finish_time = Time.now
217
+
218
+ first.response.code.should == 200
219
+ second.response.code.should == 200
220
+ third.response.code.should == 200
221
+ (finish_time - start_time).should > 2.0
222
+ end
223
+
224
+ it "should respect the follow_location option when set on a request" do
225
+ hydra = Typhoeus::Hydra.new
226
+ request = Typhoeus::Request.new("http://localhost:3000/redirect", :follow_location => true)
227
+ hydra.queue request
228
+ hydra.run
229
+
230
+ request.response.code.should == 200
231
+ end
232
+
233
+ it "should pass through the max_redirects option when set on a request" do
234
+ hydra = Typhoeus::Hydra.new
235
+ request = Typhoeus::Request.new("http://localhost:3000/bad_redirect", :max_redirects => 5)
236
+ hydra.queue request
237
+ hydra.run
238
+
239
+ request.response.code.should == 302
240
+ end
241
+ end
242
+
243
+ describe Typhoeus::Hydra::Stubbing do
244
+ shared_examples_for "any stubbable target" do
245
+ before(:each) do
246
+ @on_complete_handler_called = nil
247
+ @request = Typhoeus::Request.new("http://localhost:3000/foo",
248
+ :user_agent => 'test')
249
+ @request.on_complete do |response|
250
+ @on_complete_handler_called = true
251
+ response.code.should == 404
252
+ response.headers.should == "whatever"
253
+ end
254
+ @response = Typhoeus::Response.new(:code => 404,
255
+ :headers => "whatever",
256
+ :body => "not found",
257
+ :time => 0.1)
258
+ end
259
+
260
+ after(:each) do
261
+ @stub_target.clear_stubs
262
+ end
263
+
264
+ it "should provide a stubs accessor" do
265
+ begin
266
+ @stub_target.stubs.should == []
267
+ @stub_target.stubs = [:foo]
268
+ ensure
269
+ @stub_target.clear_stubs
270
+ end
271
+ end
272
+
273
+ it "stubs requests to a specific URI" do
274
+ @stub_target.stub(:get, "http://localhost:3000/foo",
275
+ :headers => { 'user-agent' => 'test'}).
276
+ and_return(@response)
277
+
278
+ @hydra.queue(@request)
279
+ @hydra.run
280
+ @on_complete_handler_called.should be_true
281
+ @response.request.should == @request
282
+ end
283
+
284
+ it "stubs requests to URIs matching a pattern" do
285
+ @stub_target.stub(:get, /foo/,
286
+ :headers => { 'user-agent' => 'test' }).
287
+ and_return(@response)
288
+ @hydra.queue(@request)
289
+ @hydra.run
290
+ @on_complete_handler_called.should be_true
291
+ @response.request.should == @request
292
+ end
293
+
294
+ it "can clear stubs" do
295
+ @stub_target.clear_stubs
296
+ end
297
+
298
+ it "clears out previously queued requests once they are called" do
299
+ @stub_target.stub(:get, "http://localhost:3000/asdf",
300
+ :headers => { 'user-agent' => 'test' }).
301
+ and_return(@response)
302
+
303
+ call_count = 0
304
+ request = Typhoeus::Request.new("http://localhost:3000/asdf", :user_agent => 'test')
305
+ request.on_complete do |response|
306
+ call_count += 1
307
+ end
308
+ @hydra.queue(request)
309
+ @hydra.run
310
+ call_count.should == 1
311
+ @hydra.run
312
+ call_count.should == 1
313
+ end
314
+
315
+ it "calls stubs for requests that are queued up in the on_complete of a first stub" do
316
+ @stub_target.stub(:get, "http://localhost:3000/asdf").and_return(@response)
317
+ @stub_target.stub(:get, "http://localhost:3000/bar").and_return(@response)
318
+
319
+ second_handler_called = false
320
+ request = Typhoeus::Request.new("http://localhost:3000/asdf")
321
+ request.on_complete do |response|
322
+ r = Typhoeus::Request.new("http://localhost:3000/bar")
323
+ r.on_complete do |res|
324
+ second_handler_called = true
325
+ end
326
+ @hydra.queue(r)
327
+ end
328
+ @hydra.queue(request)
329
+ @hydra.run
330
+
331
+ second_handler_called.should be_true
332
+ end
333
+ end
334
+
335
+ describe "global (class-level) stubbing" do
336
+ before(:each) do
337
+ @hydra = Typhoeus::Hydra.new
338
+ @stub_target = Typhoeus::Hydra
339
+ end
340
+
341
+ it_should_behave_like "any stubbable target"
342
+ end
343
+
344
+ describe "instance stubbing" do
345
+ before(:each) do
346
+ @hydra = Typhoeus::Hydra.new
347
+ @stub_target = @hydra
348
+ end
349
+
350
+ it_should_behave_like "any stubbable target"
351
+ end
352
+ end
353
+
354
+ describe Typhoeus::Hydra::Callbacks do
355
+ before(:all) do
356
+ @klass = Typhoeus::Hydra
357
+ end
358
+
359
+ describe "#after_request_before_on_complete" do
360
+ it "should provide a global hook after a request" do
361
+ begin
362
+ http_method = nil
363
+ @klass.after_request_before_on_complete do |request|
364
+ http_method = request.method
365
+ end
366
+
367
+ hydra = @klass.new
368
+ request = Typhoeus::Request.new('http://localhost:3000',
369
+ :method => :get)
370
+ response = Typhoeus::Response.new(:code => 404,
371
+ :headers => "whatever",
372
+ :body => "not found",
373
+ :time => 0.1)
374
+ hydra.stub(:get, 'http://localhost:3000').
375
+ and_return(response)
376
+
377
+ hydra.queue(request)
378
+ hydra.run
379
+
380
+ http_method.should == :get
381
+ ensure
382
+ @klass.clear_global_hooks
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ describe Typhoeus::Hydra::ConnectOptions do
389
+ before(:all) do
390
+ @klass = Typhoeus::Hydra
391
+ end
392
+
393
+ describe "#ignore_localhost" do
394
+ it "should allow localhost requests over the wire if true" do
395
+ begin
396
+ old_net_connect = @klass.allow_net_connect
397
+ old_ignore_localhost = @klass.ignore_localhost
398
+ @klass.allow_net_connect = false
399
+ @klass.ignore_localhost = true
400
+
401
+ req = Typhoeus::Request.new("http://localhost:3000")
402
+ hydra = @klass.new
403
+
404
+ lambda {
405
+ hydra.queue(req)
406
+ }.should_not raise_error
407
+ ensure
408
+ @klass.allow_net_connect = old_net_connect
409
+ @klass.ignore_localhost = old_ignore_localhost
410
+ end
411
+ end
412
+ end
413
+
414
+ describe "#allow_net_connect" do
415
+ it "should be settable" do
416
+ begin
417
+ # make sure to not mess up other specs.
418
+ old = @klass.allow_net_connect
419
+ @klass.allow_net_connect = true
420
+ @klass.allow_net_connect.should be_true
421
+ ensure
422
+ @klass.allow_net_connect = old
423
+ end
424
+ end
425
+
426
+ it "should default to true" do
427
+ @klass.allow_net_connect.should be_true
428
+ end
429
+
430
+ it "should raise an error if we queue a request while its false" do
431
+ begin
432
+ request = Typhoeus::Request.new("http://localhost:3000")
433
+ old_net_connect = @klass.allow_net_connect
434
+ old_ignore_localhost = @klass.ignore_localhost
435
+
436
+ @klass.allow_net_connect = false
437
+ @klass.ignore_localhost = false
438
+
439
+ hydra = Typhoeus::Hydra.new
440
+
441
+ lambda {
442
+ hydra.queue(request)
443
+ }.should raise_error(Typhoeus::Hydra::NetConnectNotAllowedError)
444
+ ensure
445
+ @klass.allow_net_connect = old_net_connect
446
+ @klass.ignore_localhost = old_ignore_localhost
447
+ end
448
+ end
449
+ end
450
+
451
+ describe "#allow_net_connect?" do
452
+ it "should return true by default" do
453
+ @klass.allow_net_connect?.should be_true
454
+ end
455
+ end
456
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Typhoeus::NormalizedHeaderHash do
4
+ before(:all) do
5
+ @klass = Typhoeus::NormalizedHeaderHash
6
+ end
7
+
8
+ it "should normalize keys on assignment" do
9
+ hash = @klass.new
10
+ hash['Content-Type'] = 'text/html'
11
+ hash['content-type'].should == 'text/html'
12
+ hash[:content_type].should == 'text/html'
13
+ hash['Accepts'] = 'text/javascript'
14
+ hash['accepts'].should == 'text/javascript'
15
+ end
16
+
17
+ it "should normalize the keys on instantiation" do
18
+ hash = @klass.new('Content-Type' => 'text/html', :x_http_header => 'foo', 'X-HTTP-USER' => 'bar')
19
+ hash.keys.should =~ ['Content-Type', 'X-Http-Header', 'X-Http-User']
20
+ end
21
+
22
+ it "should merge keys correctly" do
23
+ hash = @klass.new
24
+ hash.merge!('Content-Type' => 'fdsa')
25
+ hash['content-type'].should == 'fdsa'
26
+ end
27
+
28
+ it "should allow any casing of keys" do
29
+ hash = @klass.new
30
+ hash['Content-Type'] = 'fdsa'
31
+ hash['content-type'].should == 'fdsa'
32
+ hash['cOnTent-TYPE'].should == 'fdsa'
33
+ hash['Content-Type'].should == 'fdsa'
34
+ end
35
+
36
+ it "should support has_key?" do
37
+ hash = @klass.new
38
+ hash['Content-Type'] = 'fdsa'
39
+ hash.has_key?('cOntent-Type').should be_true
40
+ end
41
+ end
@@ -2,9 +2,9 @@ require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
3
  describe Typhoeus do
4
4
  it "should be deprecated" do
5
- fail "This entire interface is deprecated!"
5
+ pending "This entire interface is deprecated!"
6
6
  end
7
-
7
+
8
8
  # before(:each) do
9
9
  # @klass = Class.new do
10
10
  # include Typhoeus