dbalatero-typhoeus 0.0.20

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.
@@ -0,0 +1,48 @@
1
+ module Typhoeus
2
+ class RemoteProxyObject
3
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
4
+
5
+ def initialize(clear_memoized_store_proc, easy, options = {})
6
+ @clear_memoized_store_proc = clear_memoized_store_proc
7
+ @easy = easy
8
+ @success = options[:on_success]
9
+ @failure = options[:on_failure]
10
+ @cache = options.delete(:cache)
11
+ @cache_key = options.delete(:cache_key)
12
+ @timeout = options.delete(:cache_timeout)
13
+ Typhoeus.add_easy_request(@easy)
14
+ end
15
+
16
+ def method_missing(sym, *args, &block)
17
+ unless @proxied_object
18
+ if @cache && @cache_key
19
+ @proxied_object = @cache.get(@cache_key) rescue nil
20
+ end
21
+
22
+ unless @proxied_object
23
+ Typhoeus.perform_easy_requests
24
+ response = Response.new(:code => @easy.response_code,
25
+ :headers => @easy.response_header,
26
+ :body => @easy.response_body,
27
+ :time => @easy.total_time_taken,
28
+ :requested_url => @easy.url,
29
+ :requested_http_method => @easy.method,
30
+ :start_time => @easy.start_time)
31
+ if @easy.response_code >= 200 && @easy.response_code < 300
32
+ Typhoeus.release_easy_object(@easy)
33
+ @proxied_object = @success.nil? ? response : @success.call(response)
34
+
35
+ if @cache && @cache_key
36
+ @cache.set(@cache_key, @proxied_object, @timeout)
37
+ end
38
+ else
39
+ @proxied_object = @failure.nil? ? response : @failure.call(response)
40
+ end
41
+ @clear_memoized_store_proc.call
42
+ end
43
+ end
44
+
45
+ @proxied_object.__send__(sym, *args, &block)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ module Typhoeus
2
+ class Response
3
+ attr_reader :code, :headers, :body, :time,
4
+ :requested_url, :requested_remote_method,
5
+ :requested_http_method, :start_time
6
+
7
+ def initialize(params = {})
8
+ @code = params[:code]
9
+ @headers = params[:headers]
10
+ @body = params[:body]
11
+ @time = params[:time]
12
+ @requested_url = params[:requested_url]
13
+ @requested_http_method = params[:requested_http_method]
14
+ @start_time = params[:start_time]
15
+ end
16
+ end
17
+ end
data/lib/typhoeus.rb ADDED
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) unless $LOAD_PATH.include?(File.dirname(__FILE__))
2
+
3
+ require 'cgi'
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
+
14
+ module Typhoeus
15
+ VERSION = "0.0.20"
16
+
17
+ def self.easy_object_pool
18
+ @easy_objects ||= []
19
+ end
20
+
21
+ def self.init_easy_object_pool
22
+ 20.times do
23
+ easy_object_pool << Typhoeus::Easy.new
24
+ end
25
+ end
26
+
27
+ def self.release_easy_object(easy)
28
+ easy.reset
29
+ easy_object_pool << easy
30
+ end
31
+
32
+ def self.get_easy_object
33
+ if easy_object_pool.empty?
34
+ Typhoeus::Easy.new
35
+ else
36
+ easy_object_pool.pop
37
+ end
38
+ end
39
+
40
+ def self.add_easy_request(easy_object)
41
+ Thread.current[:curl_multi] ||= Typhoeus::Multi.new
42
+ Thread.current[:curl_multi].add(easy_object)
43
+ end
44
+
45
+ def self.perform_easy_requests
46
+ multi = Thread.current[:curl_multi]
47
+ start_time = Time.now
48
+ multi.easy_handles.each do |easy|
49
+ easy.start_time = start_time
50
+ end
51
+ multi.perform
52
+ end
53
+ end
@@ -0,0 +1,66 @@
1
+ # this server simply accepts requests and blocks for a passed in interval before returning a passed in reqeust value to
2
+ # the client
3
+ require 'rubygems'
4
+ require 'eventmachine'
5
+ require 'evma_httpserver'
6
+
7
+ class DelayFixtureServer < EventMachine::Connection
8
+ include EventMachine::HttpServer
9
+
10
+ def process_http_request
11
+ EventMachine.stop if ENV["PATH_INFO"] == "/die"
12
+ puts "got a request #{ENV['PATH_INFO']}"
13
+ resp = EventMachine::DelegatedHttpResponse.new( self )
14
+
15
+ # Block which fulfills the request
16
+ operation = proc do
17
+ sleep DelayFixtureServer.response_delay
18
+
19
+ resp.status = 200
20
+ resp.content = "whatever"
21
+ end
22
+
23
+ # Callback block to execute once the request is fulfilled
24
+ callback = proc do |res|
25
+ resp.send_response
26
+ end
27
+
28
+ # Let the thread pool (20 Ruby threads) handle request
29
+ EM.defer(operation, callback)
30
+ end
31
+
32
+ def self.response_fixture
33
+ @response_fixture ||= ""
34
+ end
35
+
36
+ def self.response_fixture=(val)
37
+ @response_fixture = val
38
+ end
39
+
40
+ def self.response_delay
41
+ @response_delay ||= 0
42
+ end
43
+
44
+ def self.response_delay=(val)
45
+ @response_delay = val
46
+ end
47
+
48
+ def self.reponse_number
49
+ @response_number
50
+ end
51
+
52
+ def self.response_number=(val)
53
+ @response_number = val
54
+ end
55
+ end
56
+
57
+ port = (ARGV[0] || 3000).to_i
58
+
59
+ DelayFixtureServer.response_delay = 0.5
60
+ DelayFixtureServer.response_number = 0
61
+ #DelayFixtureServer.response_fixture = File.read(File.dirname(__FILE__) + "/../fixtures/result_set.xml")
62
+
63
+ EventMachine::run {
64
+ EventMachine.epoll
65
+ EventMachine::start_server("0.0.0.0", port, DelayFixtureServer)
66
+ }
@@ -0,0 +1,51 @@
1
+ # this server simply is for testing out the different http methods. it echoes back the passed in info
2
+ require 'rubygems'
3
+ require 'eventmachine'
4
+ require 'evma_httpserver'
5
+
6
+ class MethodServer < EventMachine::Connection
7
+ include EventMachine::HttpServer
8
+
9
+ def process_http_request
10
+ EventMachine.stop if ENV["PATH_INFO"] == "/die"
11
+
12
+ resp = EventMachine::DelegatedHttpResponse.new( self )
13
+
14
+ # Block which fulfills the request
15
+ operation = proc do
16
+ sleep MethodServer.sleep_time
17
+ resp.status = 200
18
+ resp.content = request_params + "\n#{@http_post_content}"
19
+ end
20
+
21
+ # Callback block to execute once the request is fulfilled
22
+ callback = proc do |res|
23
+ resp.send_response
24
+ end
25
+
26
+ # Let the thread pool (20 Ruby threads) handle request
27
+ EM.defer(operation, callback)
28
+ end
29
+
30
+ def request_params
31
+ %w( PATH_INFO QUERY_STRING HTTP_COOKIE IF_NONE_MATCH CONTENT_TYPE REQUEST_METHOD REQUEST_URI ).collect do |param|
32
+ "#{param}=#{ENV[param]}"
33
+ end.join("\n")
34
+ end
35
+
36
+ def self.sleep_time=(val)
37
+ @sleep_time = val
38
+ end
39
+
40
+ def self.sleep_time
41
+ @sleep_time || 0
42
+ end
43
+ end
44
+ #
45
+ # port = (ARGV[0] || 3000).to_i
46
+ # #Process.fork do
47
+ # EventMachine::run {
48
+ # EventMachine.epoll
49
+ # EventMachine::start_server("0.0.0.0", port, MethodServer)
50
+ # }
51
+ # #end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --diff
2
+ --color
@@ -0,0 +1,29 @@
1
+ require "rubygems"
2
+ require "spec"
3
+
4
+ # gem install redgreen for colored test output
5
+ begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
6
+
7
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
8
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
9
+
10
+ require "lib/typhoeus"
11
+
12
+ # local servers for running tests
13
+ require File.dirname(__FILE__) + "/servers/method_server.rb"
14
+
15
+ def start_method_server(port, sleep = 0)
16
+ MethodServer.sleep_time = sleep
17
+ pid = Process.fork do
18
+ EventMachine::run {
19
+ EventMachine.epoll
20
+ EventMachine::start_server("0.0.0.0", port, MethodServer)
21
+ }
22
+ end
23
+ sleep 0.2
24
+ pid
25
+ end
26
+
27
+ def stop_method_server(pid)
28
+ Process.kill("HUP", pid)
29
+ end
@@ -0,0 +1,148 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Easy do
4
+ before(:all) do
5
+ @pid = start_method_server(3002)
6
+ end
7
+
8
+ after(:all) do
9
+ stop_method_server(@pid)
10
+ end
11
+
12
+ describe "options" do
13
+ it "should allow for following redirects"
14
+ it "should allow you to set the user agent"
15
+ it "should provide a timeout in milliseconds" do
16
+ pid = start_method_server(3001, 5)
17
+ e = Typhoeus::Easy.new
18
+ e.url = "http://localhost:3001"
19
+ e.method = :get
20
+ e.timeout = 50
21
+ e.perform
22
+ puts e.response_code
23
+ puts e.total_time_taken
24
+ # e.timed_out?.should == true
25
+ stop_method_server(pid)
26
+ end
27
+ end
28
+
29
+ describe "get" do
30
+ it "should perform a get" do
31
+ easy = Typhoeus::Easy.new
32
+ easy.url = "http://localhost:3002"
33
+ easy.method = :get
34
+ easy.perform
35
+ easy.response_code.should == 200
36
+ easy.response_body.should include("REQUEST_METHOD=GET")
37
+ end
38
+
39
+ it "should send a request body" do
40
+ easy = Typhoeus::Easy.new
41
+ easy.url = "http://localhost:3002"
42
+ easy.method = :get
43
+ easy.request_body = "this is a body!"
44
+ easy.perform
45
+ easy.response_code.should == 200
46
+ easy.response_body.should include("this is a body!")
47
+ end
48
+ end
49
+
50
+ describe "start_time" do
51
+ it "should be get/settable" do
52
+ time = Time.now
53
+ easy = Typhoeus::Easy.new
54
+ easy.start_time.should be_nil
55
+ easy.start_time = time
56
+ easy.start_time.should == time
57
+ end
58
+ end
59
+
60
+ describe "params=" do
61
+ it "should handle arrays of params" do
62
+ easy = Typhoeus::Easy.new
63
+ easy.url = "http://localhost:3002/index.html"
64
+ easy.method = :get
65
+ easy.request_body = "this is a body!"
66
+ easy.params = {
67
+ :foo => 'bar',
68
+ :username => ['dbalatero', 'dbalatero2']
69
+ }
70
+
71
+ easy.url.should =~ /\?.*username=dbalatero&username=dbalatero2/
72
+ end
73
+ end
74
+
75
+
76
+ describe "put" do
77
+ it "should perform a put" do
78
+ easy = Typhoeus::Easy.new
79
+ easy.url = "http://localhost:3002"
80
+ easy.method = :put
81
+ easy.perform
82
+ easy.response_code.should == 200
83
+ easy.response_body.should include("REQUEST_METHOD=PUT")
84
+ end
85
+
86
+ it "should send a request body" do
87
+ easy = Typhoeus::Easy.new
88
+ easy.url = "http://localhost:3002"
89
+ easy.method = :put
90
+ easy.request_body = "this is a body!"
91
+ easy.perform
92
+ easy.response_code.should == 200
93
+ easy.response_body.should include("this is a body!")
94
+ end
95
+ end
96
+
97
+ describe "post" do
98
+ it "should perform a post" do
99
+ easy = Typhoeus::Easy.new
100
+ easy.url = "http://localhost:3002"
101
+ easy.method = :post
102
+ easy.perform
103
+ easy.response_code.should == 200
104
+ easy.response_body.should include("REQUEST_METHOD=POST")
105
+ end
106
+
107
+ it "should send a request body" do
108
+ easy = Typhoeus::Easy.new
109
+ easy.url = "http://localhost:3002"
110
+ easy.method = :post
111
+ easy.request_body = "this is a body!"
112
+ easy.perform
113
+ easy.response_code.should == 200
114
+ easy.response_body.should include("this is a body!")
115
+ end
116
+
117
+ it "should handle params" do
118
+ easy = Typhoeus::Easy.new
119
+ easy.url = "http://localhost:3002"
120
+ easy.method = :post
121
+ easy.params = {:foo => "bar"}
122
+ easy.perform
123
+ easy.response_code.should == 200
124
+ easy.response_body.should include("foo=bar")
125
+ end
126
+ end
127
+
128
+ describe "delete" do
129
+ it "should perform a delete" do
130
+ easy = Typhoeus::Easy.new
131
+ easy.url = "http://localhost:3002"
132
+ easy.method = :delete
133
+ easy.perform
134
+ easy.response_code.should == 200
135
+ easy.response_body.should include("REQUEST_METHOD=DELETE")
136
+ end
137
+
138
+ it "should send a request body" do
139
+ easy = Typhoeus::Easy.new
140
+ easy.url = "http://localhost:3002"
141
+ easy.method = :delete
142
+ easy.request_body = "this is a body!"
143
+ easy.perform
144
+ easy.response_code.should == 200
145
+ easy.response_body.should include("this is a body!")
146
+ end
147
+ end
148
+ 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
@@ -0,0 +1,82 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Easy do
4
+ before(:all) do
5
+ @pid = start_method_server(3002)
6
+ end
7
+
8
+ after(:all) do
9
+ stop_method_server(@pid)
10
+ end
11
+
12
+ it "should save easy handles that get added" do
13
+ multi = Typhoeus::Multi.new
14
+ easy = Typhoeus::Easy.new
15
+ easy.url = "http://localhost:3002"
16
+ easy.method = :get
17
+
18
+ multi.add(easy)
19
+ multi.easy_handles.should == [easy]
20
+ multi.perform
21
+ multi.easy_handles.should == []
22
+ end
23
+
24
+ it "should be reusable" do
25
+ easy = Typhoeus::Easy.new
26
+ easy.url = "http://localhost:3002"
27
+ easy.method = :get
28
+
29
+ multi = Typhoeus::Multi.new
30
+ multi.add(easy)
31
+ multi.perform
32
+ easy.response_code.should == 200
33
+ easy.response_body.should include("METHOD=GET")
34
+
35
+ e2 = Typhoeus::Easy.new
36
+ e2.url = "http://localhost:3002"
37
+ e2.method = :post
38
+ multi.add(e2)
39
+ multi.perform
40
+
41
+ e2.response_code.should == 200
42
+ e2.response_body.should include("METHOD=POST")
43
+ end
44
+
45
+ it "should perform easy handles added after the first one runs" do
46
+ easy = Typhoeus::Easy.new
47
+ easy.url = "http://localhost:3002"
48
+ easy.method = :get
49
+ multi = Typhoeus::Multi.new
50
+ multi.add(easy)
51
+
52
+ e2 = Typhoeus::Easy.new
53
+ e2.url = "http://localhost:3002"
54
+ e2.method = :post
55
+ easy.on_success do |e|
56
+ multi.add(e2)
57
+ end
58
+
59
+ multi.perform
60
+ easy.response_code.should == 200
61
+ easy.response_body.should include("METHOD=GET")
62
+ e2.response_code.should == 200
63
+ e2.response_body.should include("METHOD=POST")
64
+ end
65
+
66
+ # it "should do multiple gets" do
67
+ # multi = Typhoeus::Multi.new
68
+ #
69
+ # handles = []
70
+ # 5.times do |i|
71
+ # easy = Typhoeus::Easy.new
72
+ # easy.url = "http://localhost:3002"
73
+ # easy.method = :get
74
+ # easy.on_success {|e| puts "get #{i} succeeded"}
75
+ # easy.on_failure {|e| puts "get #{i} failed with #{e.response_code}"}
76
+ # handles << easy
77
+ # multi.add(easy)
78
+ # end
79
+ #
80
+ # multi.perform
81
+ # end
82
+ end
@@ -0,0 +1,141 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::RemoteMethod do
4
+ it "should take options" do
5
+ Typhoeus::RemoteMethod.new(:body => "foo")
6
+ end
7
+
8
+ describe "http_method" do
9
+ it "should return the http method" do
10
+ m = Typhoeus::RemoteMethod.new(:method => :put)
11
+ m.http_method.should == :put
12
+ end
13
+
14
+ it "should default to :get" do
15
+ m = Typhoeus::RemoteMethod.new
16
+ m.http_method.should == :get
17
+ end
18
+ end
19
+
20
+ it "should return the options" do
21
+ m = Typhoeus::RemoteMethod.new(:body => "foo")
22
+ m.options.should == {:body => "foo"}
23
+ end
24
+
25
+ it "should pull uri out of the options hash" do
26
+ m = Typhoeus::RemoteMethod.new(:base_uri => "http://pauldix.net")
27
+ m.base_uri.should == "http://pauldix.net"
28
+ m.options.should_not have_key(:base_uri)
29
+ end
30
+
31
+ describe "on_success" do
32
+ it "should return the block" do
33
+ m = Typhoeus::RemoteMethod.new(:on_success => lambda {:foo})
34
+ m.on_success.call.should == :foo
35
+ end
36
+ end
37
+
38
+ describe "on_failure" do
39
+ it "should return method name" do
40
+ m = Typhoeus::RemoteMethod.new(:on_failure => lambda {:bar})
41
+ m.on_failure.call.should == :bar
42
+ end
43
+ end
44
+
45
+ describe "path" do
46
+ it "should pull path out of the options hash" do
47
+ m = Typhoeus::RemoteMethod.new(:path => "foo")
48
+ m.path.should == "foo"
49
+ m.options.should_not have_key(:path)
50
+ end
51
+
52
+ it "should output argument names from the symbols in the path" do
53
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/:post_id/comments/:comment_id")
54
+ m.argument_names.should == [:post_id, :comment_id]
55
+ end
56
+
57
+ it "should output an empty string when there are no arguments in path" do
58
+ m = Typhoeus::RemoteMethod.new(:path => "/default.html")
59
+ m.argument_names.should == []
60
+ end
61
+
62
+ it "should output and empty string when there is no path specified" do
63
+ m = Typhoeus::RemoteMethod.new
64
+ m.argument_names.should == []
65
+ end
66
+
67
+ it "should interpolate a path with arguments" do
68
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/:post_id/comments/:comment_id")
69
+ m.interpolate_path_with_arguments(:post_id => 1, :comment_id => "asdf").should == "/posts/1/comments/asdf"
70
+ end
71
+
72
+ it "should provide the path when interpolated called and there is nothing to interpolate" do
73
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/123")
74
+ m.interpolate_path_with_arguments(:foo => :bar).should == "/posts/123"
75
+ end
76
+ end
77
+
78
+ describe "#merge_options" do
79
+ it "should keep the passed in options first" do
80
+ m = Typhoeus::RemoteMethod.new("User-Agent" => "whatev", :foo => :bar)
81
+ m.merge_options({"User-Agent" => "http-machine"}).should == {"User-Agent" => "http-machine", :foo => :bar}
82
+ end
83
+
84
+ it "should combine the params" do
85
+ m = Typhoeus::RemoteMethod.new(:foo => :bar, :params => {:id => :asdf})
86
+ m.merge_options({:params => {:desc => :jkl}}).should == {:foo => :bar, :params => {:id => :asdf, :desc => :jkl}}
87
+ end
88
+ end
89
+
90
+ describe "memoize_reponses" do
91
+ before(:each) do
92
+ @m = Typhoeus::RemoteMethod.new(:memoize_responses => true)
93
+ @args = ["foo", "bar"]
94
+ @options = {:asdf => {:jkl => :bar}}
95
+ end
96
+
97
+ it "should store if responses should be memoized" do
98
+ @m.memoize_responses?.should be_true
99
+ @m.options.should == {}
100
+ end
101
+
102
+ it "should tell when a method is already called" do
103
+ @m.already_called?(@args, @options).should be_false
104
+ @m.calling(@args, @options)
105
+ @m.already_called?(@args, @options).should be_true
106
+ @m.already_called?([], {}).should be_false
107
+ end
108
+
109
+ it "should call response blocks and clear the methods that have been called" do
110
+ response_block_called = mock('response_block')
111
+ response_block_called.should_receive(:call).exactly(1).times
112
+
113
+ @m.add_response_block(lambda {|res| res.should == :foo; response_block_called.call}, @args, @options)
114
+ @m.calling(@args, @options)
115
+ @m.call_response_blocks(:foo, @args, @options)
116
+ @m.already_called?(@args, @options).should be_false
117
+ @m.call_response_blocks(:asdf, @args, @options) #just to make sure it doesn't actually call that block again
118
+ end
119
+ end
120
+
121
+ describe "cache_reponses" do
122
+ before(:each) do
123
+ @m = Typhoeus::RemoteMethod.new(:cache_responses => true)
124
+ @args = ["foo", "bar"]
125
+ @options = {:asdf => {:jkl => :bar}}
126
+ end
127
+
128
+ it "should store if responses should be cached" do
129
+ @m.cache_responses?.should be_true
130
+ @m.options.should == {}
131
+ end
132
+
133
+ it "should force memoization if caching is enabled" do
134
+ @m.memoize_responses?.should be_true
135
+ end
136
+
137
+ it "should store cache ttl" do
138
+ Typhoeus::RemoteMethod.new(:cache_responses => 30).cache_ttl.should == 30
139
+ end
140
+ end
141
+ end