fblee-typhoeus 0.1.31

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