arachni-typhoeus 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 (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