typhoeus_curly 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 =~ /^__|object_id/ }
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,117 @@
1
+ module Typhoeus
2
+ class Request
3
+ attr_accessor :method, :params, :body, :headers, :timeout, :user_agent, :response, :cache_timeout, :follow_location, :max_redirects
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
+ @follow_location = options[:follow_location]
15
+ @max_redirects = options[:max_redirects]
16
+ if @method == :post
17
+ @url = url
18
+ else
19
+ @url = @params ? "#{url}?#{params_string}" : url
20
+ end
21
+ @on_complete = nil
22
+ @after_complete = nil
23
+ @handled_response = nil
24
+ end
25
+
26
+ def host
27
+ slash_location = @url.index('/', 8)
28
+ if slash_location
29
+ @url.slice(0, slash_location)
30
+ else
31
+ query_string_location = @url.index('?')
32
+ return query_string_location ? @url.slice(0, query_string_location) : @url
33
+ end
34
+ end
35
+
36
+ def headers
37
+ @headers["User-Agent"] = @user_agent
38
+ @headers
39
+ end
40
+
41
+ def params_string
42
+ params.keys.sort.collect do |k|
43
+ value = params[k]
44
+ if value.is_a? Hash
45
+ value.keys.collect {|sk| Rack::Utils.escape("#{k}[#{sk}]") + "=" + Rack::Utils.escape(value[sk].to_s)}
46
+ elsif value.is_a? Array
47
+ key = Rack::Utils.escape(k.to_s)
48
+ value.collect { |v| "#{key}=#{Rack::Utils.escape(v.to_s)}" }.join('&')
49
+ else
50
+ "#{Rack::Utils.escape(k.to_s)}=#{Rack::Utils.escape(params[k].to_s)}"
51
+ end
52
+ end.flatten.join("&")
53
+ end
54
+
55
+ def on_complete(&block)
56
+ @on_complete = block
57
+ end
58
+
59
+ def on_complete=(proc)
60
+ @on_complete = proc
61
+ end
62
+
63
+ def after_complete(&block)
64
+ @after_complete = block
65
+ end
66
+
67
+ def after_complete=(proc)
68
+ @after_complete = proc
69
+ end
70
+
71
+ def call_handlers
72
+ if @on_complete
73
+ @handled_response = @on_complete.call(response)
74
+ call_after_complete
75
+ end
76
+ end
77
+
78
+ def call_after_complete
79
+ @after_complete.call(@handled_response) if @after_complete
80
+ end
81
+
82
+ def handled_response=(val)
83
+ @handled_response = val
84
+ end
85
+
86
+ def handled_response
87
+ @handled_response || response
88
+ end
89
+
90
+ def cache_key
91
+ Digest::SHA1.hexdigest(url)
92
+ end
93
+
94
+ def self.run(url, params)
95
+ r = new(url, params)
96
+ Typhoeus::Hydra.hydra.queue r
97
+ Typhoeus::Hydra.hydra.run
98
+ r.response
99
+ end
100
+
101
+ def self.get(url, params = {})
102
+ run(url, params.merge(:method => :get))
103
+ end
104
+
105
+ def self.post(url, params = {})
106
+ run(url, params.merge(:method => :post))
107
+ end
108
+
109
+ def self.put(url, params = {})
110
+ run(url, params.merge(:method => :put))
111
+ end
112
+
113
+ def self.delete(url, params = {})
114
+ run(url, params.merge(:method => :delete))
115
+ end
116
+ end
117
+ 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,69 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ require 'json'
5
+
6
+ @@fail_count = 0
7
+ get '/fail/:number' do
8
+ if @@fail_count >= params[:number].to_i
9
+ "ok"
10
+ else
11
+ @@fail_count += 1
12
+ error 500, "oh noes!"
13
+ end
14
+ end
15
+
16
+ get '/fail_forever' do
17
+ error 500, "oh noes!"
18
+ end
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
+
51
+ get '/**' do
52
+ sleep params["delay"].to_i if params.has_key?("delay")
53
+ request.env.merge!(:body => request.body.read).to_json
54
+ end
55
+
56
+ put '/**' do
57
+ puts request.inspect
58
+ request.env.merge!(:body => request.body.read).to_json
59
+ end
60
+
61
+ post '/**' do
62
+ puts request.inspect
63
+ request.env.merge!(:body => request.body.read).to_json
64
+ end
65
+
66
+ delete '/**' do
67
+ puts request.inspect
68
+ request.env.merge!(:body => request.body.read).to_json
69
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --diff
2
+ --color
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require 'json'
3
+ require "spec"
4
+
5
+ # gem install redgreen for colored test output
6
+ begin require "redgreen" unless ENV['TM_CURRENT_LINE']; rescue LoadError; end
7
+
8
+ path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
9
+ $LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
10
+
11
+ require "lib/typhoeus"
@@ -0,0 +1,202 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Easy do
4
+ describe "options" do
5
+ it "should not follow redirects if not instructed to" do
6
+ e = Typhoeus::Easy.new
7
+ e.url = "http://localhost:3001/redirect"
8
+ e.method = :get
9
+ e.perform
10
+ e.response_code.should == 302
11
+ end
12
+
13
+ it "should allow for following redirects" do
14
+ e = Typhoeus::Easy.new
15
+ e.url = "http://localhost:3001/redirect"
16
+ e.method = :get
17
+ e.follow_location = true
18
+ e.perform
19
+ e.response_code.should == 200
20
+ JSON.parse(e.response_body)["REQUEST_METHOD"].should == "GET"
21
+ end
22
+
23
+ it "should allow you to set the user agent" do
24
+ easy = Typhoeus::Easy.new
25
+ easy.url = "http://localhost:3002"
26
+ easy.method = :get
27
+ easy.user_agent = "myapp"
28
+ easy.perform
29
+ easy.response_code.should == 200
30
+ JSON.parse(easy.response_body)["HTTP_USER_AGENT"].should == "myapp"
31
+ end
32
+
33
+ it "should provide a timeout in milliseconds" do
34
+ e = Typhoeus::Easy.new
35
+ e.url = "http://localhost:3001"
36
+ e.method = :get
37
+ e.timeout = 50
38
+ e.perform
39
+ # this doesn't work on a mac for some reason
40
+ # e.timed_out?.should == true
41
+ end
42
+
43
+ it "should allow the setting of the max redirects to follow" do
44
+ e = Typhoeus::Easy.new
45
+ e.url = "http://localhost:3001/redirect"
46
+ e.method = :get
47
+ e.follow_location = true
48
+ e.max_redirects = 5
49
+ e.perform
50
+ e.response_code.should == 200
51
+ end
52
+
53
+ it "should handle our bad redirect action, provided we've set max_redirects properly" do
54
+ e = Typhoeus::Easy.new
55
+ e.url = "http://localhost:3001/bad_redirect"
56
+ e.method = :get
57
+ e.follow_location = true
58
+ e.max_redirects = 5
59
+ e.perform
60
+ e.response_code.should == 302
61
+ end
62
+ end
63
+
64
+ describe "authentication" do
65
+ it "should allow to set username and password" do
66
+ e = Typhoeus::Easy.new
67
+ username, password = 'foo', 'bar'
68
+ e.auth = { :username => username, :password => password }
69
+ e.url = "http://localhost:3001/auth_basic/#{username}/#{password}"
70
+ e.method = :get
71
+ e.perform
72
+ e.response_code.should == 200
73
+ end
74
+
75
+ it "should allow to query auth methods support by the server" do
76
+ e = Typhoeus::Easy.new
77
+ e.url = "http://localhost:3001/auth_basic/foo/bar"
78
+ e.method = :get
79
+ e.perform
80
+ e.auth_methods.should == Typhoeus::Easy::AUTH_TYPES[:CURLAUTH_BASIC]
81
+ end
82
+
83
+ it "should allow to set authentication method" do
84
+ e = Typhoeus::Easy.new
85
+ e.auth = { :username => 'username', :password => 'password', :method => Typhoeus::Easy::AUTH_TYPES[:CURLAUTH_NTLM] }
86
+ e.url = "http://localhost:3001/auth_ntlm"
87
+ e.method = :get
88
+ e.perform
89
+ e.response_code.should == 200
90
+ end
91
+ end
92
+
93
+ describe "get" do
94
+ it "should perform a get" do
95
+ easy = Typhoeus::Easy.new
96
+ easy.url = "http://localhost:3002"
97
+ easy.method = :get
98
+ easy.perform
99
+ easy.response_code.should == 200
100
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "GET"
101
+ end
102
+ end
103
+
104
+ describe "start_time" do
105
+ it "should be get/settable" do
106
+ time = Time.now
107
+ easy = Typhoeus::Easy.new
108
+ easy.start_time.should be_nil
109
+ easy.start_time = time
110
+ easy.start_time.should == time
111
+ end
112
+ end
113
+
114
+ describe "params=" do
115
+ it "should handle arrays of params" do
116
+ easy = Typhoeus::Easy.new
117
+ easy.url = "http://localhost:3002/index.html"
118
+ easy.method = :get
119
+ easy.request_body = "this is a body!"
120
+ easy.params = {
121
+ :foo => 'bar',
122
+ :username => ['dbalatero', 'dbalatero2']
123
+ }
124
+
125
+ easy.url.should =~ /\?.*username=dbalatero&username=dbalatero2/
126
+ end
127
+ end
128
+
129
+
130
+ describe "put" do
131
+ it "should perform a put" do
132
+ easy = Typhoeus::Easy.new
133
+ easy.url = "http://localhost:3002"
134
+ easy.method = :put
135
+ easy.perform
136
+ easy.response_code.should == 200
137
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "PUT"
138
+ end
139
+
140
+ it "should send a request body" do
141
+ easy = Typhoeus::Easy.new
142
+ easy.url = "http://localhost:3002"
143
+ easy.method = :put
144
+ easy.request_body = "this is a body!"
145
+ easy.perform
146
+ easy.response_code.should == 200
147
+ easy.response_body.should include("this is a body!")
148
+ end
149
+ end
150
+
151
+ describe "post" do
152
+ it "should perform a post" do
153
+ easy = Typhoeus::Easy.new
154
+ easy.url = "http://localhost:3002"
155
+ easy.method = :post
156
+ easy.perform
157
+ easy.response_code.should == 200
158
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "POST"
159
+ end
160
+
161
+ it "should send a request body" do
162
+ easy = Typhoeus::Easy.new
163
+ easy.url = "http://localhost:3002"
164
+ easy.method = :post
165
+ easy.request_body = "this is a body!"
166
+ easy.perform
167
+ easy.response_code.should == 200
168
+ easy.response_body.should include("this is a body!")
169
+ end
170
+
171
+ it "should handle params" do
172
+ easy = Typhoeus::Easy.new
173
+ easy.url = "http://localhost:3002"
174
+ easy.method = :post
175
+ easy.params = {:foo => "bar"}
176
+ easy.perform
177
+ easy.response_code.should == 200
178
+ easy.response_body.should include("foo=bar")
179
+ end
180
+ end
181
+
182
+ describe "delete" do
183
+ it "should perform a delete" do
184
+ easy = Typhoeus::Easy.new
185
+ easy.url = "http://localhost:3002"
186
+ easy.method = :delete
187
+ easy.perform
188
+ easy.response_code.should == 200
189
+ JSON.parse(easy.response_body)["REQUEST_METHOD"].should == "DELETE"
190
+ end
191
+
192
+ it "should send a request body" do
193
+ easy = Typhoeus::Easy.new
194
+ easy.url = "http://localhost:3002"
195
+ easy.method = :delete
196
+ easy.request_body = "this is a body!"
197
+ easy.perform
198
+ easy.response_code.should == 200
199
+ easy.response_body.should include("this is a body!")
200
+ end
201
+ end
202
+ end