fblee-typhoeus 0.1.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGELOG.markdown +27 -0
  3. data/README.textile +333 -0
  4. data/Rakefile +39 -0
  5. data/VERSION +1 -0
  6. data/benchmarks/profile.rb +25 -0
  7. data/benchmarks/vs_nethttp.rb +35 -0
  8. data/examples/twitter.rb +21 -0
  9. data/ext/typhoeus/.gitignore +5 -0
  10. data/ext/typhoeus/Makefile +157 -0
  11. data/ext/typhoeus/extconf.rb +65 -0
  12. data/ext/typhoeus/native.c +11 -0
  13. data/ext/typhoeus/native.h +21 -0
  14. data/ext/typhoeus/typhoeus_easy.c +207 -0
  15. data/ext/typhoeus/typhoeus_easy.h +19 -0
  16. data/ext/typhoeus/typhoeus_multi.c +225 -0
  17. data/ext/typhoeus/typhoeus_multi.h +16 -0
  18. data/lib/typhoeus/.gitignore +1 -0
  19. data/lib/typhoeus/easy.rb +348 -0
  20. data/lib/typhoeus/filter.rb +28 -0
  21. data/lib/typhoeus/hydra.rb +243 -0
  22. data/lib/typhoeus/multi.rb +35 -0
  23. data/lib/typhoeus/remote.rb +306 -0
  24. data/lib/typhoeus/remote_method.rb +108 -0
  25. data/lib/typhoeus/remote_proxy_object.rb +48 -0
  26. data/lib/typhoeus/request.rb +172 -0
  27. data/lib/typhoeus/response.rb +49 -0
  28. data/lib/typhoeus/service.rb +20 -0
  29. data/lib/typhoeus.rb +55 -0
  30. data/profilers/valgrind.rb +24 -0
  31. data/spec/fixtures/result_set.xml +60 -0
  32. data/spec/servers/app.rb +84 -0
  33. data/spec/spec.opts +2 -0
  34. data/spec/spec_helper.rb +11 -0
  35. data/spec/typhoeus/easy_spec.rb +249 -0
  36. data/spec/typhoeus/filter_spec.rb +35 -0
  37. data/spec/typhoeus/hydra_spec.rb +311 -0
  38. data/spec/typhoeus/multi_spec.rb +74 -0
  39. data/spec/typhoeus/remote_method_spec.rb +141 -0
  40. data/spec/typhoeus/remote_proxy_object_spec.rb +65 -0
  41. data/spec/typhoeus/remote_spec.rb +695 -0
  42. data/spec/typhoeus/request_spec.rb +195 -0
  43. data/spec/typhoeus/response_spec.rb +63 -0
  44. data/typhoeus.gemspec +113 -0
  45. metadata +188 -0
data/lib/typhoeus.rb ADDED
@@ -0,0 +1,55 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
2
+
3
+ require 'rack/utils'
4
+ require 'digest/sha2'
5
+ require 'typhoeus/easy'
6
+ require 'typhoeus/multi'
7
+ require 'typhoeus/native'
8
+ require 'typhoeus/filter'
9
+ require 'typhoeus/remote_method'
10
+ require 'typhoeus/remote'
11
+ require 'typhoeus/remote_proxy_object'
12
+ require 'typhoeus/response'
13
+ require 'typhoeus/request'
14
+ require 'typhoeus/hydra'
15
+
16
+ module Typhoeus
17
+ VERSION = File.read(File.dirname(__FILE__) + "/../VERSION").chomp
18
+
19
+ def self.easy_object_pool
20
+ @easy_objects ||= []
21
+ end
22
+
23
+ def self.init_easy_object_pool
24
+ 20.times do
25
+ easy_object_pool << Typhoeus::Easy.new
26
+ end
27
+ end
28
+
29
+ def self.release_easy_object(easy)
30
+ easy.reset
31
+ easy_object_pool << easy
32
+ end
33
+
34
+ def self.get_easy_object
35
+ if easy_object_pool.empty?
36
+ Typhoeus::Easy.new
37
+ else
38
+ easy_object_pool.pop
39
+ end
40
+ end
41
+
42
+ def self.add_easy_request(easy_object)
43
+ Thread.current[:curl_multi] ||= Typhoeus::Multi.new
44
+ Thread.current[:curl_multi].add(easy_object)
45
+ end
46
+
47
+ def self.perform_easy_requests
48
+ multi = Thread.current[:curl_multi]
49
+ start_time = Time.now
50
+ multi.easy_handles.each do |easy|
51
+ easy.start_time = start_time
52
+ end
53
+ multi.perform
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # go to ext/typhoeus and run ruby extconf.rb && make before running
3
+ # this.
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/../ext")
6
+ require File.dirname(__FILE__) + "/../lib/typhoeus"
7
+
8
+ klass = Class.new { include Typhoeus }
9
+
10
+ loops = ENV['LOOPS'].to_i
11
+ url = ARGV.first || (raise "requires URL!")
12
+
13
+ loops.times do |i|
14
+ puts "On loop #{i}" if i % 10 == 0
15
+ results = []
16
+ 5.times do
17
+ results << klass.get(url)
18
+ end
19
+
20
+ # fire requests
21
+ results[0].code
22
+ end
23
+
24
+ puts "Ran #{loops} loops on #{url}!"
@@ -0,0 +1,60 @@
1
+ <result_set>
2
+ <ttl>20</ttl>
3
+ <result>
4
+ <id>1</id>
5
+ <name>hello</name>
6
+ <description>
7
+ this is a long description for a text field of some kind.
8
+ this is a long description for a text field of some kind.
9
+ this is a long description for a text field of some kind.
10
+ this is a long description for a text field of some kind.
11
+ this is a long description for a text field of some kind.
12
+ this is a long description for a text field of some kind.
13
+ this is a long description for a text field of some kind.
14
+ this is a long description for a text field of some kind.
15
+ this is a long description for a text field of some kind.
16
+ this is a long description for a text field of some kind.
17
+ this is a long description for a text field of some kind.
18
+ this is a long description for a text field of some kind.
19
+ this is a long description for a text field of some kind.
20
+ </description>
21
+ </result>
22
+ <result>
23
+ <id>2</id>
24
+ <name>hello</name>
25
+ <description>
26
+ this is a long description for a text field of some kind.
27
+ this is a long description for a text field of some kind.
28
+ this is a long description for a text field of some kind.
29
+ this is a long description for a text field of some kind.
30
+ this is a long description for a text field of some kind.
31
+ this is a long description for a text field of some kind.
32
+ this is a long description for a text field of some kind.
33
+ this is a long description for a text field of some kind.
34
+ this is a long description for a text field of some kind.
35
+ this is a long description for a text field of some kind.
36
+ this is a long description for a text field of some kind.
37
+ this is a long description for a text field of some kind.
38
+ this is a long description for a text field of some kind.
39
+ </description>
40
+ </result>
41
+ <result>
42
+ <id>3</id>
43
+ <name>hello</name>
44
+ <description>
45
+ this is a long description for a text field of some kind.
46
+ this is a long description for a text field of some kind.
47
+ this is a long description for a text field of some kind.
48
+ this is a long description for a text field of some kind.
49
+ this is a long description for a text field of some kind.
50
+ this is a long description for a text field of some kind.
51
+ this is a long description for a text field of some kind.
52
+ this is a long description for a text field of some kind.
53
+ this is a long description for a text field of some kind.
54
+ this is a long description for a text field of some kind.
55
+ this is a long description for a text field of some kind.
56
+ this is a long description for a text field of some kind.
57
+ this is a long description for a text field of some kind.
58
+ </description>
59
+ </result>
60
+ </result_set>
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ require 'json'
5
+ require 'zlib'
6
+
7
+ @@fail_count = 0
8
+ get '/fail/:number' do
9
+ if @@fail_count >= params[:number].to_i
10
+ "ok"
11
+ else
12
+ @@fail_count += 1
13
+ error 500, "oh noes!"
14
+ end
15
+ end
16
+
17
+ get '/fail_forever' do
18
+ error 500, "oh noes!"
19
+ end
20
+
21
+ get '/redirect' do
22
+ redirect '/'
23
+ end
24
+
25
+ get '/bad_redirect' do
26
+ redirect '/bad_redirect'
27
+ end
28
+
29
+ get '/auth_basic/:username/:password' do
30
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
31
+ # Check that we've got a basic auth, and that it's credentials match the ones
32
+ # provided in the request
33
+ if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
34
+ # auth is valid - confirm it
35
+ true
36
+ else
37
+ # invalid auth - request the authentication
38
+ response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
39
+ throw(:halt, [401, "Not authorized\n"])
40
+ end
41
+ end
42
+
43
+ get '/auth_ntlm' do
44
+ # we're just checking for the existence if NTLM auth header here. It's validation
45
+ # is too troublesome and really doesn't bother is much, it's up to libcurl to make
46
+ # it valid
47
+ response['WWW-Authenticate'] = 'NTLM'
48
+ is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
49
+ true if is_ntlm_auth
50
+ throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
51
+ end
52
+
53
+ get '/gzipped' do
54
+ req_env = request.env.to_json
55
+ z = Zlib::Deflate.new
56
+ gzipped_env = z.deflate(req_env, Zlib::FINISH)
57
+ z.close
58
+ response['Content-Encoding'] = 'gzip'
59
+ gzipped_env
60
+ end
61
+
62
+ get '/**' do
63
+ sleep params["delay"].to_i if params.has_key?("delay")
64
+ request.env.merge!(:body => request.body.read).to_json
65
+ end
66
+
67
+ head '/**' do
68
+ sleep params["delay"].to_i if params.has_key?("delay")
69
+ end
70
+
71
+ put '/**' do
72
+ puts request.inspect
73
+ request.env.merge!(:body => request.body.read).to_json
74
+ end
75
+
76
+ post '/**' do
77
+ puts request.inspect
78
+ request.env.merge!(:body => request.body.read).to_json
79
+ end
80
+
81
+ delete '/**' do
82
+ puts request.inspect
83
+ request.env.merge!(:body => request.body.read).to_json
84
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --diff
2
+ --color
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require 'json'
3
+ require "spec"
4
+
5
+ # gem install redgreen for colored test output
6
+ begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
7
+
8
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
9
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
10
+
11
+ require "lib/typhoeus"
@@ -0,0 +1,249 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Easy do
4
+ describe "#supports_zlib" do
5
+ before(:each) do
6
+ @easy = Typhoeus::Easy.new
7
+ end
8
+
9
+ it "should return true if the version string has zlib" do
10
+ @easy.stub!(:curl_version).and_return("libcurl/7.20.0 OpenSSL/0.9.8l zlib/1.2.3 libidn/1.16")
11
+ @easy.supports_zlib?.should be_true
12
+ end
13
+
14
+ it "should return false if the version string doesn't have zlib" do
15
+ @easy.stub!(:curl_version).and_return("libcurl/7.20.0 OpenSSL/0.9.8l libidn/1.16")
16
+ @easy.supports_zlib?.should be_false
17
+ end
18
+ end
19
+
20
+ describe "options" do
21
+ it "should not follow redirects if not instructed to" do
22
+ e = Typhoeus::Easy.new
23
+ e.url = "http://localhost:3001/redirect"
24
+ e.method = :get
25
+ e.perform
26
+ e.response_code.should == 302
27
+ end
28
+
29
+ it "should allow for following redirects" do
30
+ e = Typhoeus::Easy.new
31
+ e.url = "http://localhost:3001/redirect"
32
+ e.method = :get
33
+ e.follow_location = true
34
+ e.perform
35
+ e.response_code.should == 200
36
+ JSON.parse(e.response_body)["REQUEST_METHOD"].should == "GET"
37
+ end
38
+
39
+ it "should allow you to set the user agent" do
40
+ easy = Typhoeus::Easy.new
41
+ easy.url = "http://localhost:3002"
42
+ easy.method = :get
43
+ easy.user_agent = "myapp"
44
+ easy.perform
45
+ easy.response_code.should == 200
46
+ JSON.parse(easy.response_body)["HTTP_USER_AGENT"].should == "myapp"
47
+ end
48
+
49
+ it "should provide a timeout in milliseconds" do
50
+ e = Typhoeus::Easy.new
51
+ e.url = "http://localhost:3001"
52
+ e.method = :get
53
+ e.timeout = 50
54
+ e.perform
55
+ # this doesn't work on a mac for some reason
56
+ # e.timed_out?.should == true
57
+ end
58
+
59
+ it "should allow the setting of the max redirects to follow" do
60
+ e = Typhoeus::Easy.new
61
+ e.url = "http://localhost:3001/redirect"
62
+ e.method = :get
63
+ e.follow_location = true
64
+ e.max_redirects = 5
65
+ e.perform
66
+ e.response_code.should == 200
67
+ end
68
+
69
+ it "should handle our bad redirect action, provided we've set max_redirects properly" do
70
+ e = Typhoeus::Easy.new
71
+ e.url = "http://localhost:3001/bad_redirect"
72
+ e.method = :get
73
+ e.follow_location = true
74
+ e.max_redirects = 5
75
+ e.perform
76
+ e.response_code.should == 302
77
+ end
78
+ end
79
+
80
+ describe "authentication" do
81
+ it "should allow to set username and password" do
82
+ e = Typhoeus::Easy.new
83
+ username, password = 'foo', 'bar'
84
+ e.auth = { :username => username, :password => password }
85
+ e.url = "http://localhost:3001/auth_basic/#{username}/#{password}"
86
+ e.method = :get
87
+ e.perform
88
+ e.response_code.should == 200
89
+ end
90
+
91
+ it "should allow to query auth methods support by the server" do
92
+ e = Typhoeus::Easy.new
93
+ e.url = "http://localhost:3001/auth_basic/foo/bar"
94
+ e.method = :get
95
+ e.perform
96
+ e.auth_methods.should == Typhoeus::Easy::AUTH_TYPES[:CURLAUTH_BASIC]
97
+ end
98
+
99
+ it "should allow to set authentication method" do
100
+ e = Typhoeus::Easy.new
101
+ e.auth = { :username => 'username', :password => 'password', :method => Typhoeus::Easy::AUTH_TYPES[:CURLAUTH_NTLM] }
102
+ e.url = "http://localhost:3001/auth_ntlm"
103
+ e.method = :get
104
+ e.perform
105
+ e.response_code.should == 200
106
+ end
107
+ end
108
+
109
+ describe "get" do
110
+ it "should perform a get" do
111
+ easy = Typhoeus::Easy.new
112
+ easy.url = "http://localhost:3002"
113
+ easy.method = :get
114
+ easy.perform
115
+ easy.response_code.should == 200
116
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "GET"
117
+ end
118
+ end
119
+
120
+ describe "purge" do
121
+ it "should set custom request to purge" do
122
+ easy = Typhoeus::Easy.new
123
+ easy.should_receive(:set_option).with(Typhoeus::Easy::OPTION_VALUES[:CURLOPT_CUSTOMREQUEST], "PURGE").once
124
+ easy.method = :purge
125
+ end
126
+ end
127
+
128
+ describe "head" do
129
+ it "should perform a head" do
130
+ easy = Typhoeus::Easy.new
131
+ easy.url = "http://localhost:3002"
132
+ easy.method = :head
133
+ easy.perform
134
+ easy.response_code.should == 200
135
+ end
136
+ end
137
+
138
+ describe "start_time" do
139
+ it "should be get/settable" do
140
+ time = Time.now
141
+ easy = Typhoeus::Easy.new
142
+ easy.start_time.should be_nil
143
+ easy.start_time = time
144
+ easy.start_time.should == time
145
+ end
146
+ end
147
+
148
+ describe "params=" do
149
+ it "should handle arrays of params" do
150
+ easy = Typhoeus::Easy.new
151
+ easy.url = "http://localhost:3002/index.html"
152
+ easy.method = :get
153
+ easy.request_body = "this is a body!"
154
+ easy.params = {
155
+ :foo => 'bar',
156
+ :username => ['dbalatero', 'dbalatero2']
157
+ }
158
+
159
+ easy.url.should =~ /\?.*username=dbalatero&username=dbalatero2/
160
+ end
161
+ end
162
+
163
+
164
+ describe "put" do
165
+ it "should perform a put" do
166
+ easy = Typhoeus::Easy.new
167
+ easy.url = "http://localhost:3002"
168
+ easy.method = :put
169
+ easy.perform
170
+ easy.response_code.should == 200
171
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "PUT"
172
+ end
173
+
174
+ it "should send a request body" do
175
+ easy = Typhoeus::Easy.new
176
+ easy.url = "http://localhost:3002"
177
+ easy.method = :put
178
+ easy.request_body = "this is a body!"
179
+ easy.perform
180
+ easy.response_code.should == 200
181
+ easy.response_body.should include("this is a body!")
182
+ end
183
+ end
184
+
185
+ describe "post" do
186
+ it "should perform a post" do
187
+ easy = Typhoeus::Easy.new
188
+ easy.url = "http://localhost:3002"
189
+ easy.method = :post
190
+ easy.perform
191
+ easy.response_code.should == 200
192
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "POST"
193
+ end
194
+
195
+ it "should send a request body" do
196
+ easy = Typhoeus::Easy.new
197
+ easy.url = "http://localhost:3002"
198
+ easy.method = :post
199
+ easy.request_body = "this is a body!"
200
+ easy.perform
201
+ easy.response_code.should == 200
202
+ easy.response_body.should include("this is a body!")
203
+ end
204
+
205
+ it "should handle params" do
206
+ easy = Typhoeus::Easy.new
207
+ easy.url = "http://localhost:3002"
208
+ easy.method = :post
209
+ easy.params = {:foo => "bar"}
210
+ easy.perform
211
+ easy.response_code.should == 200
212
+ easy.response_body.should include("foo=bar")
213
+ end
214
+ end
215
+
216
+ describe "delete" do
217
+ it "should perform a delete" do
218
+ easy = Typhoeus::Easy.new
219
+ easy.url = "http://localhost:3002"
220
+ easy.method = :delete
221
+ easy.perform
222
+ easy.response_code.should == 200
223
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "DELETE"
224
+ end
225
+
226
+ it "should send a request body" do
227
+ easy = Typhoeus::Easy.new
228
+ easy.url = "http://localhost:3002"
229
+ easy.method = :delete
230
+ easy.request_body = "this is a body!"
231
+ easy.perform
232
+ easy.response_code.should == 200
233
+ easy.response_body.should include("this is a body!")
234
+ end
235
+ end
236
+
237
+ describe "encoding/compression support" do
238
+
239
+ it "should send valid encoding headers and decode the response" do
240
+ easy = Typhoeus::Easy.new
241
+ easy.url = "http://localhost:3002/gzipped"
242
+ easy.method = :get
243
+ easy.perform
244
+ easy.response_code.should == 200
245
+ JSON.parse(easy.response_body)["HTTP_ACCEPT_ENCODING"].should == "deflate, gzip"
246
+ end
247
+
248
+ end
249
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Filter do
4
+ it "should take a method name and optionally take options" do
5
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
6
+ filter = Typhoeus::Filter.new(:bar)
7
+ end
8
+
9
+ describe "#apply_filter?" do
10
+ it "should return true for any method when :only and :except aren't specified" do
11
+ filter = Typhoeus::Filter.new(:bar)
12
+ filter.apply_filter?(:asdf).should be_true
13
+ end
14
+
15
+ it "should return true if a method is in only" do
16
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
17
+ filter.apply_filter?(:foo).should be_true
18
+ end
19
+
20
+ it "should return false if a method isn't in only" do
21
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
22
+ filter.apply_filter?(:bar).should be_false
23
+ end
24
+
25
+ it "should return true if a method isn't in except" do
26
+ filter = Typhoeus::Filter.new(:bar, :except => :foo)
27
+ filter.apply_filter?(:bar).should be_true
28
+ end
29
+
30
+ it "should return false if a method is in except" do
31
+ filter = Typhoeus::Filter.new(:bar, :except => :foo)
32
+ filter.apply_filter?(:foo).should be_false
33
+ end
34
+ end
35
+ end