unwind 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Unwind
2
- VERSION = "0.9.3"
2
+ VERSION = "0.9.4"
3
3
  end
data/lib/unwind.rb CHANGED
@@ -3,63 +3,112 @@ require 'faraday'
3
3
 
4
4
  module Unwind
5
5
 
6
- class TooManyRedirects < StandardError; end
7
- class MissingRedirectLocation < StandardError; end
6
+ class TooManyRedirects < StandardError; end
7
+ class MissingRedirectLocation < StandardError; end
8
+
9
+ class RedirectFollower
10
+
11
+ attr_reader :final_url, :original_url, :redirect_limit, :response, :redirects
12
+
13
+ def initialize(original_url, limit=5)
14
+ @original_url, @redirect_limit = original_url, limit
15
+ @redirects = []
16
+
17
+ end
18
+
19
+ def redirected?
20
+ !(self.final_url == self.original_url)
21
+ end
22
+
23
+ def resolve(current_url=nil, options={})
24
+
25
+ ok_to_continue?
26
+
27
+ current_url ||= self.original_url
28
+ #adding this header because we really only care about resolving the url
29
+ headers = (options || {}).merge({"accept-encoding" => "none"})
30
+ response = Faraday.get(current_url, headers)
31
+
32
+ if [301, 302, 303].include?(response.status)
33
+ @redirects << current_url.to_s
34
+ @redirect_limit -= 1
35
+ resolve(redirect_url(response).normalize, apply_cookie(response, headers))
36
+ else
37
+ @final_url = current_url.to_s
38
+ @response = response
39
+ self
40
+ end
41
+ end
42
+
43
+
44
+ def self.resolve(original_url, limit=5)
45
+ new(original_url, limit).resolve
46
+ end
47
+
48
+ private
49
+
50
+
51
+ def ok_to_continue?
52
+ raise TooManyRedirects if redirect_limit < 0
53
+ end
54
+
55
+ def redirect_url(response)
56
+ if response['location'].nil?
57
+ body_match = response.body.match(/<a href=\"([^>]+)\">/i)
58
+ raise MissingRedirectLocation unless body_match
59
+ Addressable::URI.parse(body_match[0])
60
+ else
61
+ redirect_uri = Addressable::URI.parse(response['location'])
62
+ redirect_uri.relative? ? response.env[:url].join(response['location']) : redirect_uri
63
+ end
64
+ end
65
+
66
+ def apply_cookie(response, headers)
67
+ if response.status == 302 && response['set-cookie']
68
+ headers.merge(:cookie => CookieHash.to_cookie_string(response['set-cookie']))
69
+ else
70
+ #todo: should we delete the cookie at this point if it exists?
71
+ headers
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ #borrowed (stolen) from HTTParty with minor updates
78
+ #to handle all cookies existing in a single string
79
+ class CookieHash < Hash
80
+
81
+ CLIENT_COOKIES = %w{path expires domain path secure httponly}
82
+
83
+ def add_cookies(value)
84
+ case value
85
+ when Hash
86
+ merge!(value)
87
+ when String
88
+ value = value.gsub(/expires=[\w,\s-:]+;/i, '')
89
+ value = value.gsub(/httponly[\,\;]*/i, '')
90
+ value.split(/[;,]\s/).each do |cookie|
91
+ array = cookie.split('=')
92
+ self[array[0].strip.to_sym] = array[1]
93
+ end
94
+ else
95
+ raise "add_cookies only takes a Hash or a String"
96
+ end
97
+ end
98
+
99
+ def to_cookie_string
100
+ delete_if { |k, v| CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
101
+ end
102
+
103
+ def self.to_cookie_string(*cookie_strings)
104
+ h = CookieHash.new
105
+ cookie_strings.each do |cs|
106
+ h.add_cookies(cs)
107
+ end
108
+
109
+ h.to_cookie_string
110
+ end
111
+ end
8
112
 
9
- class RedirectFollower
10
-
11
- attr_reader :final_url, :original_url, :redirect_limit, :response, :redirects
12
-
13
- def initialize(original_url, limit=5)
14
- @original_url, @redirect_limit = original_url, limit
15
- @redirects = []
16
- end
17
-
18
- def redirected?
19
- !(self.final_url == self.original_url)
20
- end
21
-
22
- def resolve(current_url=nil)
23
-
24
- ok_to_continue?
25
-
26
- current_url ||= self.original_url
27
- response = Faraday.get(current_url)
28
-
29
- if [301, 302, 307].include?(response.status)
30
- @redirects << current_url.to_s
31
- @redirect_limit -= 1
32
- resolve redirect_url(response).normalize
33
- else
34
- @final_url = current_url.to_s
35
- @response = response
36
- self
37
- end
38
- end
39
-
40
-
41
- def self.resolve(original_url, limit=5)
42
- new(original_url, limit).resolve
43
- end
44
-
45
- private
46
-
47
- def ok_to_continue?
48
- raise TooManyRedirects if redirect_limit < 0
49
- end
50
-
51
- def redirect_url(response)
52
- if response['location'].nil?
53
- body_match = response.body.match(/<a href=\"([^>]+)\">/i)
54
- raise MissingRedirectLocation unless body_match
55
- Addressable::URI.parse(body_match[0])
56
- else
57
- redirect_uri = Addressable::URI.parse(response['location'])
58
- redirect_uri.relative? ? response.env[:url].join(response['location']) : redirect_uri
59
- end
60
- end
61
-
62
-
63
- end
64
113
 
65
114
  end
@@ -3,84 +3,93 @@ require 'vcr'
3
3
  require './lib/unwind'
4
4
 
5
5
  VCR.config do |c|
6
- c.stub_with :fakeweb
7
- c.cassette_library_dir = 'vcr_cassettes'
6
+ c.stub_with :fakeweb
7
+ c.cassette_library_dir = 'vcr_cassettes'
8
8
  end
9
9
 
10
- describe 'Tests :)' do
10
+ describe Unwind::RedirectFollower do
11
11
 
12
- it 'should resolve the url' do
13
- VCR.use_cassette('xZVND1') do
14
- follower = Unwind::RedirectFollower.resolve('http://j.mp/xZVND1')
15
- assert_equal 'http://ow.ly/i/s1O0', follower.final_url
16
- assert_equal 'http://j.mp/xZVND1', follower.original_url
17
- assert_equal 2, follower.redirects.count
18
- assert follower.redirected?
19
- end
20
- end
12
+ # needs to be regenerated to properly test...need to stop that :(
13
+ it 'should handle url with cookie requirement' do
14
+ VCR.use_cassette('with cookie') do
15
+ follower = Unwind::RedirectFollower.resolve('http://ow.ly/1hf37P')
16
+ assert_equal 200, follower.response.status
17
+ assert follower.redirected?
18
+ end
19
+ end
21
20
 
22
- it 'should handle relative redirects' do
23
- VCR.use_cassette('relative stackoverflow') do
24
- follower = Unwind::RedirectFollower.resolve('http://stackoverflow.com/q/9277007/871617?stw=1')
25
- assert follower.redirected?
26
- assert_equal 'http://stackoverflow.com/questions/9277007/gitlabhq-w-denied-for-rails', follower.final_url
27
- end
28
- end
21
+ it 'should resolve the url' do
22
+ VCR.use_cassette('xZVND1') do
23
+ follower = Unwind::RedirectFollower.resolve('http://j.mp/xZVND1')
24
+ assert_equal 'http://ow.ly/i/s1O0', follower.final_url
25
+ assert_equal 'http://j.mp/xZVND1', follower.original_url
26
+ assert_equal 2, follower.redirects.count
27
+ assert follower.redirected?
28
+ end
29
+ end
29
30
 
30
- it 'should still handine relative redirects' do
31
- # http://bit.ly/A4H3a2
32
- VCR.use_cassette('relative stackoverflow 2') do
33
- follower = Unwind::RedirectFollower.resolve('http://bit.ly/A4H3a2')
34
- assert follower.redirected?
35
- end
36
- end
31
+ it 'should handle relative redirects' do
32
+ VCR.use_cassette('relative stackoverflow') do
33
+ follower = Unwind::RedirectFollower.resolve('http://stackoverflow.com/q/9277007/871617?stw=1')
34
+ assert follower.redirected?
35
+ assert_equal 'http://stackoverflow.com/questions/9277007/gitlabhq-w-denied-for-rails', follower.final_url
36
+ end
37
+ end
37
38
 
38
- it 'should handle redirects to pdfs' do
39
- VCR.use_cassette('pdf') do
40
- follower = Unwind::RedirectFollower.resolve('http://binged.it/wVSFs5')
41
- assert follower.redirected?
42
- assert_equal 'https://microsoft.promo.eprize.com/bingtwitter/public/fulfillment/rules.pdf', follower.final_url
43
- end
44
- end
39
+ it 'should still handine relative redirects' do
40
+ # http://bit.ly/A4H3a2
41
+ VCR.use_cassette('relative stackoverflow 2') do
42
+ follower = Unwind::RedirectFollower.resolve('http://bit.ly/A4H3a2')
43
+ assert follower.redirected?
44
+ end
45
+ end
45
46
 
46
- it 'should handle the lame amazon spaces' do
47
- VCR.use_cassette('amazon') do
48
- follower = Unwind::RedirectFollower.resolve('http://amzn.to/xrHQWS')
49
- assert follower.redirected?
50
- end
51
- end
47
+ it 'should handle redirects to pdfs' do
48
+ VCR.use_cassette('pdf') do
49
+ follower = Unwind::RedirectFollower.resolve('http://binged.it/wVSFs5')
50
+ assert follower.redirected?
51
+ assert_equal 'https://microsoft.promo.eprize.com/bingtwitter/public/fulfillment/rules.pdf', follower.final_url
52
+ end
53
+ end
52
54
 
53
- #http://amzn.to/xrHQWS
55
+ it 'should handle the lame amazon spaces' do
56
+ VCR.use_cassette('amazon') do
57
+ follower = Unwind::RedirectFollower.resolve('http://amzn.to/xrHQWS')
58
+ assert follower.redirected?
59
+ end
60
+ end
54
61
 
55
- it 'should handle a https redirect' do
56
- VCR.use_cassette('ssl tpope') do
57
- follower = Unwind::RedirectFollower.resolve('http://github.com/tpope/vim-rails')
58
- assert follower.redirected?
59
- assert_equal 'https://github.com/tpope/vim-rails', follower.final_url
60
- end
61
- end
62
+ #http://amzn.to/xrHQWS
62
63
 
63
- it 'should not be redirected' do
64
- VCR.use_cassette('no redirect') do
65
- follower = Unwind::RedirectFollower.resolve('http://www.scottw.com')
66
- assert !follower.redirected?
67
- end
68
- end
64
+ it 'should handle a https redirect' do
65
+ VCR.use_cassette('ssl tpope') do
66
+ follower = Unwind::RedirectFollower.resolve('http://github.com/tpope/vim-rails')
67
+ assert follower.redirected?
68
+ assert_equal 'https://github.com/tpope/vim-rails', follower.final_url
69
+ end
70
+ end
69
71
 
70
- it 'should raise TooManyRedirects' do
71
- VCR.use_cassette('xZVND1') do
72
- follower = Unwind::RedirectFollower.new('http://j.mp/xZVND1', 1)
73
- too_many_redirects = lambda {follower.resolve}
74
- too_many_redirects.must_raise Unwind::TooManyRedirects
75
- end
76
- end
72
+ it 'should not be redirected' do
73
+ VCR.use_cassette('no redirect') do
74
+ follower = Unwind::RedirectFollower.resolve('http://www.scottw.com')
75
+ assert !follower.redirected?
76
+ end
77
+ end
77
78
 
78
- it 'should raise MissingRedirectLocation' do
79
- VCR.use_cassette('missing redirect') do
80
- follower = Unwind::RedirectFollower.new('http://tinyurl.com/6oqzkff')
81
- missing_redirect_location = lambda{follower.resolve}
82
- missing_redirect_location.must_raise Unwind::MissingRedirectLocation
83
- end
84
- end
79
+ it 'should raise TooManyRedirects' do
80
+ VCR.use_cassette('xZVND1') do
81
+ follower = Unwind::RedirectFollower.new('http://j.mp/xZVND1', 1)
82
+ too_many_redirects = lambda {follower.resolve}
83
+ too_many_redirects.must_raise Unwind::TooManyRedirects
84
+ end
85
+ end
86
+
87
+ it 'should raise MissingRedirectLocation' do
88
+ VCR.use_cassette('missing redirect') do
89
+ follower = Unwind::RedirectFollower.new('http://tinyurl.com/6oqzkff')
90
+ missing_redirect_location = lambda{follower.resolve}
91
+ missing_redirect_location.must_raise Unwind::MissingRedirectLocation
92
+ end
93
+ end
85
94
 
86
95
  end