views:

217

answers:

2

I'm trying to parse a URI that has brackets - [ and ] - in it. I have tried to parse this directly with URI.parse but the brackets cause this to fail. I therefore tried to encode the URI with CGI::escape which takes care of the brackets but when I try to parse this encoded URI with URI.parse it doesn't seem to recognise it as a URI and puts the entire URI into the path object.

To demonstrate in an irb session;

irb(main):001:0> require 'uri'
=> true
irb(main):002:0> require 'cgi'
=> true
irb(main):003:0> name = "http://www.website.com/dir1/dir[2]/file.txt"
=> "http://www.website.com/dir1/dir[2]/file.txt"
irb(main):004:0> encoded_name = CGI::escape(name)
=> "http%3A%2F%2Fwww.website.com%2Fdir1%2Fdir%5B2%5D%2Ffile.txt"
irb(main):005:0> parsed_name = URI.parse(encoded_name)
=> #<URI::Generic:0x00000001e8f520 URL:http%3A%2F%2Fwww.website.com%2Fdir1%2Fdir%5B2%5D%2Ffile.txt>
irb(main):006:0> parsed_name.scheme
=> nil
irb(main):007:0> parsed_name.host
=> nil
irb(main):008:0> parsed_name.path
=> "http%3A%2F%2Fwww.website.com%2Fdir1%2Fdir%5B2%5D%2Ffile.txt"
irb(main):009:0> URI.split(encoded_name)
=> [nil, nil, nil, nil, nil, "http%3A%2F%2Fwww.website.com%2Fdir1%2Fdir%5B2%5D%2Ffile.txt", nil, nil, nil]

Anyway, my work around at the moment is the following ugly, but effective, hack

encoded_name = name.gsub(/\[/,"%5B").gsub(/\]/,"%5D")

Parsing this with URI.parse produces the desired result but won't cope if other strange characters find their way into my URIs. So my question is, is there a solid way of doing this that won't fall down?

+1  A: 

You could use URI.encode:

encoded_name = URI.encode(name, '[]')
mtyaka
+3  A: 

The problem lies in trying to apply CGI::escape to the whole URI. When you do that, you lose the front part of the URI that holds the scheme and the URI parser gets lost after that. You may want to try something based on mtyaka's answer:

irb(main):015:0> encoded_name = URI.encode(name, '[]')
=> "http://www.website.com/dir1/dir%5B2%5D/file.txt"
irb(main):016:0> parsed_name = URI.parse(encoded_name)
=> #<URI::HTTP:0xb76ff358 URL:http://www.website.com/dir1/dir%5B2%5D/file.txt&gt;
irb(main):017:0> parsed_name.scheme
=> "http"
irb(main):018:0> parsed_name.host
=> "www.website.com"
irb(main):019:0> parsed_name.path
=> "/dir1/dir%5B2%5D/file.txt"

To get the original path, just URI.decode whatever you get from parsed_name.path.

Thiago Arrais
Beautiful. That solves the problem and produces much cleaner code.
brad