├── .gitignore ├── .travis.yml ├── README.md ├── Rakefile ├── build_config.rb ├── mrbgem.rake ├── mrblib ├── 00_rfc2396_parser.rb ├── 00_rfc3986_parser.rb ├── common.rb ├── generic.rb ├── http.rb ├── https.rb ├── mailto.rb └── uri.rb ├── src └── uri.c └── test ├── test_common.rb ├── test_generic.rb ├── test_http.rb ├── test_mailto.rb └── test_parser.rb /.gitignore: -------------------------------------------------------------------------------- 1 | mruby 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get -qq update 7 | install: 8 | - sudo apt-get -qq install rake bison git gperf 9 | script: 10 | - rake test 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mruby-uri 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/zzak/mruby-uri.svg?branch=master)](https://travis-ci.org/zzak/mruby-uri) 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || "build_config.rb") 2 | 3 | file :mruby do 4 | sh "git clone --depth 1 git://github.com/mruby/mruby.git" 5 | end 6 | 7 | desc "compile binary" 8 | task :compile => :mruby do 9 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all" 10 | end 11 | 12 | desc "test" 13 | task :test => :mruby do 14 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake test" 15 | end 16 | 17 | desc "cleanup" 18 | task :clean do 19 | sh "cd mruby && rake deep_clean" 20 | end 21 | -------------------------------------------------------------------------------- /build_config.rb: -------------------------------------------------------------------------------- 1 | MRuby::Build.new do |conf| 2 | toolchain :gcc 3 | 4 | conf.gem File.expand_path(File.dirname(__FILE__)) 5 | end 6 | 7 | MRuby::Build.new('test') do |conf| 8 | toolchain :gcc 9 | 10 | enable_debug 11 | #conf.enable_bintest 12 | conf.enable_test 13 | 14 | conf.gembox 'default' 15 | conf.gem File.expand_path(File.dirname(__FILE__)) 16 | end 17 | -------------------------------------------------------------------------------- /mrbgem.rake: -------------------------------------------------------------------------------- 1 | MRuby::Gem::Specification.new('mruby-uri') do |spec| 2 | spec.license = 'MIT' 3 | spec.author = 'zzak' 4 | spec.summary = 'Extension for handling URI in mruby' 5 | 6 | spec.add_dependency 'mruby-string-ext', core: 'mruby-string-ext' 7 | spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext' 8 | spec.add_dependency 'mruby-onig-regexp', mgem: 'mruby-onig-regexp' 9 | spec.add_test_dependency 'mruby-mtest', mgem: 'mruby-mtest' 10 | end 11 | -------------------------------------------------------------------------------- /mrblib/00_rfc2396_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | #-- 3 | # = uri/common.rb 4 | # 5 | # Author:: Akira Yamada 6 | # Revision:: $Id$ 7 | # License:: 8 | # You can redistribute it and/or modify it under the same term as Ruby. 9 | # 10 | # See URI for general documentation 11 | # 12 | 13 | module URI 14 | # 15 | # Includes URI::REGEXP::PATTERN 16 | # 17 | module RFC2396_REGEXP 18 | # 19 | # Patterns used to parse URI's 20 | # 21 | module PATTERN 22 | # :stopdoc: 23 | 24 | # RFC 2396 (URI Generic Syntax) 25 | # RFC 2732 (IPv6 Literal Addresses in URL's) 26 | # RFC 2373 (IPv6 Addressing Architecture) 27 | 28 | # alpha = lowalpha | upalpha 29 | ALPHA = "a-zA-Z" 30 | # alphanum = alpha | digit 31 | ALNUM = "#{ALPHA}\\d" 32 | 33 | # hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | 34 | # "a" | "b" | "c" | "d" | "e" | "f" 35 | HEX = "a-fA-F\\d" 36 | # escaped = "%" hex hex 37 | ESCAPED = "%[#{HEX}]{2}" 38 | # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | 39 | # "(" | ")" 40 | # unreserved = alphanum | mark 41 | UNRESERVED = "\\-_.!~*'()#{ALNUM}" 42 | # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | 43 | # "$" | "," 44 | # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | 45 | # "$" | "," | "[" | "]" (RFC 2732) 46 | RESERVED = ";/?:@&=+$,\\[\\]" 47 | 48 | # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum 49 | DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)" 50 | # toplabel = alpha | alpha *( alphanum | "-" ) alphanum 51 | TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)" 52 | # hostname = *( domainlabel "." ) toplabel [ "." ] 53 | HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?" 54 | 55 | # :startdoc: 56 | end # PATTERN 57 | 58 | # :startdoc: 59 | end # REGEXP 60 | 61 | # class that Parses String's into URI's 62 | # 63 | # It contains a Hash set of patterns and Regexp's that match and validate. 64 | # 65 | class RFC2396_Parser 66 | include RFC2396_REGEXP 67 | 68 | # 69 | # == Synopsis 70 | # 71 | # URI::Parser.new([opts]) 72 | # 73 | # == Args 74 | # 75 | # The constructor accepts a hash as options for parser. 76 | # Keys of options are pattern names of URI components 77 | # and values of options are pattern strings. 78 | # The constructor generates set of regexps for parsing URIs. 79 | # 80 | # You can use the following keys: 81 | # 82 | # * :ESCAPED (URI::PATTERN::ESCAPED in default) 83 | # * :UNRESERVED (URI::PATTERN::UNRESERVED in default) 84 | # * :DOMLABEL (URI::PATTERN::DOMLABEL in default) 85 | # * :TOPLABEL (URI::PATTERN::TOPLABEL in default) 86 | # * :HOSTNAME (URI::PATTERN::HOSTNAME in default) 87 | # 88 | # == Examples 89 | # 90 | # p = URI::Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})") 91 | # u = p.parse("http://example.jp/%uABCD") #=> # 92 | # URI.parse(u.to_s) #=> raises URI::InvalidURIError 93 | # 94 | # s = "http://example.com/ABCD" 95 | # u1 = p.parse(s) #=> # 96 | # u2 = URI.parse(s) #=> # 97 | # u1 == u2 #=> true 98 | # u1.eql?(u2) #=> false 99 | # 100 | def initialize(opts = {}) 101 | @pattern = initialize_pattern(opts) 102 | @pattern.freeze 103 | 104 | @regexp = initialize_regexp(@pattern) 105 | @regexp.freeze 106 | end 107 | 108 | # The Hash of patterns. 109 | # 110 | # see also URI::Parser.initialize_pattern 111 | attr_reader :pattern 112 | 113 | # The Hash of Regexp 114 | # 115 | # see also URI::Parser.initialize_regexp 116 | attr_reader :regexp 117 | 118 | # Returns a split URI against regexp[:ABS_URI] 119 | def split(uri) 120 | case uri 121 | when '' 122 | # null uri 123 | 124 | when @regexp[:ABS_URI] 125 | scheme, opaque, userinfo, host, port, 126 | registry, path, query, fragment = $~[1..-1] 127 | 128 | # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] 129 | 130 | # absoluteURI = scheme ":" ( hier_part | opaque_part ) 131 | # hier_part = ( net_path | abs_path ) [ "?" query ] 132 | # opaque_part = uric_no_slash *uric 133 | 134 | # abs_path = "/" path_segments 135 | # net_path = "//" authority [ abs_path ] 136 | 137 | # authority = server | reg_name 138 | # server = [ [ userinfo "@" ] hostport ] 139 | 140 | if !scheme 141 | raise InvalidURIError, 142 | "bad URI(absolute but no scheme): #{uri}" 143 | end 144 | if !opaque && (!path && (!host && !registry)) 145 | raise InvalidURIError, 146 | "bad URI(absolute but no path): #{uri}" 147 | end 148 | 149 | when @regexp[:REL_URI] 150 | scheme = nil 151 | opaque = nil 152 | 153 | userinfo, host, port, registry, 154 | rel_segment, abs_path, query, fragment = $~[1..-1] 155 | if rel_segment && abs_path 156 | path = rel_segment + abs_path 157 | elsif rel_segment 158 | path = rel_segment 159 | elsif abs_path 160 | path = abs_path 161 | end 162 | 163 | # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] 164 | 165 | # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] 166 | 167 | # net_path = "//" authority [ abs_path ] 168 | # abs_path = "/" path_segments 169 | # rel_path = rel_segment [ abs_path ] 170 | 171 | # authority = server | reg_name 172 | # server = [ [ userinfo "@" ] hostport ] 173 | 174 | else 175 | raise InvalidURIError, "bad URI(is not URI?): #{uri}" 176 | end 177 | 178 | path = '' if !path && !opaque # (see RFC2396 Section 5.2) 179 | ret = [ 180 | scheme, 181 | userinfo, host, port, # X 182 | registry, # X 183 | path, # Y 184 | opaque, # Y 185 | query, 186 | fragment 187 | ] 188 | return ret 189 | end 190 | 191 | # 192 | # == Args 193 | # 194 | # +uri+:: 195 | # String 196 | # 197 | # == Description 198 | # 199 | # parses +uri+ and constructs either matching URI scheme object 200 | # (FTP, HTTP, HTTPS, LDAP, LDAPS, or MailTo) or URI::Generic 201 | # 202 | # == Usage 203 | # 204 | # p = URI::Parser.new 205 | # p.parse("ldap://ldap.example.com/dc=example?user=john") 206 | # #=> # 207 | # 208 | def parse(uri) 209 | scheme, userinfo, host, port, 210 | registry, path, opaque, query, fragment = self.split(uri) 211 | 212 | if scheme && URI.scheme_list.include?(scheme.upcase) 213 | URI.scheme_list[scheme.upcase].new(scheme, userinfo, host, port, 214 | registry, path, opaque, query, 215 | fragment, self) 216 | else 217 | Generic.new(scheme, userinfo, host, port, 218 | registry, path, opaque, query, 219 | fragment, self) 220 | end 221 | end 222 | 223 | 224 | # 225 | # == Args 226 | # 227 | # +uris+:: 228 | # an Array of Strings 229 | # 230 | # == Description 231 | # 232 | # Attempts to parse and merge a set of URIs 233 | # 234 | def join(*uris) 235 | uris[0] = convert_to_uri(uris[0]) 236 | uris.inject :merge 237 | end 238 | 239 | # 240 | # :call-seq: 241 | # extract( str ) 242 | # extract( str, schemes ) 243 | # extract( str, schemes ) {|item| block } 244 | # 245 | # == Args 246 | # 247 | # +str+:: 248 | # String to search 249 | # +schemes+:: 250 | # Patterns to apply to +str+ 251 | # 252 | # == Description 253 | # 254 | # Attempts to parse and merge a set of URIs 255 | # If no +block+ given , then returns the result, 256 | # else it calls +block+ for each element in result. 257 | # 258 | # see also URI::Parser.make_regexp 259 | # 260 | def extract(str, schemes = nil) 261 | if block_given? 262 | str.scan(make_regexp(schemes)) { yield $& } 263 | nil 264 | else 265 | result = [] 266 | str.scan(make_regexp(schemes)) { result.push $& } 267 | result 268 | end 269 | end 270 | 271 | # returns Regexp that is default self.regexp[:ABS_URI_REF], 272 | # unless +schemes+ is provided. Then it is a Regexp.union with self.pattern[:X_ABS_URI] 273 | def make_regexp(schemes = nil) 274 | unless schemes 275 | @regexp[:ABS_URI_REF] 276 | else 277 | /(?=#{schemes.join("|")}:)#{@pattern[:X_ABS_URI]}/x 278 | end 279 | end 280 | 281 | # 282 | # :call-seq: 283 | # escape( str ) 284 | # escape( str, unsafe ) 285 | # 286 | # == Args 287 | # 288 | # +str+:: 289 | # String to make safe 290 | # +unsafe+:: 291 | # Regexp to apply. Defaults to self.regexp[:UNSAFE] 292 | # 293 | # == Description 294 | # 295 | # constructs a safe String from +str+, removing unsafe characters, 296 | # replacing them with codes. 297 | # 298 | def escape(str, unsafe = @regexp[:UNSAFE]) 299 | unless unsafe.kind_of?(Regexp) 300 | # perhaps unsafe is String object 301 | unsafe = Regexp.new("[#{Regexp.quote(unsafe)}]", nil) 302 | end 303 | str.gsub(unsafe) do 304 | us = $& 305 | tmp = '' 306 | us.each_byte do |uc| 307 | tmp << sprintf('%%%02X', uc) 308 | end 309 | tmp 310 | end 311 | end 312 | 313 | # 314 | # :call-seq: 315 | # unescape( str ) 316 | # unescape( str, unsafe ) 317 | # 318 | # == Args 319 | # 320 | # +str+:: 321 | # String to remove escapes from 322 | # +unsafe+:: 323 | # Regexp to apply. Defaults to self.regexp[:ESCAPED] 324 | # 325 | # == Description 326 | # 327 | # Removes escapes from +str+ 328 | # 329 | def unescape(str, escaped = @regexp[:ESCAPED]) 330 | str.gsub(escaped) { $&[1, 2].hex.chr } 331 | end 332 | 333 | def inspect 334 | to_s 335 | end 336 | 337 | private 338 | 339 | # Constructs the default Hash of patterns 340 | def initialize_pattern(opts = {}) 341 | ret = {} 342 | ret[:ESCAPED] = escaped = (opts.delete(:ESCAPED) || PATTERN::ESCAPED) 343 | ret[:UNRESERVED] = unreserved = opts.delete(:UNRESERVED) || PATTERN::UNRESERVED 344 | ret[:RESERVED] = reserved = opts.delete(:RESERVED) || PATTERN::RESERVED 345 | ret[:DOMLABEL] = opts.delete(:DOMLABEL) || PATTERN::DOMLABEL 346 | ret[:TOPLABEL] = opts.delete(:TOPLABEL) || PATTERN::TOPLABEL 347 | ret[:HOSTNAME] = hostname = opts.delete(:HOSTNAME) 348 | 349 | # RFC 2396 (URI Generic Syntax) 350 | # RFC 2732 (IPv6 Literal Addresses in URL's) 351 | # RFC 2373 (IPv6 Addressing Architecture) 352 | 353 | # uric = reserved | unreserved | escaped 354 | ret[:URIC] = uric = "(?:[#{unreserved}#{reserved}]|#{escaped})" 355 | # uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | 356 | # "&" | "=" | "+" | "$" | "," 357 | ret[:URIC_NO_SLASH] = uric_no_slash = "(?:[#{unreserved};?:@&=+$,]|#{escaped})" 358 | # query = *uric 359 | ret[:QUERY] = query = "#{uric}*" 360 | # fragment = *uric 361 | ret[:FRAGMENT] = fragment = "#{uric}*" 362 | 363 | # hostname = *( domainlabel "." ) toplabel [ "." ] 364 | # reg-name = *( unreserved / pct-encoded / sub-delims ) # RFC3986 365 | unless hostname 366 | ret[:HOSTNAME] = hostname = "(?:[a-zA-Z0-9\\-.]|%\\h\\h)+" 367 | end 368 | 369 | # RFC 2373, APPENDIX B: 370 | # IPv6address = hexpart [ ":" IPv4address ] 371 | # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT 372 | # hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ] 373 | # hexseq = hex4 *( ":" hex4) 374 | # hex4 = 1*4HEXDIG 375 | # 376 | # XXX: This definition has a flaw. "::" + IPv4address must be 377 | # allowed too. Here is a replacement. 378 | # 379 | # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT 380 | ret[:IPV4ADDR] = ipv4addr = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}" 381 | # hex4 = 1*4HEXDIG 382 | hex4 = "[#{PATTERN::HEX}]{1,4}" 383 | # lastpart = hex4 | IPv4address 384 | lastpart = "(?:#{hex4}|#{ipv4addr})" 385 | # hexseq1 = *( hex4 ":" ) hex4 386 | hexseq1 = "(?:#{hex4}:)*#{hex4}" 387 | # hexseq2 = *( hex4 ":" ) lastpart 388 | hexseq2 = "(?:#{hex4}:)*#{lastpart}" 389 | # IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ] 390 | ret[:IPV6ADDR] = ipv6addr = "(?:#{hexseq2}|(?:#{hexseq1})?::(?:#{hexseq2})?)" 391 | 392 | # IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT 393 | # unused 394 | 395 | # ipv6reference = "[" IPv6address "]" (RFC 2732) 396 | ret[:IPV6REF] = ipv6ref = "\\[#{ipv6addr}\\]" 397 | 398 | # host = hostname | IPv4address 399 | # host = hostname | IPv4address | IPv6reference (RFC 2732) 400 | ret[:HOST] = host = "(?:#{hostname}|#{ipv4addr}|#{ipv6ref})" 401 | # port = *digit 402 | ret[:PORT] = port = '\d*' 403 | # hostport = host [ ":" port ] 404 | ret[:HOSTPORT] = hostport = "#{host}(?::#{port})?" 405 | 406 | # userinfo = *( unreserved | escaped | 407 | # ";" | ":" | "&" | "=" | "+" | "$" | "," ) 408 | ret[:USERINFO] = userinfo = "(?:[#{unreserved};:&=+$,]|#{escaped})*" 409 | 410 | # pchar = unreserved | escaped | 411 | # ":" | "@" | "&" | "=" | "+" | "$" | "," 412 | pchar = "(?:[#{unreserved}:@&=+$,]|#{escaped})" 413 | # param = *pchar 414 | param = "#{pchar}*" 415 | # segment = *pchar *( ";" param ) 416 | segment = "#{pchar}*(?:;#{param})*" 417 | # path_segments = segment *( "/" segment ) 418 | ret[:PATH_SEGMENTS] = path_segments = "#{segment}(?:/#{segment})*" 419 | 420 | # server = [ [ userinfo "@" ] hostport ] 421 | server = "(?:#{userinfo}@)?#{hostport}" 422 | # reg_name = 1*( unreserved | escaped | "$" | "," | 423 | # ";" | ":" | "@" | "&" | "=" | "+" ) 424 | ret[:REG_NAME] = reg_name = "(?:[#{unreserved}$,;:@&=+]|#{escaped})+" 425 | # authority = server | reg_name 426 | authority = "(?:#{server}|#{reg_name})" 427 | 428 | # rel_segment = 1*( unreserved | escaped | 429 | # ";" | "@" | "&" | "=" | "+" | "$" | "," ) 430 | ret[:REL_SEGMENT] = rel_segment = "(?:[#{unreserved};@&=+$,]|#{escaped})+" 431 | 432 | # scheme = alpha *( alpha | digit | "+" | "-" | "." ) 433 | ret[:SCHEME] = scheme = "[#{PATTERN::ALPHA}][\\-+.#{PATTERN::ALPHA}\\d]*" 434 | 435 | # abs_path = "/" path_segments 436 | ret[:ABS_PATH] = abs_path = "/#{path_segments}" 437 | # rel_path = rel_segment [ abs_path ] 438 | ret[:REL_PATH] = rel_path = "#{rel_segment}(?:#{abs_path})?" 439 | # net_path = "//" authority [ abs_path ] 440 | ret[:NET_PATH] = net_path = "//#{authority}(?:#{abs_path})?" 441 | 442 | # hier_part = ( net_path | abs_path ) [ "?" query ] 443 | ret[:HIER_PART] = hier_part = "(?:#{net_path}|#{abs_path})(?:\\?(?:#{query}))?" 444 | # opaque_part = uric_no_slash *uric 445 | ret[:OPAQUE_PART] = opaque_part = "#{uric_no_slash}#{uric}*" 446 | 447 | # absoluteURI = scheme ":" ( hier_part | opaque_part ) 448 | ret[:ABS_URI] = abs_uri = "#{scheme}:(?:#{hier_part}|#{opaque_part})" 449 | # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] 450 | ret[:REL_URI] = rel_uri = "(?:#{net_path}|#{abs_path}|#{rel_path})(?:\\?#{query})?" 451 | 452 | # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] 453 | ret[:URI_REF] = "(?:#{abs_uri}|#{rel_uri})?(?:##{fragment})?" 454 | 455 | ret[:X_ABS_URI] = " 456 | (#{scheme}): (?# 1: scheme) 457 | (?: 458 | (#{opaque_part}) (?# 2: opaque) 459 | | 460 | (?:(?: 461 | //(?: 462 | (?:(?:(#{userinfo})@)? (?# 3: userinfo) 463 | (?:(#{host})(?::(\\d*))?))? (?# 4: host, 5: port) 464 | | 465 | (#{reg_name}) (?# 6: registry) 466 | ) 467 | | 468 | (?!//)) (?# XXX: '//' is the mark for hostport) 469 | (#{abs_path})? (?# 7: path) 470 | )(?:\\?(#{query}))? (?# 8: query) 471 | ) 472 | (?:\\#(#{fragment}))? (?# 9: fragment) 473 | " 474 | 475 | ret[:X_REL_URI] = " 476 | (?: 477 | (?: 478 | // 479 | (?: 480 | (?:(#{userinfo})@)? (?# 1: userinfo) 481 | (#{host})?(?::(\\d*))? (?# 2: host, 3: port) 482 | | 483 | (#{reg_name}) (?# 4: registry) 484 | ) 485 | ) 486 | | 487 | (#{rel_segment}) (?# 5: rel_segment) 488 | )? 489 | (#{abs_path})? (?# 6: abs_path) 490 | (?:\\?(#{query}))? (?# 7: query) 491 | (?:\\#(#{fragment}))? (?# 8: fragment) 492 | " 493 | 494 | ret 495 | end 496 | 497 | # Constructs the default Hash of Regexp's 498 | def initialize_regexp(pattern) 499 | ret = {} 500 | 501 | # for URI::split 502 | ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED) 503 | ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED) 504 | 505 | # for URI::extract 506 | ret[:URI_REF] = Regexp.new(pattern[:URI_REF]) 507 | ret[:ABS_URI_REF] = Regexp.new(pattern[:X_ABS_URI], Regexp::EXTENDED) 508 | ret[:REL_URI_REF] = Regexp.new(pattern[:X_REL_URI], Regexp::EXTENDED) 509 | 510 | # for URI::escape/unescape 511 | ret[:ESCAPED] = Regexp.new(pattern[:ESCAPED]) 512 | ret[:UNSAFE] = Regexp.new("[^#{pattern[:UNRESERVED]}#{pattern[:RESERVED]}]") 513 | 514 | # for Generic#initialize 515 | ret[:SCHEME] = Regexp.new("\\A#{pattern[:SCHEME]}\\z") 516 | ret[:USERINFO] = Regexp.new("\\A#{pattern[:USERINFO]}\\z") 517 | ret[:HOST] = Regexp.new("\\A#{pattern[:HOST]}\\z") 518 | ret[:PORT] = Regexp.new("\\A#{pattern[:PORT]}\\z") 519 | ret[:OPAQUE] = Regexp.new("\\A#{pattern[:OPAQUE_PART]}\\z") 520 | ret[:REGISTRY] = Regexp.new("\\A#{pattern[:REG_NAME]}\\z") 521 | ret[:ABS_PATH] = Regexp.new("\\A#{pattern[:ABS_PATH]}\\z") 522 | ret[:REL_PATH] = Regexp.new("\\A#{pattern[:REL_PATH]}\\z") 523 | ret[:QUERY] = Regexp.new("\\A#{pattern[:QUERY]}\\z") 524 | ret[:FRAGMENT] = Regexp.new("\\A#{pattern[:FRAGMENT]}\\z") 525 | 526 | ret 527 | end 528 | 529 | def convert_to_uri(uri) 530 | if uri.is_a?(URI::Generic) 531 | uri 532 | elsif String === uri 533 | parse(uri) 534 | else 535 | raise ArgumentError, 536 | "bad argument (expected URI object or URI string)" 537 | end 538 | end 539 | 540 | end # class Parser 541 | end # module URI 542 | -------------------------------------------------------------------------------- /mrblib/00_rfc3986_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: false 2 | module URI 3 | class RFC3986_Parser # :nodoc: 4 | # URI defined in RFC3986 5 | # this regexp is modified not to host is not empty string 6 | RFC3986_URI = /\A(?(?[A-Za-z][+\-.0-9A-Za-z]*):(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?\g(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ 7 | RFC3986_relative_ref = /\A(?(?\/\/(?(?:(?(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?(?\[(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{1,4}?::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?\d*))?)(?(?:\/(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?\/(?:(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g)*)?)|(?(?(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g)*)|(?))(?:\?(?[^#]*))?(?:\#(?(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/ 8 | attr_reader :regexp 9 | 10 | def initialize 11 | @regexp = default_regexp 12 | end 13 | 14 | def split(uri) #:nodoc: 15 | begin 16 | uri = uri.to_str 17 | rescue NoMethodError 18 | raise InvalidURIError, "bad URI(is not URI?): #{uri}" 19 | end 20 | # uri.ascii_only? or 21 | # raise InvalidURIError, "URI must be ascii only #{uri.dump}" 22 | if m = RFC3986_URI.match(uri) 23 | query = m["query".freeze] 24 | scheme = m["scheme".freeze] 25 | opaque = m["pathRootless".freeze] 26 | if opaque 27 | opaque << "?#{query}" if query 28 | [ scheme, 29 | nil, # userinfo 30 | nil, # host 31 | nil, # port 32 | nil, # registry 33 | nil, # path 34 | opaque, 35 | nil, # query 36 | m["fragment".freeze] 37 | ] 38 | else # normal 39 | [ scheme, 40 | m["userinfo".freeze], 41 | m["host".freeze], 42 | m["port".freeze], 43 | nil, # registry 44 | (m["pathAbempty".freeze] || 45 | m["pathAbsolute".freeze] || 46 | m["pathEmpty".freeze]), 47 | nil, # opaque 48 | query, 49 | m["fragment".freeze] 50 | ] 51 | end 52 | elsif m = RFC3986_relative_ref.match(uri) 53 | [ nil, # scheme 54 | m["userinfo".freeze], 55 | m["host".freeze], 56 | m["port".freeze], 57 | nil, # registry, 58 | (m["pathAbempty".freeze] || 59 | m["pathAbsolute".freeze] || 60 | m["pathNoscheme".freeze] || 61 | m["pathEmpty".freeze]), 62 | nil, # opaque 63 | m["query".freeze], 64 | m["fragment".freeze] 65 | ] 66 | else 67 | raise InvalidURIError, "bad URI(is not URI?): #{uri}" 68 | end 69 | end 70 | 71 | def parse(uri) # :nodoc: 72 | scheme, userinfo, host, port, 73 | registry, path, opaque, query, fragment = self.split(uri) 74 | scheme_list = URI.scheme_list 75 | if scheme && scheme_list.include?(uc = scheme.upcase) 76 | scheme_list[uc].new(scheme, userinfo, host, port, 77 | registry, path, opaque, query, 78 | fragment, self) 79 | else 80 | Generic.new(scheme, userinfo, host, port, 81 | registry, path, opaque, query, 82 | fragment, self) 83 | end 84 | end 85 | 86 | 87 | def join(*uris) # :nodoc: 88 | uris[0] = convert_to_uri(uris[0]) 89 | uris.inject :merge 90 | end 91 | 92 | def inspect 93 | to_s 94 | end 95 | 96 | private 97 | 98 | def default_regexp # :nodoc: 99 | { 100 | SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/, 101 | USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/, 102 | HOST: /\A(?:(?\[(?:(?(?:\h{1,4}:){6}(?\h{1,4}:\h{1,4}|(?(?[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g\.\g\.\g))|::(?:\h{1,4}:){5}\g|\h{,4}::(?:\h{1,4}:){4}\g|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g|(?(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/, 103 | ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, 104 | REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/, 105 | QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, 106 | FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/, 107 | OPAQUE: /\A(?:[^\/].*)?\z/, 108 | PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/, 109 | } 110 | end 111 | 112 | def convert_to_uri(uri) 113 | if uri.is_a?(URI::Generic) 114 | uri 115 | elsif String === uri 116 | parse(uri) 117 | else 118 | raise ArgumentError, 119 | "bad argument (expected URI object or URI string)" 120 | end 121 | end 122 | 123 | end # class Parser 124 | end # module URI 125 | -------------------------------------------------------------------------------- /mrblib/common.rb: -------------------------------------------------------------------------------- 1 | # = uri/common.rb 2 | # 3 | # Author:: Akira Yamada 4 | # Revision:: $Id$ 5 | # License:: 6 | # You can redistribute it and/or modify it under the same term as Ruby. 7 | # 8 | 9 | module URI 10 | REGEXP = RFC2396_REGEXP 11 | Parser = RFC2396_Parser 12 | RFC3986_PARSER = RFC3986_Parser.new 13 | 14 | # URI::Parser.new 15 | DEFAULT_PARSER = Parser.new 16 | DEFAULT_PARSER.pattern.each do |sym, str| 17 | unless REGEXP::PATTERN.const_defined?(sym) 18 | REGEXP::PATTERN.const_set(sym, str) 19 | end 20 | end 21 | DEFAULT_PARSER.regexp.each do |sym, str| 22 | const_set(sym, str) 23 | end 24 | 25 | module Util # :nodoc: 26 | def make_components_hash(klass, array_hash) 27 | tmp = {} 28 | if array_hash.kind_of?(Array) && 29 | array_hash.size == klass.component.size - 1 30 | klass.component[1..-1].each_index do |i| 31 | begin 32 | tmp[klass.component[i + 1]] = array_hash[i].clone 33 | rescue TypeError 34 | tmp[klass.component[i + 1]] = array_hash[i] 35 | end 36 | end 37 | 38 | elsif array_hash.kind_of?(Hash) 39 | array_hash.each do |key, value| 40 | begin 41 | tmp[key] = value.clone 42 | rescue TypeError 43 | tmp[key] = value 44 | end 45 | end 46 | else 47 | raise ArgumentError, 48 | "expected Array of or Hash of components of #{klass.to_s} (#{klass.component[1..-1].join(', ')})" 49 | end 50 | tmp[:scheme] = klass.to_s.sub(/\A.*::/, '').downcase 51 | 52 | return tmp 53 | end 54 | module_function :make_components_hash 55 | end 56 | 57 | module Escape 58 | include REGEXP 59 | 60 | # 61 | # == Synopsis 62 | # 63 | # URI.escape(str [, unsafe]) 64 | # 65 | # == Args 66 | # 67 | # +str+:: 68 | # String to replaces in. 69 | # +unsafe+:: 70 | # Regexp that matches all symbols that must be replaced with codes. 71 | # By default uses REGEXP::UNSAFE. 72 | # When this argument is a String, it represents a character set. 73 | # 74 | # == Description 75 | # 76 | # Escapes the string, replacing all unsafe characters with codes. 77 | # 78 | # == Usage 79 | # 80 | # require 'uri' 81 | # 82 | # enc_uri = URI.escape("http://example.com/?a=\11\15") 83 | # p enc_uri 84 | # # => "http://example.com/?a=%09%0D" 85 | # 86 | # p URI.unescape(enc_uri) 87 | # # => "http://example.com/?a=\t\r" 88 | # 89 | # p URI.escape("@?@!", "!?") 90 | # # => "@%3F@%21" 91 | # 92 | def escape(str, unsafe = UNSAFE) 93 | # URI.escape is obsolete" 94 | DEFAULT_PARSER.escape(str, unsafe) 95 | end 96 | alias encode escape 97 | # 98 | # == Synopsis 99 | # 100 | # URI.unescape(str) 101 | # 102 | # == Args 103 | # 104 | # +str+:: 105 | # Unescapes the string. 106 | # 107 | # == Usage 108 | # 109 | # require 'uri' 110 | # 111 | # enc_uri = URI.escape("http://example.com/?a=\11\15") 112 | # p enc_uri 113 | # # => "http://example.com/?a=%09%0D" 114 | # 115 | # p URI.unescape(enc_uri) 116 | # # => "http://example.com/?a=\t\r" 117 | # 118 | def unescape(str) 119 | # URI.unescape is obsolete 120 | DEFAULT_PARSER.unescape(str) 121 | end 122 | alias decode unescape 123 | end 124 | 125 | include REGEXP 126 | extend Escape 127 | 128 | @@schemes = {} 129 | # Returns a Hash of the defined schemes 130 | def self.scheme_list 131 | @@schemes 132 | end 133 | 134 | # 135 | # Base class for all URI exceptions. 136 | # 137 | class Error < StandardError; end 138 | # 139 | # Not a URI. 140 | # 141 | class InvalidURIError < Error; end 142 | # 143 | # Not a URI component. 144 | # 145 | class InvalidComponentError < Error; end 146 | # 147 | # URI is valid, bad usage is not. 148 | # 149 | class BadURIError < Error; end 150 | 151 | # 152 | # == Synopsis 153 | # 154 | # URI::split(uri) 155 | # 156 | # == Args 157 | # 158 | # +uri+:: 159 | # String with URI. 160 | # 161 | # == Description 162 | # 163 | # Splits the string on following parts and returns array with result: 164 | # 165 | # * Scheme 166 | # * Userinfo 167 | # * Host 168 | # * Port 169 | # * Registry 170 | # * Path 171 | # * Opaque 172 | # * Query 173 | # * Fragment 174 | # 175 | # == Usage 176 | # 177 | # require 'uri' 178 | # 179 | # p URI.split("http://www.ruby-lang.org/") 180 | # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil] 181 | # 182 | def self.split(uri) 183 | RFC3986_PARSER.split(uri) 184 | end 185 | 186 | # 187 | # == Synopsis 188 | # 189 | # URI::parse(uri_str) 190 | # 191 | # == Args 192 | # 193 | # +uri_str+:: 194 | # String with URI. 195 | # 196 | # == Description 197 | # 198 | # Creates one of the URI's subclasses instance from the string. 199 | # 200 | # == Raises 201 | # 202 | # URI::InvalidURIError 203 | # Raised if URI given is not a correct one. 204 | # 205 | # == Usage 206 | # 207 | # require 'uri' 208 | # 209 | # uri = URI.parse("http://www.ruby-lang.org/") 210 | # p uri 211 | # # => # 212 | # p uri.scheme 213 | # # => "http" 214 | # p uri.host 215 | # # => "www.ruby-lang.org" 216 | # 217 | def self.parse(uri) 218 | RFC3986_PARSER.parse(uri) 219 | end 220 | 221 | # 222 | # == Synopsis 223 | # 224 | # URI::join(str[, str, ...]) 225 | # 226 | # == Args 227 | # 228 | # +str+:: 229 | # String(s) to work with 230 | # 231 | # == Description 232 | # 233 | # Joins URIs. 234 | # 235 | # == Usage 236 | # 237 | # require 'uri' 238 | # 239 | # p URI.join("http://localhost/","main.rbx") 240 | # # => # 241 | # 242 | def self.join(*str) 243 | RFC3986_PARSER.join(*str) 244 | end 245 | 246 | # 247 | # == Synopsis 248 | # 249 | # URI::extract(str[, schemes][,&blk]) 250 | # 251 | # == Args 252 | # 253 | # +str+:: 254 | # String to extract URIs from. 255 | # +schemes+:: 256 | # Limit URI matching to a specific schemes. 257 | # 258 | # == Description 259 | # 260 | # Extracts URIs from a string. If block given, iterates through all matched URIs. 261 | # Returns nil if block given or array with matches. 262 | # 263 | # == Usage 264 | # 265 | # require "uri" 266 | # 267 | # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.") 268 | # # => ["http://foo.example.com/bla", "mailto:test@example.com"] 269 | # 270 | def self.extract(str, schemes = nil, &block) 271 | # URI.extract is obsolete 272 | DEFAULT_PARSER.extract(str, schemes, &block) 273 | end 274 | 275 | 276 | # 277 | # == Synopsis 278 | # 279 | # URI::regexp([match_schemes]) 280 | # 281 | # == Args 282 | # 283 | # +match_schemes+:: 284 | # Array of schemes. If given, resulting regexp matches to URIs 285 | # whose scheme is one of the match_schemes. 286 | # 287 | # == Description 288 | # Returns a Regexp object which matches to URI-like strings. 289 | # The Regexp object returned by this method includes arbitrary 290 | # number of capture group (parentheses). Never rely on it's number. 291 | # 292 | # == Usage 293 | # 294 | # require 'uri' 295 | # 296 | # # extract first URI from html_string 297 | # html_string.slice(URI.regexp) 298 | # 299 | # # remove ftp URIs 300 | # html_string.sub(URI.regexp(['ftp']) 301 | # 302 | # # You should not rely on the number of parentheses 303 | # html_string.scan(URI.regexp) do |*matches| 304 | # p $& 305 | # end 306 | # 307 | def self.regexp(schemes = nil) 308 | # URI.regexp is obsolete 309 | DEFAULT_PARSER.make_regexp(schemes) 310 | end 311 | 312 | # :nodoc: 313 | TBLENCWWWCOMP_ = {} 314 | 256.times do |i| 315 | TBLENCWWWCOMP_[i.chr] = '%%%02X' % i 316 | end 317 | TBLENCWWWCOMP_[' '] = '+' 318 | 319 | # :nodoc: 320 | TBLDECWWWCOMP_ = {} 321 | 256.times do |i| 322 | h, l = i>>4, i&15 323 | TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr 324 | TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr 325 | TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr 326 | TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr 327 | end 328 | TBLDECWWWCOMP_['+'] = ' ' 329 | 330 | # Encode given +str+ to URL-encoded form data. 331 | # 332 | # This doesn't convert *, -, ., 0-9, A-Z, _, a-z, 333 | # does convert SP to +, and convert others to %XX. 334 | # 335 | # This refers http://www.w3.org/TR/html5/forms.html#url-encoded-form-data 336 | # 337 | # See URI.decode_www_form_component(str), URI.encode_www_form(enum) 338 | def self.encode_www_form_component(str) 339 | str.to_s.gsub(/[^*\-.0-9A-Z_a-z]/n) { TBLENCWWWCOMP_[$&] } 340 | end 341 | 342 | # Decode given +str+ of URL-encoded form data. 343 | # 344 | # This decods + to SP. 345 | # 346 | # See URI.encode_www_form_component(str) 347 | def self.decode_www_form_component(str) 348 | str.gsub(/\+|%\h\h/){ TBLDECWWWCOMP_[$&] } 349 | end 350 | 351 | # Generate URL-encoded form data from given +enum+. 352 | # 353 | # This generates application/x-www-form-urlencoded data defined in HTML5 354 | # from given an Enumerable object. 355 | # 356 | # This internally uses URI.encode_www_form_component(str). 357 | # 358 | # This doesn't convert encodings of give items, so convert them before call 359 | # this method if you want to send data as other than original encoding or 360 | # mixed encoding data. 361 | # 362 | # This doesn't treat files. When you send a file, use multipart/form-data. 363 | # 364 | # This refers http://www.w3.org/TR/html5/forms.html#url-encoded-form-data 365 | # 366 | # See URI.encode_www_form_component(str) 367 | def self.encode_www_form(enum) 368 | enum.map do |k, v| 369 | if v.nil? 370 | encode_www_form_component(k) 371 | elsif v.respond_to?(:to_a) 372 | v.to_a.map do |w| 373 | str = encode_www_form_component(k) 374 | unless w.nil? 375 | str << '=' 376 | str << encode_www_form_component(w) 377 | end 378 | end.join('&') 379 | else 380 | str = encode_www_form_component(k) 381 | str << '=' 382 | str << encode_www_form_component(v) 383 | end 384 | end.join("&") 385 | end 386 | 387 | def self.decode_www_form(str, opts = {}) 388 | separator = opts[:separator] || '&' 389 | ary = [] 390 | return ary if str.empty? 391 | str.split(separator).map do |string| 392 | key, val = string.split('=', 2) 393 | 394 | [ 395 | key.nil? ? "" : decode_www_form_component(key), 396 | val.nil? ? "" : decode_www_form_component(val), 397 | ] 398 | end 399 | end 400 | end 401 | 402 | module Kernel 403 | # alias for URI.parse. 404 | # 405 | # This method is introduced at 1.8.2. 406 | def URI(uri_str) # :doc: 407 | URI.parse(uri_str) 408 | end 409 | module_function :URI 410 | end 411 | -------------------------------------------------------------------------------- /mrblib/generic.rb: -------------------------------------------------------------------------------- 1 | # 2 | # = uri/generic.rb 3 | # 4 | # Author:: Akira Yamada 5 | # License:: You can redistribute it and/or modify it under the same term as Ruby. 6 | # Revision:: $Id$ 7 | # 8 | 9 | module URI 10 | 11 | # 12 | # Base class for all URI classes. 13 | # Implements generic URI syntax as per RFC 2396. 14 | # 15 | class Generic 16 | include URI 17 | include REGEXP 18 | 19 | DEFAULT_PORT = nil 20 | 21 | # 22 | # Returns default port 23 | # 24 | def self.default_port 25 | self::DEFAULT_PORT 26 | end 27 | 28 | def default_port 29 | self.class.default_port 30 | end 31 | 32 | COMPONENT = [ 33 | :scheme, 34 | :userinfo, :host, :port, :registry, 35 | :path, :opaque, 36 | :query, 37 | :fragment 38 | ] 39 | 40 | # 41 | # Components of the URI in the order. 42 | # 43 | def self.component 44 | self::COMPONENT 45 | end 46 | 47 | USE_REGISTRY = false 48 | 49 | # 50 | # DOC: FIXME! 51 | # 52 | def self.use_registry 53 | self::USE_REGISTRY 54 | end 55 | 56 | # 57 | # == Synopsis 58 | # 59 | # See #new 60 | # 61 | # == Description 62 | # 63 | # At first, tries to create a new URI::Generic instance using 64 | # URI::Generic::build. But, if exception URI::InvalidComponentError is raised, 65 | # then it URI::Escape.escape all URI components and tries again. 66 | # 67 | # 68 | def self.build2(args) 69 | begin 70 | return self.build(args) 71 | rescue InvalidComponentError 72 | if args.kind_of?(Array) 73 | return self.build(args.collect{|x| 74 | if x 75 | URI.escape(x) 76 | else 77 | x 78 | end 79 | }) 80 | elsif args.kind_of?(Hash) 81 | tmp = {} 82 | args.each do |key, value| 83 | tmp[key] = if value 84 | URI.escape(value) 85 | else 86 | value 87 | end 88 | end 89 | return self.build(tmp) 90 | end 91 | end 92 | end 93 | 94 | # 95 | # == Synopsis 96 | # 97 | # See #new 98 | # 99 | # == Description 100 | # 101 | # Creates a new URI::Generic instance from components of URI::Generic 102 | # with check. Components are: scheme, userinfo, host, port, registry, path, 103 | # opaque, query and fragment. You can provide arguments either by an Array or a Hash. 104 | # See #new for hash keys to use or for order of array items. 105 | # 106 | def self.build(args) 107 | if args.kind_of?(Array) && 108 | args.size == ::URI::Generic::COMPONENT.size 109 | tmp = args.dup 110 | elsif args.kind_of?(Hash) 111 | tmp = ::URI::Generic::COMPONENT.collect do |c| 112 | if args.include?(c) 113 | args[c] 114 | else 115 | nil 116 | end 117 | end 118 | else 119 | component = self.class.component rescue ::URI::Generic::COMPONENT 120 | raise ArgumentError, 121 | "expected Array of or Hash of components of #{self.class} (#{component.join(', ')})" 122 | end 123 | 124 | tmp << true 125 | return self.new(*tmp) 126 | end 127 | # 128 | # == Args 129 | # 130 | # +scheme+:: 131 | # Protocol scheme, i.e. 'http','ftp','mailto' and so on. 132 | # +userinfo+:: 133 | # User name and password, i.e. 'sdmitry:bla' 134 | # +host+:: 135 | # Server host name 136 | # +port+:: 137 | # Server port 138 | # +registry+:: 139 | # DOC: FIXME! 140 | # +path+:: 141 | # Path on server 142 | # +opaque+:: 143 | # DOC: FIXME! 144 | # +query+:: 145 | # Query data 146 | # +fragment+:: 147 | # A part of URI after '#' sign 148 | # +parser+:: 149 | # Parser for internal use [URI::DEFAULT_PARSER by default] 150 | # +arg_check+:: 151 | # Check arguments [false by default] 152 | # 153 | # == Description 154 | # 155 | # Creates a new URI::Generic instance from ``generic'' components without check. 156 | # 157 | def initialize(scheme, 158 | userinfo, host, port, registry, 159 | path, opaque, 160 | query, 161 | fragment, 162 | parser = DEFAULT_PARSER, 163 | arg_check = false) 164 | @scheme = nil 165 | @user = nil 166 | @password = nil 167 | @host = nil 168 | @port = nil 169 | @path = nil 170 | @query = nil 171 | @opaque = nil 172 | @registry = nil 173 | @fragment = nil 174 | @parser = parser == DEFAULT_PARSER ? nil : parser 175 | 176 | if arg_check 177 | self.scheme = scheme 178 | self.userinfo = userinfo 179 | self.host = host 180 | self.port = port 181 | self.path = path 182 | self.query = query 183 | self.opaque = opaque 184 | self.registry = registry 185 | self.fragment = fragment 186 | else 187 | self.set_scheme(scheme) 188 | self.set_userinfo(userinfo) 189 | self.set_host(host) 190 | self.set_port(port) 191 | self.set_path(path) 192 | self.query = query 193 | self.set_opaque(opaque) 194 | self.set_registry(registry) 195 | self.fragment=(fragment) 196 | end 197 | if @registry && !self.class.use_registry 198 | raise InvalidURIError, 199 | "the scheme #{@scheme} does not accept registry part: #{@registry} (or bad hostname?)" 200 | end 201 | 202 | self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2) 203 | self.set_port(self.default_port) if self.default_port && !@port 204 | end 205 | attr_reader :scheme 206 | attr_reader :host 207 | attr_reader :port 208 | attr_reader :registry 209 | attr_reader :path 210 | attr_reader :query 211 | attr_reader :opaque 212 | attr_reader :fragment 213 | 214 | def parser 215 | if @parser.nil? || !@parser 216 | DEFAULT_PARSER 217 | else 218 | @parser || DEFAULT_PARSER 219 | end 220 | end 221 | 222 | # replace self by other URI object 223 | def replace!(oth) 224 | if self.class != oth.class 225 | raise ArgumentError, "expected #{self.class} object" 226 | end 227 | 228 | component.each do |c| 229 | self.__send__("#{c}=", oth.__send__(c)) 230 | end 231 | end 232 | private :replace! 233 | 234 | def component 235 | self.class.component 236 | end 237 | 238 | def check_scheme(v) 239 | if v && parser.regexp[:SCHEME] !~ v 240 | raise InvalidComponentError, 241 | "bad component(expected scheme component): #{v}" 242 | end 243 | 244 | return true 245 | end 246 | private :check_scheme 247 | 248 | def set_scheme(v) 249 | @scheme = v 250 | end 251 | protected :set_scheme 252 | 253 | def scheme=(v) 254 | check_scheme(v) 255 | set_scheme(v) 256 | v 257 | end 258 | 259 | def check_userinfo(user, password = nil) 260 | if !password 261 | user, password = split_userinfo(user) 262 | end 263 | check_user(user) 264 | check_password(password, user) 265 | 266 | return true 267 | end 268 | private :check_userinfo 269 | 270 | def check_user(v) 271 | if @registry || @opaque 272 | raise InvalidURIError, 273 | "can not set user with registry or opaque" 274 | end 275 | 276 | return v unless v 277 | 278 | if parser.regexp[:USERINFO] !~ v 279 | raise InvalidComponentError, 280 | "bad component(expected userinfo component or user component): #{v}" 281 | end 282 | 283 | return true 284 | end 285 | private :check_user 286 | 287 | def check_password(v, user = @user) 288 | if @registry || @opaque 289 | raise InvalidURIError, 290 | "can not set password with registry or opaque" 291 | end 292 | return v unless v 293 | 294 | if !user 295 | raise InvalidURIError, 296 | "password component depends user component" 297 | end 298 | 299 | if parser.regexp[:USERINFO] !~ v 300 | raise InvalidComponentError, 301 | "bad component(expected user component): #{v}" 302 | end 303 | 304 | return true 305 | end 306 | private :check_password 307 | 308 | # 309 | # Sets userinfo, argument is string like 'name:pass' 310 | # 311 | def userinfo=(userinfo) 312 | if userinfo.nil? 313 | return nil 314 | end 315 | check_userinfo(*userinfo) 316 | set_userinfo(*userinfo) 317 | # returns userinfo 318 | end 319 | 320 | def user=(user) 321 | check_user(user) 322 | set_user(user) 323 | # returns user 324 | end 325 | 326 | def password=(password) 327 | check_password(password) 328 | set_password(password) 329 | # returns password 330 | end 331 | 332 | def set_userinfo(user, password = nil) 333 | unless password 334 | user, password = split_userinfo(user) 335 | end 336 | @user = user 337 | @password = password if password 338 | 339 | [@user, @password] 340 | end 341 | protected :set_userinfo 342 | 343 | def set_user(v) 344 | set_userinfo(v, @password) 345 | v 346 | end 347 | protected :set_user 348 | 349 | def set_password(v) 350 | @password = v 351 | # returns v 352 | end 353 | protected :set_password 354 | 355 | def split_userinfo(ui) 356 | return nil, nil unless ui 357 | user, password = ui.split(/:/, 2) 358 | 359 | return user, password 360 | end 361 | private :split_userinfo 362 | 363 | def escape_userpass(v) 364 | parser.escape(v, /[@:\/]/) # RFC 1738 section 3.1 #/ 365 | end 366 | private :escape_userpass 367 | 368 | def userinfo 369 | if @user.nil? 370 | nil 371 | elsif @password.nil? 372 | @user 373 | else 374 | @user + ':' + @password 375 | end 376 | end 377 | 378 | def user 379 | @user 380 | end 381 | 382 | def password 383 | @password 384 | end 385 | 386 | def check_host(v) 387 | return v unless v 388 | 389 | if @registry || @opaque 390 | raise InvalidURIError, 391 | "can not set host with registry or opaque" 392 | elsif parser.regexp[:HOST] !~ v 393 | raise InvalidComponentError, 394 | "bad component(expected host component): #{v}" 395 | end 396 | 397 | return true 398 | end 399 | private :check_host 400 | 401 | def set_host(v) 402 | @host = v 403 | end 404 | protected :set_host 405 | 406 | def host=(v) 407 | check_host(v) 408 | set_host(v) 409 | v 410 | end 411 | 412 | def check_port(v) 413 | return v unless v 414 | 415 | if @registry || @opaque 416 | raise InvalidURIError, 417 | "can not set port with registry or opaque" 418 | elsif !v.kind_of?(Fixnum) && parser.regexp[:PORT] !~ v 419 | raise InvalidComponentError, 420 | "bad component(expected port component): #{v}" 421 | end 422 | 423 | return true 424 | end 425 | private :check_port 426 | 427 | def set_port(v) 428 | unless !v || v.kind_of?(Fixnum) 429 | if v.empty? 430 | v = nil 431 | else 432 | v = v.to_i 433 | end 434 | end 435 | @port = v 436 | end 437 | protected :set_port 438 | 439 | def port=(v) 440 | check_port(v) 441 | set_port(v) 442 | port 443 | end 444 | 445 | def check_registry(v) 446 | return v unless v 447 | 448 | # raise if both server and registry are not nil, because: 449 | # authority = server | reg_name 450 | # server = [ [ userinfo "@" ] hostport ] 451 | if @host || @port || @user # userinfo = @user + ':' + @password 452 | raise InvalidURIError, 453 | "can not set registry with host, port, or userinfo" 454 | elsif v && REGISTRY !~ v 455 | raise InvalidComponentError, 456 | "bad component(expected registry component): #{v}" 457 | end 458 | 459 | return true 460 | end 461 | private :check_registry 462 | 463 | def set_registry(v) 464 | @registry = v 465 | end 466 | protected :set_registry 467 | 468 | def registry=(v) 469 | check_registry(v) 470 | set_registry(v) 471 | v 472 | end 473 | 474 | def check_path(v) 475 | # raise if both hier and opaque are not nil, because: 476 | # absoluteURI = scheme ":" ( hier_part | opaque_part ) 477 | # hier_part = ( net_path | abs_path ) [ "?" query ] 478 | if v && @opaque 479 | raise InvalidURIError, 480 | "path conflicts with opaque" 481 | end 482 | 483 | if @scheme 484 | if v && v != '' && parser.regexp[:ABS_PATH] !~ v 485 | raise InvalidComponentError, 486 | "bad component(expected absolute path component): #{v}" 487 | end 488 | else 489 | if v && v != '' && parser.regexp[:ABS_PATH] !~ v && 490 | parser.regexp[:REL_PATH] !~ v 491 | raise InvalidComponentError, 492 | "bad component(expected relative path component): #{v}" 493 | end 494 | end 495 | 496 | return true 497 | end 498 | private :check_path 499 | 500 | def set_path(v) 501 | @path = v 502 | end 503 | protected :set_path 504 | 505 | def path=(v) 506 | check_path(v) 507 | set_path(v) 508 | v 509 | end 510 | 511 | def query=(v) 512 | return @query = nil unless v 513 | raise InvalidURIError, "query conflicts with opaque" if @opaque 514 | 515 | x = v.to_str 516 | v = x.dup if x.equal? v 517 | v.gsub!(/\t|\r|\n/, '') 518 | v.gsub!(/(?!%\h\h|[!$-&(-;=?-_a-~])./n){'%%%02X' % $&.ord} 519 | @query = v 520 | end 521 | 522 | def check_opaque(v) 523 | return v unless v 524 | 525 | # raise if both hier and opaque are not nil, because: 526 | # absoluteURI = scheme ":" ( hier_part | opaque_part ) 527 | # hier_part = ( net_path | abs_path ) [ "?" query ] 528 | if @host || @port || @user || @path # userinfo = @user + ':' + @password 529 | raise InvalidURIError, 530 | "can not set opaque with host, port, userinfo or path" 531 | elsif v && OPAQUE !~ v 532 | raise InvalidComponentError, 533 | "bad component(expected opaque component): #{v}" 534 | end 535 | 536 | return true 537 | end 538 | private :check_opaque 539 | 540 | def set_opaque(v) 541 | @opaque = v 542 | end 543 | protected :set_opaque 544 | 545 | def opaque=(v) 546 | check_opaque(v) 547 | set_opaque(v) 548 | v 549 | end 550 | 551 | def fragment=(v) 552 | return @fragment = nil unless v 553 | 554 | x = v.to_str 555 | v = x.dup if x.equal? v 556 | v.gsub!(/\t|\r|\n/, '') 557 | v.gsub!(/(?!%\h\h|[!-~])./n){'%%%02X' % $&.ord} 558 | @fragment = v 559 | end 560 | 561 | # 562 | # Checks if URI has a path 563 | # 564 | def hierarchical? 565 | if @path 566 | true 567 | else 568 | false 569 | end 570 | end 571 | 572 | # 573 | # Checks if URI is an absolute one 574 | # 575 | def absolute? 576 | if @scheme 577 | true 578 | else 579 | false 580 | end 581 | end 582 | alias absolute absolute? 583 | 584 | # 585 | # Checks if URI is relative 586 | # 587 | def relative? 588 | !absolute? 589 | end 590 | 591 | def split_path(path) 592 | path.split("/", -1) 593 | end 594 | private :split_path 595 | 596 | def merge_path(base, rel) 597 | 598 | # RFC2396, Section 5.2, 5) 599 | # RFC2396, Section 5.2, 6) 600 | base_path = split_path(base) 601 | rel_path = split_path(rel) 602 | 603 | # RFC2396, Section 5.2, 6), a) 604 | base_path << '' if base_path.last == '..' 605 | while i = base_path.index('..') 606 | base_path.slice!(i - 1, 2) 607 | end 608 | 609 | if (first = rel_path.first) and first.empty? 610 | base_path.clear 611 | rel_path.shift 612 | end 613 | 614 | # RFC2396, Section 5.2, 6), c) 615 | # RFC2396, Section 5.2, 6), d) 616 | rel_path.push('') if rel_path.last == '.' || rel_path.last == '..' 617 | rel_path.delete('.') 618 | 619 | # RFC2396, Section 5.2, 6), e) 620 | tmp = [] 621 | rel_path.each do |x| 622 | if x == '..' && 623 | !(tmp.empty? || tmp.last == '..') 624 | tmp.pop 625 | else 626 | tmp << x 627 | end 628 | end 629 | 630 | add_trailer_slash = !tmp.empty? 631 | if base_path.empty? 632 | base_path = [''] # keep '/' for root directory 633 | elsif add_trailer_slash 634 | base_path.pop 635 | end 636 | while x = tmp.shift 637 | if x == '..' 638 | # RFC2396, Section 4 639 | # a .. or . in an absolute path has no special meaning 640 | base_path.pop if base_path.size > 1 641 | else 642 | # if x == '..' 643 | # valid absolute (but abnormal) path "/../..." 644 | # else 645 | # valid absolute path 646 | # end 647 | base_path << x 648 | tmp.each {|t| base_path << t} 649 | add_trailer_slash = false 650 | break 651 | end 652 | end 653 | base_path.push('') if add_trailer_slash 654 | 655 | return base_path.join('/') 656 | end 657 | private :merge_path 658 | 659 | # 660 | # == Args 661 | # 662 | # +oth+:: 663 | # URI or String 664 | # 665 | # == Description 666 | # 667 | # Destructive form of #merge 668 | # 669 | # == Usage 670 | # 671 | # require 'uri' 672 | # 673 | # uri = URI.parse("http://my.example.com") 674 | # uri.merge!("/main.rbx?page=1") 675 | # p uri 676 | # # => # 677 | # 678 | def merge!(oth) 679 | t = merge(oth) 680 | if self == t 681 | nil 682 | else 683 | replace!(t) 684 | self 685 | end 686 | end 687 | 688 | # 689 | # == Args 690 | # 691 | # +oth+:: 692 | # URI or String 693 | # 694 | # == Description 695 | # 696 | # Merges two URI's. 697 | # 698 | # == Usage 699 | # 700 | # require 'uri' 701 | # 702 | # uri = URI.parse("http://my.example.com") 703 | # p uri.merge("/main.rbx?page=1") 704 | # # => # 705 | # 706 | def merge(oth) 707 | rel = parser.send(:convert_to_uri, oth) 708 | 709 | if rel.absolute? 710 | #raise BadURIError, "both URI are absolute" if absolute? 711 | # hmm... should return oth for usability? 712 | return rel 713 | end 714 | 715 | unless self.absolute? 716 | raise BadURIError, "both URI are relative" 717 | end 718 | 719 | base = self.dup 720 | 721 | authority = rel.userinfo || rel.host || rel.port 722 | 723 | # RFC2396, Section 5.2, 2) 724 | if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query 725 | base.fragment=(rel.fragment) if rel.fragment 726 | return base 727 | end 728 | 729 | base.query = nil 730 | base.fragment=(nil) 731 | 732 | # RFC2396, Section 5.2, 4) 733 | if !authority 734 | base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path 735 | else 736 | # RFC2396, Section 5.2, 4) 737 | base.set_path(rel.path) if rel.path 738 | end 739 | 740 | # RFC2396, Section 5.2, 7) 741 | base.set_userinfo(rel.userinfo) if rel.userinfo 742 | base.set_host(rel.host) if rel.host 743 | base.set_port(rel.port) if rel.port 744 | base.query = rel.query if rel.query 745 | base.fragment=(rel.fragment) if rel.fragment 746 | 747 | return base 748 | end # merge 749 | alias + merge 750 | 751 | # return base and rel. 752 | # you can modify `base', but can not `rel'. 753 | def merge0(oth) 754 | case oth 755 | when Generic 756 | when String 757 | oth = URI.parse(oth) 758 | else 759 | raise ArgumentError, 760 | "bad argument(expected URI object or URI string)" 761 | end 762 | 763 | if self.relative? && oth.relative? 764 | raise BadURIError, 765 | "both URI are relative" 766 | end 767 | 768 | if self.absolute? && oth.absolute? 769 | #raise BadURIError, 770 | # "both URI are absolute" 771 | # hmm... should return oth for usability? 772 | return oth, oth 773 | end 774 | 775 | if self.absolute? 776 | return self.dup, oth 777 | else 778 | return oth, oth 779 | end 780 | end 781 | private :merge0 782 | 783 | def route_from_path(src, dst) 784 | case dst 785 | when src 786 | # RFC2396, Section 4.2 787 | return '' 788 | when %r{(?:\A|/)\.\.?(?:/|\z)} 789 | # dst has abnormal absolute path, 790 | # like "/./", "/../", "/x/../", ... 791 | return dst.dup 792 | end 793 | 794 | src_path = src.scan(%r{[^/]*/}) 795 | dst_path = dst.scan(%r{[^/]*/?}) 796 | 797 | # discard same parts 798 | while !dst_path.empty? && dst_path.first == src_path.first 799 | src_path.shift 800 | dst_path.shift 801 | end 802 | 803 | tmp = dst_path.join 804 | 805 | # calculate 806 | if src_path.empty? 807 | if tmp.empty? 808 | return './' 809 | elsif dst_path.first.include?(':') # (see RFC2396 Section 5) 810 | return './' + tmp 811 | else 812 | return tmp 813 | end 814 | end 815 | 816 | return '../' * src_path.size + tmp 817 | end 818 | private :route_from_path 819 | 820 | def route_from0(oth) 821 | oth = parser.send(:convert_to_uri, oth) 822 | if self.relative? 823 | raise BadURIError, 824 | "relative URI: #{self}" 825 | end 826 | if oth.relative? 827 | raise BadURIError, 828 | "relative URI: #{oth}" 829 | end 830 | 831 | if self.scheme != oth.scheme 832 | return self, self.dup 833 | end 834 | rel = URI::Generic.new(nil, # it is relative URI 835 | self.userinfo, self.host, self.port, 836 | self.registry, self.path, self.opaque, 837 | self.query, self.fragment, parser) 838 | 839 | if rel.userinfo != oth.userinfo || 840 | rel.host.to_s.downcase != oth.host.to_s.downcase || 841 | rel.port != oth.port 842 | if self.userinfo.nil? && self.host.nil? 843 | return self, self.dup 844 | end 845 | rel.set_port(nil) if rel.port == oth.default_port 846 | return rel, rel 847 | end 848 | rel.set_userinfo(nil) 849 | rel.set_host(nil) 850 | rel.set_port(nil) 851 | 852 | if rel.path && rel.path == oth.path 853 | rel.set_path('') 854 | rel.query = nil if rel.query == oth.query 855 | return rel, rel 856 | elsif rel.opaque && rel.opaque == oth.opaque 857 | rel.set_opaque('') 858 | rel.query = nil if rel.query == oth.query 859 | return rel, rel 860 | end 861 | 862 | # you can modify `rel', but can not `oth'. 863 | return oth, rel 864 | end 865 | private :route_from0 866 | 867 | # 868 | # == Args 869 | # 870 | # +oth+:: 871 | # URI or String 872 | # 873 | # == Description 874 | # 875 | # Calculates relative path from oth to self 876 | # 877 | # == Usage 878 | # 879 | # require 'uri' 880 | # 881 | # uri = URI.parse('http://my.example.com/main.rbx?page=1') 882 | # p uri.route_from('http://my.example.com') 883 | # #=> # 884 | # 885 | def route_from(oth) 886 | # you can modify `rel', but can not `oth'. 887 | begin 888 | oth, rel = route_from0(oth) 889 | rescue 890 | raise $!.class, $!.message 891 | end 892 | if oth == rel 893 | return rel 894 | end 895 | rel.set_path(route_from_path(oth.path, self.path)) 896 | if rel.path == './' && self.query 897 | # "./?foo" -> "?foo" 898 | rel.set_path('') 899 | end 900 | 901 | return rel 902 | end 903 | 904 | alias - route_from 905 | 906 | # 907 | # == Args 908 | # 909 | # +oth+:: 910 | # URI or String 911 | # 912 | # == Description 913 | # 914 | # Calculates relative path to oth from self 915 | # 916 | # == Usage 917 | # 918 | # require 'uri' 919 | # 920 | # uri = URI.parse('http://my.example.com') 921 | # p uri.route_to('http://my.example.com/main.rbx?page=1') 922 | # #=> # 923 | # 924 | def route_to(oth) 925 | parser.send(:convert_to_uri, oth).route_from(self) 926 | end 927 | 928 | # 929 | # Returns normalized URI 930 | # 931 | def normalize 932 | uri = dup 933 | uri.normalize! 934 | uri 935 | end 936 | 937 | # 938 | # Destructive version of #normalize 939 | # 940 | def normalize! 941 | if path && path.empty? 942 | set_path('/') 943 | end 944 | if scheme && scheme != scheme.downcase 945 | set_scheme(self.scheme.downcase) 946 | end 947 | if host && host != host.downcase 948 | set_host(self.host.downcase) 949 | end 950 | end 951 | 952 | def path_query 953 | str = @path 954 | if @query 955 | str += '?' + @query 956 | end 957 | str 958 | end 959 | private :path_query 960 | 961 | # 962 | # Constructs String from URI 963 | # 964 | def to_s 965 | str = '' 966 | if @scheme 967 | str << @scheme 968 | str << ':' 969 | end 970 | 971 | if @opaque 972 | str << @opaque 973 | 974 | else 975 | if @registry 976 | str << @registry 977 | else 978 | if @host || %w[file postgres].include?(@scheme) 979 | str << '//' 980 | end 981 | if self.userinfo 982 | str << self.userinfo 983 | str << '@' 984 | end 985 | if @host 986 | str << @host 987 | end 988 | if @port && @port != self.default_port 989 | str << ':' 990 | str << @port.to_s 991 | end 992 | end 993 | str << @path 994 | if @query 995 | str << '?' 996 | str << @query 997 | end 998 | end 999 | if @fragment 1000 | str << '#' 1001 | str << @fragment 1002 | end 1003 | str 1004 | end 1005 | 1006 | # 1007 | # Compares to URI's 1008 | # 1009 | def ==(oth) 1010 | if self.class == oth.class 1011 | self.normalize.component_ary == oth.normalize.component_ary 1012 | else 1013 | false 1014 | end 1015 | end 1016 | 1017 | def hash 1018 | self.component_ary.hash 1019 | end 1020 | 1021 | def eql?(oth) 1022 | self.class == oth.class && 1023 | parser == oth.parser && 1024 | self.component_ary.eql?(oth.component_ary) 1025 | end 1026 | 1027 | =begin 1028 | 1029 | --- URI::Generic#===(oth) 1030 | 1031 | =end 1032 | # def ===(oth) 1033 | # raise NotImplementedError 1034 | # end 1035 | 1036 | =begin 1037 | =end 1038 | def component_ary 1039 | component.collect do |x| 1040 | self.__send__(x) 1041 | end 1042 | end 1043 | protected :component_ary 1044 | 1045 | # == Args 1046 | # 1047 | # +components+:: 1048 | # Multiple Symbol arguments defined in URI::HTTP 1049 | # 1050 | # == Description 1051 | # 1052 | # Selects specified components from URI 1053 | # 1054 | # == Usage 1055 | # 1056 | # require 'uri' 1057 | # 1058 | # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx') 1059 | # p uri.select(:userinfo, :host, :path) 1060 | # # => ["myuser:mypass", "my.example.com", "/test.rbx"] 1061 | # 1062 | def select(*components) 1063 | components.collect do |c| 1064 | if component.include?(c) 1065 | self.__send__(c) 1066 | else 1067 | raise ArgumentError, 1068 | "expected of components of #{self.class} (#{self.class.component.join(', ')})" 1069 | end 1070 | end 1071 | end 1072 | 1073 | #@@to_s = Kernel.instance_method(:to_s) 1074 | #def inspect 1075 | # @@to_s.bind(self).call.sub!(/>\z/) {" URL:#{self}>"} 1076 | #end 1077 | 1078 | def coerce(oth) 1079 | case oth 1080 | when String 1081 | oth = parser.parse(oth) 1082 | else 1083 | super 1084 | end 1085 | 1086 | return oth, self 1087 | end 1088 | end 1089 | end 1090 | -------------------------------------------------------------------------------- /mrblib/http.rb: -------------------------------------------------------------------------------- 1 | # 2 | # = uri/http.rb 3 | # 4 | # Author:: Akira Yamada 5 | # License:: You can redistribute it and/or modify it under the same term as Ruby. 6 | # Revision:: $Id$ 7 | # 8 | 9 | module URI 10 | 11 | # 12 | # The syntax of HTTP URIs is defined in RFC1738 section 3.3. 13 | # 14 | # Note that the Ruby URI library allows HTTP URLs containing usernames and 15 | # passwords. This is not legal as per the RFC, but used to be 16 | # supported in Internet Explorer 5 and 6, before the MS04-004 security 17 | # update. See . 18 | # 19 | class HTTP < Generic 20 | DEFAULT_PORT = 80 21 | 22 | COMPONENT = [ 23 | :scheme, 24 | :userinfo, :host, :port, 25 | :path, 26 | :query, 27 | :fragment 28 | ] 29 | 30 | # 31 | # == Description 32 | # 33 | # Create a new URI::HTTP object from components, with syntax checking. 34 | # 35 | # The components accepted are userinfo, host, port, path, query and 36 | # fragment. 37 | # 38 | # The components should be provided either as an Array, or as a Hash 39 | # with keys formed by preceding the component names with a colon. 40 | # 41 | # If an Array is used, the components must be passed in the order 42 | # [userinfo, host, port, path, query, fragment]. 43 | # 44 | # Example: 45 | # 46 | # newuri = URI::HTTP.build({:host => 'www.example.com', 47 | # :path> => '/foo/bar'}) 48 | # 49 | # newuri = URI::HTTP.build([nil, "www.example.com", nil, "/path", 50 | # "query", 'fragment']) 51 | # 52 | # Currently, if passed userinfo components this method generates 53 | # invalid HTTP URIs as per RFC 1738. 54 | # 55 | def self.build(args) 56 | tmp = Util::make_components_hash(self, args) 57 | return super(tmp) 58 | end 59 | 60 | # 61 | # == Description 62 | # 63 | # Create a new URI::HTTP object from generic URI components as per 64 | # RFC 2396. No HTTP-specific syntax checking (as per RFC 1738) is 65 | # performed. 66 | # 67 | # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+, 68 | # +opaque+, +query+ and +fragment+, in that order. 69 | # 70 | # Example: 71 | # 72 | # uri = URI::HTTP.new(['http', nil, "www.example.com", nil, "/path", 73 | # "query", 'fragment']) 74 | # 75 | def initialize(*arg) 76 | super(*arg) 77 | end 78 | 79 | # 80 | # == Description 81 | # 82 | # Returns the full path for an HTTP request, as required by Net::HTTP::Get. 83 | # 84 | # If the URI contains a query, the full path is URI#path + '?' + URI#query. 85 | # Otherwise, the path is simply URI#path. 86 | # 87 | def request_uri 88 | r = path_query 89 | return nil unless r 90 | if r[0] != ?/ 91 | r = '/' + r 92 | end 93 | 94 | r 95 | end 96 | end 97 | 98 | @@schemes['HTTP'] = HTTP 99 | end 100 | -------------------------------------------------------------------------------- /mrblib/https.rb: -------------------------------------------------------------------------------- 1 | # = uri/https.rb 2 | # 3 | # Author:: Akira Yamada 4 | # License:: You can redistribute it and/or modify it under the same term as Ruby. 5 | # Revision:: $Id$ 6 | # 7 | # See URI for general documentation 8 | # 9 | 10 | module URI 11 | 12 | # The default port for HTTPS URIs is 443, and the scheme is 'https:' rather 13 | # than 'http:'. Other than that, HTTPS URIs are identical to HTTP URIs; 14 | # see URI::HTTP. 15 | class HTTPS < HTTP 16 | # A Default port of 443 for URI::HTTPS 17 | DEFAULT_PORT = 443 18 | end 19 | @@schemes['HTTPS'] = HTTPS 20 | end 21 | -------------------------------------------------------------------------------- /mrblib/mailto.rb: -------------------------------------------------------------------------------- 1 | # = uri/mailto.rb 2 | # 3 | # Author:: Akira Yamada 4 | # License:: You can redistribute it and/or modify it under the same term as Ruby. 5 | # Revision:: $Id$ 6 | # 7 | # See URI for general documentation 8 | # 9 | 10 | module URI 11 | 12 | # 13 | # RFC6068, The mailto URL scheme 14 | # 15 | class MailTo < Generic 16 | include REGEXP 17 | 18 | # A Default port of nil for URI::MailTo 19 | DEFAULT_PORT = nil 20 | 21 | # An Array of the available components for URI::MailTo 22 | COMPONENT = [ :scheme, :to, :headers ] 23 | 24 | # :stopdoc: 25 | # "hname" and "hvalue" are encodings of an RFC 822 header name and 26 | # value, respectively. As with "to", all URL reserved characters must 27 | # be encoded. 28 | # 29 | # "#mailbox" is as specified in RFC 822 [RFC822]. This means that it 30 | # consists of zero or more comma-separated mail addresses, possibly 31 | # including "phrase" and "comment" components. Note that all URL 32 | # reserved characters in "to" must be encoded: in particular, 33 | # parentheses, commas, and the percent sign ("%"), which commonly occur 34 | # in the "mailbox" syntax. 35 | # 36 | # Within mailto URLs, the characters "?", "=", "&" are reserved. 37 | 38 | # ; RFC 6068 39 | # hfields = "?" hfield *( "&" hfield ) 40 | # hfield = hfname "=" hfvalue 41 | # hfname = *qchar 42 | # hfvalue = *qchar 43 | # qchar = unreserved / pct-encoded / some-delims 44 | # some-delims = "!" / "$" / "'" / "(" / ")" / "*" 45 | # / "+" / "," / ";" / ":" / "@" 46 | # 47 | # ; RFC3986 48 | # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 49 | # pct-encoded = "%" HEXDIG HEXDIG 50 | HEADER_REGEXP = /\A(?(?:%\h\h|[!$'-.0-;@-Z_a-z~])*=(?:%\h\h|[!$'-.0-;@-Z_a-z~])*)(?:&\g)*\z/ 51 | # practical regexp for email address 52 | # http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address 53 | EMAIL_REGEXP = /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/ 54 | # :startdoc: 55 | 56 | # 57 | # == Description 58 | # 59 | # Creates a new URI::MailTo object from components, with syntax checking. 60 | # 61 | # Components can be provided as an Array or Hash. If an Array is used, 62 | # the components must be supplied as [to, headers]. 63 | # 64 | # If a Hash is used, the keys are the component names preceded by colons. 65 | # 66 | # The headers can be supplied as a pre-encoded string, such as 67 | # "subject=subscribe&cc=address", or as an Array of Arrays like 68 | # [['subject', 'subscribe'], ['cc', 'address']] 69 | # 70 | # Examples: 71 | # 72 | # require 'uri' 73 | # 74 | # m1 = URI::MailTo.build(['joe@example.com', 'subject=Ruby']) 75 | # puts m1.to_s -> mailto:joe@example.com?subject=Ruby 76 | # 77 | # m2 = URI::MailTo.build(['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]]) 78 | # puts m2.to_s -> mailto:john@example.com?Subject=Ruby&Cc=jack@example.com 79 | # 80 | # m3 = URI::MailTo.build({:to => 'listman@example.com', :headers => [['subject', 'subscribe']]}) 81 | # puts m3.to_s -> mailto:listman@example.com?subject=subscribe 82 | # 83 | def self.build(args) 84 | tmp = Util.make_components_hash(self, args) 85 | 86 | case tmp[:to] 87 | when Array 88 | tmp[:opaque] = tmp[:to].join(',') 89 | when String 90 | tmp[:opaque] = tmp[:to].dup 91 | else 92 | tmp[:opaque] = '' 93 | end 94 | 95 | if tmp[:headers] 96 | query = 97 | case tmp[:headers] 98 | when Array 99 | tmp[:headers].collect { |x| 100 | if x.kind_of?(Array) 101 | x[0] + '=' + x[1..-1].join 102 | else 103 | x.to_s 104 | end 105 | }.join('&') 106 | when Hash 107 | tmp[:headers].collect { |h,v| 108 | h + '=' + v 109 | }.join('&') 110 | else 111 | tmp[:headers].to_s 112 | end 113 | unless query.empty? 114 | tmp[:opaque] << '?' << query 115 | end 116 | end 117 | 118 | super(tmp) 119 | end 120 | 121 | # 122 | # == Description 123 | # 124 | # Creates a new URI::MailTo object from generic URL components with 125 | # no syntax checking. 126 | # 127 | # This method is usually called from URI::parse, which checks 128 | # the validity of each component. 129 | # 130 | def initialize(*arg) 131 | super(*arg) 132 | 133 | @to = nil 134 | @headers = [] 135 | 136 | # The RFC3986 parser does not normally populate opaque 137 | @opaque = "?#{@query}" if @query && !@opaque 138 | 139 | unless @opaque 140 | raise InvalidComponentError, 141 | "missing opaque part for mailto URL" 142 | end 143 | to, header = @opaque.split('?', 2) 144 | # allow semicolon as a addr-spec separator 145 | # http://support.microsoft.com/kb/820868 146 | unless /\A(?:[^@,;]+@[^@,;]+(?:\z|[,;]))*\z/ =~ to 147 | raise InvalidComponentError, 148 | "unrecognised opaque part for mailtoURL: #{@opaque}" 149 | end 150 | self.to = to 151 | self.headers = header 152 | end 153 | 154 | # The primary e-mail address of the URL, as a String 155 | attr_reader :to 156 | 157 | # E-mail headers set by the URL, as an Array of Arrays 158 | attr_reader :headers 159 | 160 | # check the to +v+ component 161 | def check_to(v) 162 | return true unless v 163 | return true if v.size == 0 164 | 165 | v.split(/[,;]/).each do |addr| 166 | # check url safety as path-rootless 167 | if /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*\z/ !~ addr 168 | raise InvalidComponentError, 169 | "an address in 'to' is invalid as URI #{addr.dump}" 170 | end 171 | 172 | # check addr-spec 173 | # don't s/\+/ /g 174 | addr.gsub!(/%\h\h/, URI::TBLDECWWWCOMP_) 175 | if EMAIL_REGEXP !~ addr 176 | raise InvalidComponentError, 177 | "an address in 'to' is invalid as uri-escaped addr-spec #{addr.dump}" 178 | end 179 | end 180 | 181 | true 182 | end 183 | private :check_to 184 | 185 | # private setter for to +v+ 186 | def set_to(v) 187 | @to = v 188 | end 189 | protected :set_to 190 | 191 | # setter for to +v+ 192 | def to=(v) 193 | check_to(v) 194 | set_to(v) 195 | v 196 | end 197 | 198 | # check the headers +v+ component against either 199 | # * HEADER_REGEXP 200 | def check_headers(v) 201 | return true unless v 202 | return true if v.size == 0 203 | if HEADER_REGEXP !~ v 204 | raise InvalidComponentError, 205 | "bad component(expected opaque component): #{v}" 206 | end 207 | 208 | true 209 | end 210 | private :check_headers 211 | 212 | # private setter for headers +v+ 213 | def set_headers(v) 214 | @headers = [] 215 | if v 216 | v.split('&').each do |x| 217 | @headers << x.split(/=/, 2) 218 | end 219 | end 220 | end 221 | protected :set_headers 222 | 223 | # setter for headers +v+ 224 | def headers=(v) 225 | check_headers(v) 226 | set_headers(v) 227 | v 228 | end 229 | 230 | # Constructs String from URI 231 | def to_s 232 | @scheme + ':' + 233 | if @to 234 | @to 235 | else 236 | '' 237 | end + 238 | if @headers.size > 0 239 | '?' + @headers.collect{|x| x.join('=')}.join('&') 240 | else 241 | '' 242 | end + 243 | if @fragment 244 | '#' + @fragment 245 | else 246 | '' 247 | end 248 | end 249 | 250 | # Returns the RFC822 e-mail text equivalent of the URL, as a String. 251 | # 252 | # Example: 253 | # 254 | # require 'uri' 255 | # 256 | # uri = URI.parse("mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr") 257 | # uri.to_mailtext 258 | # # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n" 259 | # 260 | def to_mailtext 261 | to = URI.decode_www_form_component(@to) 262 | head = '' 263 | body = '' 264 | @headers.each do |x| 265 | case x[0] 266 | when 'body' 267 | body = URI.decode_www_form_component(x[1]) 268 | when 'to' 269 | to << ', ' + URI.decode_www_form_component(x[1]) 270 | else 271 | head << URI.decode_www_form_component(x[0]).capitalize + ': ' + 272 | URI.decode_www_form_component(x[1]) + "\n" 273 | end 274 | end 275 | 276 | "To: #{to} 277 | #{head} 278 | #{body} 279 | " 280 | end 281 | alias to_rfc822text to_mailtext 282 | end 283 | 284 | @@schemes['MAILTO'] = MailTo 285 | end 286 | -------------------------------------------------------------------------------- /mrblib/uri.rb: -------------------------------------------------------------------------------- 1 | # 2 | # URI support for Ruby 3 | # 4 | # Author:: Akira Yamada 5 | # Documentation:: Akira Yamada , Dmitry V. Sabanin 6 | # License:: 7 | # Copyright (c) 2001 akira yamada 8 | # You can redistribute it and/or modify it under the same term as Ruby. 9 | # Revision:: $Id$ 10 | # 11 | # See URI for documentation 12 | # 13 | 14 | module URI 15 | # :stopdoc: 16 | #VERSION_CODE = '000911' 17 | #VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.') 18 | VERSION = "1.0.0" 19 | # :startdoc: 20 | end 21 | -------------------------------------------------------------------------------- /src/uri.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static mrb_value 4 | mrb_uri_s_encode_www_component(mrb_state *mrb, mrb_value uri) 5 | { 6 | mrb_value *argv; 7 | mrb_int argc; 8 | mrb_warn(mrb, "[Deprecated] Please use `URI.encode_www_form_component' instead of `URI.encode_www_component' \n"); 9 | mrb_get_args(mrb, "*", &argv, &argc); 10 | return mrb_funcall_argv(mrb, uri, mrb_intern_lit(mrb, "encode_www_form_component"), argc, argv); 11 | } 12 | 13 | static mrb_value 14 | mrb_uri_s_decode_www_component(mrb_state *mrb, mrb_value uri) 15 | { 16 | mrb_value *argv; 17 | mrb_int argc; 18 | mrb_warn(mrb, "[Deprecated] Please use `URI.decode_www_form_component' instead of `URI.decode_www_component' \n"); 19 | mrb_get_args(mrb, "*", &argv, &argc); 20 | return mrb_funcall_argv(mrb, uri, mrb_intern_lit(mrb, "decode_www_form_component"), argc, argv); 21 | } 22 | 23 | void 24 | mrb_mruby_uri_gem_init(mrb_state *mrb) 25 | { 26 | struct RClass *uri = mrb_define_module(mrb, "URI"); 27 | mrb_define_class_method(mrb, uri, "encode_www_component", mrb_uri_s_encode_www_component, MRB_ARGS_ANY()); 28 | mrb_define_class_method(mrb, uri, "decode_www_component", mrb_uri_s_decode_www_component, MRB_ARGS_ANY()); 29 | } 30 | 31 | void 32 | mrb_mruby_uri_gem_final(mrb_state *mrb) 33 | { 34 | } 35 | -------------------------------------------------------------------------------- /test/test_common.rb: -------------------------------------------------------------------------------- 1 | class TestCommon < MTest::Unit::TestCase 2 | def setup 3 | end 4 | 5 | def teardown 6 | end 7 | 8 | def test_extract 9 | assert_equal(['http://example.com'], 10 | URI.extract('http://example.com')) 11 | assert_equal(['http://example.com'], 12 | URI.extract('(http://example.com)')) 13 | assert_equal(['http://example.com/foo)'], 14 | URI.extract('(http://example.com/foo)')) 15 | assert_equal(['http://example.jphttp://example.jp'], 16 | URI.extract('http://example.jphttp://example.jp'), "[ruby-list:36086]") 17 | assert_equal(['http://example.jphttp://example.jp'], 18 | URI.extract('http://example.jphttp://example.jp', ['http']), "[ruby-list:36086]") 19 | #assert_equal(['http://', 'mailto:'].sort, 20 | # URI.extract('ftp:// http:// mailto: https://', ['http', 'mailto']).sort) 21 | ## reported by Doug Kearns 22 | #assert_equal(['From:', 'mailto:xxx@xxx.xxx.xxx]'].sort, 23 | # URI.extract('From: XXX [mailto:xxx@xxx.xxx.xxx]').sort) 24 | end 25 | 26 | def test_regexp 27 | assert_instance_of Regexp, URI.regexp 28 | assert_instance_of Regexp, URI.regexp(['http']) 29 | assert_equal URI.regexp, URI.regexp 30 | assert_equal 'http://', 'x http:// x'.slice(URI.regexp) 31 | assert_equal 'http://', 'x http:// x'.slice(URI.regexp(['http'])) 32 | assert_equal 'http://', 'x http:// x ftp://'.slice(URI.regexp(['http'])) 33 | assert_equal nil, 'http://'.slice(URI.regexp([])) 34 | assert_equal nil, ''.slice(URI.regexp) 35 | assert_equal nil, 'xxxx'.slice(URI.regexp) 36 | assert_equal nil, ':'.slice(URI.regexp) 37 | assert_equal 'From:', 'From:'.slice(URI.regexp) 38 | end 39 | 40 | def test_kernel_uri 41 | expected = URI.parse("http://www.ruby-lang.org/") 42 | assert_equal(expected, URI("http://www.ruby-lang.org/")) 43 | assert_equal(expected, Kernel::URI("http://www.ruby-lang.org/")) 44 | #assert_raise(NoMethodError) { Object.new.URI("http://www.ruby-lang.org/") } 45 | end 46 | 47 | def test_encode_www_form_component 48 | assert_equal("+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E", 49 | URI.encode_www_form_component(" !\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~")) 50 | assert_equal("%00%01", URI.encode_www_form_component("\x00\x01")) 51 | assert_equal("%AA%FF", URI.encode_www_form_component("\xaa\xff")) 52 | end 53 | 54 | def test_decode_www_form_component 55 | assert_equal(" !\"\#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~", 56 | URI.decode_www_form_component( 57 | "+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D%7E")) 58 | assert_equal("\x00\x01", URI.decode_www_form_component("%00%01")) 59 | assert_equal("\xaa\xff", URI.decode_www_form_component("%AA%FF")) 60 | end 61 | 62 | def test_encode_www_form 63 | assert_equal("a=1", URI.encode_www_form("a" => "1")) 64 | assert_equal("a=1", URI.encode_www_form(a: 1)) 65 | assert_equal("a=1", URI.encode_www_form([["a", "1"]])) 66 | assert_equal("a=1", URI.encode_www_form([[:a, 1]])) 67 | expected = "a=1&%E3%81%82=%E6%BC%A2" 68 | assert_equal(expected, URI.encode_www_form("a" => "1", 'あ' => '漢')) 69 | assert_equal(expected, URI.encode_www_form(a: 1, :'あ' => '漢')) 70 | assert_equal(expected, URI.encode_www_form([["a", "1"], ['あ', '漢']])) 71 | assert_equal(expected, URI.encode_www_form([[:a, 1], [:'あ', '漢']])) 72 | 73 | assert_equal('&', URI.encode_www_form([['', nil], ['', nil]])) 74 | assert_equal('&=', URI.encode_www_form([['', nil], ['', '']])) 75 | assert_equal('=&', URI.encode_www_form([['', ''], ['', nil]])) 76 | assert_equal('=&=', URI.encode_www_form([['', ''], ['', '']])) 77 | assert_equal('', URI.encode_www_form([['', nil]])) 78 | assert_equal('', URI.encode_www_form([])) 79 | assert_equal('=', URI.encode_www_form([['', '']])) 80 | assert_equal('a%26b=1&c=2%3B3&e=4', URI.encode_www_form([['a&b', '1'], ['c', '2;3'], ['e', '4']])) 81 | assert_equal('image&title&price', URI.encode_www_form([['image', nil], ['title', nil], ['price', nil]])) 82 | assert_equal("q=ruby&q=perl&lang=en", URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")) 83 | end 84 | 85 | def test_decode_www_form 86 | assert_equal([["あ", "漢"]], URI.decode_www_form("%E3%81%82=%E6%BC%A2")) 87 | assert_equal([%w[a 1], %w[a 2]], URI.decode_www_form("a=1&a=2")) 88 | assert_equal([%w[a 1;a=2]], URI.decode_www_form("a=1;a=2")) 89 | assert_equal([%w[a 1], ['', ''], %w[a 2]], URI.decode_www_form("a=1&&a=2")) 90 | assert_equal([%w[?a 1], %w[a 2]], URI.decode_www_form("?a=1&a=2")) 91 | assert_equal([], URI.decode_www_form("")) 92 | assert_equal([%w[% 1]], URI.decode_www_form("%=1")) 93 | assert_equal([%w[a %]], URI.decode_www_form("a=%")) 94 | assert_equal([%w[a 1], %w[% 2]], URI.decode_www_form("a=1&%=2")) 95 | assert_equal([%w[a 1], %w[b %]], URI.decode_www_form("a=1&b=%")) 96 | assert_equal([['a', ''], ['b', '']], URI.decode_www_form("a&b")) 97 | end 98 | 99 | def test_escape 100 | assert_equal("http://example.com/?a=%09%0D", URI.escape("http://example.com/?a=\11\15")) 101 | assert_equal("@%3F@%21", URI.escape("@?@!", "!?")) 102 | end 103 | 104 | def test_unescape 105 | assert_equal("http://example.com/?a=\t\r", URI.unescape("http://example.com/?a=%09%0D")) 106 | assert_equal("\xe3\x83\x90", URI.unescape("\xe3\x83\x90")) 107 | assert_equal("\xe3\x83\x90", URI.unescape('%e3%83%90')) 108 | assert_equal("\u3042", URI.unescape('%e3%81%82')) 109 | assert_equal("\xe3\x83\x90\xe3\x83\x90", URI.unescape("\xe3\x83\x90%e3%83%90")) 110 | end 111 | end 112 | 113 | MTest::Unit.new.run 114 | -------------------------------------------------------------------------------- /test/test_generic.rb: -------------------------------------------------------------------------------- 1 | class URI::TestGeneric < MTest::Unit::TestCase 2 | def setup 3 | @url = 'http://a/b/c/d;p?q' 4 | @base_url = URI.parse(@url) 5 | end 6 | 7 | def teardown 8 | end 9 | 10 | def uri_to_ary(uri) 11 | uri.class.component.collect {|c| uri.__send__(c)} 12 | end 13 | 14 | def test_to_s 15 | exp = 'http://example.com/'.freeze 16 | str = URI(exp).to_s 17 | assert_equal exp, str 18 | 19 | assert_equal "file:///foo", URI("file:///foo").to_s 20 | assert_equal "postgres:///foo", URI("postgres:///foo").to_s 21 | assert_equal "http:/foo", URI("http:///foo").to_s 22 | end 23 | 24 | def test_parse 25 | # 0 26 | assert_kind_of(URI::HTTP, @base_url) 27 | 28 | exp = [ 29 | 'http', 30 | nil, 'a', URI::HTTP.default_port, 31 | '/b/c/d;p', 32 | 'q', 33 | nil 34 | ] 35 | ary = uri_to_ary(@base_url) 36 | assert_equal(exp, ary) 37 | 38 | # 1 39 | # url = URI.parse('ftp://ftp.is.co.za/rfc/rfc1808.txt') 40 | # assert_kind_of(URI::FTP, url) 41 | # 42 | # exp = [ 43 | # 'ftp', 44 | # nil, 'ftp.is.co.za', URI::FTP.default_port, 45 | # 'rfc/rfc1808.txt', nil, 46 | # ] 47 | # ary = uri_to_ary(url) 48 | # assert_equal(exp, ary) 49 | # 1' 50 | # url = URI.parse('ftp://ftp.is.co.za/%2Frfc/rfc1808.txt') 51 | # assert_kind_of(URI::FTP, url) 52 | # 53 | # exp = [ 54 | # 'ftp', 55 | # nil, 'ftp.is.co.za', URI::FTP.default_port, 56 | # '/rfc/rfc1808.txt', nil, 57 | # ] 58 | # ary = uri_to_ary(url) 59 | # assert_equal(exp, ary) 60 | 61 | # 2 62 | url = URI.parse('gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles') 63 | assert_kind_of(URI::Generic, url) 64 | 65 | exp = [ 66 | 'gopher', 67 | nil, 'spinaltap.micro.umn.edu', nil, nil, 68 | '/00/Weather/California/Los%20Angeles', nil, 69 | nil, 70 | nil 71 | ] 72 | ary = uri_to_ary(url) 73 | assert_equal(exp, ary) 74 | 75 | # 3 76 | url = URI.parse('http://www.math.uio.no/faq/compression-faq/part1.html') 77 | assert_kind_of(URI::HTTP, url) 78 | 79 | exp = [ 80 | 'http', 81 | nil, 'www.math.uio.no', URI::HTTP.default_port, 82 | '/faq/compression-faq/part1.html', 83 | nil, 84 | nil 85 | ] 86 | ary = uri_to_ary(url) 87 | assert_equal(exp, ary) 88 | 89 | # 4 90 | url = URI.parse('mailto:mduerst@ifi.unizh.ch') 91 | assert_kind_of(URI::Generic, url) 92 | 93 | exp = [ 94 | 'mailto', 95 | 'mduerst@ifi.unizh.ch', 96 | [] 97 | ] 98 | ary = uri_to_ary(url) 99 | assert_equal(exp, ary) 100 | 101 | # 5 102 | url = URI.parse('news:comp.infosystems.www.servers.unix') 103 | assert_kind_of(URI::Generic, url) 104 | 105 | exp = [ 106 | 'news', 107 | nil, nil, nil, nil, 108 | nil, 'comp.infosystems.www.servers.unix', 109 | nil, 110 | nil 111 | ] 112 | ary = uri_to_ary(url) 113 | assert_equal(exp, ary) 114 | 115 | # 6 116 | url = URI.parse('telnet://melvyl.ucop.edu/') 117 | assert_kind_of(URI::Generic, url) 118 | 119 | exp = [ 120 | 'telnet', 121 | nil, 'melvyl.ucop.edu', nil, nil, 122 | '/', nil, 123 | nil, 124 | nil 125 | ] 126 | ary = uri_to_ary(url) 127 | assert_equal(exp, ary) 128 | 129 | # 7 130 | # reported by Mr. Kubota 131 | assert_nothing_raised(URI::InvalidURIError) { URI.parse('http://a_b:80/') } 132 | assert_nothing_raised(URI::InvalidURIError) { URI.parse('http://a_b/') } 133 | 134 | # 8 135 | # reported by m_seki 136 | url = URI.parse('file:///foo/bar.txt') 137 | assert_kind_of(URI::Generic, url) 138 | url = URI.parse('file:/foo/bar.txt') 139 | assert_kind_of(URI::Generic, url) 140 | 141 | # 9 142 | url = URI.parse('ftp://:pass@localhost/') 143 | assert_equal('', url.user, "[ruby-dev:25667]") 144 | assert_equal('pass', url.password) 145 | assert_equal(':pass', url.userinfo, "[ruby-dev:25667]") 146 | url = URI.parse('ftp://user@localhost/') 147 | assert_equal('user', url.user) 148 | assert_equal(nil, url.password) 149 | assert_equal('user', url.userinfo) 150 | url = URI.parse('ftp://localhost/') 151 | assert_equal(nil, url.user) 152 | assert_equal(nil, url.password) 153 | assert_equal(nil, url.userinfo) 154 | end 155 | 156 | def test_merge 157 | u1 = URI.parse('http://foo') 158 | u2 = URI.parse('http://foo/') 159 | u3 = URI.parse('http://foo/bar') 160 | u4 = URI.parse('http://foo/bar/') 161 | 162 | { 163 | u1 => { 164 | 'baz' => 'http://foo/baz', 165 | '/baz' => 'http://foo/baz', 166 | }, 167 | u2 => { 168 | 'baz' => 'http://foo/baz', 169 | '/baz' => 'http://foo/baz', 170 | }, 171 | u3 => { 172 | 'baz' => 'http://foo/baz', 173 | '/baz' => 'http://foo/baz', 174 | }, 175 | u4 => { 176 | 'baz' => 'http://foo/bar/baz', 177 | '/baz' => 'http://foo/baz', 178 | }, 179 | }.each { |base, map| 180 | map.each { |url, result| 181 | expected = URI.parse(result) 182 | uri = URI.parse(url) 183 | assert_equal expected, base + url, "<#{base}> + #{url.inspect} to become <#{expected}>" 184 | assert_equal expected, base + uri, "<#{base}> + <#{uri}> to become <#{expected}>" 185 | } 186 | } 187 | 188 | url = URI.parse('http://hoge/a.html') + 'b.html' 189 | assert_equal('http://hoge/b.html', url.to_s, "[ruby-dev:11508]") 190 | 191 | # reported by Mr. Kubota 192 | url = URI.parse('http://a/b') + 'http://x/y' 193 | assert_equal('http://x/y', url.to_s) 194 | assert_equal(url, URI.parse('') + 'http://x/y') 195 | assert_equal(url, URI.parse('').normalize + 'http://x/y') 196 | assert_equal(url, URI.parse('http://a/b').normalize + 'http://x/y') 197 | 198 | u = URI.parse('http://foo/bar/baz') 199 | assert_equal(nil, u.merge!("")) 200 | assert_equal(nil, u.merge!(u)) 201 | assert(nil != u.merge!(".")) 202 | assert_equal('http://foo/bar/', u.to_s) 203 | assert(nil != u.merge!("../baz")) 204 | assert_equal('http://foo/baz', u.to_s) 205 | 206 | url = URI.parse('http://a/b//c') + 'd//e' 207 | assert_equal('http://a/b//d//e', url.to_s) 208 | 209 | u0 = URI.parse('mailto:foo@example.com') 210 | u1 = URI.parse('mailto:foo@example.com#bar') 211 | assert_equal(uri_to_ary(u0 + '#bar'), uri_to_ary(u1), "[ruby-dev:23628]") 212 | 213 | u0 = URI.parse('http://www.example.com/') 214 | u1 = URI.parse('http://www.example.com/foo/..') + './' 215 | assert_equal(u0, u1, "[ruby-list:39838]") 216 | u0 = URI.parse('http://www.example.com/foo/') 217 | u1 = URI.parse('http://www.example.com/foo/bar/..') + './' 218 | assert_equal(u0, u1) 219 | u0 = URI.parse('http://www.example.com/foo/bar/') 220 | u1 = URI.parse('http://www.example.com/foo/bar/baz/..') + './' 221 | assert_equal(u0, u1) 222 | u0 = URI.parse('http://www.example.com/') 223 | u1 = URI.parse('http://www.example.com/foo/bar/../..') + './' 224 | assert_equal(u0, u1) 225 | u0 = URI.parse('http://www.example.com/foo/') 226 | u1 = URI.parse('http://www.example.com/foo/bar/baz/../..') + './' 227 | assert_equal(u0, u1) 228 | 229 | u = URI.parse('http://www.example.com/') 230 | u0 = u + './foo/' 231 | u1 = u + './foo/bar/..' 232 | assert_equal(u0, u1, "[ruby-list:39844]") 233 | u = URI.parse('http://www.example.com/') 234 | u0 = u + './' 235 | u1 = u + './foo/bar/../..' 236 | assert_equal(u0, u1) 237 | end 238 | 239 | def test_route 240 | url = URI.parse('http://hoge/a.html').route_to('http://hoge/b.html') 241 | assert_equal('b.html', url.to_s) 242 | 243 | url = URI.parse('http://hoge/a/').route_to('http://hoge/b/') 244 | assert_equal('../b/', url.to_s) 245 | url = URI.parse('http://hoge/a/b').route_to('http://hoge/b/') 246 | assert_equal('../b/', url.to_s) 247 | 248 | url = URI.parse('http://hoge/a/b/').route_to('http://hoge/b/') 249 | assert_equal('../../b/', url.to_s) 250 | 251 | url = URI.parse('http://hoge/a/b/').route_to('http://HOGE/b/') 252 | assert_equal('../../b/', url.to_s) 253 | 254 | url = URI.parse('http://hoge/a/b/').route_to('http://MOGE/b/') 255 | assert_equal('//MOGE/b/', url.to_s) 256 | 257 | url = URI.parse('http://hoge/b').route_to('http://hoge/b/') 258 | assert_equal('b/', url.to_s) 259 | url = URI.parse('http://hoge/b/a').route_to('http://hoge/b/') 260 | assert_equal('./', url.to_s) 261 | url = URI.parse('http://hoge/b/').route_to('http://hoge/b') 262 | assert_equal('../b', url.to_s) 263 | url = URI.parse('http://hoge/b').route_to('http://hoge/b:c') 264 | assert_equal('./b:c', url.to_s) 265 | 266 | url = URI.parse('http://hoge/b//c').route_to('http://hoge/b/c') 267 | assert_equal('../c', url.to_s) 268 | 269 | url = URI.parse('file:///a/b/').route_to('file:///a/b/') 270 | assert_equal('', url.to_s) 271 | url = URI.parse('file:///a/b/').route_to('file:///a/b') 272 | assert_equal('../b', url.to_s) 273 | 274 | url = URI.parse('mailto:foo@example.com').route_to('mailto:foo@example.com#bar') 275 | assert_equal('#bar', url.to_s) 276 | 277 | url = URI.parse('mailto:foo@example.com#bar').route_to('mailto:foo@example.com') 278 | assert_equal('', url.to_s) 279 | 280 | url = URI.parse('mailto:foo@example.com').route_to('mailto:foo@example.com') 281 | assert_equal('', url.to_s) 282 | end 283 | 284 | def test_rfc3986_examples 285 | # http://a/b/c/d;p?q 286 | # g:h = g:h 287 | url = @base_url.merge('g:h') 288 | assert_kind_of(URI::Generic, url) 289 | assert_equal('g:h', url.to_s) 290 | url = @base_url.route_to('g:h') 291 | assert_kind_of(URI::Generic, url) 292 | assert_equal('g:h', url.to_s) 293 | 294 | # http://a/b/c/d;p?q 295 | # g = http://a/b/c/g 296 | url = @base_url.merge('g') 297 | assert_kind_of(URI::HTTP, url) 298 | assert_equal('http://a/b/c/g', url.to_s) 299 | url = @base_url.route_to('http://a/b/c/g') 300 | assert_kind_of(URI::Generic, url) 301 | assert_equal('g', url.to_s) 302 | 303 | # http://a/b/c/d;p?q 304 | # ./g = http://a/b/c/g 305 | url = @base_url.merge('./g') 306 | assert_kind_of(URI::HTTP, url) 307 | assert_equal('http://a/b/c/g', url.to_s) 308 | url = @base_url.route_to('http://a/b/c/g') 309 | assert_kind_of(URI::Generic, url) 310 | assert('./g' != url.to_s) # ok 311 | assert_equal('g', url.to_s) 312 | 313 | # http://a/b/c/d;p?q 314 | # g/ = http://a/b/c/g/ 315 | url = @base_url.merge('g/') 316 | assert_kind_of(URI::HTTP, url) 317 | assert_equal('http://a/b/c/g/', url.to_s) 318 | url = @base_url.route_to('http://a/b/c/g/') 319 | assert_kind_of(URI::Generic, url) 320 | assert_equal('g/', url.to_s) 321 | 322 | # http://a/b/c/d;p?q 323 | # /g = http://a/g 324 | url = @base_url.merge('/g') 325 | assert_kind_of(URI::HTTP, url) 326 | assert_equal('http://a/g', url.to_s) 327 | url = @base_url.route_to('http://a/g') 328 | assert_kind_of(URI::Generic, url) 329 | assert('/g' != url.to_s) # ok 330 | assert_equal('../../g', url.to_s) 331 | 332 | # http://a/b/c/d;p?q 333 | # //g = http://g 334 | url = @base_url.merge('//g') 335 | assert_kind_of(URI::HTTP, url) 336 | assert_equal('http://g', url.to_s) 337 | url = @base_url.route_to('http://g') 338 | assert_kind_of(URI::Generic, url) 339 | assert_equal('//g', url.to_s) 340 | 341 | # http://a/b/c/d;p?q 342 | # ?y = http://a/b/c/d;p?y 343 | url = @base_url.merge('?y') 344 | assert_kind_of(URI::HTTP, url) 345 | assert_equal('http://a/b/c/d;p?y', url.to_s) 346 | url = @base_url.route_to('http://a/b/c/d;p?y') 347 | assert_kind_of(URI::Generic, url) 348 | assert_equal('?y', url.to_s) 349 | 350 | # http://a/b/c/d;p?q 351 | # g?y = http://a/b/c/g?y 352 | url = @base_url.merge('g?y') 353 | assert_kind_of(URI::HTTP, url) 354 | assert_equal('http://a/b/c/g?y', url.to_s) 355 | url = @base_url.route_to('http://a/b/c/g?y') 356 | assert_kind_of(URI::Generic, url) 357 | assert_equal('g?y', url.to_s) 358 | 359 | # http://a/b/c/d;p?q 360 | # #s = http://a/b/c/d;p?q#s 361 | url = @base_url.merge('#s') 362 | assert_kind_of(URI::HTTP, url) 363 | assert_equal('http://a/b/c/d;p?q#s', url.to_s) 364 | url = @base_url.route_to('http://a/b/c/d;p?q#s') 365 | assert_kind_of(URI::Generic, url) 366 | assert_equal('#s', url.to_s) 367 | 368 | # http://a/b/c/d;p?q 369 | # g#s = http://a/b/c/g#s 370 | url = @base_url.merge('g#s') 371 | assert_kind_of(URI::HTTP, url) 372 | assert_equal('http://a/b/c/g#s', url.to_s) 373 | url = @base_url.route_to('http://a/b/c/g#s') 374 | assert_kind_of(URI::Generic, url) 375 | assert_equal('g#s', url.to_s) 376 | 377 | # http://a/b/c/d;p?q 378 | # g?y#s = http://a/b/c/g?y#s 379 | url = @base_url.merge('g?y#s') 380 | assert_kind_of(URI::HTTP, url) 381 | assert_equal('http://a/b/c/g?y#s', url.to_s) 382 | url = @base_url.route_to('http://a/b/c/g?y#s') 383 | assert_kind_of(URI::Generic, url) 384 | assert_equal('g?y#s', url.to_s) 385 | 386 | # http://a/b/c/d;p?q 387 | # ;x = http://a/b/c/;x 388 | url = @base_url.merge(';x') 389 | assert_kind_of(URI::HTTP, url) 390 | assert_equal('http://a/b/c/;x', url.to_s) 391 | url = @base_url.route_to('http://a/b/c/;x') 392 | assert_kind_of(URI::Generic, url) 393 | assert_equal(';x', url.to_s) 394 | 395 | # http://a/b/c/d;p?q 396 | # g;x = http://a/b/c/g;x 397 | url = @base_url.merge('g;x') 398 | assert_kind_of(URI::HTTP, url) 399 | assert_equal('http://a/b/c/g;x', url.to_s) 400 | url = @base_url.route_to('http://a/b/c/g;x') 401 | assert_kind_of(URI::Generic, url) 402 | assert_equal('g;x', url.to_s) 403 | 404 | # http://a/b/c/d;p?q 405 | # g;x?y#s = http://a/b/c/g;x?y#s 406 | url = @base_url.merge('g;x?y#s') 407 | assert_kind_of(URI::HTTP, url) 408 | assert_equal('http://a/b/c/g;x?y#s', url.to_s) 409 | url = @base_url.route_to('http://a/b/c/g;x?y#s') 410 | assert_kind_of(URI::Generic, url) 411 | assert_equal('g;x?y#s', url.to_s) 412 | 413 | # http://a/b/c/d;p?q 414 | # . = http://a/b/c/ 415 | url = @base_url.merge('.') 416 | assert_kind_of(URI::HTTP, url) 417 | assert_equal('http://a/b/c/', url.to_s) 418 | url = @base_url.route_to('http://a/b/c/') 419 | assert_kind_of(URI::Generic, url) 420 | assert('.' != url.to_s) # ok 421 | assert_equal('./', url.to_s) 422 | 423 | # http://a/b/c/d;p?q 424 | # ./ = http://a/b/c/ 425 | url = @base_url.merge('./') 426 | assert_kind_of(URI::HTTP, url) 427 | assert_equal('http://a/b/c/', url.to_s) 428 | url = @base_url.route_to('http://a/b/c/') 429 | assert_kind_of(URI::Generic, url) 430 | assert_equal('./', url.to_s) 431 | 432 | # http://a/b/c/d;p?q 433 | # .. = http://a/b/ 434 | url = @base_url.merge('..') 435 | assert_kind_of(URI::HTTP, url) 436 | assert_equal('http://a/b/', url.to_s) 437 | url = @base_url.route_to('http://a/b/') 438 | assert_kind_of(URI::Generic, url) 439 | assert('..' != url.to_s) # ok 440 | assert_equal('../', url.to_s) 441 | 442 | # http://a/b/c/d;p?q 443 | # ../ = http://a/b/ 444 | url = @base_url.merge('../') 445 | assert_kind_of(URI::HTTP, url) 446 | assert_equal('http://a/b/', url.to_s) 447 | url = @base_url.route_to('http://a/b/') 448 | assert_kind_of(URI::Generic, url) 449 | assert_equal('../', url.to_s) 450 | 451 | # http://a/b/c/d;p?q 452 | # ../g = http://a/b/g 453 | url = @base_url.merge('../g') 454 | assert_kind_of(URI::HTTP, url) 455 | assert_equal('http://a/b/g', url.to_s) 456 | url = @base_url.route_to('http://a/b/g') 457 | assert_kind_of(URI::Generic, url) 458 | assert_equal('../g', url.to_s) 459 | 460 | # http://a/b/c/d;p?q 461 | # ../.. = http://a/ 462 | url = @base_url.merge('../..') 463 | assert_kind_of(URI::HTTP, url) 464 | assert_equal('http://a/', url.to_s) 465 | url = @base_url.route_to('http://a/') 466 | assert_kind_of(URI::Generic, url) 467 | assert('../..' != url.to_s) # ok 468 | assert_equal('../../', url.to_s) 469 | 470 | # http://a/b/c/d;p?q 471 | # ../../ = http://a/ 472 | url = @base_url.merge('../../') 473 | assert_kind_of(URI::HTTP, url) 474 | assert_equal('http://a/', url.to_s) 475 | url = @base_url.route_to('http://a/') 476 | assert_kind_of(URI::Generic, url) 477 | assert_equal('../../', url.to_s) 478 | 479 | # http://a/b/c/d;p?q 480 | # ../../g = http://a/g 481 | url = @base_url.merge('../../g') 482 | assert_kind_of(URI::HTTP, url) 483 | assert_equal('http://a/g', url.to_s) 484 | url = @base_url.route_to('http://a/g') 485 | assert_kind_of(URI::Generic, url) 486 | assert_equal('../../g', url.to_s) 487 | 488 | # http://a/b/c/d;p?q 489 | # <> = (current document) 490 | url = @base_url.merge('') 491 | assert_kind_of(URI::HTTP, url) 492 | assert_equal('http://a/b/c/d;p?q', url.to_s) 493 | url = @base_url.route_to('http://a/b/c/d;p?q') 494 | assert_kind_of(URI::Generic, url) 495 | assert_equal('', url.to_s) 496 | 497 | # http://a/b/c/d;p?q 498 | # /./g = http://a/g 499 | url = @base_url.merge('/./g') 500 | assert_kind_of(URI::HTTP, url) 501 | assert_equal('http://a/g', url.to_s) 502 | # url = @base_url.route_to('http://a/./g') 503 | # assert_kind_of(URI::Generic, url) 504 | # assert_equal('/./g', url.to_s) 505 | 506 | # http://a/b/c/d;p?q 507 | # /../g = http://a/g 508 | url = @base_url.merge('/../g') 509 | assert_kind_of(URI::HTTP, url) 510 | assert_equal('http://a/g', url.to_s) 511 | # url = @base_url.route_to('http://a/../g') 512 | # assert_kind_of(URI::Generic, url) 513 | # assert_equal('/../g', url.to_s) 514 | 515 | # http://a/b/c/d;p?q 516 | # g. = http://a/b/c/g. 517 | url = @base_url.merge('g.') 518 | assert_kind_of(URI::HTTP, url) 519 | assert_equal('http://a/b/c/g.', url.to_s) 520 | url = @base_url.route_to('http://a/b/c/g.') 521 | assert_kind_of(URI::Generic, url) 522 | assert_equal('g.', url.to_s) 523 | 524 | # http://a/b/c/d;p?q 525 | # .g = http://a/b/c/.g 526 | url = @base_url.merge('.g') 527 | assert_kind_of(URI::HTTP, url) 528 | assert_equal('http://a/b/c/.g', url.to_s) 529 | url = @base_url.route_to('http://a/b/c/.g') 530 | assert_kind_of(URI::Generic, url) 531 | assert_equal('.g', url.to_s) 532 | 533 | # http://a/b/c/d;p?q 534 | # g.. = http://a/b/c/g.. 535 | url = @base_url.merge('g..') 536 | assert_kind_of(URI::HTTP, url) 537 | assert_equal('http://a/b/c/g..', url.to_s) 538 | url = @base_url.route_to('http://a/b/c/g..') 539 | assert_kind_of(URI::Generic, url) 540 | assert_equal('g..', url.to_s) 541 | 542 | # http://a/b/c/d;p?q 543 | # ..g = http://a/b/c/..g 544 | url = @base_url.merge('..g') 545 | assert_kind_of(URI::HTTP, url) 546 | assert_equal('http://a/b/c/..g', url.to_s) 547 | url = @base_url.route_to('http://a/b/c/..g') 548 | assert_kind_of(URI::Generic, url) 549 | assert_equal('..g', url.to_s) 550 | 551 | # http://a/b/c/d;p?q 552 | # ../../../g = http://a/g 553 | url = @base_url.merge('../../../g') 554 | assert_kind_of(URI::HTTP, url) 555 | assert_equal('http://a/g', url.to_s) 556 | url = @base_url.route_to('http://a/g') 557 | assert_kind_of(URI::Generic, url) 558 | assert('../../../g' != url.to_s) # ok? yes, it confuses you 559 | assert_equal('../../g', url.to_s) # and it is clearly 560 | 561 | # http://a/b/c/d;p?q 562 | # ../../../../g = http://a/g 563 | url = @base_url.merge('../../../../g') 564 | assert_kind_of(URI::HTTP, url) 565 | assert_equal('http://a/g', url.to_s) 566 | url = @base_url.route_to('http://a/g') 567 | assert_kind_of(URI::Generic, url) 568 | assert('../../../../g' != url.to_s) # ok? yes, it confuses you 569 | assert_equal('../../g', url.to_s) # and it is clearly 570 | 571 | # http://a/b/c/d;p?q 572 | # ./../g = http://a/b/g 573 | url = @base_url.merge('./../g') 574 | assert_kind_of(URI::HTTP, url) 575 | assert_equal('http://a/b/g', url.to_s) 576 | url = @base_url.route_to('http://a/b/g') 577 | assert_kind_of(URI::Generic, url) 578 | assert('./../g' != url.to_s) # ok 579 | assert_equal('../g', url.to_s) 580 | 581 | # http://a/b/c/d;p?q 582 | # ./g/. = http://a/b/c/g/ 583 | url = @base_url.merge('./g/.') 584 | assert_kind_of(URI::HTTP, url) 585 | assert_equal('http://a/b/c/g/', url.to_s) 586 | url = @base_url.route_to('http://a/b/c/g/') 587 | assert_kind_of(URI::Generic, url) 588 | assert('./g/.' != url.to_s) # ok 589 | assert_equal('g/', url.to_s) 590 | 591 | # http://a/b/c/d;p?q 592 | # g/./h = http://a/b/c/g/h 593 | url = @base_url.merge('g/./h') 594 | assert_kind_of(URI::HTTP, url) 595 | assert_equal('http://a/b/c/g/h', url.to_s) 596 | url = @base_url.route_to('http://a/b/c/g/h') 597 | assert_kind_of(URI::Generic, url) 598 | assert('g/./h' != url.to_s) # ok 599 | assert_equal('g/h', url.to_s) 600 | 601 | # http://a/b/c/d;p?q 602 | # g/../h = http://a/b/c/h 603 | url = @base_url.merge('g/../h') 604 | assert_kind_of(URI::HTTP, url) 605 | assert_equal('http://a/b/c/h', url.to_s) 606 | url = @base_url.route_to('http://a/b/c/h') 607 | assert_kind_of(URI::Generic, url) 608 | assert('g/../h' != url.to_s) # ok 609 | assert_equal('h', url.to_s) 610 | 611 | # http://a/b/c/d;p?q 612 | # g;x=1/./y = http://a/b/c/g;x=1/y 613 | url = @base_url.merge('g;x=1/./y') 614 | assert_kind_of(URI::HTTP, url) 615 | assert_equal('http://a/b/c/g;x=1/y', url.to_s) 616 | url = @base_url.route_to('http://a/b/c/g;x=1/y') 617 | assert_kind_of(URI::Generic, url) 618 | assert('g;x=1/./y' != url.to_s) # ok 619 | assert_equal('g;x=1/y', url.to_s) 620 | 621 | # http://a/b/c/d;p?q 622 | # g;x=1/../y = http://a/b/c/y 623 | url = @base_url.merge('g;x=1/../y') 624 | assert_kind_of(URI::HTTP, url) 625 | assert_equal('http://a/b/c/y', url.to_s) 626 | url = @base_url.route_to('http://a/b/c/y') 627 | assert_kind_of(URI::Generic, url) 628 | assert('g;x=1/../y' != url.to_s) # ok 629 | assert_equal('y', url.to_s) 630 | 631 | # http://a/b/c/d;p?q 632 | # g?y/./x = http://a/b/c/g?y/./x 633 | url = @base_url.merge('g?y/./x') 634 | assert_kind_of(URI::HTTP, url) 635 | assert_equal('http://a/b/c/g?y/./x', url.to_s) 636 | url = @base_url.route_to('http://a/b/c/g?y/./x') 637 | assert_kind_of(URI::Generic, url) 638 | assert_equal('g?y/./x', url.to_s) 639 | 640 | # http://a/b/c/d;p?q 641 | # g?y/../x = http://a/b/c/g?y/../x 642 | url = @base_url.merge('g?y/../x') 643 | assert_kind_of(URI::HTTP, url) 644 | assert_equal('http://a/b/c/g?y/../x', url.to_s) 645 | url = @base_url.route_to('http://a/b/c/g?y/../x') 646 | assert_kind_of(URI::Generic, url) 647 | assert_equal('g?y/../x', url.to_s) 648 | 649 | # http://a/b/c/d;p?q 650 | # g#s/./x = http://a/b/c/g#s/./x 651 | url = @base_url.merge('g#s/./x') 652 | assert_kind_of(URI::HTTP, url) 653 | assert_equal('http://a/b/c/g#s/./x', url.to_s) 654 | url = @base_url.route_to('http://a/b/c/g#s/./x') 655 | assert_kind_of(URI::Generic, url) 656 | assert_equal('g#s/./x', url.to_s) 657 | 658 | # http://a/b/c/d;p?q 659 | # g#s/../x = http://a/b/c/g#s/../x 660 | url = @base_url.merge('g#s/../x') 661 | assert_kind_of(URI::HTTP, url) 662 | assert_equal('http://a/b/c/g#s/../x', url.to_s) 663 | url = @base_url.route_to('http://a/b/c/g#s/../x') 664 | assert_kind_of(URI::Generic, url) 665 | assert_equal('g#s/../x', url.to_s) 666 | 667 | # http://a/b/c/d;p?q 668 | # http:g = http:g ; for validating parsers 669 | # | http://a/b/c/g ; for backwards compatibility 670 | url = @base_url.merge('http:g') 671 | assert_kind_of(URI::HTTP, url) 672 | assert_equal('http:g', url.to_s) 673 | url = @base_url.route_to('http:g') 674 | assert_kind_of(URI::Generic, url) 675 | assert_equal('http:g', url.to_s) 676 | end 677 | 678 | def test_join 679 | assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo/bar')) 680 | assert_equal(URI.parse('http://foo/bar'), URI.join('http://foo', 'bar')) 681 | assert_equal(URI.parse('http://foo/bar/'), URI.join('http://foo', 'bar/')) 682 | 683 | assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', 'baz')) 684 | assert_equal(URI.parse('http://foo/baz'), URI.join('http://foo', 'bar', '/baz')) 685 | assert_equal(URI.parse('http://foo/baz/'), URI.join('http://foo', 'bar', '/baz/')) 686 | assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/', 'baz')) 687 | assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar', 'baz', 'hoge')) 688 | 689 | assert_equal(URI.parse('http://foo/bar/baz'), URI.join('http://foo', 'bar/baz')) 690 | assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) 691 | assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) 692 | assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) 693 | assert_equal(URI.parse('http://foo/bar/hoge'), URI.join('http://foo', 'bar/baz', 'hoge')) 694 | assert_equal(URI.parse('http://foo/bar/baz/hoge'), URI.join('http://foo', 'bar/baz/', 'hoge')) 695 | assert_equal(URI.parse('http://foo/hoge'), URI.join('http://foo', 'bar/baz', '/hoge')) 696 | end 697 | 698 | # ruby-dev:16728 699 | def test_set_component 700 | uri = URI.parse('http://foo:bar@baz') 701 | assert_equal('oof', uri.user = 'oof') 702 | assert_equal('http://oof:bar@baz', uri.to_s) 703 | assert_equal('rab', uri.password = 'rab') 704 | assert_equal('http://oof:rab@baz', uri.to_s) 705 | assert_equal('foo', uri.userinfo = 'foo') 706 | assert_equal('http://foo:rab@baz', uri.to_s) 707 | assert_equal(['foo', 'bar'], uri.userinfo = ['foo', 'bar']) 708 | assert_equal('http://foo:bar@baz', uri.to_s) 709 | assert_equal(['foo'], uri.userinfo = ['foo']) 710 | assert_equal('http://foo:bar@baz', uri.to_s) 711 | assert_equal('zab', uri.host = 'zab') 712 | assert_equal('http://foo:bar@zab', uri.to_s) 713 | uri.port = "" 714 | assert_nil(uri.port) 715 | uri.port = "80" 716 | assert_equal(80, uri.port) 717 | uri.port = "080" 718 | assert_equal(80, uri.port) 719 | uri.port = " 080 " 720 | assert_equal(80, uri.port) 721 | assert_equal(8080, uri.port = 8080) 722 | assert_equal('http://foo:bar@zab:8080', uri.to_s) 723 | assert_equal('/', uri.path = '/') 724 | assert_equal('http://foo:bar@zab:8080/', uri.to_s) 725 | assert_equal('a=1', uri.query = 'a=1') 726 | assert_equal('http://foo:bar@zab:8080/?a=1', uri.to_s) 727 | assert_equal('b123', uri.fragment = 'b123') 728 | assert_equal('http://foo:bar@zab:8080/?a=1#b123', uri.to_s) 729 | assert_equal('a[]=1', uri.query = 'a[]=1') 730 | assert_equal('http://foo:bar@zab:8080/?a[]=1#b123', uri.to_s) 731 | uri = URI.parse('http://foo:bar@zab:8080/?a[]=1#b123') 732 | assert_equal('http://foo:bar@zab:8080/?a[]=1#b123', uri.to_s) 733 | 734 | uri = URI.parse('http://example.com') 735 | assert_raise(URI::InvalidURIError) { uri.password = 'bar' } 736 | assert_equal("foo\nbar", uri.query = "foo\nbar") 737 | uri.userinfo = 'foo:bar' 738 | assert_equal('http://foo:bar@example.com?foobar', uri.to_s) 739 | assert_raise(URI::InvalidURIError) { uri.registry = 'bar' } 740 | assert_raise(URI::InvalidURIError) { uri.opaque = 'bar' } 741 | 742 | uri = URI.parse('mailto:foo@example.com') 743 | assert_raise(URI::InvalidURIError) { uri.user = 'bar' } 744 | assert_raise(URI::InvalidURIError) { uri.password = 'bar' } 745 | assert_raise(URI::InvalidURIError) { uri.userinfo = ['bar', 'baz'] } 746 | assert_raise(URI::InvalidURIError) { uri.host = 'bar' } 747 | assert_raise(URI::InvalidURIError) { uri.port = 'bar' } 748 | assert_raise(URI::InvalidURIError) { uri.path = 'bar' } 749 | assert_raise(URI::InvalidURIError) { uri.query = 'bar' } 750 | 751 | uri = URI.parse('foo:bar') 752 | assert_raise(URI::InvalidComponentError) { uri.opaque = '/baz' } 753 | uri.opaque = 'xyzzy' 754 | assert_equal('foo:xyzzy', uri.to_s) 755 | end 756 | end 757 | 758 | MTest::Unit.new.run 759 | -------------------------------------------------------------------------------- /test/test_http.rb: -------------------------------------------------------------------------------- 1 | class TestHTTP < MTest::Unit::TestCase 2 | def setup 3 | end 4 | 5 | def teardown 6 | end 7 | 8 | def uri_to_ary(uri) 9 | uri.class.component.collect {|c| uri.__send__(c)} 10 | end 11 | 12 | def test_parse 13 | u = URI.parse('http://a') 14 | assert_kind_of(URI::HTTP, u) 15 | assert_equal(['http', 16 | nil, 'a', URI::HTTP.default_port, 17 | '', nil, nil], uri_to_ary(u)) 18 | end 19 | 20 | def test_normalize 21 | host = 'aBcD' 22 | u1 = URI.parse('http://' + host + '/eFg?HiJ') 23 | u2 = URI.parse('http://' + host.downcase + '/eFg?HiJ') 24 | assert(u1.normalize.host == 'abcd') 25 | assert(u1.normalize.path == u1.path) 26 | assert(u1.normalize == u2.normalize) 27 | assert(!u1.normalize.host.equal?(u1.host)) 28 | assert( u2.normalize.host.equal?(u2.host)) 29 | 30 | assert_equal('http://abc/', URI.parse('http://abc').normalize.to_s) 31 | end 32 | 33 | def test_equal 34 | assert(URI.parse('http://abc') == URI.parse('http://ABC')) 35 | assert(URI.parse('http://abc/def') == URI.parse('http://ABC/def')) 36 | assert(URI.parse('http://abc/def') != URI.parse('http://ABC/DEF')) 37 | end 38 | 39 | def test_request_uri 40 | assert_equal('/', URI.parse('http://a.b.c/').request_uri) 41 | assert_equal('/?abc=def', URI.parse('http://a.b.c/?abc=def').request_uri) 42 | assert_equal('/', URI.parse('http://a.b.c').request_uri) 43 | assert_equal('/?abc=def', URI.parse('http://a.b.c?abc=def').request_uri) 44 | u = URI.parse('http:foo') 45 | #assert_equal nil, u.path 46 | assert_equal nil, u.request_uri 47 | # assert_equal(nil, URI.parse('http:foo').request_uri) 48 | end 49 | 50 | def test_select 51 | assert_equal(['http', 'a.b.c', 80], URI.parse('http://a.b.c/').select(:scheme, :host, :port)) 52 | u = URI.parse('http://a.b.c/') 53 | assert_equal(uri_to_ary(u), u.select(*u.component)) 54 | assert_raise(ArgumentError) do 55 | u.select(:scheme, :host, :not_exist, :port) 56 | end 57 | end 58 | end 59 | 60 | MTest::Unit.new.run 61 | -------------------------------------------------------------------------------- /test/test_mailto.rb: -------------------------------------------------------------------------------- 1 | class URI::TestMailTo < MTest::Unit::TestCase 2 | def setup 3 | @u = URI::MailTo 4 | end 5 | 6 | def teardown 7 | end 8 | 9 | def uri_to_ary(uri) 10 | uri.class.component.collect {|c| uri.__send__(c)} 11 | end 12 | 13 | def test_build 14 | ok = [] 15 | bad = [] 16 | 17 | # RFC2368, 6. Examples 18 | # mailto:chris@example.com 19 | ok << ["mailto:chris@example.com"] 20 | ok[-1] << ["chris@example.com", nil] 21 | ok[-1] << {:to => "chris@example.com"} 22 | 23 | ok << ["mailto:foo+@example.com,bar@example.com"] 24 | ok[-1] << [["foo+@example.com", "bar@example.com"], nil] 25 | ok[-1] << {:to => "foo+@example.com,bar@example.com"} 26 | 27 | # mailto:infobot@example.com?subject=current-issue 28 | ok << ["mailto:infobot@example.com?subject=current-issue"] 29 | ok[-1] << ["infobot@example.com", ["subject=current-issue"]] 30 | ok[-1] << {:to => "infobot@example.com", 31 | :headers => ["subject=current-issue"]} 32 | 33 | # mailto:infobot@example.com?body=send%20current-issue 34 | ok << ["mailto:infobot@example.com?body=send%20current-issue"] 35 | ok[-1] << ["infobot@example.com", ["body=send%20current-issue"]] 36 | ok[-1] << {:to => "infobot@example.com", 37 | :headers => ["body=send%20current-issue"]} 38 | 39 | # mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index 40 | ok << ["mailto:infobot@example.com?body=send%20current-issue%0D%0Asend%20index"] 41 | ok[-1] << ["infobot@example.com", 42 | ["body=send%20current-issue%0D%0Asend%20index"]] 43 | ok[-1] << {:to => "infobot@example.com", 44 | :headers => ["body=send%20current-issue%0D%0Asend%20index"]} 45 | 46 | # mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com 47 | ok << ["mailto:foobar@example.com?In-Reply-To=%3c3469A91.D10AF4C@example.com"] 48 | ok[-1] << ["foobar@example.com", 49 | ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]] 50 | ok[-1] << {:to => "foobar@example.com", 51 | :headers => ["In-Reply-To=%3c3469A91.D10AF4C@example.com"]} 52 | 53 | # mailto:majordomo@example.com?body=subscribe%20bamboo-l 54 | ok << ["mailto:majordomo@example.com?body=subscribe%20bamboo-l"] 55 | ok[-1] << ["majordomo@example.com", ["body=subscribe%20bamboo-l"]] 56 | ok[-1] << {:to => "majordomo@example.com", 57 | :headers => ["body=subscribe%20bamboo-l"]} 58 | 59 | # mailto:joe@example.com?cc=bob@example.com&body=hello 60 | ok << ["mailto:joe@example.com?cc=bob@example.com&body=hello"] 61 | ok[-1] << ["joe@example.com", ["cc=bob@example.com", "body=hello"]] 62 | ok[-1] << {:to => "joe@example.com", 63 | :headers => ["cc=bob@example.com", "body=hello"]} 64 | 65 | # mailto:?to=joe@example.com&cc=bob@example.com&body=hello 66 | ok << ["mailto:?to=joe@example.com&cc=bob@example.com&body=hello"] 67 | ok[-1] << [nil, 68 | ["to=joe@example.com", "cc=bob@example.com", "body=hello"]] 69 | ok[-1] << {:headers => ["to=joe@example.com", 70 | "cc=bob@example.com", "body=hello"]} 71 | 72 | # mailto:gorby%25kremvax@example.com 73 | ok << ["mailto:gorby%25kremvax@example.com"] 74 | ok[-1] << ["gorby%25kremvax@example.com", nil] 75 | ok[-1] << {:to => "gorby%25kremvax@example.com"} 76 | 77 | # mailto:unlikely%3Faddress@example.com?blat=foop 78 | ok << ["mailto:unlikely%3Faddress@example.com?blat=foop"] 79 | ok[-1] << ["unlikely%3Faddress@example.com", ["blat=foop"]] 80 | ok[-1] << {:to => "unlikely%3Faddress@example.com", 81 | :headers => ["blat=foop"]} 82 | 83 | # mailto:john@example.com?Subject=Ruby&Cc=jack@example.com 84 | ok << ["mailto:john@example.com?Subject=Ruby&Cc=jack@example.com"] 85 | ok[-1] << ['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]] 86 | ok[-1] << {:to=>"john@example.com", :headers=>[["Subject", "Ruby"], ["Cc", "jack@example.com"]]} 87 | 88 | # mailto:listman@example.com?subject=subscribe 89 | ok << ["mailto:listman@example.com?subject=subscribe"] 90 | ok[-1] << {:to => 'listman@example.com', :headers => [['subject', 'subscribe']]} 91 | ok[-1] << {:to => 'listman@example.com', :headers => [['subject', 'subscribe']]} 92 | 93 | # mailto:listman@example.com?subject=subscribe 94 | ok << ["mailto:listman@example.com?subject=subscribe"] 95 | ok[-1] << {:to => 'listman@example.com', :headers => { 'subject' => 'subscribe' }} 96 | ok[-1] << {:to => 'listman@example.com', :headers => 'subject=subscribe' } 97 | 98 | ok_all = ok.flatten.join("\0") 99 | 100 | # mailto:joe@example.com?cc=bob@example.com?body=hello ; WRONG! 101 | bad << ["joe@example.com", ["cc=bob@example.com?body=hello"]] 102 | 103 | # mailto:javascript:alert() 104 | bad << ["javascript:alert()", []] 105 | 106 | # mailto:/example.com/ ; WRONG, not a mail address 107 | bad << ["/example.com/", []] 108 | 109 | # '=' which is in hname or hvalue is wrong. 110 | bad << ["foo@example.jp?subject=1+1=2", []] 111 | 112 | ok.each do |x| 113 | assert_equal(x[0], URI.parse(x[0]).to_s) 114 | assert_equal(x[0], @u.build(x[1]).to_s) 115 | assert_equal(x[0], @u.build(x[2]).to_s) 116 | end 117 | 118 | bad.each do |x| 119 | assert_raise(URI::InvalidURIError) { 120 | URI.parse(x) 121 | } 122 | assert_raise(URI::InvalidComponentError) { 123 | @u.build(x) 124 | } 125 | end 126 | 127 | assert_equal(ok_all, ok.flatten.join("\0")) 128 | end 129 | 130 | def test_initializer 131 | assert_raise(URI::InvalidComponentError) do 132 | URI::MailTo.new('mailto', 'sdmitry:bla', 'localhost', '2000', nil, 133 | 'joe@example.com', nil, nil, 'subject=Ruby') 134 | end 135 | end 136 | 137 | def test_check_to 138 | u = URI::MailTo.build(['joe@example.com', 'subject=Ruby']) 139 | 140 | assert_raise(URI::InvalidComponentError) do 141 | u.to = '#1@mail.com' 142 | end 143 | 144 | assert_raise(URI::InvalidComponentError) do 145 | u.to = '@invalid.email' 146 | end 147 | end 148 | 149 | def test_to_s 150 | u = URI::MailTo.build([nil, 'subject=Ruby']) 151 | 152 | u.__send__(:set_to, nil) 153 | assert_equal('mailto:?subject=Ruby', u.to_s) 154 | 155 | u.fragment = 'test' 156 | assert_equal('mailto:?subject=Ruby#test', u.to_s) 157 | end 158 | 159 | def test_to_mailtext 160 | results = [] 161 | results << ["To: ruby-list@ruby-lang.org\nSubject: subscribe\n\n\n"] 162 | results[-1] << { to: 'ruby-list@ruby-lang.org', headers: { 'subject' => 'subscribe' } } 163 | 164 | results << ["To: ruby-list@ruby-lang.org\n\nBody\n"] 165 | results[-1] << { to: 'ruby-list@ruby-lang.org', headers: { 'body' => 'Body' } } 166 | 167 | results << ["To: ruby-list@ruby-lang.org, cc@ruby-lang.org\n\n\n"] 168 | results[-1] << { to: 'ruby-list@ruby-lang.org', headers: { 'to' => 'cc@ruby-lang.org' } } 169 | 170 | results.each do |expected, params| 171 | u = URI::MailTo.build(params) 172 | assert_equal(expected, u.to_mailtext) 173 | end 174 | 175 | u = URI.parse('mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr') 176 | assert_equal "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n", 177 | u.to_mailtext 178 | end 179 | 180 | def test_select 181 | u = URI.parse('mailto:joe@example.com?cc=bob@example.com&body=hello') 182 | assert_equal(uri_to_ary(u), u.select(*u.component)) 183 | assert_raise(ArgumentError) do 184 | u.select(:scheme, :host, :not_exist, :port) 185 | end 186 | end 187 | end 188 | 189 | MTest::Unit.new.run 190 | -------------------------------------------------------------------------------- /test/test_parser.rb: -------------------------------------------------------------------------------- 1 | class URI::TestParser < MTest::Unit::TestCase 2 | def uri_to_ary(uri) 3 | uri.class.component.collect {|c| uri.send(c)} 4 | end 5 | 6 | def test_compare 7 | url = 'http://a/b/c/d;p?q' 8 | u0 = URI.parse(url) 9 | u1 = URI.parse(url) 10 | p = URI::Parser.new 11 | u2 = p.parse(url) 12 | u3 = p.parse(url) 13 | 14 | assert(u0 == u1) 15 | assert(u0.eql?(u1)) 16 | assert(!u0.equal?(u1)) 17 | 18 | assert(u1 == u2) 19 | assert(!u1.eql?(u2)) 20 | assert(!u1.equal?(u2)) 21 | 22 | assert(u2 == u3) 23 | assert(u2.eql?(u3)) 24 | assert(!u2.equal?(u3)) 25 | end 26 | 27 | def test_parse 28 | escaped = URI::REGEXP::PATTERN::ESCAPED 29 | hex = URI::REGEXP::PATTERN::HEX 30 | p1 = URI::Parser.new(:ESCAPED => "(?:#{escaped}|%u[#{hex}]{4})") 31 | u1 = p1.parse('http://a/b/%uABCD') 32 | assert_equal(['http', nil, 'a', URI::HTTP.default_port, '/b/%uABCD', nil, nil], 33 | uri_to_ary(u1)) 34 | u1.path = '/%uDCBA' 35 | assert_equal(['http', nil, 'a', URI::HTTP.default_port, '/%uDCBA', nil, nil], 36 | uri_to_ary(u1)) 37 | end 38 | 39 | def test_raise_bad_uri_for_integer 40 | assert_raise(URI::InvalidURIError) do 41 | URI.parse(1) 42 | end 43 | end 44 | end 45 | 46 | MTest::Unit.new.run 47 | --------------------------------------------------------------------------------