PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/ruby/stdlib/uri/generic.rb

http://github.com/jruby/jruby
Ruby | 1569 lines | 756 code | 146 blank | 667 comment | 177 complexity | 8d117c4badd0cf2370a25e1868d18e1b MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, JSON, LGPL-2.1
  1. # frozen_string_literal: true
  2. # = uri/generic.rb
  3. #
  4. # Author:: Akira Yamada <akira@ruby-lang.org>
  5. # License:: You can redistribute it and/or modify it under the same term as Ruby.
  6. # Revision:: $Id$
  7. #
  8. # See URI for general documentation
  9. #
  10. require 'uri/common'
  11. module URI
  12. #
  13. # Base class for all URI classes.
  14. # Implements generic URI syntax as per RFC 2396.
  15. #
  16. class Generic
  17. include URI
  18. #
  19. # A Default port of nil for URI::Generic
  20. #
  21. DEFAULT_PORT = nil
  22. #
  23. # Returns default port
  24. #
  25. def self.default_port
  26. self::DEFAULT_PORT
  27. end
  28. #
  29. # Returns default port
  30. #
  31. def default_port
  32. self.class.default_port
  33. end
  34. #
  35. # An Array of the available components for URI::Generic
  36. #
  37. COMPONENT = [
  38. :scheme,
  39. :userinfo, :host, :port, :registry,
  40. :path, :opaque,
  41. :query,
  42. :fragment
  43. ].freeze
  44. #
  45. # Components of the URI in the order.
  46. #
  47. def self.component
  48. self::COMPONENT
  49. end
  50. USE_REGISTRY = false # :nodoc:
  51. def self.use_registry # :nodoc:
  52. self::USE_REGISTRY
  53. end
  54. #
  55. # == Synopsis
  56. #
  57. # See #new
  58. #
  59. # == Description
  60. #
  61. # At first, tries to create a new URI::Generic instance using
  62. # URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
  63. # then it URI::Escape.escape all URI components and tries again.
  64. #
  65. #
  66. def self.build2(args)
  67. begin
  68. return self.build(args)
  69. rescue InvalidComponentError
  70. if args.kind_of?(Array)
  71. return self.build(args.collect{|x|
  72. if x.is_a?(String)
  73. DEFAULT_PARSER.escape(x)
  74. else
  75. x
  76. end
  77. })
  78. elsif args.kind_of?(Hash)
  79. tmp = {}
  80. args.each do |key, value|
  81. tmp[key] = if value
  82. DEFAULT_PARSER.escape(value)
  83. else
  84. value
  85. end
  86. end
  87. return self.build(tmp)
  88. end
  89. end
  90. end
  91. #
  92. # == Synopsis
  93. #
  94. # See #new
  95. #
  96. # == Description
  97. #
  98. # Creates a new URI::Generic instance from components of URI::Generic
  99. # with check. Components are: scheme, userinfo, host, port, registry, path,
  100. # opaque, query and fragment. You can provide arguments either by an Array or a Hash.
  101. # See #new for hash keys to use or for order of array items.
  102. #
  103. def self.build(args)
  104. if args.kind_of?(Array) &&
  105. args.size == ::URI::Generic::COMPONENT.size
  106. tmp = args.dup
  107. elsif args.kind_of?(Hash)
  108. tmp = ::URI::Generic::COMPONENT.collect do |c|
  109. if args.include?(c)
  110. args[c]
  111. else
  112. nil
  113. end
  114. end
  115. else
  116. component = self.class.component rescue ::URI::Generic::COMPONENT
  117. raise ArgumentError,
  118. "expected Array of or Hash of components of #{self.class} (#{component.join(', ')})"
  119. end
  120. tmp << nil
  121. tmp << true
  122. return self.new(*tmp)
  123. end
  124. #
  125. # == Args
  126. #
  127. # +scheme+::
  128. # Protocol scheme, i.e. 'http','ftp','mailto' and so on.
  129. # +userinfo+::
  130. # User name and password, i.e. 'sdmitry:bla'
  131. # +host+::
  132. # Server host name
  133. # +port+::
  134. # Server port
  135. # +registry+::
  136. # Registry of naming authorities.
  137. # +path+::
  138. # Path on server
  139. # +opaque+::
  140. # Opaque part
  141. # +query+::
  142. # Query data
  143. # +fragment+::
  144. # A part of URI after '#' sign
  145. # +parser+::
  146. # Parser for internal use [URI::DEFAULT_PARSER by default]
  147. # +arg_check+::
  148. # Check arguments [false by default]
  149. #
  150. # == Description
  151. #
  152. # Creates a new URI::Generic instance from ``generic'' components without check.
  153. #
  154. def initialize(scheme,
  155. userinfo, host, port, registry,
  156. path, opaque,
  157. query,
  158. fragment,
  159. parser = DEFAULT_PARSER,
  160. arg_check = false)
  161. @scheme = nil
  162. @user = nil
  163. @password = nil
  164. @host = nil
  165. @port = nil
  166. @path = nil
  167. @query = nil
  168. @opaque = nil
  169. @fragment = nil
  170. @parser = parser == DEFAULT_PARSER ? nil : parser
  171. if arg_check
  172. self.scheme = scheme
  173. self.userinfo = userinfo
  174. self.hostname = host
  175. self.port = port
  176. self.path = path
  177. self.query = query
  178. self.opaque = opaque
  179. self.fragment = fragment
  180. else
  181. self.set_scheme(scheme)
  182. self.set_userinfo(userinfo)
  183. self.set_host(host)
  184. self.set_port(port)
  185. self.set_path(path)
  186. self.query = query
  187. self.set_opaque(opaque)
  188. self.fragment=(fragment)
  189. end
  190. if registry
  191. raise InvalidURIError,
  192. "the scheme #{@scheme} does not accept registry part: #{registry} (or bad hostname?)"
  193. end
  194. @scheme.freeze if @scheme
  195. self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
  196. self.set_port(self.default_port) if self.default_port && !@port
  197. end
  198. #
  199. # returns the scheme component of the URI.
  200. #
  201. # URI("http://foo/bar/baz").scheme #=> "http"
  202. #
  203. attr_reader :scheme
  204. # returns the host component of the URI.
  205. #
  206. # URI("http://foo/bar/baz").host #=> "foo"
  207. #
  208. # It returns nil if no host component.
  209. #
  210. # URI("mailto:foo@example.org").host #=> nil
  211. #
  212. # The component doesn't contains the port number.
  213. #
  214. # URI("http://foo:8080/bar/baz").host #=> "foo"
  215. #
  216. # Since IPv6 addresses are wrapped by brackets in URIs,
  217. # this method returns IPv6 addresses wrapped by brackets.
  218. # This form is not appropriate to pass socket methods such as TCPSocket.open.
  219. # If unwrapped host names are required, use "hostname" method.
  220. #
  221. # URI("http://[::1]/bar/baz").host #=> "[::1]"
  222. # URI("http://[::1]/bar/baz").hostname #=> "::1"
  223. #
  224. attr_reader :host
  225. # returns the port component of the URI.
  226. #
  227. # URI("http://foo/bar/baz").port #=> "80"
  228. #
  229. # URI("http://foo:8080/bar/baz").port #=> "8080"
  230. #
  231. attr_reader :port
  232. def registry # :nodoc:
  233. nil
  234. end
  235. # returns the path component of the URI.
  236. #
  237. # URI("http://foo/bar/baz").path #=> "/bar/baz"
  238. #
  239. attr_reader :path
  240. # returns the query component of the URI.
  241. #
  242. # URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar"
  243. #
  244. attr_reader :query
  245. # returns the opaque part of the URI.
  246. #
  247. # URI("mailto:foo@example.org").opaque #=> "foo@example.org"
  248. #
  249. # Portion of the path that does make use of the slash '/'.
  250. # The path typically refers to the absolute path and the opaque part.
  251. # (see RFC2396 Section 3 and 5.2)
  252. #
  253. attr_reader :opaque
  254. # returns the fragment component of the URI.
  255. #
  256. # URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies"
  257. #
  258. attr_reader :fragment
  259. # returns the parser to be used.
  260. #
  261. # Unless a URI::Parser is defined, then DEFAULT_PARSER is used.
  262. #
  263. def parser
  264. if !defined?(@parser) || !@parser
  265. DEFAULT_PARSER
  266. else
  267. @parser || DEFAULT_PARSER
  268. end
  269. end
  270. # replace self by other URI object
  271. def replace!(oth)
  272. if self.class != oth.class
  273. raise ArgumentError, "expected #{self.class} object"
  274. end
  275. component.each do |c|
  276. self.__send__("#{c}=", oth.__send__(c))
  277. end
  278. end
  279. private :replace!
  280. #
  281. # Components of the URI in the order.
  282. #
  283. def component
  284. self.class.component
  285. end
  286. #
  287. # check the scheme +v+ component against the URI::Parser Regexp for :SCHEME
  288. #
  289. def check_scheme(v)
  290. if v && parser.regexp[:SCHEME] !~ v
  291. raise InvalidComponentError,
  292. "bad component(expected scheme component): #{v}"
  293. end
  294. return true
  295. end
  296. private :check_scheme
  297. # protected setter for the scheme component +v+
  298. #
  299. # see also URI::Generic.scheme=
  300. #
  301. def set_scheme(v)
  302. @scheme = v ? v.downcase : v
  303. end
  304. protected :set_scheme
  305. #
  306. # == Args
  307. #
  308. # +v+::
  309. # String
  310. #
  311. # == Description
  312. #
  313. # public setter for the scheme component +v+.
  314. # (with validation)
  315. #
  316. # see also URI::Generic.check_scheme
  317. #
  318. # == Usage
  319. #
  320. # require 'uri'
  321. #
  322. # uri = URI.parse("http://my.example.com")
  323. # uri.scheme = "https"
  324. # # => "https"
  325. # uri
  326. # #=> #<URI::HTTP:0x000000008e89e8 URL:https://my.example.com>
  327. #
  328. def scheme=(v)
  329. check_scheme(v)
  330. set_scheme(v)
  331. v
  332. end
  333. #
  334. # check the +user+ and +password+.
  335. #
  336. # If +password+ is not provided, then +user+ is
  337. # split, using URI::Generic.split_userinfo, to
  338. # pull +user+ and +password.
  339. #
  340. # see also URI::Generic.check_user, URI::Generic.check_password
  341. #
  342. def check_userinfo(user, password = nil)
  343. if !password
  344. user, password = split_userinfo(user)
  345. end
  346. check_user(user)
  347. check_password(password, user)
  348. return true
  349. end
  350. private :check_userinfo
  351. #
  352. # check the user +v+ component for RFC2396 compliance
  353. # and against the URI::Parser Regexp for :USERINFO
  354. #
  355. # Can not have a registry or opaque component defined,
  356. # with a user component defined.
  357. #
  358. def check_user(v)
  359. if @opaque
  360. raise InvalidURIError,
  361. "can not set user with opaque"
  362. end
  363. return v unless v
  364. if parser.regexp[:USERINFO] !~ v
  365. raise InvalidComponentError,
  366. "bad component(expected userinfo component or user component): #{v}"
  367. end
  368. return true
  369. end
  370. private :check_user
  371. #
  372. # check the password +v+ component for RFC2396 compliance
  373. # and against the URI::Parser Regexp for :USERINFO
  374. #
  375. # Can not have a registry or opaque component defined,
  376. # with a user component defined.
  377. #
  378. def check_password(v, user = @user)
  379. if @opaque
  380. raise InvalidURIError,
  381. "can not set password with opaque"
  382. end
  383. return v unless v
  384. if !user
  385. raise InvalidURIError,
  386. "password component depends user component"
  387. end
  388. if parser.regexp[:USERINFO] !~ v
  389. raise InvalidComponentError,
  390. "bad component(expected user component): #{v}"
  391. end
  392. return true
  393. end
  394. private :check_password
  395. #
  396. # Sets userinfo, argument is string like 'name:pass'
  397. #
  398. def userinfo=(userinfo)
  399. if userinfo.nil?
  400. return nil
  401. end
  402. check_userinfo(*userinfo)
  403. set_userinfo(*userinfo)
  404. # returns userinfo
  405. end
  406. #
  407. # == Args
  408. #
  409. # +v+::
  410. # String
  411. #
  412. # == Description
  413. #
  414. # public setter for the +user+ component.
  415. # (with validation)
  416. #
  417. # see also URI::Generic.check_user
  418. #
  419. # == Usage
  420. #
  421. # require 'uri'
  422. #
  423. # uri = URI.parse("http://john:S3nsit1ve@my.example.com")
  424. # uri.user = "sam"
  425. # # => "sam"
  426. # uri
  427. # #=> #<URI::HTTP:0x00000000881d90 URL:http://sam:V3ry_S3nsit1ve@my.example.com>
  428. #
  429. def user=(user)
  430. check_user(user)
  431. set_user(user)
  432. # returns user
  433. end
  434. #
  435. # == Args
  436. #
  437. # +v+::
  438. # String
  439. #
  440. # == Description
  441. #
  442. # public setter for the +password+ component.
  443. # (with validation)
  444. #
  445. # see also URI::Generic.check_password
  446. #
  447. # == Usage
  448. #
  449. # require 'uri'
  450. #
  451. # uri = URI.parse("http://john:S3nsit1ve@my.example.com")
  452. # uri.password = "V3ry_S3nsit1ve"
  453. # # => "V3ry_S3nsit1ve"
  454. # uri
  455. # #=> #<URI::HTTP:0x00000000881d90 URL:http://john:V3ry_S3nsit1ve@my.example.com>
  456. #
  457. def password=(password)
  458. check_password(password)
  459. set_password(password)
  460. # returns password
  461. end
  462. # protect setter for the +user+ component, and +password+ if available.
  463. # (with validation)
  464. #
  465. # see also URI::Generic.userinfo=
  466. #
  467. def set_userinfo(user, password = nil)
  468. unless password
  469. user, password = split_userinfo(user)
  470. end
  471. @user = user
  472. @password = password if password
  473. [@user, @password]
  474. end
  475. protected :set_userinfo
  476. # protected setter for the user component +v+
  477. #
  478. # see also URI::Generic.user=
  479. #
  480. def set_user(v)
  481. set_userinfo(v, @password)
  482. v
  483. end
  484. protected :set_user
  485. # protected setter for the password component +v+
  486. #
  487. # see also URI::Generic.password=
  488. #
  489. def set_password(v)
  490. @password = v
  491. # returns v
  492. end
  493. protected :set_password
  494. # returns the userinfo +ui+ as user, password
  495. # if properly formatted as 'user:password'
  496. def split_userinfo(ui)
  497. return nil, nil unless ui
  498. user, password = ui.split(':', 2)
  499. return user, password
  500. end
  501. private :split_userinfo
  502. # escapes 'user:password' +v+ based on RFC 1738 section 3.1
  503. def escape_userpass(v)
  504. parser.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
  505. end
  506. private :escape_userpass
  507. # returns the userinfo, either as 'user' or 'user:password'
  508. def userinfo
  509. if @user.nil?
  510. nil
  511. elsif @password.nil?
  512. @user
  513. else
  514. @user + ':' + @password
  515. end
  516. end
  517. # returns the user component
  518. def user
  519. @user
  520. end
  521. # returns the password component
  522. def password
  523. @password
  524. end
  525. #
  526. # check the host +v+ component for RFC2396 compliance
  527. # and against the URI::Parser Regexp for :HOST
  528. #
  529. # Can not have a registry or opaque component defined,
  530. # with a host component defined.
  531. #
  532. def check_host(v)
  533. return v unless v
  534. if @opaque
  535. raise InvalidURIError,
  536. "can not set host with registry or opaque"
  537. elsif parser.regexp[:HOST] !~ v
  538. raise InvalidComponentError,
  539. "bad component(expected host component): #{v}"
  540. end
  541. return true
  542. end
  543. private :check_host
  544. # protected setter for the host component +v+
  545. #
  546. # see also URI::Generic.host=
  547. #
  548. def set_host(v)
  549. @host = v
  550. end
  551. protected :set_host
  552. #
  553. # == Args
  554. #
  555. # +v+::
  556. # String
  557. #
  558. # == Description
  559. #
  560. # public setter for the host component +v+.
  561. # (with validation)
  562. #
  563. # see also URI::Generic.check_host
  564. #
  565. # == Usage
  566. #
  567. # require 'uri'
  568. #
  569. # uri = URI.parse("http://my.example.com")
  570. # uri.host = "foo.com"
  571. # # => "foo.com"
  572. # uri
  573. # #=> #<URI::HTTP:0x000000008e89e8 URL:http://foo.com>
  574. #
  575. def host=(v)
  576. check_host(v)
  577. set_host(v)
  578. v
  579. end
  580. # extract the host part of the URI and unwrap brackets for IPv6 addresses.
  581. #
  582. # This method is same as URI::Generic#host except
  583. # brackets for IPv6 (and future IP) addresses are removed.
  584. #
  585. # u = URI("http://[::1]/bar")
  586. # p u.hostname #=> "::1"
  587. # p u.host #=> "[::1]"
  588. #
  589. def hostname
  590. v = self.host
  591. /\A\[(.*)\]\z/ =~ v ? $1 : v
  592. end
  593. # set the host part of the URI as the argument with brackets for IPv6 addresses.
  594. #
  595. # This method is same as URI::Generic#host= except
  596. # the argument can be bare IPv6 address.
  597. #
  598. # u = URI("http://foo/bar")
  599. # p u.to_s #=> "http://foo/bar"
  600. # u.hostname = "::1"
  601. # p u.to_s #=> "http://[::1]/bar"
  602. #
  603. # If the argument seems IPv6 address,
  604. # it is wrapped by brackets.
  605. #
  606. def hostname=(v)
  607. v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
  608. self.host = v
  609. end
  610. #
  611. # check the port +v+ component for RFC2396 compliance
  612. # and against the URI::Parser Regexp for :PORT
  613. #
  614. # Can not have a registry or opaque component defined,
  615. # with a port component defined.
  616. #
  617. def check_port(v)
  618. return v unless v
  619. if @opaque
  620. raise InvalidURIError,
  621. "can not set port with registry or opaque"
  622. elsif !v.kind_of?(Fixnum) && parser.regexp[:PORT] !~ v
  623. raise InvalidComponentError,
  624. "bad component(expected port component): #{v.inspect}"
  625. end
  626. return true
  627. end
  628. private :check_port
  629. # protected setter for the port component +v+
  630. #
  631. # see also URI::Generic.port=
  632. #
  633. def set_port(v)
  634. v = v.empty? ? nil : v.to_i unless !v || v.kind_of?(Fixnum)
  635. @port = v
  636. end
  637. protected :set_port
  638. #
  639. # == Args
  640. #
  641. # +v+::
  642. # String
  643. #
  644. # == Description
  645. #
  646. # public setter for the port component +v+.
  647. # (with validation)
  648. #
  649. # see also URI::Generic.check_port
  650. #
  651. # == Usage
  652. #
  653. # require 'uri'
  654. #
  655. # uri = URI.parse("http://my.example.com")
  656. # uri.port = 8080
  657. # # => 8080
  658. # uri
  659. # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com:8080>
  660. #
  661. def port=(v)
  662. check_port(v)
  663. set_port(v)
  664. port
  665. end
  666. def check_registry(v) # :nodoc:
  667. raise InvalidURIError, "can not set registry"
  668. end
  669. private :check_registry
  670. def set_registry(v) #:nodoc:
  671. raise InvalidURIError, "can not set registry"
  672. end
  673. protected :set_registry
  674. def registry=(v)
  675. raise InvalidURIError, "can not set registry"
  676. end
  677. #
  678. # check the path +v+ component for RFC2396 compliance
  679. # and against the URI::Parser Regexp
  680. # for :ABS_PATH and :REL_PATH
  681. #
  682. # Can not have a opaque component defined,
  683. # with a path component defined.
  684. #
  685. def check_path(v)
  686. # raise if both hier and opaque are not nil, because:
  687. # absoluteURI = scheme ":" ( hier_part | opaque_part )
  688. # hier_part = ( net_path | abs_path ) [ "?" query ]
  689. if v && @opaque
  690. raise InvalidURIError,
  691. "path conflicts with opaque"
  692. end
  693. # If scheme is ftp, path may be relative.
  694. # See RFC 1738 section 3.2.2, and RFC 2396.
  695. if @scheme && @scheme != "ftp"
  696. if v && v != '' && parser.regexp[:ABS_PATH] !~ v
  697. raise InvalidComponentError,
  698. "bad component(expected absolute path component): #{v}"
  699. end
  700. else
  701. if v && v != '' && parser.regexp[:ABS_PATH] !~ v &&
  702. parser.regexp[:REL_PATH] !~ v
  703. raise InvalidComponentError,
  704. "bad component(expected relative path component): #{v}"
  705. end
  706. end
  707. return true
  708. end
  709. private :check_path
  710. # protected setter for the path component +v+
  711. #
  712. # see also URI::Generic.path=
  713. #
  714. def set_path(v)
  715. @path = v
  716. end
  717. protected :set_path
  718. #
  719. # == Args
  720. #
  721. # +v+::
  722. # String
  723. #
  724. # == Description
  725. #
  726. # public setter for the path component +v+.
  727. # (with validation)
  728. #
  729. # see also URI::Generic.check_path
  730. #
  731. # == Usage
  732. #
  733. # require 'uri'
  734. #
  735. # uri = URI.parse("http://my.example.com/pub/files")
  736. # uri.path = "/faq/"
  737. # # => "/faq/"
  738. # uri
  739. # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com/faq/>
  740. #
  741. def path=(v)
  742. check_path(v)
  743. set_path(v)
  744. v
  745. end
  746. #
  747. # == Args
  748. #
  749. # +v+::
  750. # String
  751. #
  752. # == Description
  753. #
  754. # public setter for the query component +v+.
  755. #
  756. # == Usage
  757. #
  758. # require 'uri'
  759. #
  760. # uri = URI.parse("http://my.example.com/?id=25")
  761. # uri.query = "id=1"
  762. # # => "id=1"
  763. # uri
  764. # #=> #<URI::HTTP:0x000000008e89e8 URL:http://my.example.com/?id=1>
  765. #
  766. def query=(v)
  767. return @query = nil unless v
  768. raise InvalidURIError, "query conflicts with opaque" if @opaque
  769. x = v.to_str
  770. v = x.dup if x.equal? v
  771. v.encode!(Encoding::UTF_8) rescue nil
  772. v.delete!("\t\r\n")
  773. v.force_encoding(Encoding::ASCII_8BIT)
  774. v.gsub!(/(?!%\h\h|[!$-&(-;=?-_a-~])./n.freeze){'%%%02X' % $&.ord}
  775. v.force_encoding(Encoding::US_ASCII)
  776. @query = v
  777. end
  778. #
  779. # check the opaque +v+ component for RFC2396 compliance and
  780. # against the URI::Parser Regexp for :OPAQUE
  781. #
  782. # Can not have a host, port, user or path component defined,
  783. # with an opaque component defined.
  784. #
  785. def check_opaque(v)
  786. return v unless v
  787. # raise if both hier and opaque are not nil, because:
  788. # absoluteURI = scheme ":" ( hier_part | opaque_part )
  789. # hier_part = ( net_path | abs_path ) [ "?" query ]
  790. if @host || @port || @user || @path # userinfo = @user + ':' + @password
  791. raise InvalidURIError,
  792. "can not set opaque with host, port, userinfo or path"
  793. elsif v && parser.regexp[:OPAQUE] !~ v
  794. raise InvalidComponentError,
  795. "bad component(expected opaque component): #{v}"
  796. end
  797. return true
  798. end
  799. private :check_opaque
  800. # protected setter for the opaque component +v+
  801. #
  802. # see also URI::Generic.opaque=
  803. #
  804. def set_opaque(v)
  805. @opaque = v
  806. end
  807. protected :set_opaque
  808. #
  809. # == Args
  810. #
  811. # +v+::
  812. # String
  813. #
  814. # == Description
  815. #
  816. # public setter for the opaque component +v+.
  817. # (with validation)
  818. #
  819. # see also URI::Generic.check_opaque
  820. #
  821. def opaque=(v)
  822. check_opaque(v)
  823. set_opaque(v)
  824. v
  825. end
  826. #
  827. # check the fragment +v+ component against the URI::Parser Regexp for :FRAGMENT
  828. #
  829. #
  830. # == Args
  831. #
  832. # +v+::
  833. # String
  834. #
  835. # == Description
  836. #
  837. # public setter for the fragment component +v+.
  838. # (with validation)
  839. #
  840. # == Usage
  841. #
  842. # require 'uri'
  843. #
  844. # uri = URI.parse("http://my.example.com/?id=25#time=1305212049")
  845. # uri.fragment = "time=1305212086"
  846. # # => "time=1305212086"
  847. # uri
  848. # #=> #<URI::HTTP:0x000000007a81f8 URL:http://my.example.com/?id=25#time=1305212086>
  849. #
  850. def fragment=(v)
  851. return @fragment = nil unless v
  852. x = v.to_str
  853. v = x.dup if x.equal? v
  854. v.encode!(Encoding::UTF_8) rescue nil
  855. v.delete!("\t\r\n")
  856. v.force_encoding(Encoding::ASCII_8BIT)
  857. v.gsub!(/(?!%\h\h|[!-~])./n){'%%%02X' % $&.ord}
  858. v.force_encoding(Encoding::US_ASCII)
  859. @fragment = v
  860. end
  861. #
  862. # Checks if URI has a path
  863. #
  864. def hierarchical?
  865. if @path
  866. true
  867. else
  868. false
  869. end
  870. end
  871. #
  872. # Checks if URI is an absolute one
  873. #
  874. def absolute?
  875. if @scheme
  876. true
  877. else
  878. false
  879. end
  880. end
  881. alias absolute absolute?
  882. #
  883. # Checks if URI is relative
  884. #
  885. def relative?
  886. !absolute?
  887. end
  888. #
  889. # returns an Array of the path split on '/'
  890. #
  891. def split_path(path)
  892. path.split(%r{/+}, -1)
  893. end
  894. private :split_path
  895. #
  896. # Merges a base path +base+, with relative path +rel+,
  897. # returns a modified base path.
  898. #
  899. def merge_path(base, rel)
  900. # RFC2396, Section 5.2, 5)
  901. # RFC2396, Section 5.2, 6)
  902. base_path = split_path(base)
  903. rel_path = split_path(rel)
  904. # RFC2396, Section 5.2, 6), a)
  905. base_path << '' if base_path.last == '..'
  906. while i = base_path.index('..')
  907. base_path.slice!(i - 1, 2)
  908. end
  909. if (first = rel_path.first) and first.empty?
  910. base_path.clear
  911. rel_path.shift
  912. end
  913. # RFC2396, Section 5.2, 6), c)
  914. # RFC2396, Section 5.2, 6), d)
  915. rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
  916. rel_path.delete('.')
  917. # RFC2396, Section 5.2, 6), e)
  918. tmp = []
  919. rel_path.each do |x|
  920. if x == '..' &&
  921. !(tmp.empty? || tmp.last == '..')
  922. tmp.pop
  923. else
  924. tmp << x
  925. end
  926. end
  927. add_trailer_slash = !tmp.empty?
  928. if base_path.empty?
  929. base_path = [''] # keep '/' for root directory
  930. elsif add_trailer_slash
  931. base_path.pop
  932. end
  933. while x = tmp.shift
  934. if x == '..'
  935. # RFC2396, Section 4
  936. # a .. or . in an absolute path has no special meaning
  937. base_path.pop if base_path.size > 1
  938. else
  939. # if x == '..'
  940. # valid absolute (but abnormal) path "/../..."
  941. # else
  942. # valid absolute path
  943. # end
  944. base_path << x
  945. tmp.each {|t| base_path << t}
  946. add_trailer_slash = false
  947. break
  948. end
  949. end
  950. base_path.push('') if add_trailer_slash
  951. return base_path.join('/')
  952. end
  953. private :merge_path
  954. #
  955. # == Args
  956. #
  957. # +oth+::
  958. # URI or String
  959. #
  960. # == Description
  961. #
  962. # Destructive form of #merge
  963. #
  964. # == Usage
  965. #
  966. # require 'uri'
  967. #
  968. # uri = URI.parse("http://my.example.com")
  969. # uri.merge!("/main.rbx?page=1")
  970. # p uri
  971. # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
  972. #
  973. def merge!(oth)
  974. t = merge(oth)
  975. if self == t
  976. nil
  977. else
  978. replace!(t)
  979. self
  980. end
  981. end
  982. #
  983. # == Args
  984. #
  985. # +oth+::
  986. # URI or String
  987. #
  988. # == Description
  989. #
  990. # Merges two URI's.
  991. #
  992. # == Usage
  993. #
  994. # require 'uri'
  995. #
  996. # uri = URI.parse("http://my.example.com")
  997. # p uri.merge("/main.rbx?page=1")
  998. # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
  999. #
  1000. def merge(oth)
  1001. begin
  1002. base, rel = merge0(oth)
  1003. rescue
  1004. raise $!.class, $!.message
  1005. end
  1006. if base == rel
  1007. return base
  1008. end
  1009. authority = rel.userinfo || rel.host || rel.port
  1010. # RFC2396, Section 5.2, 2)
  1011. if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
  1012. base.fragment=(rel.fragment) if rel.fragment
  1013. return base
  1014. end
  1015. base.query = nil
  1016. base.fragment=(nil)
  1017. # RFC2396, Section 5.2, 4)
  1018. if !authority
  1019. base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
  1020. else
  1021. # RFC2396, Section 5.2, 4)
  1022. base.set_path(rel.path) if rel.path
  1023. end
  1024. # RFC2396, Section 5.2, 7)
  1025. base.set_userinfo(rel.userinfo) if rel.userinfo
  1026. base.set_host(rel.host) if rel.host
  1027. base.set_port(rel.port) if rel.port
  1028. base.query = rel.query if rel.query
  1029. base.fragment=(rel.fragment) if rel.fragment
  1030. return base
  1031. end # merge
  1032. alias + merge
  1033. # return base and rel.
  1034. # you can modify `base', but can not `rel'.
  1035. def merge0(oth)
  1036. oth = parser.send(:convert_to_uri, oth)
  1037. if self.relative? && oth.relative?
  1038. raise BadURIError,
  1039. "both URI are relative"
  1040. end
  1041. if self.absolute? && oth.absolute?
  1042. #raise BadURIError,
  1043. # "both URI are absolute"
  1044. # hmm... should return oth for usability?
  1045. return oth, oth
  1046. end
  1047. if self.absolute?
  1048. return self.dup, oth
  1049. else
  1050. return oth, oth
  1051. end
  1052. end
  1053. private :merge0
  1054. # :stopdoc:
  1055. def route_from_path(src, dst)
  1056. case dst
  1057. when src
  1058. # RFC2396, Section 4.2
  1059. return ''
  1060. when %r{(?:\A|/)\.\.?(?:/|\z)}
  1061. # dst has abnormal absolute path,
  1062. # like "/./", "/../", "/x/../", ...
  1063. return dst.dup
  1064. end
  1065. src_path = src.scan(%r{(?:\A|[^/]+)/})
  1066. dst_path = dst.scan(%r{(?:\A|[^/]+)/?})
  1067. # discard same parts
  1068. while !dst_path.empty? && dst_path.first == src_path.first
  1069. src_path.shift
  1070. dst_path.shift
  1071. end
  1072. tmp = dst_path.join
  1073. # calculate
  1074. if src_path.empty?
  1075. if tmp.empty?
  1076. return './'
  1077. elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
  1078. return './' + tmp
  1079. else
  1080. return tmp
  1081. end
  1082. end
  1083. return '../' * src_path.size + tmp
  1084. end
  1085. private :route_from_path
  1086. # :startdoc:
  1087. # :stopdoc:
  1088. def route_from0(oth)
  1089. oth = parser.send(:convert_to_uri, oth)
  1090. if self.relative?
  1091. raise BadURIError,
  1092. "relative URI: #{self}"
  1093. end
  1094. if oth.relative?
  1095. raise BadURIError,
  1096. "relative URI: #{oth}"
  1097. end
  1098. if self.scheme != oth.scheme
  1099. return self, self.dup
  1100. end
  1101. rel = URI::Generic.new(nil, # it is relative URI
  1102. self.userinfo, self.host, self.port,
  1103. nil, self.path, self.opaque,
  1104. self.query, self.fragment, parser)
  1105. if rel.userinfo != oth.userinfo ||
  1106. rel.host.to_s.downcase != oth.host.to_s.downcase ||
  1107. rel.port != oth.port
  1108. if self.userinfo.nil? && self.host.nil?
  1109. return self, self.dup
  1110. end
  1111. rel.set_port(nil) if rel.port == oth.default_port
  1112. return rel, rel
  1113. end
  1114. rel.set_userinfo(nil)
  1115. rel.set_host(nil)
  1116. rel.set_port(nil)
  1117. if rel.path && rel.path == oth.path
  1118. rel.set_path('')
  1119. rel.query = nil if rel.query == oth.query
  1120. return rel, rel
  1121. elsif rel.opaque && rel.opaque == oth.opaque
  1122. rel.set_opaque('')
  1123. rel.query = nil if rel.query == oth.query
  1124. return rel, rel
  1125. end
  1126. # you can modify `rel', but can not `oth'.
  1127. return oth, rel
  1128. end
  1129. private :route_from0
  1130. # :startdoc:
  1131. #
  1132. # == Args
  1133. #
  1134. # +oth+::
  1135. # URI or String
  1136. #
  1137. # == Description
  1138. #
  1139. # Calculates relative path from oth to self
  1140. #
  1141. # == Usage
  1142. #
  1143. # require 'uri'
  1144. #
  1145. # uri = URI.parse('http://my.example.com/main.rbx?page=1')
  1146. # p uri.route_from('http://my.example.com')
  1147. # #=> #<URI::Generic:0x20218858 URL:/main.rbx?page=1>
  1148. #
  1149. def route_from(oth)
  1150. # you can modify `rel', but can not `oth'.
  1151. begin
  1152. oth, rel = route_from0(oth)
  1153. rescue
  1154. raise $!.class, $!.message
  1155. end
  1156. if oth == rel
  1157. return rel
  1158. end
  1159. rel.set_path(route_from_path(oth.path, self.path))
  1160. if rel.path == './' && self.query
  1161. # "./?foo" -> "?foo"
  1162. rel.set_path('')
  1163. end
  1164. return rel
  1165. end
  1166. alias - route_from
  1167. #
  1168. # == Args
  1169. #
  1170. # +oth+::
  1171. # URI or String
  1172. #
  1173. # == Description
  1174. #
  1175. # Calculates relative path to oth from self
  1176. #
  1177. # == Usage
  1178. #
  1179. # require 'uri'
  1180. #
  1181. # uri = URI.parse('http://my.example.com')
  1182. # p uri.route_to('http://my.example.com/main.rbx?page=1')
  1183. # #=> #<URI::Generic:0x2020c2f6 URL:/main.rbx?page=1>
  1184. #
  1185. def route_to(oth)
  1186. parser.send(:convert_to_uri, oth).route_from(self)
  1187. end
  1188. #
  1189. # Returns normalized URI
  1190. #
  1191. def normalize
  1192. uri = dup
  1193. uri.normalize!
  1194. uri
  1195. end
  1196. #
  1197. # Destructive version of #normalize
  1198. #
  1199. def normalize!
  1200. if path&.empty?
  1201. set_path('/')
  1202. end
  1203. if scheme && scheme != scheme.downcase
  1204. set_scheme(self.scheme.downcase)
  1205. end
  1206. if host && host != host.downcase
  1207. set_host(self.host.downcase)
  1208. end
  1209. end
  1210. #
  1211. # Constructs String from URI
  1212. #
  1213. def to_s
  1214. str = String.new
  1215. if @scheme
  1216. str << @scheme
  1217. str << ':'
  1218. end
  1219. if @opaque
  1220. str << @opaque
  1221. else
  1222. if @host
  1223. str << '//'
  1224. end
  1225. if self.userinfo
  1226. str << self.userinfo
  1227. str << '@'
  1228. end
  1229. if @host
  1230. str << @host
  1231. end
  1232. if @port && @port != self.default_port
  1233. str << ':'
  1234. str << @port.to_s
  1235. end
  1236. str << @path
  1237. if @query
  1238. str << '?'
  1239. str << @query
  1240. end
  1241. end
  1242. if @fragment
  1243. str << '#'
  1244. str << @fragment
  1245. end
  1246. str
  1247. end
  1248. #
  1249. # Compares to URI's
  1250. #
  1251. def ==(oth)
  1252. if self.class == oth.class
  1253. self.normalize.component_ary == oth.normalize.component_ary
  1254. else
  1255. false
  1256. end
  1257. end
  1258. def hash
  1259. self.component_ary.hash
  1260. end
  1261. def eql?(oth)
  1262. self.class == oth.class &&
  1263. parser == oth.parser &&
  1264. self.component_ary.eql?(oth.component_ary)
  1265. end
  1266. =begin
  1267. --- URI::Generic#===(oth)
  1268. =end
  1269. # def ===(oth)
  1270. # raise NotImplementedError
  1271. # end
  1272. =begin
  1273. =end
  1274. # returns an Array of the components defined from the COMPONENT Array
  1275. def component_ary
  1276. component.collect do |x|
  1277. self.send(x)
  1278. end
  1279. end
  1280. protected :component_ary
  1281. # == Args
  1282. #
  1283. # +components+::
  1284. # Multiple Symbol arguments defined in URI::HTTP
  1285. #
  1286. # == Description
  1287. #
  1288. # Selects specified components from URI
  1289. #
  1290. # == Usage
  1291. #
  1292. # require 'uri'
  1293. #
  1294. # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
  1295. # p uri.select(:userinfo, :host, :path)
  1296. # # => ["myuser:mypass", "my.example.com", "/test.rbx"]
  1297. #
  1298. def select(*components)
  1299. components.collect do |c|
  1300. if component.include?(c)
  1301. self.send(c)
  1302. else
  1303. raise ArgumentError,
  1304. "expected of components of #{self.class} (#{self.class.component.join(', ')})"
  1305. end
  1306. end
  1307. end
  1308. def inspect
  1309. "#<#{self.class} #{self}>"
  1310. end
  1311. #
  1312. # == Args
  1313. #
  1314. # +v+::
  1315. # URI or String
  1316. #
  1317. # == Description
  1318. #
  1319. # attempt to parse other URI +oth+
  1320. # return [parsed_oth, self]
  1321. #
  1322. # == Usage
  1323. #
  1324. # require 'uri'
  1325. #
  1326. # uri = URI.parse("http://my.example.com")
  1327. # uri.coerce("http://foo.com")
  1328. # #=> [#<URI::HTTP:0x00000000bcb028 URL:http://foo.com/>, #<URI::HTTP:0x00000000d92178 URL:http://my.example.com>]
  1329. #
  1330. def coerce(oth)
  1331. case oth
  1332. when String
  1333. oth = parser.parse(oth)
  1334. else
  1335. super
  1336. end
  1337. return oth, self
  1338. end
  1339. # returns a proxy URI.
  1340. # The proxy URI is obtained from environment variables such as http_proxy,
  1341. # ftp_proxy, no_proxy, etc.
  1342. # If there is no proper proxy, nil is returned.
  1343. #
  1344. # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
  1345. # are examined too.
  1346. #
  1347. # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
  1348. # It's because HTTP_PROXY may be set by Proxy: header.
  1349. # So HTTP_PROXY is not used.
  1350. # http_proxy is not used too if the variable is case insensitive.
  1351. # CGI_HTTP_PROXY can be used instead.
  1352. def find_proxy
  1353. raise BadURIError, "relative URI: #{self}" if self.relative?
  1354. name = self.scheme.downcase + '_proxy'
  1355. proxy_uri = nil
  1356. if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
  1357. # HTTP_PROXY conflicts with *_proxy for proxy settings and
  1358. # HTTP_* for header information in CGI.
  1359. # So it should be careful to use it.
  1360. pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
  1361. case pairs.length
  1362. when 0 # no proxy setting anyway.
  1363. proxy_uri = nil
  1364. when 1
  1365. k, _ = pairs.shift
  1366. if k == 'http_proxy' && ENV[k.upcase] == nil
  1367. # http_proxy is safe to use because ENV is case sensitive.
  1368. proxy_uri = ENV[name]
  1369. else
  1370. proxy_uri = nil
  1371. end
  1372. else # http_proxy is safe to use because ENV is case sensitive.
  1373. proxy_uri = ENV.to_hash[name]
  1374. end
  1375. if !proxy_uri
  1376. # Use CGI_HTTP_PROXY. cf. libwww-perl.
  1377. proxy_uri = ENV["CGI_#{name.upcase}"]
  1378. end
  1379. elsif name == 'http_proxy'
  1380. if p_addr = ENV_JAVA['http.proxyHost']
  1381. p_port = ENV_JAVA['http.proxyPort']
  1382. if p_user = ENV_JAVA['http.proxyUser']
  1383. p_pass = ENV_JAVA['http.proxyPass']
  1384. proxy_uri = "http://#{p_user}:#{p_pass}@#{p_addr}:#{p_port}"
  1385. else
  1386. proxy_uri = "http://#{p_addr}:#{p_port}"
  1387. end
  1388. else
  1389. unless proxy_uri = ENV[name]
  1390. if proxy_uri = ENV[name.upcase]
  1391. warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
  1392. end
  1393. end
  1394. end
  1395. else
  1396. proxy_uri = ENV[name] || ENV[name.upcase]
  1397. end
  1398. if proxy_uri.nil? || proxy_uri.empty?
  1399. return nil
  1400. end
  1401. if self.hostname
  1402. require 'socket'
  1403. begin
  1404. addr = IPSocket.getaddress(self.hostname)
  1405. return nil if /\A127\.|\A::1\z/ =~ addr
  1406. rescue SocketError
  1407. end
  1408. end
  1409. name = 'no_proxy'
  1410. if no_proxy = ENV[name] || ENV[name.upcase]
  1411. no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
  1412. if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
  1413. (!port || self.port == port.to_i)
  1414. return nil
  1415. end
  1416. }
  1417. end
  1418. URI.parse(proxy_uri)
  1419. end
  1420. end
  1421. end