typhoeus_curly 0.1.14

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 =~ /^__|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