downspout 0.2.8 → 0.2.9
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.
- data/VERSION +1 -1
- data/lib/downspout/base.rb +23 -21
- data/lib/downspout/config.rb +11 -4
- data/lib/downspout/credential.rb +30 -0
- data/lib/downspout/downloader.rb +18 -3
- data/test/focused_test.rb +3 -42
- data/test/unit/base_test.rb +16 -0
- data/test/unit/credential_test.rb +64 -17
- data/test/unit/downloader_test.rb +47 -0
- metadata +4 -4
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.9
|
data/lib/downspout/base.rb
CHANGED
|
@@ -48,22 +48,20 @@ module Downspout
|
|
|
48
48
|
Utility method for validating a URL without initiating a download
|
|
49
49
|
=end
|
|
50
50
|
def self.viable_url?( url_string )
|
|
51
|
-
$logger.info("downspout |
|
|
52
|
-
|
|
53
|
-
# remove user/password prefix if provided
|
|
54
|
-
clean_url = self.extract_credentials_from_url!( url_string )
|
|
51
|
+
$logger.info("downspout | viable_url? | URL : #{url_string} ")
|
|
55
52
|
|
|
56
53
|
begin
|
|
54
|
+
# remove user/password prefix if provided
|
|
55
|
+
clean_url = self.extract_credentials_from_url!( url_string )
|
|
56
|
+
|
|
57
57
|
uri = URI.parse( clean_url )
|
|
58
58
|
rescue URI::InvalidURIError
|
|
59
|
-
$logger.warn("downspout |
|
|
59
|
+
$logger.warn("downspout | viable_url? | The format of the url is not valid : #{url_string}")
|
|
60
60
|
return false
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
return false unless self.supported_protocol?( uri.scheme )
|
|
64
64
|
|
|
65
|
-
# TODO : do more in-depth checks on URL validity
|
|
66
|
-
|
|
67
65
|
return true
|
|
68
66
|
end
|
|
69
67
|
|
|
@@ -104,24 +102,28 @@ module Downspout
|
|
|
104
102
|
end
|
|
105
103
|
|
|
106
104
|
def self.extract_credentials_from_url!( some_url )
|
|
107
|
-
the_uri = URI.parse( some_url )
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
begin
|
|
107
|
+
some_uri = URI::parse( some_url )
|
|
108
|
+
rescue NoMethodError => e
|
|
109
|
+
# convert to Invalid URI as that's the more pertinent issue
|
|
110
|
+
raise URI::InvalidURIError, e.to_s
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
:pass_word => the_uri.password
|
|
118
|
-
)
|
|
119
|
-
ensure
|
|
120
|
-
the_uri.user = nil
|
|
121
|
-
the_uri.password = nil
|
|
113
|
+
cred = Downspout::Credential.create_from_uri( some_uri )
|
|
114
|
+
|
|
115
|
+
if cred.nil? then
|
|
116
|
+
return some_url
|
|
122
117
|
end
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
|
|
119
|
+
Downspout::Config.add_credential( cred )
|
|
120
|
+
|
|
121
|
+
# zero out the user info
|
|
122
|
+
some_uri.user = nil
|
|
123
|
+
some_uri.password = nil
|
|
124
|
+
|
|
125
|
+
# return sanitized URL string
|
|
126
|
+
return some_uri.to_s
|
|
125
127
|
end
|
|
126
128
|
|
|
127
129
|
end
|
data/lib/downspout/config.rb
CHANGED
|
@@ -83,14 +83,21 @@ module Downspout
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def self.add_credential( options = nil )
|
|
86
|
-
return nil unless options
|
|
87
|
-
options = {:scheme => 'ftp'}.merge!( options )
|
|
86
|
+
return nil unless options
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
if (options.class == Downspout::Credential) then
|
|
89
|
+
c = options
|
|
90
|
+
else
|
|
91
|
+
return nil unless options.respond_to?(:keys)
|
|
92
|
+
|
|
93
|
+
options = {:scheme => 'ftp'}.merge!( options ) # defaults to FTP
|
|
90
94
|
|
|
95
|
+
c = Credential.new( options )
|
|
96
|
+
end
|
|
97
|
+
|
|
91
98
|
$logger.debug("downspout | config | add_credential | #{c.host}, #{c.user_name}, #{c.scheme} ")
|
|
92
99
|
|
|
93
|
-
@@credentials << c
|
|
100
|
+
@@credentials << c
|
|
94
101
|
|
|
95
102
|
return c
|
|
96
103
|
end
|
data/lib/downspout/credential.rb
CHANGED
|
@@ -15,6 +15,36 @@ module Downspout
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def self.create_from_url( some_url )
|
|
19
|
+
cred_hash = {}
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
some_uri = URI::parse( some_url )
|
|
23
|
+
rescue NoMethodError => e
|
|
24
|
+
# convert to Invalid URI as that's the more pertinent issue
|
|
25
|
+
raise URI::InvalidURIError, e.to_s
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
return Credential.create_from_uri( some_uri )
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.create_from_uri( some_uri )
|
|
32
|
+
|
|
33
|
+
if some_uri.userinfo.nil? then
|
|
34
|
+
return nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
cred_hash = {}
|
|
38
|
+
cred_hash[:scheme] = some_uri.scheme
|
|
39
|
+
cred_hash[:host] = some_uri.host
|
|
40
|
+
cred_hash[:port] = some_uri.port
|
|
41
|
+
cred_hash[:user_name] = some_uri.user
|
|
42
|
+
cred_hash[:pass_word] = some_uri.password
|
|
43
|
+
|
|
44
|
+
cred = Credential.new( cred_hash )
|
|
45
|
+
|
|
46
|
+
return cred
|
|
47
|
+
end
|
|
18
48
|
end
|
|
19
49
|
|
|
20
50
|
end
|
data/lib/downspout/downloader.rb
CHANGED
|
@@ -167,13 +167,23 @@ module Downspout
|
|
|
167
167
|
|
|
168
168
|
# look up the credentials for this host
|
|
169
169
|
cred = Downspout::Config.credentials.select{|c| c.scheme == 'ftp' }.select{ |c| c.host == @uri.host }.first
|
|
170
|
+
|
|
170
171
|
if cred.nil? then
|
|
171
|
-
$logger.warn("downspout | downloader | net_ftp_download | No credentials found for '#{@uri.host}'.")
|
|
172
|
-
|
|
172
|
+
$logger.warn("downspout | downloader | net_ftp_download | No established credentials found for '#{@uri.host}'.")
|
|
173
|
+
|
|
174
|
+
# attempt to extract credential from the URL
|
|
175
|
+
cred = Downspout::Credential.create_from_url( @url )
|
|
173
176
|
else
|
|
174
177
|
$logger.debug("downspout | downloader | net_ftp_download | Loaded credentials for #{cred.host} ...")
|
|
175
178
|
end
|
|
176
179
|
|
|
180
|
+
if cred.nil? then
|
|
181
|
+
$logger.warn("downspout | downloader | net_ftp_download | No embedded credentials found in URL.")
|
|
182
|
+
# proceed anyway - slight possibility it's an un-authorized FTP account...
|
|
183
|
+
else
|
|
184
|
+
$logger.warn("downspout | downloader | net_ftp_download | Using embedded credentials found in URL with user: #{cred.user_name}.")
|
|
185
|
+
end
|
|
186
|
+
|
|
177
187
|
begin
|
|
178
188
|
ftp = Net::FTP.open( @uri.host ) do |ftp|
|
|
179
189
|
ftp.login( cred.user_name, cred.pass_word ) unless cred.nil?
|
|
@@ -240,7 +250,12 @@ module Downspout
|
|
|
240
250
|
raise Downspout::ExcessiveRedirects, 'HTTP redirect too deep'
|
|
241
251
|
end
|
|
242
252
|
|
|
243
|
-
|
|
253
|
+
begin
|
|
254
|
+
u = URI.parse( url_str )
|
|
255
|
+
rescue NoMethodError => e
|
|
256
|
+
# convert to Invalid URI as that's the more pertinent issue
|
|
257
|
+
raise URI::InvalidURIError, e.to_s
|
|
258
|
+
end
|
|
244
259
|
|
|
245
260
|
http = Net::HTTP.new( u.host, u.port )
|
|
246
261
|
|
data/test/focused_test.rb
CHANGED
|
@@ -4,51 +4,12 @@ class FocusedTest < Test::Unit::TestCase
|
|
|
4
4
|
context "Downspout" do
|
|
5
5
|
context "Focus" do
|
|
6
6
|
setup do
|
|
7
|
-
@
|
|
8
|
-
@header_str = "HTTP/1.1 302 Found
|
|
9
|
-
Server: nginx/0.7.67
|
|
10
|
-
Date: Mon, 18 Apr 2011 01:53:16 GMT
|
|
11
|
-
Content-Type: text/html; charset=utf-8
|
|
12
|
-
Connection: keep-alive
|
|
13
|
-
Status: 302 Found
|
|
14
|
-
Location: https://nodeload.github.com/rails/rails/tarball/v2.3.11
|
|
15
|
-
X-Runtime: 3ms
|
|
16
|
-
Content-Length: 121
|
|
17
|
-
Set-Cookie: _gh_sess=BAh7BzoRbG9jYWxlX2d1ZXNzMCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA%3D%3D--719def2ab2d99283383e374327fcdbb716c45c12; path=/; expires=Fri, 01 Jan 2021 00:00:00 GMT; secure; HttpOnly
|
|
18
|
-
Cache-Control: no-cache
|
|
19
|
-
Strict-Transport-Security: max-age=2592000
|
|
20
|
-
|
|
21
|
-
HTTP/1.1 302 Moved Temporarily
|
|
22
|
-
Server: nginx/0.7.67
|
|
23
|
-
Date: Mon, 18 Apr 2011 01:53:17 GMT
|
|
24
|
-
Transfer-Encoding: chunked
|
|
25
|
-
Connection: keep-alive
|
|
26
|
-
Location: https://download.github.com/rails-rails-v2.3.11-0-gb0c3d45.tar.gz
|
|
27
|
-
|
|
28
|
-
HTTP/1.1 200 OK
|
|
29
|
-
Server: nginx/0.7.67
|
|
30
|
-
Date: Mon, 18 Apr 2011 01:53:17 GMT
|
|
31
|
-
Content-Type: application/octet-stream
|
|
32
|
-
Content-Length: 3416081
|
|
33
|
-
Last-Modified: Mon, 18 Apr 2011 01:45:25 GMT
|
|
34
|
-
Connection: keep-alive
|
|
35
|
-
Accept-Ranges: bytes"
|
|
7
|
+
@test_string = "foo"
|
|
36
8
|
end
|
|
37
9
|
|
|
38
|
-
should "
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assert_equal @expected_url, last_location
|
|
10
|
+
should "behave properly" do
|
|
11
|
+
assert_equal @test_string, "foo"
|
|
42
12
|
end
|
|
43
|
-
|
|
44
|
-
should "match last_location method" do
|
|
45
|
-
@obj = Downspout::Downloader.new( :url => "http://bad.url/path" )
|
|
46
|
-
last_location = @obj.send('curb_last_location', @header_str )
|
|
47
|
-
|
|
48
|
-
assert_equal String, last_location.class
|
|
49
|
-
assert_equal @expected_url, last_location
|
|
50
|
-
end
|
|
51
|
-
|
|
52
13
|
end
|
|
53
14
|
end
|
|
54
15
|
end
|
data/test/unit/base_test.rb
CHANGED
|
@@ -114,11 +114,27 @@ class DownspoutTest < Test::Unit::TestCase
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
should "be accepted with FTP protocol containing user & password" do
|
|
117
|
+
num = Downspout::Config.credentials.size
|
|
118
|
+
|
|
117
119
|
assert Downspout.viable_url?( "ftp://uzer:passw0rd@host.domain.tld/resource/file.format" )
|
|
120
|
+
|
|
121
|
+
assert_equal num + 1, Downspout::Config.credentials.size
|
|
122
|
+
|
|
123
|
+
assert_equal 'host.domain.tld', Downspout::Config.credentials.last.host
|
|
118
124
|
end
|
|
119
125
|
|
|
120
126
|
end
|
|
121
127
|
end
|
|
122
128
|
|
|
129
|
+
should "clean URLs with user & password via extract credentials" do
|
|
130
|
+
test_url = "ftp://uzer:passw0rd@host.domain.tld/resource/file.format"
|
|
131
|
+
|
|
132
|
+
new_url = Downspout.extract_credentials_from_url!( test_url )
|
|
133
|
+
|
|
134
|
+
assert !(new_url == test_url)
|
|
135
|
+
assert_equal URI::parse( test_url ).host, URI::parse( new_url ).host
|
|
136
|
+
assert_equal URI::parse( test_url ).path, URI::parse( new_url ).path
|
|
137
|
+
end
|
|
138
|
+
|
|
123
139
|
end
|
|
124
140
|
end
|
|
@@ -4,28 +4,75 @@ class CredentialTest < Test::Unit::TestCase
|
|
|
4
4
|
context "Downspout" do
|
|
5
5
|
context "Credential" do
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
context "object" do
|
|
8
|
+
|
|
9
|
+
should "respond to scheme" do
|
|
10
|
+
assert Downspout::Credential.new.respond_to?(:scheme)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should "respond to host" do
|
|
14
|
+
assert Downspout::Credential.new.respond_to?(:host)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
should "respond to port" do
|
|
18
|
+
assert Downspout::Credential.new.respond_to?(:port)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
should "respond to user_name" do
|
|
22
|
+
assert Downspout::Credential.new.respond_to?(:user_name)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should "respond to pass_word" do
|
|
26
|
+
assert Downspout::Credential.new.respond_to?(:pass_word)
|
|
27
|
+
end
|
|
28
|
+
|
|
9
29
|
end
|
|
10
30
|
|
|
11
|
-
should "respond to host" do
|
|
12
|
-
assert Downspout::Credential.new.respond_to?(:host)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
should "respond to port" do
|
|
16
|
-
assert Downspout::Credential.new.respond_to?(:port)
|
|
17
|
-
end
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
context "created from URL" do
|
|
33
|
+
|
|
34
|
+
should "return nil if no credential extractable from otherwise valid URL" do
|
|
35
|
+
@my_url = "ftp://ftp-test.domain.tld/path/2011-04-01_13-42-52/file.ext"
|
|
36
|
+
|
|
37
|
+
@no_cred = Downspout::Credential.create_from_url( @my_url )
|
|
38
|
+
|
|
39
|
+
assert_nil @no_cred
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
should "create credential from URL with embedded info" do
|
|
43
|
+
@my_url = "ftp://test-name:test-secret@ftp-test.domain.tld/path/2011-04-01_13-42-52/file.ext"
|
|
44
|
+
|
|
45
|
+
@my_cred = Downspout::Credential.create_from_url( @my_url )
|
|
46
|
+
|
|
47
|
+
assert_not_nil @my_cred
|
|
48
|
+
|
|
49
|
+
assert_equal Downspout::Credential, @my_cred.class
|
|
50
|
+
|
|
51
|
+
assert_equal 'ftp', @my_cred.scheme
|
|
52
|
+
assert_equal 'ftp-test.domain.tld', @my_cred.host
|
|
53
|
+
assert_equal 21, @my_cred.port
|
|
54
|
+
assert_equal 'test-name', @my_cred.user_name
|
|
55
|
+
assert_equal 'test-secret', @my_cred.pass_word
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should "raise error when parsing invalid URL [partially encoded]" do
|
|
59
|
+
@bad_url = "ftp:%20%20test-name:test-secret@ftp-test.domain.tld/path/2011-04-01_13-42-52/file.ext"
|
|
60
|
+
|
|
61
|
+
assert_raise URI::InvalidURIError do
|
|
62
|
+
@bad_cred = Downspout::Credential.create_from_url( @bad_url )
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
should "raise error when parsing invalid URL [inserted space]" do
|
|
67
|
+
@bad_url = "ft p://test-name:test-secret@ftp-test.domain.tld/path/2011-04-01_13-42-52/file.ext"
|
|
68
|
+
|
|
69
|
+
assert_raise URI::InvalidURIError do
|
|
70
|
+
@bad_cred = Downspout::Credential.create_from_url( @bad_url )
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
21
74
|
end
|
|
22
|
-
|
|
23
|
-
should "respond to pass_word" do
|
|
24
|
-
assert Downspout::Credential.new.respond_to?(:pass_word)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
75
|
end
|
|
28
|
-
|
|
29
76
|
end
|
|
30
77
|
|
|
31
78
|
end
|
|
@@ -243,4 +243,51 @@ class DownloaderTest < Test::Unit::TestCase
|
|
|
243
243
|
|
|
244
244
|
end
|
|
245
245
|
|
|
246
|
+
context "last location for curb redirects" do
|
|
247
|
+
setup do
|
|
248
|
+
@expected_url = "https://download.github.com/rails-rails-v2.3.11-0-gb0c3d45.tar.gz"
|
|
249
|
+
@header_str = "HTTP/1.1 302 Found
|
|
250
|
+
Server: nginx/0.7.67
|
|
251
|
+
Date: Mon, 18 Apr 2011 01:53:16 GMT
|
|
252
|
+
Content-Type: text/html; charset=utf-8
|
|
253
|
+
Connection: keep-alive
|
|
254
|
+
Status: 302 Found
|
|
255
|
+
Location: https://nodeload.github.com/rails/rails/tarball/v2.3.11
|
|
256
|
+
X-Runtime: 3ms
|
|
257
|
+
Content-Length: 121
|
|
258
|
+
Cache-Control: no-cache
|
|
259
|
+
Strict-Transport-Security: max-age=2592000
|
|
260
|
+
|
|
261
|
+
HTTP/1.1 302 Moved Temporarily
|
|
262
|
+
Server: nginx/0.7.67
|
|
263
|
+
Date: Mon, 18 Apr 2011 01:53:17 GMT
|
|
264
|
+
Transfer-Encoding: chunked
|
|
265
|
+
Connection: keep-alive
|
|
266
|
+
Location: https://download.github.com/rails-rails-v2.3.11-0-gb0c3d45.tar.gz
|
|
267
|
+
|
|
268
|
+
HTTP/1.1 200 OK
|
|
269
|
+
Server: nginx/0.7.67
|
|
270
|
+
Date: Mon, 18 Apr 2011 01:53:17 GMT
|
|
271
|
+
Content-Type: application/octet-stream
|
|
272
|
+
Content-Length: 3416081
|
|
273
|
+
Last-Modified: Mon, 18 Apr 2011 01:45:25 GMT
|
|
274
|
+
Connection: keep-alive
|
|
275
|
+
Accept-Ranges: bytes"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
should "grep last location header" do
|
|
279
|
+
last_location = @header_str.scan(/Location\:\s?(.*)\W/).last.first
|
|
280
|
+
|
|
281
|
+
assert_equal @expected_url, last_location
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
should "match last_location method" do
|
|
285
|
+
@obj = Downspout::Downloader.new( :url => "http://bad.url/path" )
|
|
286
|
+
last_location = @obj.send('curb_last_location', @header_str )
|
|
287
|
+
|
|
288
|
+
assert_equal String, last_location.class
|
|
289
|
+
assert_equal @expected_url, last_location
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
246
293
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: downspout
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash:
|
|
4
|
+
hash: 5
|
|
5
5
|
prerelease:
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 2
|
|
9
|
-
-
|
|
10
|
-
version: 0.2.
|
|
9
|
+
- 9
|
|
10
|
+
version: 0.2.9
|
|
11
11
|
platform: ruby
|
|
12
12
|
authors:
|
|
13
13
|
- Phi.Sanders
|
|
@@ -15,7 +15,7 @@ autorequire:
|
|
|
15
15
|
bindir: bin
|
|
16
16
|
cert_chain: []
|
|
17
17
|
|
|
18
|
-
date: 2011-04-
|
|
18
|
+
date: 2011-04-27 00:00:00 -04:00
|
|
19
19
|
default_executable:
|
|
20
20
|
dependencies:
|
|
21
21
|
- !ruby/object:Gem::Dependency
|