arachni-typhoeus 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG.markdown +43 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +30 -0
  5. data/README.textile +6 -0
  6. data/Rakefile +40 -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 +7 -0
  12. data/ext/typhoeus/extconf.rb +65 -0
  13. data/ext/typhoeus/native.c +11 -0
  14. data/ext/typhoeus/native.h +21 -0
  15. data/ext/typhoeus/typhoeus_easy.c +220 -0
  16. data/ext/typhoeus/typhoeus_easy.h +19 -0
  17. data/ext/typhoeus/typhoeus_multi.c +211 -0
  18. data/ext/typhoeus/typhoeus_multi.h +16 -0
  19. data/lib/typhoeus.rb +58 -0
  20. data/lib/typhoeus/.gitignore +1 -0
  21. data/lib/typhoeus/easy.rb +366 -0
  22. data/lib/typhoeus/filter.rb +28 -0
  23. data/lib/typhoeus/hydra.rb +245 -0
  24. data/lib/typhoeus/hydra/callbacks.rb +24 -0
  25. data/lib/typhoeus/hydra/connect_options.rb +61 -0
  26. data/lib/typhoeus/hydra/stubbing.rb +52 -0
  27. data/lib/typhoeus/hydra_mock.rb +131 -0
  28. data/lib/typhoeus/multi.rb +37 -0
  29. data/lib/typhoeus/normalized_header_hash.rb +58 -0
  30. data/lib/typhoeus/remote.rb +306 -0
  31. data/lib/typhoeus/remote_method.rb +108 -0
  32. data/lib/typhoeus/remote_proxy_object.rb +50 -0
  33. data/lib/typhoeus/request.rb +210 -0
  34. data/lib/typhoeus/response.rb +91 -0
  35. data/lib/typhoeus/service.rb +20 -0
  36. data/lib/typhoeus/utils.rb +24 -0
  37. data/profilers/valgrind.rb +24 -0
  38. data/spec/fixtures/result_set.xml +60 -0
  39. data/spec/servers/app.rb +84 -0
  40. data/spec/spec.opts +2 -0
  41. data/spec/spec_helper.rb +11 -0
  42. data/spec/typhoeus/easy_spec.rb +284 -0
  43. data/spec/typhoeus/filter_spec.rb +35 -0
  44. data/spec/typhoeus/hydra_mock_spec.rb +300 -0
  45. data/spec/typhoeus/hydra_spec.rb +526 -0
  46. data/spec/typhoeus/multi_spec.rb +74 -0
  47. data/spec/typhoeus/normalized_header_hash_spec.rb +41 -0
  48. data/spec/typhoeus/remote_method_spec.rb +141 -0
  49. data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
  50. data/spec/typhoeus/remote_spec.rb +695 -0
  51. data/spec/typhoeus/request_spec.rb +276 -0
  52. data/spec/typhoeus/response_spec.rb +151 -0
  53. data/spec/typhoeus/utils_spec.rb +22 -0
  54. data/typhoeus.gemspec +123 -0
  55. metadata +196 -0
@@ -0,0 +1,526 @@
1
+ require File.expand_path(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 "should store the curl return codes on the reponses" do
50
+ hydra = Typhoeus::Hydra.new
51
+ first = Typhoeus::Request.new("http://localhost:3001/?delay=1", :timeout => 100)
52
+ second = Typhoeus::Request.new("http://localhost:3999", :connect_timout => 100)
53
+ hydra.queue first
54
+ hydra.queue second
55
+ hydra.run
56
+ first.response.curl_return_code == 28
57
+ second.response.curl_return_code == 7
58
+ end
59
+
60
+ it "aborts a batch of requests" do
61
+ urls = [
62
+ 'http://localhost:3000',
63
+ 'http://localhost:3001',
64
+ 'http://localhost:3002'
65
+ ]
66
+
67
+ # this will make testing easier
68
+ hydra = Typhoeus::Hydra.new( :max_concurrency => 1 )
69
+ completed = 0
70
+
71
+ 10.times {
72
+ |i|
73
+
74
+ req = Typhoeus::Request.new( urls[ i % urls.size], :params => { :cnt => i } )
75
+ req.on_complete {
76
+ |res|
77
+ completed += 1
78
+ hydra.abort if completed == 5
79
+ }
80
+
81
+ hydra.queue( req )
82
+ }
83
+
84
+ hydra.run
85
+
86
+ # technically this should be '== 6' but I don't trust it...
87
+ completed.should < 10
88
+ end
89
+
90
+ it "has a cache_setter proc" do
91
+ hydra = Typhoeus::Hydra.new
92
+ hydra.cache_setter do |request|
93
+ # @cache.set(request.cache_key, request.response, request.cache_timeout)
94
+ end
95
+ end
96
+
97
+ it "has a cache_getter" do
98
+ hydra = Typhoeus::Hydra.new
99
+ hydra.cache_getter do |request|
100
+ # @cache.get(request.cache_key) rescue nil
101
+ end
102
+ end
103
+
104
+ it "memoizes GET reqeusts" do
105
+ hydra = Typhoeus::Hydra.new
106
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
107
+ second = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
108
+ hydra.queue first
109
+ hydra.queue second
110
+ start_time = Time.now
111
+ hydra.run
112
+ first.response.body.should include("foo")
113
+ first.handled_response.body.should include("foo")
114
+ first.response.should == second.response
115
+ first.handled_response.should == second.handled_response
116
+ (Time.now - start_time).should < 1.2 # if it had run twice it would be ~ 2 seconds
117
+ end
118
+
119
+ it "can turn off memoization for GET requests" do
120
+ hydra = Typhoeus::Hydra.new
121
+ hydra.disable_memoization
122
+ first = Typhoeus::Request.new("http://localhost:3000/foo")
123
+ second = Typhoeus::Request.new("http://localhost:3000/foo")
124
+ hydra.queue first
125
+ hydra.queue second
126
+ hydra.run
127
+ first.response.body.should include("foo")
128
+ first.response.object_id.should_not == second.response.object_id
129
+ end
130
+
131
+ it "pulls GETs from cache" do
132
+ hydra = Typhoeus::Hydra.new
133
+ start_time = Time.now
134
+ hydra.cache_getter do |request|
135
+ @cache.get(request.cache_key) rescue nil
136
+ end
137
+ hydra.cache_setter do |request|
138
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
139
+ end
140
+
141
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
142
+ @cache.set(first.cache_key, :foo, 60)
143
+ hydra.queue first
144
+ hydra.run
145
+ (Time.now - start_time).should < 0.1
146
+ first.response.should == :foo
147
+ end
148
+
149
+ it "sets GET responses to cache when the request has a cache_timeout value" do
150
+ hydra = Typhoeus::Hydra.new
151
+ hydra.cache_getter do |request|
152
+ @cache.get(request.cache_key) rescue nil
153
+ end
154
+ hydra.cache_setter do |request|
155
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
156
+ end
157
+
158
+ first = Typhoeus::Request.new("http://localhost:3000/first", :cache_timeout => 0)
159
+ second = Typhoeus::Request.new("http://localhost:3000/second")
160
+ hydra.queue first
161
+ hydra.queue second
162
+ hydra.run
163
+ first.response.body.should include("first")
164
+ @cache.get(first.cache_key).should == first.response
165
+ @cache.get(second.cache_key).should be_nil
166
+ end
167
+
168
+ it "has a global on_complete" do
169
+ foo = nil
170
+ hydra = Typhoeus::Hydra.new
171
+ hydra.on_complete do |response|
172
+ foo = :called
173
+ end
174
+
175
+ first = Typhoeus::Request.new("http://localhost:3000/first")
176
+ hydra.queue first
177
+ hydra.run
178
+ first.response.body.should include("first")
179
+ foo.should == :called
180
+ end
181
+
182
+ it "has a global on_complete setter" do
183
+ foo = nil
184
+ hydra = Typhoeus::Hydra.new
185
+ proc = Proc.new {|response| foo = :called}
186
+ hydra.on_complete = proc
187
+
188
+ first = Typhoeus::Request.new("http://localhost:3000/first")
189
+ hydra.queue first
190
+ hydra.run
191
+ first.response.body.should include("first")
192
+ foo.should == :called
193
+ end
194
+
195
+ it "should reuse connections from the pool for a host"
196
+
197
+ it "should queue up requests while others are running" do
198
+ hydra = Typhoeus::Hydra.new
199
+
200
+ start_time = Time.now
201
+ @responses = []
202
+
203
+ request = Typhoeus::Request.new("http://localhost:3000/first", :params => {:delay => 1})
204
+ request.on_complete do |response|
205
+ @responses << response
206
+ response.body.should include("first")
207
+ end
208
+
209
+ request.after_complete do |object|
210
+ second_request = Typhoeus::Request.new("http://localhost:3001/second", :params => {:delay => 2})
211
+ second_request.on_complete do |response|
212
+ @responses << response
213
+ response.body.should include("second")
214
+ end
215
+ hydra.queue second_request
216
+ end
217
+ hydra.queue request
218
+
219
+ third_request = Typhoeus::Request.new("http://localhost:3002/third", :params => {:delay => 3})
220
+ third_request.on_complete do |response|
221
+ @responses << response
222
+ response.body.should include("third")
223
+ end
224
+ hydra.queue third_request
225
+
226
+ hydra.run
227
+ @responses.size.should == 3
228
+ (Time.now - start_time).should < 3.3
229
+ end
230
+
231
+ it "should fire and forget" do
232
+ # this test is totally hacky. I have no clue how to make it verify. I just look at the test servers
233
+ # to verify that stuff is running
234
+ hydra = Typhoeus::Hydra.new
235
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
236
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=2")
237
+ hydra.queue first
238
+ hydra.queue second
239
+ hydra.fire_and_forget
240
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=3")
241
+ hydra.queue third
242
+ hydra.fire_and_forget
243
+ sleep 3 # have to do this or future tests may break.
244
+ end
245
+
246
+ it "should take the maximum number of concurrent requests as an argument" do
247
+ hydra = Typhoeus::Hydra.new(:max_concurrency => 2)
248
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
249
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=1")
250
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=1")
251
+ hydra.queue first
252
+ hydra.queue second
253
+ hydra.queue third
254
+
255
+ start_time = Time.now
256
+ hydra.run
257
+ finish_time = Time.now
258
+
259
+ first.response.code.should == 200
260
+ second.response.code.should == 200
261
+ third.response.code.should == 200
262
+ (finish_time - start_time).should > 2.0
263
+ end
264
+
265
+ it "should respect the follow_location option when set on a request" do
266
+ hydra = Typhoeus::Hydra.new
267
+ request = Typhoeus::Request.new("http://localhost:3000/redirect", :follow_location => true)
268
+ hydra.queue request
269
+ hydra.run
270
+
271
+ request.response.code.should == 200
272
+ end
273
+
274
+ it "should pass through the max_redirects option when set on a request" do
275
+ hydra = Typhoeus::Hydra.new
276
+ request = Typhoeus::Request.new("http://localhost:3000/bad_redirect", :max_redirects => 5)
277
+ hydra.queue request
278
+ hydra.run
279
+
280
+ request.response.code.should == 302
281
+ end
282
+ end
283
+
284
+ describe Typhoeus::Hydra::Stubbing do
285
+ shared_examples_for "any stubbable target" do
286
+ before(:each) do
287
+ @on_complete_handler_called = nil
288
+ @request = Typhoeus::Request.new("http://localhost:3000/foo",
289
+ :user_agent => 'test')
290
+ @request.on_complete do |response|
291
+ @on_complete_handler_called = true
292
+ response.code.should == 404
293
+ response.headers.should == "whatever"
294
+ end
295
+ @response = Typhoeus::Response.new(:code => 404,
296
+ :headers => "whatever",
297
+ :body => "not found",
298
+ :time => 0.1)
299
+ end
300
+
301
+ after(:each) do
302
+ @stub_target.clear_stubs
303
+ end
304
+
305
+ it "should provide a stubs accessor" do
306
+ begin
307
+ @stub_target.stubs.should == []
308
+ @stub_target.stubs = [:foo]
309
+ ensure
310
+ @stub_target.clear_stubs
311
+ end
312
+ end
313
+
314
+ it "stubs requests to a specific URI" do
315
+ @stub_target.stub(:get, "http://localhost:3000/foo",
316
+ :headers => { 'user-agent' => 'test'}).
317
+ and_return(@response)
318
+
319
+ @hydra.queue(@request)
320
+ @hydra.run
321
+ @on_complete_handler_called.should be_true
322
+ @response.request.should == @request
323
+ end
324
+
325
+ it "stubs requests to URIs matching a pattern" do
326
+ @stub_target.stub(:get, /foo/,
327
+ :headers => { 'user-agent' => 'test' }).
328
+ and_return(@response)
329
+ @hydra.queue(@request)
330
+ @hydra.run
331
+ @on_complete_handler_called.should be_true
332
+ @response.request.should == @request
333
+ end
334
+
335
+ it "can clear stubs" do
336
+ @stub_target.clear_stubs
337
+ end
338
+
339
+ it "clears out previously queued requests once they are called" do
340
+ @stub_target.stub(:get, "http://localhost:3000/asdf",
341
+ :headers => { 'user-agent' => 'test' }).
342
+ and_return(@response)
343
+
344
+ call_count = 0
345
+ request = Typhoeus::Request.new("http://localhost:3000/asdf", :user_agent => 'test')
346
+ request.on_complete do |response|
347
+ call_count += 1
348
+ end
349
+ @hydra.queue(request)
350
+ @hydra.run
351
+ call_count.should == 1
352
+ @hydra.run
353
+ call_count.should == 1
354
+ end
355
+
356
+ it "calls stubs for requests that are queued up in the on_complete of a first stub" do
357
+ @stub_target.stub(:get, "http://localhost:3000/asdf").and_return(@response)
358
+ @stub_target.stub(:get, "http://localhost:3000/bar").and_return(@response)
359
+
360
+ second_handler_called = false
361
+ request = Typhoeus::Request.new("http://localhost:3000/asdf")
362
+ request.on_complete do |response|
363
+ r = Typhoeus::Request.new("http://localhost:3000/bar")
364
+ r.on_complete do |res|
365
+ second_handler_called = true
366
+ end
367
+ @hydra.queue(r)
368
+ end
369
+ @hydra.queue(request)
370
+ @hydra.run
371
+
372
+ second_handler_called.should be_true
373
+ end
374
+ end
375
+
376
+ describe "global (class-level) stubbing" do
377
+ before(:each) do
378
+ @hydra = Typhoeus::Hydra.new
379
+ @stub_target = Typhoeus::Hydra
380
+ end
381
+
382
+ it_should_behave_like "any stubbable target"
383
+ end
384
+
385
+ describe "instance stubbing" do
386
+ before(:each) do
387
+ @hydra = Typhoeus::Hydra.new
388
+ @stub_target = @hydra
389
+ end
390
+
391
+ it_should_behave_like "any stubbable target"
392
+ end
393
+ end
394
+
395
+ describe Typhoeus::Hydra::Callbacks do
396
+ before(:all) do
397
+ @klass = Typhoeus::Hydra
398
+ end
399
+
400
+ describe "#after_request_before_on_complete" do
401
+ it "should provide a global hook after a request" do
402
+ begin
403
+ http_method = nil
404
+ @klass.after_request_before_on_complete do |request|
405
+ http_method = request.method
406
+ end
407
+
408
+ hydra = @klass.new
409
+ request = Typhoeus::Request.new('http://localhost:3000',
410
+ :method => :get)
411
+ response = Typhoeus::Response.new(:code => 404,
412
+ :headers => "whatever",
413
+ :body => "not found",
414
+ :time => 0.1)
415
+ hydra.stub(:get, 'http://localhost:3000').
416
+ and_return(response)
417
+
418
+ hydra.queue(request)
419
+ hydra.run
420
+
421
+ http_method.should == :get
422
+ ensure
423
+ @klass.clear_global_hooks
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ describe Typhoeus::Hydra::ConnectOptions do
430
+ before(:all) do
431
+ @klass = Typhoeus::Hydra
432
+ end
433
+
434
+ let!(:old_net_connect) { @klass.allow_net_connect }
435
+ let!(:old_ignore_localhost) { @klass.ignore_localhost }
436
+ let(:hydra) { @klass.new }
437
+
438
+ after(:each) do
439
+ @klass.allow_net_connect = old_net_connect
440
+ @klass.ignore_localhost = old_ignore_localhost
441
+ end
442
+
443
+ def request_for(host)
444
+ Typhoeus::Request.new("http://#{host}:3000")
445
+ end
446
+
447
+ describe "#ignore_localhost" do
448
+ context "when set to true" do
449
+ before(:each) { @klass.ignore_localhost = true }
450
+
451
+ [true, false].each do |val|
452
+ it "allows localhost requests when allow_net_connect is #{val}" do
453
+ @klass.allow_net_connect = val
454
+ expect { hydra.queue(request_for('localhost')) }.to_not raise_error
455
+ end
456
+ end
457
+ end
458
+
459
+ context "when set to false" do
460
+ before(:each) { @klass.ignore_localhost = false }
461
+
462
+ it "allows localhost requests when allow_net_connect is true" do
463
+ @klass.allow_net_connect = true
464
+ expect { hydra.queue(request_for('localhost')) }.to_not raise_error
465
+ end
466
+
467
+ it "does not allow localhost requests when allow_net_connect is false" do
468
+ @klass.allow_net_connect = false
469
+ expect { hydra.queue(request_for('localhost')) }.to raise_error(Typhoeus::Hydra::NetConnectNotAllowedError)
470
+ end
471
+ end
472
+ end
473
+
474
+ describe "#ignore_hosts" do
475
+ context 'when allow_net_connect is set to false' do
476
+ before(:each) do
477
+ @klass.ignore_localhost = false
478
+ @klass.allow_net_connect = false
479
+ end
480
+
481
+ Typhoeus::Request::LOCALHOST_ALIASES.each do |disallowed_host|
482
+ ignore_hosts = Typhoeus::Request::LOCALHOST_ALIASES - [disallowed_host]
483
+
484
+ context "when set to #{ignore_hosts.join(' and ')}" do
485
+ before(:each) { @klass.ignore_hosts = ignore_hosts }
486
+
487
+ it "does not allow a request to #{disallowed_host}" do
488
+ expect { hydra.queue(request_for(disallowed_host)) }.to raise_error(Typhoeus::Hydra::NetConnectNotAllowedError)
489
+ end
490
+
491
+ ignore_hosts.each do |host|
492
+ it "allows a request to #{host}" do
493
+ expect { hydra.queue(request_for(host)) }.to_not raise_error
494
+ end
495
+ end
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ describe "#allow_net_connect" do
502
+ it "should be settable" do
503
+ @klass.allow_net_connect = true
504
+ @klass.allow_net_connect.should be_true
505
+ end
506
+
507
+ it "should default to true" do
508
+ @klass.allow_net_connect.should be_true
509
+ end
510
+
511
+ it "should raise an error if we queue a request while its false" do
512
+ @klass.allow_net_connect = false
513
+ @klass.ignore_localhost = false
514
+
515
+ expect {
516
+ hydra.queue(request_for('example.com'))
517
+ }.to raise_error(Typhoeus::Hydra::NetConnectNotAllowedError)
518
+ end
519
+ end
520
+
521
+ describe "#allow_net_connect?" do
522
+ it "should return true by default" do
523
+ @klass.allow_net_connect?.should be_true
524
+ end
525
+ end
526
+ end