typhoeus 0.1.24 → 0.1.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -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