typhoeus 0.1.6 → 0.1.28

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.
@@ -1,8 +1,34 @@
1
1
  module Typhoeus
2
2
  class Request
3
- attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout
3
+ attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout, :follow_location, :max_redirects, :proxy, :disable_ssl_peer_verification, :ssl_cert, :ssl_cert_type, :ssl_key, :ssl_key_type, :ssl_key_password, :ssl_cacert, :ssl_capath
4
+
5
+
4
6
  attr_reader :url
5
-
7
+
8
+ # Initialize a new Request
9
+ #
10
+ # Options:
11
+ # * +url+ : Endpoint (URL) of the request
12
+ # * +options+ : A hash containing options among :
13
+ # ** +:method+ : :get (default) / :post / :put
14
+ # ** +:params+ : params as a Hash
15
+ # ** +:body+
16
+ # ** +:timeout+ : timeout (ms)
17
+ # ** +:headers+ : headers as Hash
18
+ # ** +:user_agent+ : user agent (string)
19
+ # ** +:cache_timeout+ : cache timeout (ms)
20
+ # ** +:follow_location
21
+ # ** +:max_redirects
22
+ # ** +:proxy
23
+ # ** +:disable_ssl_peer_verification
24
+ # ** +:ssl_cert
25
+ # ** +:ssl_cert_type
26
+ # ** +:ssl_key
27
+ # ** +:ssl_key_type
28
+ # ** +:ssl_key_password
29
+ # ** +:ssl_cacert
30
+ # ** +:ssl_capath
31
+ #
6
32
  def initialize(url, options = {})
7
33
  @method = options[:method] || :get
8
34
  @params = options[:params]
@@ -11,12 +37,28 @@ module Typhoeus
11
37
  @headers = options[:headers] || {}
12
38
  @user_agent = options[:user_agent] || Typhoeus::USER_AGENT
13
39
  @cache_timeout = options[:cache_timeout]
14
- @url = @params ? "#{url}?#{params_string}" : url
40
+ @follow_location = options[:follow_location]
41
+ @max_redirects = options[:max_redirects]
42
+ @proxy = options[:proxy]
43
+ @disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
44
+ @ssl_cert = options[:ssl_cert]
45
+ @ssl_cert_type = options[:ssl_cert_type]
46
+ @ssl_key = options[:ssl_key]
47
+ @ssl_key_type = options[:ssl_key_type]
48
+ @ssl_key_password = options[:ssl_key_password]
49
+ @ssl_cacert = options[:ssl_cacert]
50
+ @ssl_capath = options[:ssl_capath]
51
+
52
+ if @method == :post
53
+ @url = url
54
+ else
55
+ @url = @params ? "#{url}?#{params_string}" : url
56
+ end
15
57
  @on_complete = nil
16
58
  @after_complete = nil
17
59
  @handled_response = nil
18
60
  end
19
-
61
+
20
62
  def host
21
63
  slash_location = @url.index('/', 8)
22
64
  if slash_location
@@ -26,14 +68,14 @@ module Typhoeus
26
68
  return query_string_location ? @url.slice(0, query_string_location) : @url
27
69
  end
28
70
  end
29
-
71
+
30
72
  def headers
31
73
  @headers["User-Agent"] = @user_agent
32
74
  @headers
33
75
  end
34
-
76
+
35
77
  def params_string
36
- params.keys.sort.collect do |k|
78
+ params.keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |k|
37
79
  value = params[k]
38
80
  if value.is_a? Hash
39
81
  value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
@@ -45,67 +87,71 @@ module Typhoeus
45
87
  end
46
88
  end.flatten.join("&")
47
89
  end
48
-
90
+
49
91
  def on_complete(&block)
50
92
  @on_complete = block
51
93
  end
52
-
94
+
53
95
  def on_complete=(proc)
54
96
  @on_complete = proc
55
97
  end
56
-
98
+
57
99
  def after_complete(&block)
58
100
  @after_complete = block
59
101
  end
60
-
102
+
61
103
  def after_complete=(proc)
62
104
  @after_complete = proc
63
105
  end
64
-
106
+
65
107
  def call_handlers
66
108
  if @on_complete
67
109
  @handled_response = @on_complete.call(response)
68
110
  call_after_complete
69
111
  end
70
112
  end
71
-
113
+
72
114
  def call_after_complete
73
115
  @after_complete.call(@handled_response) if @after_complete
74
116
  end
75
-
117
+
76
118
  def handled_response=(val)
77
119
  @handled_response = val
78
120
  end
79
-
121
+
80
122
  def handled_response
81
123
  @handled_response || response
82
124
  end
83
-
125
+
84
126
  def cache_key
85
127
  Digest::SHA1.hexdigest(url)
86
128
  end
87
-
129
+
88
130
  def self.run(url, params)
89
131
  r = new(url, params)
90
132
  Typhoeus::Hydra.hydra.queue r
91
133
  Typhoeus::Hydra.hydra.run
92
134
  r.response
93
135
  end
94
-
95
- def self.get(url, params)
136
+
137
+ def self.get(url, params = {})
96
138
  run(url, params.merge(:method => :get))
97
139
  end
98
-
99
- def self.post(url, params)
140
+
141
+ def self.post(url, params = {})
100
142
  run(url, params.merge(:method => :post))
101
143
  end
102
-
103
- def self.put(url, params)
144
+
145
+ def self.put(url, params = {})
104
146
  run(url, params.merge(:method => :put))
105
147
  end
106
-
107
- def self.delete(url, params)
148
+
149
+ def self.delete(url, params = {})
108
150
  run(url, params.merge(:method => :delete))
109
151
  end
152
+
153
+ def self.head(url, params = {})
154
+ run(url, params.merge(:method => :head))
155
+ end
110
156
  end
111
- end
157
+ end
@@ -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,6 +15,35 @@ 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]
19
+ end
20
+
21
+ def headers_hash
22
+ headers.split("\n").map {|o| o.strip}.inject({}) do |hash, o|
23
+ if o.empty?
24
+ hash
25
+ else
26
+ i = o.index(":") || o.size
27
+ key = o.slice(0, i)
28
+ value = o.slice(i + 1, o.size)
29
+ value = value.strip unless value.nil?
30
+ if hash.has_key? key
31
+ hash[key] = [hash[key], value].flatten
32
+ else
33
+ hash[key] = value
34
+ end
35
+
36
+ hash
37
+ end
38
+ end
39
+ end
40
+
41
+ def success?
42
+ @code >= 200 && @code < 300
43
+ end
44
+
45
+ def modified?
46
+ @code != 304
17
47
  end
18
48
  end
19
49
  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
data/lib/typhoeus.rb CHANGED
@@ -14,23 +14,23 @@ require 'typhoeus/request'
14
14
  require 'typhoeus/hydra'
15
15
 
16
16
  module Typhoeus
17
- VERSION = "0.1.6"
18
-
17
+ VERSION = File.read(File.dirname(__FILE__) + "/../VERSION").chomp
18
+
19
19
  def self.easy_object_pool
20
20
  @easy_objects ||= []
21
21
  end
22
-
22
+
23
23
  def self.init_easy_object_pool
24
24
  20.times do
25
25
  easy_object_pool << Typhoeus::Easy.new
26
26
  end
27
27
  end
28
-
28
+
29
29
  def self.release_easy_object(easy)
30
30
  easy.reset
31
31
  easy_object_pool << easy
32
32
  end
33
-
33
+
34
34
  def self.get_easy_object
35
35
  if easy_object_pool.empty?
36
36
  Typhoeus::Easy.new
@@ -38,12 +38,12 @@ module Typhoeus
38
38
  easy_object_pool.pop
39
39
  end
40
40
  end
41
-
41
+
42
42
  def self.add_easy_request(easy_object)
43
43
  Thread.current[:curl_multi] ||= Typhoeus::Multi.new
44
44
  Thread.current[:curl_multi].add(easy_object)
45
45
  end
46
-
46
+
47
47
  def self.perform_easy_requests
48
48
  multi = Thread.current[:curl_multi]
49
49
  start_time = Time.now
@@ -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>
data/spec/servers/app.rb CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env ruby
1
2
  require 'rubygems'
2
3
  require 'sinatra'
3
4
  require 'json'
@@ -16,11 +17,46 @@ get '/fail_forever' do
16
17
  error 500, "oh noes!"
17
18
  end
18
19
 
20
+ get '/redirect' do
21
+ redirect '/'
22
+ end
23
+
24
+ get '/bad_redirect' do
25
+ redirect '/bad_redirect'
26
+ end
27
+
28
+ get '/auth_basic/:username/:password' do
29
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
30
+ # Check that we've got a basic auth, and that it's credentials match the ones
31
+ # provided in the request
32
+ if @auth.provided? && @auth.basic? && @auth.credentials == [ params[:username], params[:password] ]
33
+ # auth is valid - confirm it
34
+ true
35
+ else
36
+ # invalid auth - request the authentication
37
+ response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth")
38
+ throw(:halt, [401, "Not authorized\n"])
39
+ end
40
+ end
41
+
42
+ get '/auth_ntlm' do
43
+ # we're just checking for the existence if NTLM auth header here. It's validation
44
+ # is too troublesome and really doesn't bother is much, it's up to libcurl to make
45
+ # it valid
46
+ is_ntlm_auth = /^NTLM/ =~ request.env['HTTP_AUTHORIZATION']
47
+ true if is_ntlm_auth
48
+ throw(:halt, [401, "Not authorized\n"]) if !is_ntlm_auth
49
+ end
50
+
19
51
  get '/**' do
20
52
  sleep params["delay"].to_i if params.has_key?("delay")
21
53
  request.env.merge!(:body => request.body.read).to_json
22
54
  end
23
55
 
56
+ head '/**' do
57
+ sleep params["delay"].to_i if params.has_key?("delay")
58
+ end
59
+
24
60
  put '/**' do
25
61
  puts request.inspect
26
62
  request.env.merge!(:body => request.body.read).to_json
@@ -34,4 +70,4 @@ end
34
70
  delete '/**' do
35
71
  puts request.inspect
36
72
  request.env.merge!(:body => request.body.read).to_json
37
- end
73
+ end
@@ -1,9 +1,51 @@
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!(: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
+
4
20
  describe "options" do
5
- it "should allow for following redirects"
6
- it "should allow you to set the user agent"
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
+
7
49
  it "should provide a timeout in milliseconds" do
8
50
  e = Typhoeus::Easy.new
9
51
  e.url = "http://localhost:3001"
@@ -13,6 +55,55 @@ describe Typhoeus::Easy do
13
55
  # this doesn't work on a mac for some reason
14
56
  # e.timed_out?.should == true
15
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
16
107
  end
17
108
 
18
109
  describe "get" do
@@ -26,6 +117,24 @@ describe Typhoeus::Easy do
26
117
  end
27
118
  end
28
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
+
29
138
  describe "start_time" do
30
139
  it "should be get/settable" do
31
140
  time = Time.now