summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-19 11:56:22 +0000
committerknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-19 11:56:22 +0000
commitcfe710763e7eb44555cd01999534ed569e9fbf44 (patch)
tree7cd47ee99ede0bd1ce681a2a709288b4fce5694a
parent09f5560c5721ad779cc2e740c4a77ad6aca1b530 (diff)
Merge this too.
* lib/uri/ftp.rb, lib/uri/generic.rb, test/uri/test_common.rb, test/uri/test_ftp.rb, test/uri/test_generic.rb: backported from 1.9. [ruby-dev:31318] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8_7@16085 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog6
-rw-r--r--NEWS14
-rw-r--r--lib/uri/ftp.rb86
-rw-r--r--lib/uri/generic.rb104
-rw-r--r--test/uri/test_common.rb5
-rw-r--r--test/uri/test_ftp.rb22
-rw-r--r--test/uri/test_generic.rb80
7 files changed, 206 insertions, 111 deletions
diff --git a/ChangeLog b/ChangeLog
index 1cce913a59..0fd21adedf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Sat Apr 19 20:54:42 2008 akira yamada <akira@arika.org>
+
+ * lib/uri/ftp.rb, lib/uri/generic.rb, test/uri/test_common.rb,
+ test/uri/test_ftp.rb, test/uri/test_generic.rb: backported from 1.9.
+ [ruby-dev:31318]
+
Sat Apr 19 20:35:02 2008 Akinori MUSHA <knu@iDaemons.org>
* lib/yaml/baseemitter.rb, lib/yaml/encoding.rb: performance
diff --git a/NEWS b/NEWS
index 835f44bf64..722dd8fe16 100644
--- a/NEWS
+++ b/NEWS
@@ -306,6 +306,17 @@ with all sufficient information, see the ChangeLog file.
* uri
* added LDAPS scheme.
+ * Change for RFC3986:
+ * FTP
+ * URI('ftp://example.com/foo').path #=> 'foo'
+ * URI('ftp://example.com/%2Ffoo').path #=> '/foo'
+ * URI::FTP.build([nil, 'example.com', nil, '/foo', 'i').to_s #=> 'ftp://example.com/%2Ffoo;type=i'
+ * URI merge
+ * URI('http://a/b/c/d;p?q').merge('?y') == URI('http://a/b/c/d;p?y')
+ * URI('http://a/b/c/d;p?q').merge('/./g') == URI('http://a/g')
+ * URI('http://a/b/c/d;p?q').merge('/../g') == URI('http://a/g')
+ * URI('http://a/b/c/d;p?q').merge('../../../g') == URI('http://a/g')
+ * URI('http://a/b/c/d;p?q').merge('../../../../g') == URI('http://a/g')
* rss
@@ -334,6 +345,9 @@ with all sufficient information, see the ChangeLog file.
* New method:
* Dir.mktmpdir
+* uri
+
+ * See above for details.
== Changes since the 1.8.5 release
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
index 26109e4d27..1a18e9cae7 100644
--- a/lib/uri/ftp.rb
+++ b/lib/uri/ftp.rb
@@ -11,7 +11,7 @@ require 'uri/generic'
module URI
#
- # RFC1738 section 3.2.
+ # FTP URI syntax is defined by RFC1738 section 3.2.
#
class FTP < Generic
DEFAULT_PORT = 21
@@ -22,12 +22,11 @@ module URI
:path, :typecode
].freeze
#
- # Typecode is, "a", "i" or "d".
- # As for "a" the text, as for "i" binary,
- # as for "d" the directory is displayed.
- # "A" with the text, as for "i" being binary,
- # is because the respective data type was called ASCII and
- # IMAGE with the protocol of FTP.
+ # Typecode is "a", "i" or "d".
+ #
+ # * "a" indicates a text file (the FTP command was ASCII)
+ # * "i" indicates a binary file (FTP command IMAGE)
+ # * "d" indicates the contents of a directory should be displayed
#
TYPECODE = ['a', 'i', 'd'].freeze
TYPECODE_PREFIX = ';type='.freeze
@@ -52,11 +51,43 @@ module URI
#
# == Description
#
- # Creates a new URI::FTP object from components of URI::FTP with
- # check. It is scheme, userinfo, host, port, path and typecode. It
- # provided by an Array or a Hash. typecode is "a", "i" or "d".
+ # Creates a new URI::FTP object from components, with syntax checking.
+ #
+ # The components accepted are +userinfo+, +host+, +port+, +path+ and
+ # +typecode+.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the order
+ # [userinfo, host, port, path, typecode]
+ #
+ # If the path supplied is absolute, it will be escaped in order to
+ # make it absolute in the URI. Examples:
+ #
+ # require 'uri'
+ #
+ # uri = URI::FTP.build(['user:password', 'ftp.example.com', nil,
+ # '/path/file.> zip', 'i'])
+ # puts uri.to_s -> ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=a
+ #
+ # uri2 = URI::FTP.build({:host => 'ftp.example.com',
+ # :path => 'ruby/src'})
+ # puts uri2.to_s -> ftp://ftp.example.com/ruby/src
#
def self.build(args)
+
+ # Fix the incoming path to be generic URL syntax
+ # FTP path -> URL path
+ # foo/bar /foo/bar
+ # /foo/bar /%2Ffoo/bar
+ #
+ if args.kind_of?(Array)
+ args[3] = '/' + args[3].sub(/^\//, '%2F')
+ else
+ args[:path] = '/' + args[:path].sub(/^\//, '%2F')
+ end
+
tmp = Util::make_components_hash(self, args)
if tmp[:typecode]
@@ -72,16 +103,14 @@ module URI
#
# == Description
#
- # Create a new URI::FTP object from ``generic'' components with no
- # check.
+ # Creates a new URI::FTP object from generic URL components with no
+ # syntax checking.
#
- # == Usage
+ # Unlike build(), this method does not escape the path component as
+ # required by RFC1738; instead it is treated as per RFC2396.
#
- # require 'uri'
- # p ftp = URI.parse("ftp://ftp.ruby-lang.org/pub/ruby/;type=d")
- # # => #<URI::FTP:0x201fad08 URL:ftp://ftp.ruby-lang.org/pub/ruby/;type=d>
- # p ftp.typecode
- # # => "d"
+ # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
+ # +opaque+, +query+ and +fragment+, in that order.
#
def initialize(*arg)
super(*arg)
@@ -130,6 +159,27 @@ module URI
return tmp
end
+ # Returns the path from an FTP URI.
+ #
+ # RFC 1738 specifically states that the path for an FTP URI does not
+ # include the / which separates the URI path from the URI host. Example:
+ #
+ # ftp://ftp.example.com/pub/ruby
+ #
+ # The above URI indicates that the client should connect to
+ # ftp.example.com then cd pub/ruby from the initial login directory.
+ #
+ # If you want to cd to an absolute directory, you must include an
+ # escaped / (%2F) in the path. Example:
+ #
+ # ftp://ftp.example.com/%2Fpub/ruby
+ #
+ # This method will then return "/pub/ruby"
+ #
+ def path
+ return @path.sub(/^\//,'').sub(/^%2F/i,'/')
+ end
+
def to_s
save_path = nil
if @typecode
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 88b5078466..d907e0b4b2 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -616,65 +616,65 @@ module URI
private :split_path
def merge_path(base, rel)
+
# RFC2396, Section 5.2, 5)
- if rel[0] == ?/ #/
- # RFC2396, Section 5.2, 5)
- return rel
+ # RFC2396, Section 5.2, 6)
+ base_path = split_path(base)
+ rel_path = split_path(rel)
+
+ # RFC2396, Section 5.2, 6), a)
+ base_path << '' if base_path.last == '..'
+ while i = base_path.index('..')
+ base_path.slice!(i - 1, 2)
+ end
- else
- # RFC2396, Section 5.2, 6)
- base_path = split_path(base)
- rel_path = split_path(rel)
-
- # RFC2396, Section 5.2, 6), a)
- base_path << '' if base_path.last == '..'
- while i = base_path.index('..')
- base_path.slice!(i - 1, 2)
- end
- if base_path.empty?
- base_path = [''] # keep '/' for root directory
- else
- base_path.pop
- end
+ if (first = rel_path.first) and first.empty?
+ base_path.clear
+ rel_path.shift
+ end
- # RFC2396, Section 5.2, 6), c)
- # RFC2396, Section 5.2, 6), d)
- rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
- rel_path.delete('.')
-
- # RFC2396, Section 5.2, 6), e)
- tmp = []
- rel_path.each do |x|
- if x == '..' &&
- !(tmp.empty? || tmp.last == '..')
- tmp.pop
- else
- tmp << x
- end
+ # RFC2396, Section 5.2, 6), c)
+ # RFC2396, Section 5.2, 6), d)
+ rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
+ rel_path.delete('.')
+
+ # RFC2396, Section 5.2, 6), e)
+ tmp = []
+ rel_path.each do |x|
+ if x == '..' &&
+ !(tmp.empty? || tmp.last == '..')
+ tmp.pop
+ else
+ tmp << x
end
+ end
- add_trailer_slash = true
- while x = tmp.shift
- if x == '..' && base_path.size > 1
- # RFC2396, Section 4
- # a .. or . in an absolute path has no special meaning
- base_path.pop
- else
- # if x == '..'
- # valid absolute (but abnormal) path "/../..."
- # else
- # valid absolute path
- # end
- base_path << x
- tmp.each {|t| base_path << t}
- add_trailer_slash = false
- break
- end
+ add_trailer_slash = !tmp.empty?
+ if base_path.empty?
+ base_path = [''] # keep '/' for root directory
+ elsif add_trailer_slash
+ base_path.pop
+ end
+ while x = tmp.shift
+ if x == '..'
+ # RFC2396, Section 4
+ # a .. or . in an absolute path has no special meaning
+ base_path.pop if base_path.size > 1
+ else
+ # if x == '..'
+ # valid absolute (but abnormal) path "/../..."
+ # else
+ # valid absolute path
+ # end
+ base_path << x
+ tmp.each {|t| base_path << t}
+ add_trailer_slash = false
+ break
end
- base_path.push('') if add_trailer_slash
-
- return base_path.join('/')
end
+ base_path.push('') if add_trailer_slash
+
+ return base_path.join('/')
end
private :merge_path
diff --git a/test/uri/test_common.rb b/test/uri/test_common.rb
index a159901ea6..0fe031bdee 100644
--- a/test/uri/test_common.rb
+++ b/test/uri/test_common.rb
@@ -12,7 +12,6 @@ class TestCommon < Test::Unit::TestCase
end
def test_extract
- # ruby-list:36086
assert_equal(['http://example.com'],
URI.extract('http://example.com'))
assert_equal(['http://example.com'],
@@ -20,9 +19,9 @@ class TestCommon < Test::Unit::TestCase
assert_equal(['http://example.com/foo)'],
URI.extract('(http://example.com/foo)'))
assert_equal(['http://example.jphttp://example.jp'],
- URI.extract('http://example.jphttp://example.jp'))
+ URI.extract('http://example.jphttp://example.jp'), "[ruby-list:36086]")
assert_equal(['http://example.jphttp://example.jp'],
- URI.extract('http://example.jphttp://example.jp', ['http']))
+ URI.extract('http://example.jphttp://example.jp', ['http']), "[ruby-list:36086]")
assert_equal(['http://', 'mailto:'].sort,
URI.extract('ftp:// http:// mailto: https://', ['http', 'mailto']).sort)
# reported by Doug Kearns <djkea2@mugca.its.monash.edu.au>
diff --git a/test/uri/test_ftp.rb b/test/uri/test_ftp.rb
index a7b59efebf..10abd29502 100644
--- a/test/uri/test_ftp.rb
+++ b/test/uri/test_ftp.rb
@@ -15,7 +15,7 @@ class TestFTP < Test::Unit::TestCase
exp = [
'ftp',
'user:pass', 'host.com', URI::FTP.default_port,
- '/abc/def', nil,
+ 'abc/def', nil,
]
ary = [
url.scheme, url.userinfo, url.host, url.port,
@@ -27,6 +27,26 @@ class TestFTP < Test::Unit::TestCase
assert_equal('pass', url.password)
end
+ def test_paths
+ # If you think what's below is wrong, please read RubyForge bug 2055,
+ # RFC 1738 section 3.2.2, and RFC 2396.
+ u = URI.parse('ftp://ftp.example.com/foo/bar/file.ext')
+ assert_equal(u.path, 'foo/bar/file.ext')
+ u = URI.parse('ftp://ftp.example.com//foo/bar/file.ext')
+ assert_equal(u.path, '/foo/bar/file.ext')
+ u = URI.parse('ftp://ftp.example.com/%2Ffoo/bar/file.ext')
+ assert_equal(u.path, '/foo/bar/file.ext')
+ end
+
+ def test_assemble
+ # uri/ftp is conservative and uses the older RFC 1738 rules, rather than
+ # assuming everyone else has implemented RFC 2396.
+ uri = URI::FTP.build(['user:password', 'ftp.example.com', nil,
+ '/path/file.zip', 'i'])
+ assert_equal(uri.to_s,
+ 'ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=i')
+ end
+
def test_select
assert_equal(['ftp', 'a.b.c', 21], URI.parse('ftp://a.b.c/').select(:scheme, :host, :port))
u = URI.parse('ftp://a.b.c/')
diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb
index 8a7feb4982..5cd4c7f7ae 100644
--- a/test/uri/test_generic.rb
+++ b/test/uri/test_generic.rb
@@ -38,6 +38,17 @@ class TestGeneric < Test::Unit::TestCase
exp = [
'ftp',
nil, 'ftp.is.co.za', URI::FTP.default_port,
+ 'rfc/rfc1808.txt', nil,
+ ]
+ ary = uri_to_ary(url)
+ assert_equal(exp, ary)
+ # 1'
+ url = URI.parse('ftp://ftp.is.co.za/%2Frfc/rfc1808.txt')
+ assert_kind_of(URI::FTP, url)
+
+ exp = [
+ 'ftp',
+ nil, 'ftp.is.co.za', URI::FTP.default_port,
'/rfc/rfc1808.txt', nil,
]
ary = uri_to_ary(url)
@@ -124,11 +135,10 @@ class TestGeneric < Test::Unit::TestCase
assert_kind_of(URI::Generic, url)
# 9
- # [ruby-dev:25667]
url = URI.parse('ftp://:pass@localhost/')
- assert_equal('', url.user)
+ assert_equal('', url.user, "[ruby-dev:25667]")
assert_equal('pass', url.password)
- assert_equal(':pass', url.userinfo)
+ assert_equal(':pass', url.userinfo, "[ruby-dev:25667]")
url = URI.parse('ftp://user@localhost/')
assert_equal('user', url.user)
assert_equal(nil, url.password)
@@ -155,9 +165,8 @@ class TestGeneric < Test::Unit::TestCase
assert_equal(URI.parse('http://foo/baz'), u3 + '/baz')
assert_equal(URI.parse('http://foo/baz'), u4 + '/baz')
- # from [ruby-dev:11508] Re: uri
url = URI.parse('http://hoge/a.html') + 'b.html'
- assert_equal('http://hoge/b.html', url.to_s)
+ assert_equal('http://hoge/b.html', url.to_s, "[ruby-dev:11508]")
# reported by Mr. Kubota <em6t-kbt@asahi-net.or.jp>
url = URI.parse('http://a/b') + 'http://x/y'
@@ -174,15 +183,13 @@ class TestGeneric < Test::Unit::TestCase
assert(nil != u.merge!("../baz"))
assert_equal('http://foo/baz', u.to_s)
- # [ruby-dev:23628]
u0 = URI.parse('mailto:foo@example.com')
u1 = URI.parse('mailto:foo@example.com#bar')
- assert_equal(uri_to_ary(u0 + '#bar'), uri_to_ary(u1))
+ assert_equal(uri_to_ary(u0 + '#bar'), uri_to_ary(u1), "[ruby-dev:23628]")
- # [ruby-list:39838]
u0 = URI.parse('http://www.example.com/')
u1 = URI.parse('http://www.example.com/foo/..') + './'
- assert_equal(u0, u1)
+ assert_equal(u0, u1, "[ruby-list:39838]")
u0 = URI.parse('http://www.example.com/foo/')
u1 = URI.parse('http://www.example.com/foo/bar/..') + './'
assert_equal(u0, u1)
@@ -196,11 +203,10 @@ class TestGeneric < Test::Unit::TestCase
u1 = URI.parse('http://www.example.com/foo/bar/baz/../..') + './'
assert_equal(u0, u1)
- # [ruby-list:39844]
u = URI.parse('http://www.example.com/')
u0 = u + './foo/'
u1 = u + './foo/bar/..'
- assert_equal(u0, u1)
+ assert_equal(u0, u1, "[ruby-list:39844]")
u = URI.parse('http://www.example.com/')
u0 = u + './'
u1 = u + './foo/bar/../..'
@@ -238,7 +244,7 @@ class TestGeneric < Test::Unit::TestCase
assert_equal('', url.to_s)
end
- def test_rfc2396_examples
+ def test_rfc3986_examples
# http://a/b/c/d;p?q
# g:h = g:h
url = @base_url.merge('g:h')
@@ -296,11 +302,11 @@ class TestGeneric < Test::Unit::TestCase
assert_equal('//g', url.to_s)
# http://a/b/c/d;p?q
-# ?y = http://a/b/c/?y
+# ?y = http://a/b/c/d;p?y
url = @base_url.merge('?y')
assert_kind_of(URI::HTTP, url)
- assert_equal('http://a/b/c/?y', url.to_s)
- url = @base_url.route_to('http://a/b/c/?y')
+ assert_equal('http://a/b/c/d;p?y', url.to_s)
+ url = @base_url.route_to('http://a/b/c/d;p?y')
assert_kind_of(URI::Generic, url)
assert_equal('?y', url.to_s)
@@ -314,11 +320,11 @@ class TestGeneric < Test::Unit::TestCase
assert_equal('g?y', url.to_s)
# http://a/b/c/d;p?q
-# #s = (current document)#s
+# #s = http://a/b/c/d;p?q#s
url = @base_url.merge('#s')
assert_kind_of(URI::HTTP, url)
- assert_equal(@base_url.to_s + '#s', url.to_s)
- url = @base_url.route_to(@base_url.to_s + '#s')
+ assert_equal('http://a/b/c/d;p?q#s', url.to_s)
+ url = @base_url.route_to('http://a/b/c/d;p?q#s')
assert_kind_of(URI::Generic, url)
assert_equal('#s', url.to_s)
@@ -452,22 +458,22 @@ class TestGeneric < Test::Unit::TestCase
assert_equal('', url.to_s)
# http://a/b/c/d;p?q
-# /./g = http://a/./g
+# /./g = http://a/g
url = @base_url.merge('/./g')
assert_kind_of(URI::HTTP, url)
- assert_equal('http://a/./g', url.to_s)
- url = @base_url.route_to('http://a/./g')
- assert_kind_of(URI::Generic, url)
- assert_equal('/./g', url.to_s)
+ assert_equal('http://a/g', url.to_s)
+# url = @base_url.route_to('http://a/./g')
+# assert_kind_of(URI::Generic, url)
+# assert_equal('/./g', url.to_s)
# http://a/b/c/d;p?q
-# /../g = http://a/../g
+# /../g = http://a/g
url = @base_url.merge('/../g')
assert_kind_of(URI::HTTP, url)
- assert_equal('http://a/../g', url.to_s)
- url = @base_url.route_to('http://a/../g')
- assert_kind_of(URI::Generic, url)
- assert_equal('/../g', url.to_s)
+ assert_equal('http://a/g', url.to_s)
+# url = @base_url.route_to('http://a/../g')
+# assert_kind_of(URI::Generic, url)
+# assert_equal('/../g', url.to_s)
# http://a/b/c/d;p?q
# g. = http://a/b/c/g.
@@ -506,24 +512,24 @@ class TestGeneric < Test::Unit::TestCase
assert_equal('..g', url.to_s)
# http://a/b/c/d;p?q
-# ../../../g = http://a/../g
+# ../../../g = http://a/g
url = @base_url.merge('../../../g')
assert_kind_of(URI::HTTP, url)
- assert_equal('http://a/../g', url.to_s)
- url = @base_url.route_to('http://a/../g')
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
assert_kind_of(URI::Generic, url)
- assert('../../../g' != url.to_s) # ok? yes, it confuses you
- assert_equal('/../g', url.to_s) # and it is clearly
+ assert('../../../g' != url.to_s) # ok? yes, it confuses you
+ assert_equal('../../g', url.to_s) # and it is clearly
# http://a/b/c/d;p?q
-# ../../../../g = http://a/../../g
+# ../../../../g = http://a/g
url = @base_url.merge('../../../../g')
assert_kind_of(URI::HTTP, url)
- assert_equal('http://a/../../g', url.to_s)
- url = @base_url.route_to('http://a/../../g')
+ assert_equal('http://a/g', url.to_s)
+ url = @base_url.route_to('http://a/g')
assert_kind_of(URI::Generic, url)
assert('../../../../g' != url.to_s) # ok? yes, it confuses you
- assert_equal('/../../g', url.to_s) # and it is clearly
+ assert_equal('../../g', url.to_s) # and it is clearly
# http://a/b/c/d;p?q
# ./../g = http://a/b/g