typhoeus 0.1.2

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,108 @@
1
+ module Typhoeus
2
+ class RemoteMethod
3
+ attr_accessor :http_method, :options, :base_uri, :path, :on_success, :on_failure, :cache_ttl
4
+
5
+ def initialize(options = {})
6
+ @http_method = options.delete(:method) || :get
7
+ @options = options
8
+ @base_uri = options.delete(:base_uri)
9
+ @path = options.delete(:path)
10
+ @on_success = options[:on_success]
11
+ @on_failure = options[:on_failure]
12
+ @cache_responses = options.delete(:cache_responses)
13
+ @memoize_responses = options.delete(:memoize_responses) || @cache_responses
14
+ @cache_ttl = @cache_responses == true ? 0 : @cache_responses
15
+ @keys = nil
16
+
17
+ clear_cache
18
+ end
19
+
20
+ def cache_responses?
21
+ @cache_responses
22
+ end
23
+
24
+ def memoize_responses?
25
+ @memoize_responses
26
+ end
27
+
28
+ def args_options_key(args, options)
29
+ "#{args.to_s}+#{options.to_s}"
30
+ end
31
+
32
+ def calling(args, options)
33
+ @called_methods[args_options_key(args, options)] = true
34
+ end
35
+
36
+ def already_called?(args, options)
37
+ @called_methods.has_key? args_options_key(args, options)
38
+ end
39
+
40
+ def add_response_block(block, args, options)
41
+ @response_blocks[args_options_key(args, options)] << block
42
+ end
43
+
44
+ def call_response_blocks(result, args, options)
45
+ key = args_options_key(args, options)
46
+ @response_blocks[key].each {|block| block.call(result)}
47
+ @response_blocks.delete(key)
48
+ @called_methods.delete(key)
49
+ end
50
+
51
+ def clear_cache
52
+ @response_blocks = Hash.new {|h, k| h[k] = []}
53
+ @called_methods = {}
54
+ end
55
+
56
+ def merge_options(new_options)
57
+ merged = options.merge(new_options)
58
+ if options.has_key?(:params) && new_options.has_key?(:params)
59
+ merged[:params] = options[:params].merge(new_options[:params])
60
+ end
61
+ argument_names.each {|a| merged.delete(a)}
62
+ merged.delete(:on_success) if merged[:on_success].nil?
63
+ merged.delete(:on_failure) if merged[:on_failure].nil?
64
+ merged
65
+ end
66
+
67
+ def interpolate_path_with_arguments(args)
68
+ interpolated_path = @path
69
+ argument_names.each do |arg|
70
+ interpolated_path = interpolated_path.gsub(":#{arg}", args[arg].to_s)
71
+ end
72
+ interpolated_path
73
+ end
74
+
75
+ def argument_names
76
+ return @keys if @keys
77
+ pattern, keys = compile(@path)
78
+ @keys = keys.collect {|k| k.to_sym}
79
+ end
80
+
81
+ # rippped from Sinatra. clean up stuff we don't need later
82
+ def compile(path)
83
+ path ||= ""
84
+ keys = []
85
+ if path.respond_to? :to_str
86
+ special_chars = %w{. + ( )}
87
+ pattern =
88
+ path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
89
+ case match
90
+ when "*"
91
+ keys << 'splat'
92
+ "(.*?)"
93
+ when *special_chars
94
+ Regexp.escape(match)
95
+ else
96
+ keys << $2[1..-1]
97
+ "([^/?&#]+)"
98
+ end
99
+ end
100
+ [/^#{pattern}$/, keys]
101
+ elsif path.respond_to? :match
102
+ [path, keys]
103
+ else
104
+ raise TypeError, path
105
+ end
106
+ end
107
+ end
108
+ end
@@ -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,88 @@
1
+ module Typhoeus
2
+ class Request
3
+ attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout
4
+ attr_reader :url
5
+
6
+ def initialize(url, options = {})
7
+ @method = options[:method] || :get
8
+ @params = options[:params]
9
+ @body = options[:body]
10
+ @timeout = options[:timeout]
11
+ @headers = options[:headers] || {}
12
+ @user_agent = options[:user_agent] || Typhoeus::USER_AGENT
13
+ @cache_timeout = options[:cache_timeout]
14
+ @url = @params ? "#{url}?#{params_string}" : url
15
+ @on_complete = nil
16
+ @after_complete = nil
17
+ @handled_response = nil
18
+ end
19
+
20
+ def host
21
+ slash_location = @url.index('/', 8)
22
+ if slash_location
23
+ @url.slice(0, slash_location)
24
+ else
25
+ query_string_location = @url.index('?')
26
+ return query_string_location ? @url.slice(0, query_string_location) : @url
27
+ end
28
+ end
29
+
30
+ def headers
31
+ @headers["User-Agent"] = @user_agent
32
+ @headers
33
+ end
34
+
35
+ def params_string
36
+ params.keys.sort.collect do |k|
37
+ value = params[k]
38
+ if value.is_a? Hash
39
+ value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
40
+ elsif value.is_a? Array
41
+ key = Rack::Utils.escape(k.to_s)
42
+ value.collect { |v| "#{key}=#{Rack::Utils.escape(v.to_s)}" }.join('&')
43
+ else
44
+ "#{Rack::Utils.escape(k.to_s)}=#{Rack::Utils.escape(params[k].to_s)}"
45
+ end
46
+ end.flatten.join("&")
47
+ end
48
+
49
+ def on_complete(&block)
50
+ @on_complete = block
51
+ end
52
+
53
+ def on_complete=(proc)
54
+ @on_complete = proc
55
+ end
56
+
57
+ def after_complete(&block)
58
+ @after_complete = block
59
+ end
60
+
61
+ def after_complete=(proc)
62
+ @after_complete = proc
63
+ end
64
+
65
+ def call_handlers
66
+ if @on_complete
67
+ @handled_response = @on_complete.call(response)
68
+ call_after_complete
69
+ end
70
+ end
71
+
72
+ def call_after_complete
73
+ @after_complete.call(@handled_response) if @after_complete
74
+ end
75
+
76
+ def handled_response=(val)
77
+ @handled_response = val
78
+ end
79
+
80
+ def handled_response
81
+ @handled_response || response
82
+ end
83
+
84
+ def cache_key
85
+ Digest::SHA1.hexdigest(url)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,19 @@
1
+ module Typhoeus
2
+ class Response
3
+ attr_accessor :request
4
+ attr_reader :code, :headers, :body, :time,
5
+ :requested_url, :requested_remote_method,
6
+ :requested_http_method, :start_time
7
+
8
+ def initialize(params = {})
9
+ @code = params[:code]
10
+ @headers = params[:headers]
11
+ @body = params[:body]
12
+ @time = params[:time]
13
+ @requested_url = params[:requested_url]
14
+ @requested_http_method = params[:requested_http_method]
15
+ @start_time = params[:start_time]
16
+ @request = params[:request]
17
+ end
18
+ end
19
+ 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