typhoeus 0.1.24 → 0.1.25

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.
@@ -130,11 +130,20 @@ module Typhoeus
130
130
 
131
131
  def get_easy_object(request)
132
132
  @running_requests += 1
133
+
133
134
  easy = @easy_pool.pop || Easy.new
135
+
136
+ # Force gzip if we support it.
137
+ headers = {}
138
+ if easy.supports_zlib?
139
+ headers['Accept-Encoding'] = 'gzip'
140
+ end
141
+ headers.merge!(request.headers) if request.headers
142
+
134
143
  easy.url = request.url
135
144
  easy.method = request.method
136
145
  easy.params = request.params if request.method == :post && !request.params.nil?
137
- easy.headers = request.headers if request.headers
146
+ easy.headers = headers
138
147
  easy.request_body = request.body if request.body
139
148
  easy.timeout = request.timeout if request.timeout
140
149
  easy.follow_location = request.follow_location if request.follow_location
@@ -192,6 +201,7 @@ module Typhoeus
192
201
  :headers => easy.response_header,
193
202
  :body => easy.response_body,
194
203
  :time => easy.total_time_taken,
204
+ :effective_url => easy.effective_url,
195
205
  :request => request)
196
206
  end
197
207
  private :response_from_easy
@@ -42,7 +42,7 @@ module Typhoeus
42
42
  end
43
43
 
44
44
  def params_string
45
- params.keys.sort.collect do |k|
45
+ params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |k|
46
46
  value = params[k]
47
47
  if value.is_a? Hash
48
48
  value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
@@ -3,8 +3,9 @@ module Typhoeus
3
3
  attr_accessor :request
4
4
  attr_reader :code, :headers, :body, :time,
5
5
  :requested_url, :requested_remote_method,
6
- :requested_http_method, :start_time
7
-
6
+ :requested_http_method, :start_time,
7
+ :effective_url
8
+
8
9
  def initialize(params = {})
9
10
  @code = params[:code]
10
11
  @headers = params[:headers]
@@ -14,8 +15,9 @@ module Typhoeus
14
15
  @requested_http_method = params[:requested_http_method]
15
16
  @start_time = params[:start_time]
16
17
  @request = params[:request]
18
+ @effective_url = params[:effective_url]
17
19
  end
18
-
20
+
19
21
  def headers_hash
20
22
  headers.split("\n").map {|o| o.strip}.inject({}) do |hash, o|
21
23
  if o.empty?
@@ -30,16 +32,16 @@ module Typhoeus
30
32
  else
31
33
  hash[key] = value
32
34
  end
33
-
35
+
34
36
  hash
35
37
  end
36
38
  end
37
39
  end
38
-
40
+
39
41
  def success?
40
42
  @code >= 200 && @code < 300
41
43
  end
42
-
44
+
43
45
  def modified?
44
46
  @code != 304
45
47
  end
@@ -0,0 +1,20 @@
1
+ module Typhoeus
2
+ class Service
3
+ def initialize(host, port)
4
+ @host = host
5
+ @port = port
6
+ end
7
+
8
+ def get(resource, params)
9
+ end
10
+
11
+ def put(resource, params)
12
+ end
13
+
14
+ def post(resource, params)
15
+ end
16
+
17
+ def delete(resource, params)
18
+ end
19
+ end
20
+ 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>
@@ -1,6 +1,22 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
- describe Typhoeus::Easy do
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!(: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!(: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
+
4
20
  describe "options" do
5
21
  it "should not follow redirects if not instructed to" do
6
22
  e = Typhoeus::Easy.new
@@ -0,0 +1,311 @@
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
+ context "#stub" do
34
+ before do
35
+ @hydra = Typhoeus::Hydra.new
36
+ @on_complete_handler_called = nil
37
+ @request = Typhoeus::Request.new("http://localhost:3000/foo")
38
+ @request.on_complete do |response|
39
+ @on_complete_handler_called = true
40
+ response.code.should == 404
41
+ response.headers.should == "whatever"
42
+ end
43
+ @response = Typhoeus::Response.new(:code => 404, :headers => "whatever", :body => "not found", :time => 0.1)
44
+ end
45
+
46
+ it "stubs requests to a specific URI" do
47
+ @hydra.stub(:get, "http://localhost:3000/foo").and_return(@response)
48
+ @hydra.queue(@request)
49
+ @hydra.run
50
+ @on_complete_handler_called.should be_true
51
+ @response.request.should == @request
52
+ end
53
+
54
+ it "stubs requests to URIs matching a pattern" do
55
+ @hydra.stub(:get, /foo/).and_return(@response)
56
+ @hydra.queue(@request)
57
+ @hydra.run
58
+ @on_complete_handler_called.should be_true
59
+ @response.request.should == @request
60
+ end
61
+
62
+ it "can clear stubs" do
63
+ @hydra.clear_stubs
64
+ end
65
+
66
+ it "clears out previously queued requests once they are called" do
67
+ @hydra.stub(:get, "asdf").and_return(@response)
68
+
69
+ call_count = 0
70
+ request = Typhoeus::Request.new("asdf")
71
+ request.on_complete do |response|
72
+ call_count += 1
73
+ end
74
+ @hydra.queue(request)
75
+ @hydra.run
76
+ call_count.should == 1
77
+ @hydra.run
78
+ call_count.should == 1
79
+ end
80
+
81
+ it "calls stubs for requests that are queued up in the on_complete of a first stub" do
82
+ @hydra.stub(:get, "asdf").and_return(@response)
83
+ @hydra.stub(:get, "bar").and_return(@response)
84
+
85
+ second_handler_called = false
86
+ request = Typhoeus::Request.new("asdf")
87
+ request.on_complete do |response|
88
+ r = Typhoeus::Request.new("bar")
89
+ r.on_complete do |res|
90
+ second_handler_called = true
91
+ end
92
+ @hydra.queue(r)
93
+ end
94
+ @hydra.queue(request)
95
+ @hydra.run
96
+
97
+ second_handler_called.should be_true
98
+ end
99
+
100
+ it "matches a stub only when the HTTP method also matches"
101
+ end
102
+
103
+ it "queues a request" do
104
+ hydra = Typhoeus::Hydra.new
105
+ hydra.queue Typhoeus::Request.new("http://localhost:3000")
106
+ end
107
+
108
+ it "runs a batch of requests" do
109
+ hydra = Typhoeus::Hydra.new
110
+ first = Typhoeus::Request.new("http://localhost:3000/first")
111
+ second = Typhoeus::Request.new("http://localhost:3001/second")
112
+ hydra.queue first
113
+ hydra.queue second
114
+ hydra.run
115
+ first.response.body.should include("first")
116
+ second.response.body.should include("second")
117
+ end
118
+
119
+ it "has a cache_setter proc" do
120
+ hydra = Typhoeus::Hydra.new
121
+ hydra.cache_setter do |request|
122
+ # @cache.set(request.cache_key, request.response, request.cache_timeout)
123
+ end
124
+ end
125
+
126
+ it "has a cache_getter" do
127
+ hydra = Typhoeus::Hydra.new
128
+ hydra.cache_getter do |request|
129
+ # @cache.get(request.cache_key) rescue nil
130
+ end
131
+ end
132
+
133
+ it "memoizes GET reqeusts" do
134
+ hydra = Typhoeus::Hydra.new
135
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
136
+ second = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
137
+ hydra.queue first
138
+ hydra.queue second
139
+ start_time = Time.now
140
+ hydra.run
141
+ first.response.body.should include("foo")
142
+ first.handled_response.body.should include("foo")
143
+ first.response.should == second.response
144
+ first.handled_response.should == second.handled_response
145
+ (Time.now - start_time).should < 1.2 # if it had run twice it would be ~ 2 seconds
146
+ end
147
+
148
+ it "can turn off memoization for GET requests" do
149
+ hydra = Typhoeus::Hydra.new
150
+ hydra.disable_memoization
151
+ first = Typhoeus::Request.new("http://localhost:3000/foo")
152
+ second = Typhoeus::Request.new("http://localhost:3000/foo")
153
+ hydra.queue first
154
+ hydra.queue second
155
+ hydra.run
156
+ first.response.body.should include("foo")
157
+ first.response.object_id.should_not == second.response.object_id
158
+ end
159
+
160
+ it "pulls GETs from cache" do
161
+ hydra = Typhoeus::Hydra.new
162
+ start_time = Time.now
163
+ hydra.cache_getter do |request|
164
+ @cache.get(request.cache_key) rescue nil
165
+ end
166
+ hydra.cache_setter do |request|
167
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
168
+ end
169
+
170
+ first = Typhoeus::Request.new("http://localhost:3000/foo", :params => {:delay => 1})
171
+ @cache.set(first.cache_key, :foo, 60)
172
+ hydra.queue first
173
+ hydra.run
174
+ (Time.now - start_time).should < 0.1
175
+ first.response.should == :foo
176
+ end
177
+
178
+ it "sets GET responses to cache when the request has a cache_timeout value" do
179
+ hydra = Typhoeus::Hydra.new
180
+ hydra.cache_getter do |request|
181
+ @cache.get(request.cache_key) rescue nil
182
+ end
183
+ hydra.cache_setter do |request|
184
+ @cache.set(request.cache_key, request.response, request.cache_timeout)
185
+ end
186
+
187
+ first = Typhoeus::Request.new("http://localhost:3000/first", :cache_timeout => 0)
188
+ second = Typhoeus::Request.new("http://localhost:3000/second")
189
+ hydra.queue first
190
+ hydra.queue second
191
+ hydra.run
192
+ first.response.body.should include("first")
193
+ @cache.get(first.cache_key).should == first.response
194
+ @cache.get(second.cache_key).should be_nil
195
+ end
196
+
197
+ it "has a global on_complete" do
198
+ foo = nil
199
+ hydra = Typhoeus::Hydra.new
200
+ hydra.on_complete do |response|
201
+ foo = :called
202
+ end
203
+
204
+ first = Typhoeus::Request.new("http://localhost:3000/first")
205
+ hydra.queue first
206
+ hydra.run
207
+ first.response.body.should include("first")
208
+ foo.should == :called
209
+ end
210
+
211
+ it "has a global on_complete setter" do
212
+ foo = nil
213
+ hydra = Typhoeus::Hydra.new
214
+ proc = Proc.new {|response| foo = :called}
215
+ hydra.on_complete = proc
216
+
217
+ first = Typhoeus::Request.new("http://localhost:3000/first")
218
+ hydra.queue first
219
+ hydra.run
220
+ first.response.body.should include("first")
221
+ foo.should == :called
222
+ end
223
+
224
+ it "should reuse connections from the pool for a host"
225
+
226
+ it "should queue up requests while others are running" do
227
+ hydra = Typhoeus::Hydra.new
228
+
229
+ start_time = Time.now
230
+ @responses = []
231
+
232
+ request = Typhoeus::Request.new("http://localhost:3000/first", :params => {:delay => 1})
233
+ request.on_complete do |response|
234
+ @responses << response
235
+ response.body.should include("first")
236
+ end
237
+
238
+ request.after_complete do |object|
239
+ second_request = Typhoeus::Request.new("http://localhost:3001/second", :params => {:delay => 2})
240
+ second_request.on_complete do |response|
241
+ @responses << response
242
+ response.body.should include("second")
243
+ end
244
+ hydra.queue second_request
245
+ end
246
+ hydra.queue request
247
+
248
+ third_request = Typhoeus::Request.new("http://localhost:3002/third", :params => {:delay => 3})
249
+ third_request.on_complete do |response|
250
+ @responses << response
251
+ response.body.should include("third")
252
+ end
253
+ hydra.queue third_request
254
+
255
+ hydra.run
256
+ @responses.size.should == 3
257
+ (Time.now - start_time).should < 3.3
258
+ end
259
+
260
+ it "should fire and forget" do
261
+ # this test is totally hacky. I have no clue how to make it verify. I just look at the test servers
262
+ # to verify that stuff is running
263
+ hydra = Typhoeus::Hydra.new
264
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
265
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=2")
266
+ hydra.queue first
267
+ hydra.queue second
268
+ hydra.fire_and_forget
269
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=3")
270
+ hydra.queue third
271
+ hydra.fire_and_forget
272
+ sleep 3 # have to do this or future tests may break.
273
+ end
274
+
275
+ it "should take the maximum number of concurrent requests as an argument" do
276
+ hydra = Typhoeus::Hydra.new(:max_concurrency => 2)
277
+ first = Typhoeus::Request.new("http://localhost:3000/first?delay=1")
278
+ second = Typhoeus::Request.new("http://localhost:3001/second?delay=1")
279
+ third = Typhoeus::Request.new("http://localhost:3002/third?delay=1")
280
+ hydra.queue first
281
+ hydra.queue second
282
+ hydra.queue third
283
+
284
+ start_time = Time.now
285
+ hydra.run
286
+ finish_time = Time.now
287
+
288
+ first.response.code.should == 200
289
+ second.response.code.should == 200
290
+ third.response.code.should == 200
291
+ (finish_time - start_time).should > 2.0
292
+ end
293
+
294
+ it "should respect the follow_location option when set on a request" do
295
+ hydra = Typhoeus::Hydra.new
296
+ request = Typhoeus::Request.new("http://localhost:3000/redirect", :follow_location => true)
297
+ hydra.queue request
298
+ hydra.run
299
+
300
+ request.response.code.should == 200
301
+ end
302
+
303
+ it "should pass through the max_redirects option when set on a request" do
304
+ hydra = Typhoeus::Hydra.new
305
+ request = Typhoeus::Request.new("http://localhost:3000/bad_redirect", :max_redirects => 5)
306
+ hydra.queue request
307
+ hydra.run
308
+
309
+ request.response.code.should == 302
310
+ end
311
+ end