itrigga-net_helper 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,13 +5,17 @@ source "http://rubygems.org"
5
5
 
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
+ gem "rest-client"
9
+ gem "hpricot"
10
+ gem 'itrigga-core_ext', :require=>'core_ext'
11
+
8
12
  group :development do
9
13
  gem "hpricot"
10
14
  gem "bundler", "~> 1.0.0"
11
15
  gem "jeweler", "~> 1.6.4"
16
+ gem "rspec", "1.3.0"
17
+ gem "rspec-rails", "1.3.2"
12
18
  gem "rcov", ">= 0"
13
- gem 'rspec'
14
19
  gem 'json_pure'
15
- gem 'itrigga-core_ext', ">= 0"
16
20
  gem "fastercsv", ">= 1.5.4"
17
21
  end
data/README.rdoc CHANGED
@@ -1,6 +1,41 @@
1
1
  = net_helper
2
2
 
3
- Description goes here.
3
+ Provides a wrapper around HTTP GET requests. Uses Net::HTTP (under RestClient) by default, but will use Typhoeus if available
4
+ Will return the raw body (unparsed) for each request.
5
+
6
+ == Usage
7
+ Rails2
8
+ gem.config 'itrigga-net_helper', :lib => 'net_helper'
9
+
10
+ Rails3 (bundler)
11
+ gem 'itrigga-net_helper', :require => 'net_helper'
12
+
13
+ === Basic example
14
+ Itrigga::NetHelper.get :url => "http://www.google.com"
15
+
16
+ === Adding params
17
+ Itrigga::NetHelper.get :url => "http://www.google.com", :params => {:p1 => "123"}
18
+ Gives url http://www.google.com?p1=123
19
+
20
+ === Variables
21
+
22
+ timeout - in seconds (default 5)
23
+ retries_on_timeout - how many times to try again (default 5)
24
+ max_redirects - number of redirects to follow (default 3)
25
+
26
+ The SystemTimer gem will be used for timeout is available.
27
+
28
+ Itrigga::NetHelper.get :url => "http://www.google.com", :timeout => 300, :max_redirects => 1
29
+
30
+ == Using Typhoeus
31
+ The Typhoeus gem needs to be installed (see https://github.com/dbalatero/typhoeus)
32
+ Rails2 - gem.config 'typhoeus'
33
+ Rails3 - gem 'typhoeus'
34
+
35
+ To enable for all requests set the constant ITNH_HTTP_ENGINE = "Typhoeus"
36
+ To use on a per request basis
37
+
38
+ Itrigga::NetHelper.get :url => "http://www.google.com", :http_engine => "Typhoeus"
4
39
 
5
40
  == Contributing to net_helper
6
41
 
data/Rakefile CHANGED
@@ -26,22 +26,27 @@ Jeweler::Tasks.new do |gem|
26
26
  end
27
27
  Jeweler::RubygemsDotOrgTasks.new
28
28
 
29
- require 'rake/testtask'
30
- Rake::TestTask.new(:test) do |test|
31
- test.libs << 'lib' << 'test'
32
- test.pattern = 'test/**/test_*.rb'
33
- test.verbose = true
34
- end
29
+ require 'spec/version'
30
+ require 'spec/rake/spectask'
31
+ require 'spec/ruby'
35
32
 
36
- require 'rcov/rcovtask'
37
- Rcov::RcovTask.new do |test|
38
- test.libs << 'test'
39
- test.pattern = 'test/**/test_*.rb'
40
- test.verbose = true
41
- test.rcov_opts << '--exclude "gems/*"'
33
+ Spec::Rake::SpecTask.new(:spec) do |spec|
34
+ RAILS_ENV = "test"
35
+ spec.spec_files = FileList['spec/**/*_spec.rb']
36
+ spec.spec_opts = ['--options', 'spec/spec.opts']
42
37
  end
43
38
 
44
- task :default => :test
39
+ task :default => :spec
40
+
41
+ desc "Run all specs with rcov"
42
+ Spec::Rake::SpecTask.new(:rcov) do |t|
43
+ t.spec_files = FileList['spec/**/*_spec.rb']
44
+ t.spec_opts = ['--options', 'spec/spec.opts']
45
+ t.rcov = true
46
+ t.rcov_dir = 'coverage'
47
+ t.rcov_opts = ['--exclude', "features,kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
48
+ t.rcov_opts << '--sort coverage --text-summary --aggregate coverage.data'
49
+ end
45
50
 
46
51
  require 'rake/rdoctask'
47
52
  Rake::RDocTask.new do |rdoc|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,3 +1,7 @@
1
+ require 'rest-client'
2
+ require 'itrigga/core_ext'
3
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)),"net_helper","*.rb")].each{|f| require f}
4
+
1
5
  require 'uri'
2
6
  require 'hpricot'
3
7
  require 'json'
@@ -10,171 +14,74 @@ module Itrigga
10
14
 
11
15
  module_function
12
16
 
13
- def typhoeus_present?
14
- if defined?(Typhoeus)
15
- true
16
- else
17
- false
18
- end
17
+ # gets a list of engines available (each is a different class in this module that implements 'get' method)
18
+ def available_http_engines
19
+ Itrigga::NetHelper.constants
19
20
  end
20
-
21
- def do_get(url, time_out=5, retries_on_timeout=5, max_redirects = 3)
22
- retrycount = 0
23
- resp = nil
24
- begin
25
- resp = get_with_timeout( url, time_out )
26
-
27
- handle_response( url, resp, retries_on_timeout, max_redirects )
28
-
29
- rescue Timeout::Error
30
- if(retrycount.to_i < retries_on_timeout.to_i)
31
- retrycount+=1
32
- retry
33
- else
34
- raise IOError.new( "HTTP request timed out #{retrycount} times" )
35
- end
36
- end
37
21
 
22
+ def default_http_engine
23
+ "RestClient"
38
24
  end
39
25
 
40
- def typhoeus_request( opts={} )
41
- opts[:timeout] ||= 30
42
- opts[:timeout] *= 1000 # Typhoeus does its timeouts in ms
43
- opts[:follow_location] ||= true
44
- opts[:disable_ssl_peer_verification] = true if opts[:disable_ssl_peer_verification].nil?
45
-
46
- request = Typhoeus::Request.new(opts[:url], opts)
47
-
48
- request.on_complete do |response|
49
- if response.success?
50
- return response.body
51
- elsif response.timed_out?
52
- # aw hell no
53
- raise IOError.new("Timed out request to #{opts[:url]} after #{opts[:timeout]}ms")
54
- elsif response.code == 0
55
- # Could not get an http response, something's wrong.
56
- raise IOError.new(response.curl_error_message)
57
- else
58
- # Received a non-successful http response.
59
- raise IOError.new("HTTP request failed: " + response.code.to_s)
60
- end
61
- end
62
-
63
- hydra = Typhoeus::Hydra.new
64
- hydra.queue(request)
65
- hydra.run
66
- end
67
26
 
68
- def get_response( url, timeout=nil )
69
- if typhoeus_present?
70
- typhoeus_request( :url=>url, :timeout=>timeout )
71
- else
72
- Net::HTTP.get_response(URI.parse(url))
73
- end
74
- end
75
-
76
- def get_with_timeout( url, time_out)
77
- resp = nil
78
- if defined?(SystemTimer)
79
- resp = SystemTimer.timeout_after(time_out) do
80
- get_response(url, time_out)
81
- end
82
- else
83
- resp = timeout(time_out) do
84
- get_response(url, time_out)
85
- end
86
- end
87
- resp
27
+ # wrapper method for backwards compat.
28
+ def do_get(url, timeout=5, retries_on_timeout=5, max_redirects = 3)
29
+ get :url => url, :timeout => timeout, :retries_on_timeout => retries_on_timeout, :max_redirects => max_redirects
88
30
  end
31
+
32
+
33
+ def get(options = {})
34
+ opts = { :timeout => 5, :retries_on_timeout => 5, :max_redirects => 3, :headers => {} }.merge(options)
35
+ raise ArgumentError.new(":url is required" ) unless opts[:url]
89
36
 
90
-
91
-
92
- def handle_response( url, resp, retries_on_timeout=5, max_redirects = 3 )
93
- if resp.is_a? Net::HTTPSuccess then resp.body
94
- elsif resp.is_a? String then resp
95
- elsif resp.is_a? Net::HTTPRedirection
96
- if max_redirects > 0
97
- do_get( URI.parse(url).merge(resp['location']).to_s, retries_on_timeout, max_redirects - 1 )
98
- else
99
- raise IOError.new("too many redirects!")
100
- end
101
- else
102
- resp.error!
103
- end
37
+ engine_klass = get_engine opts
38
+ with_timeout(opts) { engine_klass.get(opts) }
104
39
  end
105
-
106
-
107
- def get( options = {} )
108
- opts = {:timeout=>5, :retries_on_timeout=>5, :max_redirects => 3, :headers=>{} }.merge(options)
109
- raise ArgumentError.new(":url is required" ) unless opts[:url]
110
40
 
111
- if (opts[:username] || opts[:headers]).to_s.empty?
112
- do_get(opts[:url], opts[:timeout], opts[:retries_on_timeout], opts[:max_redirects])
113
- else
114
-
115
- retrycount = 0
116
- resp = begin
117
- if defined?(SystemTimer)
118
- SystemTimer.timeout_after(opts[:timeout]) do
119
- raw_get(opts)
120
- end
121
- else
122
- timeout( opts[:timeout] ) do
123
- raw_get(opts)
124
- end
41
+
42
+
43
+ def with_timeout(opts, &block)
44
+ retrycount = 0
45
+ resp = begin
46
+ if defined?(SystemTimer)
47
+ SystemTimer.timeout_after(opts[:timeout]) do
48
+ yield
125
49
  end
126
- rescue TimeoutError
127
- if(retrycount < opts[:retries_on_timeout])
128
- retrycount+=1
129
- retry
130
- else
131
- raise IOError.new( "HTTP request timed out #{retrycount} times" )
50
+ else
51
+ timeout( opts[:timeout] ) do
52
+ yield
132
53
  end
133
54
  end
134
- resp
55
+ rescue ::TimeoutError, ::RestClient::RequestTimeout
56
+ if(retrycount < opts[:retries_on_timeout])
57
+ retrycount+=1
58
+ retry
59
+ else
60
+ raise IOError.new( "HTTP request timed out #{retrycount} times" )
61
+ end
135
62
  end
63
+ resp
136
64
  end
137
-
138
- def raw_get(opts)
65
+
66
+
67
+
68
+ def get_engine(opts = {})
69
+ # default to rest_client if not given an engine
70
+ opts[:http_engine] ||= ( defined?(ITNH_HTTP_ENGINE) ? ITNH_HTTP_ENGINE : default_http_engine )
139
71
 
140
- if typhoeus_present?
141
- return typhoeus_request(opts)
142
- end
72
+ # only use typhoeus if it is actually been required
73
+ opts[:http_engine] = default_http_engine if opts[:http_engine] == "Typhoeus" && defined?(::Typhoeus) == nil
143
74
 
144
- resp = nil
145
- establish_session_if_needed(opts)
146
-
147
- if opts[:username]
148
- resp = get_with_auth(opts)
149
- retries = 0
150
- while resp.is_a? Net::HTTPRedirection do
151
- retries += 1
152
- raise IOError.new( "HTTP request timed out #{retries} times" ) if retries > (opts[:max_redirects] || 3)
153
-
154
- resp = get_with_auth(opts.merge(:parsed_url=>URI.parse(resp['location'])))
155
- end
156
-
157
- resp.body
158
-
75
+ if available_http_engines.include?(opts[:http_engine])
76
+ Itrigga::NetHelper.const_get opts[:http_engine]
159
77
  else
160
- response = opts[:http_session].request_get(opts[:parsed_url].path, opts[:headers])
161
- response.body
78
+ puts "[NetHelper] Could not find http_engine '#{opts[:http_engine]}'. Available engines are: #{available_http_engines.join(',')}. Defaulting to Net::HTTP"
79
+ Itrigga::NetHelper.const_get default_http_engine
162
80
  end
81
+
163
82
  end
164
-
165
- def get_with_auth( opts )
166
- establish_session_if_needed(opts)
167
83
 
168
- req = Net::HTTP::Get.new(opts[:parsed_url].path)
169
- req.basic_auth( opts[:username], opts[:password] ) if opts[:username]
170
- resp = opts[:http_session].request(req, opts[:headers])
171
- end
172
-
173
- def establish_session_if_needed(opts)
174
- opts[:parsed_url] ||= URI.parse(opts[:url])
175
- opts[:http_session] ||= Net::HTTP.new(opts[:parsed_url].host, opts[:parsed_url].port)
176
- opts[:http_session].use_ssl = true if opts[:parsed_url].scheme == 'https'
177
- end
84
+
178
85
 
179
86
  def query_string( h, opts={:encode_values=>false, :skip_empty=>false} )
180
87
  params = []
@@ -0,0 +1,19 @@
1
+ module Itrigga
2
+ module NetHelper
3
+ class RestClient
4
+
5
+ def self.get( opts={} )
6
+ raise ArgumentError.new(":url is required") unless opts[:url]
7
+
8
+ # rest_client expects :user and not :username
9
+ opts[:user] ||= opts.delete(:username) if opts[:username]
10
+
11
+ opts[:headers] = {:params => opts.delete(:params)} if opts[:params]
12
+
13
+ response = ::RestClient::Request.new(opts.merge(:method => :get)).execute
14
+ response.to_str
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Itrigga
2
+ module NetHelper
3
+ class Typhoeus
4
+
5
+ def self.get( opts={} )
6
+ opts[:timeout] ||= 30
7
+ opts[:timeout] *= 1000 # Typhoeus does its timeouts in ms
8
+ opts[:follow_location] ||= true
9
+ opts[:disable_ssl_peer_verification] = true if opts[:disable_ssl_peer_verification].nil?
10
+
11
+ request = ::Typhoeus::Request.new(opts[:url], opts)
12
+
13
+ request.on_complete do |response|
14
+ if response.success?
15
+ return response.body
16
+ elsif response.timed_out?
17
+ # aw hell no
18
+ raise ::TimeoutError.new("Timed out request to #{opts[:url]} after #{opts[:timeout]}ms")
19
+ elsif response.code == 0
20
+ # Could not get an http response, something's wrong.
21
+ raise IOError.new(response.curl_error_message)
22
+ else
23
+ # Received a non-successful http response.
24
+ raise IOError.new("HTTP request failed: " + response.code.to_s)
25
+ end
26
+ end
27
+
28
+ hydra = ::Typhoeus::Hydra.new
29
+ hydra.queue(request)
30
+ hydra.run
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe "Itrigga::NetHelper::RestClient" do
4
+ before do
5
+ @mock_response = mock("Response", :to_str => "stuff")
6
+ @client = mock("RestClient::Request")
7
+ @client.stub!(:execute).and_return(@mock_response)
8
+ RestClient::Request.stub!(:new).and_return(@client)
9
+ end
10
+
11
+ it "should raise error if no url" do
12
+ lambda { Itrigga::NetHelper::RestClient.get }.should raise_error(ArgumentError,":url is required")
13
+ end
14
+
15
+ it "should call execute on new Request" do
16
+ RestClient::Request.should_receive(:new).with(hash_including(:method => :get, :url => "abc", :param1 => "blart")).and_return(@client)
17
+ Itrigga::NetHelper::RestClient.get :url => "abc", :param1 => "blart"
18
+ end
19
+
20
+ it "should return the correct value" do
21
+ @mock_response.should_receive(:to_str).and_return("stuff")
22
+ Itrigga::NetHelper::RestClient.get(:url => "abc").should == "stuff"
23
+ end
24
+
25
+ context "when given a :username" do
26
+ it "should convert it to :user" do
27
+ RestClient::Request.should_receive(:new).with(hash_including(:method => :get, :url => "abc", :user => "blart")).and_return(@client)
28
+ Itrigga::NetHelper::RestClient.get :url => "abc", :username => "blart"
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,90 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+
4
+
5
+ describe "Itrigga::NetHelper::Typhoeus" do
6
+ before do
7
+ # define Typhoeus stubs as not installed as gem dependancy
8
+ Object.class_eval %{
9
+ module Typhoeus
10
+ class Request
11
+ attr_accessor :on_complete
12
+ def initialize(url,opts = {}); end
13
+ def on_complete(&block); @on_complete = block; end
14
+ end
15
+
16
+ class Hydra
17
+ attr_accessor :request
18
+ def queue(request); end
19
+ def run; end
20
+ end
21
+ end
22
+ }
23
+
24
+ @opts = {:url => "blart"}
25
+ @response = mock("Response", :success? => true, :body => "stuff")
26
+ @request = Typhoeus::Request.new("blart", @opts)
27
+ # @request.stub!(:on_complete).and_yield(@response)
28
+ Typhoeus::Request.stub!(:new).and_return(@request)
29
+ @hydra = Typhoeus::Hydra.new
30
+ Typhoeus::Hydra.stub!(:new).and_return(@hydra)
31
+ end
32
+
33
+ after do
34
+ Object.send(:remove_const, :Typhoeus)
35
+ end
36
+
37
+ it "should create a Request object" do
38
+ Typhoeus::Request.should_receive(:new).with("blart",@opts).and_return(@request)
39
+ Itrigga::NetHelper::Typhoeus.get @opts
40
+ end
41
+
42
+ it "should call queue on hydra" do
43
+ @hydra.should_receive(:queue).with(@request)
44
+ Itrigga::NetHelper::Typhoeus.get(@opts)
45
+ end
46
+
47
+ it "should call run on hydra" do
48
+ @hydra.should_receive(:run)
49
+ Itrigga::NetHelper::Typhoeus.get(@opts)
50
+ end
51
+
52
+ it "should return the response body" do
53
+ @request.stub!(:on_complete).and_yield(@response)
54
+ Itrigga::NetHelper::Typhoeus.get(@opts).should == "stuff"
55
+ end
56
+
57
+ context "a timeout" do
58
+ before do
59
+ @response = mock("Response", :success? => false, :timed_out? => true, :body => "stuff")
60
+ @request.stub!(:on_complete).and_yield(@response)
61
+ end
62
+
63
+ it "should raise a TimeoutError" do
64
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(TimeoutError)
65
+ end
66
+ end
67
+
68
+ context "response code == 0" do
69
+ before do
70
+ @response = mock("Response", :success? => false, :timed_out? => false, :code => 0, :body => "stuff", :curl_error_message => "blart")
71
+ @request.stub!(:on_complete).and_yield(@response)
72
+ end
73
+
74
+ it "should raise a TimeoutError" do
75
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(IOError)
76
+ end
77
+ end
78
+
79
+ context "any other http status code" do
80
+ before do
81
+ @response = mock("Response", :success? => false, :timed_out? => false, :code => 500, :body => "stuff")
82
+ @request.stub!(:on_complete).and_yield(@response)
83
+ end
84
+
85
+ it "should raise a TimeoutError" do
86
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(IOError)
87
+ end
88
+ end
89
+
90
+ end