PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/uri/generic.rb

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