PageRenderTime 60ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/bundle/gems/addressable-2.3.6/lib/addressable/uri.rb

https://github.com/trmchale1/personal_website
Ruby | 2351 lines | 1625 code | 124 blank | 602 comment | 220 complexity | 21a26dd3fc99c289d983e56e79d55d63 MD5 | raw file
Possible License(s): Apache-2.0, 0BSD, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. # encoding:utf-8
  2. #--
  3. # Copyright (C) 2006-2013 Bob Aman
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #++
  17. require "addressable/version"
  18. require "addressable/idna"
  19. ##
  20. # Addressable is a library for processing links and URIs.
  21. module Addressable
  22. ##
  23. # This is an implementation of a URI parser based on
  24. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
  25. # <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
  26. class URI
  27. ##
  28. # Raised if something other than a uri is supplied.
  29. class InvalidURIError < StandardError
  30. end
  31. ##
  32. # Container for the character classes specified in
  33. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  34. module CharacterClasses
  35. ALPHA = "a-zA-Z"
  36. DIGIT = "0-9"
  37. GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
  38. SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
  39. RESERVED = GEN_DELIMS + SUB_DELIMS
  40. UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
  41. PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
  42. SCHEME = ALPHA + DIGIT + "\\-\\+\\."
  43. AUTHORITY = PCHAR
  44. PATH = PCHAR + "\\/"
  45. QUERY = PCHAR + "\\/\\?"
  46. FRAGMENT = PCHAR + "\\/\\?"
  47. end
  48. SLASH = '/'
  49. EMPTY_STR = ''
  50. URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
  51. PORT_MAPPING = {
  52. "http" => 80,
  53. "https" => 443,
  54. "ftp" => 21,
  55. "tftp" => 69,
  56. "sftp" => 22,
  57. "ssh" => 22,
  58. "svn+ssh" => 22,
  59. "telnet" => 23,
  60. "nntp" => 119,
  61. "gopher" => 70,
  62. "wais" => 210,
  63. "ldap" => 389,
  64. "prospero" => 1525
  65. }
  66. ##
  67. # Returns a URI object based on the parsed string.
  68. #
  69. # @param [String, Addressable::URI, #to_str] uri
  70. # The URI string to parse.
  71. # No parsing is performed if the object is already an
  72. # <code>Addressable::URI</code>.
  73. #
  74. # @return [Addressable::URI] The parsed URI.
  75. def self.parse(uri)
  76. # If we were given nil, return nil.
  77. return nil unless uri
  78. # If a URI object is passed, just return itself.
  79. return uri.dup if uri.kind_of?(self)
  80. # If a URI object of the Ruby standard library variety is passed,
  81. # convert it to a string, then parse the string.
  82. # We do the check this way because we don't want to accidentally
  83. # cause a missing constant exception to be thrown.
  84. if uri.class.name =~ /^URI\b/
  85. uri = uri.to_s
  86. end
  87. # Otherwise, convert to a String
  88. begin
  89. uri = uri.to_str
  90. rescue TypeError, NoMethodError
  91. raise TypeError, "Can't convert #{uri.class} into String."
  92. end if not uri.is_a? String
  93. # This Regexp supplied as an example in RFC 3986, and it works great.
  94. scan = uri.scan(URIREGEX)
  95. fragments = scan[0]
  96. scheme = fragments[1]
  97. authority = fragments[3]
  98. path = fragments[4]
  99. query = fragments[6]
  100. fragment = fragments[8]
  101. user = nil
  102. password = nil
  103. host = nil
  104. port = nil
  105. if authority != nil
  106. # The Regexp above doesn't split apart the authority.
  107. userinfo = authority[/^([^\[\]]*)@/, 1]
  108. if userinfo != nil
  109. user = userinfo.strip[/^([^:]*):?/, 1]
  110. password = userinfo.strip[/:(.*)$/, 1]
  111. end
  112. host = authority.gsub(
  113. /^([^\[\]]*)@/, EMPTY_STR
  114. ).gsub(
  115. /:([^:@\[\]]*?)$/, EMPTY_STR
  116. )
  117. port = authority[/:([^:@\[\]]*?)$/, 1]
  118. end
  119. if port == EMPTY_STR
  120. port = nil
  121. end
  122. return new(
  123. :scheme => scheme,
  124. :user => user,
  125. :password => password,
  126. :host => host,
  127. :port => port,
  128. :path => path,
  129. :query => query,
  130. :fragment => fragment
  131. )
  132. end
  133. ##
  134. # Converts an input to a URI. The input does not have to be a valid
  135. # URI — the method will use heuristics to guess what URI was intended.
  136. # This is not standards-compliant, merely user-friendly.
  137. #
  138. # @param [String, Addressable::URI, #to_str] uri
  139. # The URI string to parse.
  140. # No parsing is performed if the object is already an
  141. # <code>Addressable::URI</code>.
  142. # @param [Hash] hints
  143. # A <code>Hash</code> of hints to the heuristic parser.
  144. # Defaults to <code>{:scheme => "http"}</code>.
  145. #
  146. # @return [Addressable::URI] The parsed URI.
  147. def self.heuristic_parse(uri, hints={})
  148. # If we were given nil, return nil.
  149. return nil unless uri
  150. # If a URI object is passed, just return itself.
  151. return uri.dup if uri.kind_of?(self)
  152. # If a URI object of the Ruby standard library variety is passed,
  153. # convert it to a string, then parse the string.
  154. # We do the check this way because we don't want to accidentally
  155. # cause a missing constant exception to be thrown.
  156. if uri.class.name =~ /^URI\b/
  157. uri = uri.to_s
  158. end
  159. if !uri.respond_to?(:to_str)
  160. raise TypeError, "Can't convert #{uri.class} into String."
  161. end
  162. # Otherwise, convert to a String
  163. uri = uri.to_str.dup
  164. hints = {
  165. :scheme => "http"
  166. }.merge(hints)
  167. case uri
  168. when /^http:\/+/
  169. uri.gsub!(/^http:\/+/, "http://")
  170. when /^https:\/+/
  171. uri.gsub!(/^https:\/+/, "https://")
  172. when /^feed:\/+http:\/+/
  173. uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
  174. when /^feed:\/+/
  175. uri.gsub!(/^feed:\/+/, "feed://")
  176. when /^file:\/+/
  177. uri.gsub!(/^file:\/+/, "file:///")
  178. when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
  179. uri.gsub!(/^/, hints[:scheme] + "://")
  180. end
  181. parsed = self.parse(uri)
  182. if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
  183. parsed = self.parse(hints[:scheme] + "://" + uri)
  184. end
  185. if parsed.path.include?(".")
  186. new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
  187. if new_host
  188. parsed.defer_validation do
  189. new_path = parsed.path.gsub(
  190. Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
  191. parsed.host = new_host
  192. parsed.path = new_path
  193. parsed.scheme = hints[:scheme] unless parsed.scheme
  194. end
  195. end
  196. end
  197. return parsed
  198. end
  199. ##
  200. # Converts a path to a file scheme URI. If the path supplied is
  201. # relative, it will be returned as a relative URI. If the path supplied
  202. # is actually a non-file URI, it will parse the URI as if it had been
  203. # parsed with <code>Addressable::URI.parse</code>. Handles all of the
  204. # various Microsoft-specific formats for specifying paths.
  205. #
  206. # @param [String, Addressable::URI, #to_str] path
  207. # Typically a <code>String</code> path to a file or directory, but
  208. # will return a sensible return value if an absolute URI is supplied
  209. # instead.
  210. #
  211. # @return [Addressable::URI]
  212. # The parsed file scheme URI or the original URI if some other URI
  213. # scheme was provided.
  214. #
  215. # @example
  216. # base = Addressable::URI.convert_path("/absolute/path/")
  217. # uri = Addressable::URI.convert_path("relative/path")
  218. # (base + uri).to_s
  219. # #=> "file:///absolute/path/relative/path"
  220. #
  221. # Addressable::URI.convert_path(
  222. # "c:\\windows\\My Documents 100%20\\foo.txt"
  223. # ).to_s
  224. # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
  225. #
  226. # Addressable::URI.convert_path("http://example.com/").to_s
  227. # #=> "http://example.com/"
  228. def self.convert_path(path)
  229. # If we were given nil, return nil.
  230. return nil unless path
  231. # If a URI object is passed, just return itself.
  232. return path if path.kind_of?(self)
  233. if !path.respond_to?(:to_str)
  234. raise TypeError, "Can't convert #{path.class} into String."
  235. end
  236. # Otherwise, convert to a String
  237. path = path.to_str.strip
  238. path.gsub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
  239. path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
  240. uri = self.parse(path)
  241. if uri.scheme == nil
  242. # Adjust windows-style uris
  243. uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
  244. "/#{$1.downcase}:/"
  245. end
  246. uri.path.gsub!(/\\/, SLASH)
  247. if File.exists?(uri.path) &&
  248. File.stat(uri.path).directory?
  249. uri.path.gsub!(/\/$/, EMPTY_STR)
  250. uri.path = uri.path + '/'
  251. end
  252. # If the path is absolute, set the scheme and host.
  253. if uri.path =~ /^\//
  254. uri.scheme = "file"
  255. uri.host = EMPTY_STR
  256. end
  257. uri.normalize!
  258. end
  259. return uri
  260. end
  261. ##
  262. # Joins several URIs together.
  263. #
  264. # @param [String, Addressable::URI, #to_str] *uris
  265. # The URIs to join.
  266. #
  267. # @return [Addressable::URI] The joined URI.
  268. #
  269. # @example
  270. # base = "http://example.com/"
  271. # uri = Addressable::URI.parse("relative/path")
  272. # Addressable::URI.join(base, uri)
  273. # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
  274. def self.join(*uris)
  275. uri_objects = uris.collect do |uri|
  276. if !uri.respond_to?(:to_str)
  277. raise TypeError, "Can't convert #{uri.class} into String."
  278. end
  279. uri.kind_of?(self) ? uri : self.parse(uri.to_str)
  280. end
  281. result = uri_objects.shift.dup
  282. for uri in uri_objects
  283. result.join!(uri)
  284. end
  285. return result
  286. end
  287. ##
  288. # Percent encodes a URI component.
  289. #
  290. # @param [String, #to_str] component The URI component to encode.
  291. #
  292. # @param [String, Regexp] character_class
  293. # The characters which are not percent encoded. If a <code>String</code>
  294. # is passed, the <code>String</code> must be formatted as a regular
  295. # expression character class. (Do not include the surrounding square
  296. # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
  297. # everything but the letters 'b' through 'z' and the numbers '0' through
  298. # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
  299. # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
  300. # useful <code>String</code> values may be found in the
  301. # <code>Addressable::URI::CharacterClasses</code> module. The default
  302. # value is the reserved plus unreserved character classes specified in
  303. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  304. #
  305. # @param [Regexp] upcase_encoded
  306. # A string of characters that may already be percent encoded, and whose
  307. # encodings should be upcased. This allows normalization of percent
  308. # encodings for characters not included in the
  309. # <code>character_class</code>.
  310. #
  311. # @return [String] The encoded component.
  312. #
  313. # @example
  314. # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
  315. # => "simple%2Fex%61mple"
  316. # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
  317. # => "simple%2Fex%61mple"
  318. # Addressable::URI.encode_component(
  319. # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
  320. # )
  321. # => "simple%2Fexample"
  322. def self.encode_component(component, character_class=
  323. CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
  324. upcase_encoded='')
  325. return nil if component.nil?
  326. begin
  327. if component.kind_of?(Symbol) ||
  328. component.kind_of?(Numeric) ||
  329. component.kind_of?(TrueClass) ||
  330. component.kind_of?(FalseClass)
  331. component = component.to_s
  332. else
  333. component = component.to_str
  334. end
  335. rescue TypeError, NoMethodError
  336. raise TypeError, "Can't convert #{component.class} into String."
  337. end if !component.is_a? String
  338. if ![String, Regexp].include?(character_class.class)
  339. raise TypeError,
  340. "Expected String or Regexp, got #{character_class.inspect}"
  341. end
  342. if character_class.kind_of?(String)
  343. character_class = /[^#{character_class}]/
  344. end
  345. if component.respond_to?(:force_encoding)
  346. # We can't perform regexps on invalid UTF sequences, but
  347. # here we need to, so switch to ASCII.
  348. component = component.dup
  349. component.force_encoding(Encoding::ASCII_8BIT)
  350. end
  351. # Avoiding gsub! because there are edge cases with frozen strings
  352. component = component.gsub(character_class) do |sequence|
  353. (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
  354. end
  355. if upcase_encoded.length > 0
  356. component = component.gsub(/%(#{upcase_encoded.chars.map do |char|
  357. char.unpack('C*').map { |c| '%02x' % c }.join
  358. end.join('|')})/i) { |s| s.upcase }
  359. end
  360. return component
  361. end
  362. class << self
  363. alias_method :encode_component, :encode_component
  364. end
  365. ##
  366. # Unencodes any percent encoded characters within a URI component.
  367. # This method may be used for unencoding either components or full URIs,
  368. # however, it is recommended to use the <code>unencode_component</code>
  369. # alias when unencoding components.
  370. #
  371. # @param [String, Addressable::URI, #to_str] uri
  372. # The URI or component to unencode.
  373. #
  374. # @param [Class] return_type
  375. # The type of object to return.
  376. # This value may only be set to <code>String</code> or
  377. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  378. # to <code>String</code>.
  379. #
  380. # @param [String] leave_encoded
  381. # A string of characters to leave encoded. If a percent encoded character
  382. # in this list is encountered then it will remain percent encoded.
  383. #
  384. # @return [String, Addressable::URI]
  385. # The unencoded component or URI.
  386. # The return type is determined by the <code>return_type</code>
  387. # parameter.
  388. def self.unencode(uri, return_type=String, leave_encoded='')
  389. return nil if uri.nil?
  390. begin
  391. uri = uri.to_str
  392. rescue NoMethodError, TypeError
  393. raise TypeError, "Can't convert #{uri.class} into String."
  394. end if !uri.is_a? String
  395. if ![String, ::Addressable::URI].include?(return_type)
  396. raise TypeError,
  397. "Expected Class (String or Addressable::URI), " +
  398. "got #{return_type.inspect}"
  399. end
  400. uri = uri.dup
  401. # Seriously, only use UTF-8. I'm really not kidding!
  402. uri.force_encoding("utf-8") if uri.respond_to?(:force_encoding)
  403. leave_encoded.force_encoding("utf-8") if leave_encoded.respond_to?(:force_encoding)
  404. result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
  405. c = sequence[1..3].to_i(16).chr
  406. c.force_encoding("utf-8") if c.respond_to?(:force_encoding)
  407. leave_encoded.include?(c) ? sequence : c
  408. end
  409. result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
  410. if return_type == String
  411. return result
  412. elsif return_type == ::Addressable::URI
  413. return ::Addressable::URI.parse(result)
  414. end
  415. end
  416. class << self
  417. alias_method :unescape, :unencode
  418. alias_method :unencode_component, :unencode
  419. alias_method :unescape_component, :unencode
  420. end
  421. ##
  422. # Normalizes the encoding of a URI component.
  423. #
  424. # @param [String, #to_str] component The URI component to encode.
  425. #
  426. # @param [String, Regexp] character_class
  427. # The characters which are not percent encoded. If a <code>String</code>
  428. # is passed, the <code>String</code> must be formatted as a regular
  429. # expression character class. (Do not include the surrounding square
  430. # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
  431. # everything but the letters 'b' through 'z' and the numbers '0'
  432. # through '9' to be percent encoded. If a <code>Regexp</code> is passed,
  433. # the value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
  434. # set of useful <code>String</code> values may be found in the
  435. # <code>Addressable::URI::CharacterClasses</code> module. The default
  436. # value is the reserved plus unreserved character classes specified in
  437. # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
  438. #
  439. # @param [String] leave_encoded
  440. # When <code>character_class</code> is a <code>String</code> then
  441. # <code>leave_encoded</code> is a string of characters that should remain
  442. # percent encoded while normalizing the component; if they appear percent
  443. # encoded in the original component, then they will be upcased ("%2f"
  444. # normalized to "%2F") but otherwise left alone.
  445. #
  446. # @return [String] The normalized component.
  447. #
  448. # @example
  449. # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
  450. # => "simple%2Fex%61mple"
  451. # Addressable::URI.normalize_component(
  452. # "simpl%65/%65xampl%65", /[^b-zB-Z]/
  453. # )
  454. # => "simple%2Fex%61mple"
  455. # Addressable::URI.normalize_component(
  456. # "simpl%65/%65xampl%65",
  457. # Addressable::URI::CharacterClasses::UNRESERVED
  458. # )
  459. # => "simple%2Fexample"
  460. # Addressable::URI.normalize_component(
  461. # "one%20two%2fthree%26four",
  462. # "0-9a-zA-Z &/",
  463. # "/"
  464. # )
  465. # => "one two%2Fthree&four"
  466. def self.normalize_component(component, character_class=
  467. CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
  468. leave_encoded='')
  469. return nil if component.nil?
  470. begin
  471. component = component.to_str
  472. rescue NoMethodError, TypeError
  473. raise TypeError, "Can't convert #{component.class} into String."
  474. end if !component.is_a? String
  475. if ![String, Regexp].include?(character_class.class)
  476. raise TypeError,
  477. "Expected String or Regexp, got #{character_class.inspect}"
  478. end
  479. if character_class.kind_of?(String)
  480. leave_re = if leave_encoded.length > 0
  481. character_class = "#{character_class}%" unless character_class.include?('%')
  482. "|%(?!#{leave_encoded.chars.map do |char|
  483. seq = char.unpack('C*').map { |c| '%02x' % c }.join
  484. [seq.upcase, seq.downcase]
  485. end.flatten.join('|')})"
  486. end
  487. character_class = /[^#{character_class}]#{leave_re}/
  488. end
  489. if component.respond_to?(:force_encoding)
  490. # We can't perform regexps on invalid UTF sequences, but
  491. # here we need to, so switch to ASCII.
  492. component = component.dup
  493. component.force_encoding(Encoding::ASCII_8BIT)
  494. end
  495. unencoded = self.unencode_component(component, String, leave_encoded)
  496. begin
  497. encoded = self.encode_component(
  498. Addressable::IDNA.unicode_normalize_kc(unencoded),
  499. character_class,
  500. leave_encoded
  501. )
  502. rescue ArgumentError
  503. encoded = self.encode_component(unencoded)
  504. end
  505. if encoded.respond_to?(:force_encoding)
  506. encoded.force_encoding(Encoding::UTF_8)
  507. end
  508. return encoded
  509. end
  510. ##
  511. # Percent encodes any special characters in the URI.
  512. #
  513. # @param [String, Addressable::URI, #to_str] uri
  514. # The URI to encode.
  515. #
  516. # @param [Class] return_type
  517. # The type of object to return.
  518. # This value may only be set to <code>String</code> or
  519. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  520. # to <code>String</code>.
  521. #
  522. # @return [String, Addressable::URI]
  523. # The encoded URI.
  524. # The return type is determined by the <code>return_type</code>
  525. # parameter.
  526. def self.encode(uri, return_type=String)
  527. return nil if uri.nil?
  528. begin
  529. uri = uri.to_str
  530. rescue NoMethodError, TypeError
  531. raise TypeError, "Can't convert #{uri.class} into String."
  532. end if !uri.is_a? String
  533. if ![String, ::Addressable::URI].include?(return_type)
  534. raise TypeError,
  535. "Expected Class (String or Addressable::URI), " +
  536. "got #{return_type.inspect}"
  537. end
  538. uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
  539. encoded_uri = Addressable::URI.new(
  540. :scheme => self.encode_component(uri_object.scheme,
  541. Addressable::URI::CharacterClasses::SCHEME),
  542. :authority => self.encode_component(uri_object.authority,
  543. Addressable::URI::CharacterClasses::AUTHORITY),
  544. :path => self.encode_component(uri_object.path,
  545. Addressable::URI::CharacterClasses::PATH),
  546. :query => self.encode_component(uri_object.query,
  547. Addressable::URI::CharacterClasses::QUERY),
  548. :fragment => self.encode_component(uri_object.fragment,
  549. Addressable::URI::CharacterClasses::FRAGMENT)
  550. )
  551. if return_type == String
  552. return encoded_uri.to_s
  553. elsif return_type == ::Addressable::URI
  554. return encoded_uri
  555. end
  556. end
  557. class << self
  558. alias_method :escape, :encode
  559. end
  560. ##
  561. # Normalizes the encoding of a URI. Characters within a hostname are
  562. # not percent encoded to allow for internationalized domain names.
  563. #
  564. # @param [String, Addressable::URI, #to_str] uri
  565. # The URI to encode.
  566. #
  567. # @param [Class] return_type
  568. # The type of object to return.
  569. # This value may only be set to <code>String</code> or
  570. # <code>Addressable::URI</code>. All other values are invalid. Defaults
  571. # to <code>String</code>.
  572. #
  573. # @return [String, Addressable::URI]
  574. # The encoded URI.
  575. # The return type is determined by the <code>return_type</code>
  576. # parameter.
  577. def self.normalized_encode(uri, return_type=String)
  578. begin
  579. uri = uri.to_str
  580. rescue NoMethodError, TypeError
  581. raise TypeError, "Can't convert #{uri.class} into String."
  582. end if !uri.is_a? String
  583. if ![String, ::Addressable::URI].include?(return_type)
  584. raise TypeError,
  585. "Expected Class (String or Addressable::URI), " +
  586. "got #{return_type.inspect}"
  587. end
  588. uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
  589. components = {
  590. :scheme => self.unencode_component(uri_object.scheme),
  591. :user => self.unencode_component(uri_object.user),
  592. :password => self.unencode_component(uri_object.password),
  593. :host => self.unencode_component(uri_object.host),
  594. :port => (uri_object.port.nil? ? nil : uri_object.port.to_s),
  595. :path => self.unencode_component(uri_object.path),
  596. :query => self.unencode_component(uri_object.query),
  597. :fragment => self.unencode_component(uri_object.fragment)
  598. }
  599. components.each do |key, value|
  600. if value != nil
  601. begin
  602. components[key] =
  603. Addressable::IDNA.unicode_normalize_kc(value.to_str)
  604. rescue ArgumentError
  605. # Likely a malformed UTF-8 character, skip unicode normalization
  606. components[key] = value.to_str
  607. end
  608. end
  609. end
  610. encoded_uri = Addressable::URI.new(
  611. :scheme => self.encode_component(components[:scheme],
  612. Addressable::URI::CharacterClasses::SCHEME),
  613. :user => self.encode_component(components[:user],
  614. Addressable::URI::CharacterClasses::UNRESERVED),
  615. :password => self.encode_component(components[:password],
  616. Addressable::URI::CharacterClasses::UNRESERVED),
  617. :host => components[:host],
  618. :port => components[:port],
  619. :path => self.encode_component(components[:path],
  620. Addressable::URI::CharacterClasses::PATH),
  621. :query => self.encode_component(components[:query],
  622. Addressable::URI::CharacterClasses::QUERY),
  623. :fragment => self.encode_component(components[:fragment],
  624. Addressable::URI::CharacterClasses::FRAGMENT)
  625. )
  626. if return_type == String
  627. return encoded_uri.to_s
  628. elsif return_type == ::Addressable::URI
  629. return encoded_uri
  630. end
  631. end
  632. ##
  633. # Encodes a set of key/value pairs according to the rules for the
  634. # <code>application/x-www-form-urlencoded</code> MIME type.
  635. #
  636. # @param [#to_hash, #to_ary] form_values
  637. # The form values to encode.
  638. #
  639. # @param [TrueClass, FalseClass] sort
  640. # Sort the key/value pairs prior to encoding.
  641. # Defaults to <code>false</code>.
  642. #
  643. # @return [String]
  644. # The encoded value.
  645. def self.form_encode(form_values, sort=false)
  646. if form_values.respond_to?(:to_hash)
  647. form_values = form_values.to_hash.to_a
  648. elsif form_values.respond_to?(:to_ary)
  649. form_values = form_values.to_ary
  650. else
  651. raise TypeError, "Can't convert #{form_values.class} into Array."
  652. end
  653. form_values = form_values.inject([]) do |accu, (key, value)|
  654. if value.kind_of?(Array)
  655. value.each do |v|
  656. accu << [key.to_s, v.to_s]
  657. end
  658. else
  659. accu << [key.to_s, value.to_s]
  660. end
  661. accu
  662. end
  663. if sort
  664. # Useful for OAuth and optimizing caching systems
  665. form_values = form_values.sort
  666. end
  667. escaped_form_values = form_values.map do |(key, value)|
  668. # Line breaks are CRLF pairs
  669. [
  670. self.encode_component(
  671. key.gsub(/(\r\n|\n|\r)/, "\r\n"),
  672. CharacterClasses::UNRESERVED
  673. ).gsub("%20", "+"),
  674. self.encode_component(
  675. value.gsub(/(\r\n|\n|\r)/, "\r\n"),
  676. CharacterClasses::UNRESERVED
  677. ).gsub("%20", "+")
  678. ]
  679. end
  680. return (escaped_form_values.map do |(key, value)|
  681. "#{key}=#{value}"
  682. end).join("&")
  683. end
  684. ##
  685. # Decodes a <code>String</code> according to the rules for the
  686. # <code>application/x-www-form-urlencoded</code> MIME type.
  687. #
  688. # @param [String, #to_str] encoded_value
  689. # The form values to decode.
  690. #
  691. # @return [Array]
  692. # The decoded values.
  693. # This is not a <code>Hash</code> because of the possibility for
  694. # duplicate keys.
  695. def self.form_unencode(encoded_value)
  696. if !encoded_value.respond_to?(:to_str)
  697. raise TypeError, "Can't convert #{encoded_value.class} into String."
  698. end
  699. encoded_value = encoded_value.to_str
  700. split_values = encoded_value.split("&").map do |pair|
  701. pair.split("=", 2)
  702. end
  703. return split_values.map do |(key, value)|
  704. [
  705. key ? self.unencode_component(
  706. key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
  707. value ? (self.unencode_component(
  708. value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
  709. ]
  710. end
  711. end
  712. ##
  713. # Creates a new uri object from component parts.
  714. #
  715. # @option [String, #to_str] scheme The scheme component.
  716. # @option [String, #to_str] user The user component.
  717. # @option [String, #to_str] password The password component.
  718. # @option [String, #to_str] userinfo
  719. # The userinfo component. If this is supplied, the user and password
  720. # components must be omitted.
  721. # @option [String, #to_str] host The host component.
  722. # @option [String, #to_str] port The port component.
  723. # @option [String, #to_str] authority
  724. # The authority component. If this is supplied, the user, password,
  725. # userinfo, host, and port components must be omitted.
  726. # @option [String, #to_str] path The path component.
  727. # @option [String, #to_str] query The query component.
  728. # @option [String, #to_str] fragment The fragment component.
  729. #
  730. # @return [Addressable::URI] The constructed URI object.
  731. def initialize(options={})
  732. if options.has_key?(:authority)
  733. if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
  734. raise ArgumentError,
  735. "Cannot specify both an authority and any of the components " +
  736. "within the authority."
  737. end
  738. end
  739. if options.has_key?(:userinfo)
  740. if (options.keys & [:user, :password]).any?
  741. raise ArgumentError,
  742. "Cannot specify both a userinfo and either the user or password."
  743. end
  744. end
  745. self.defer_validation do
  746. # Bunch of crazy logic required because of the composite components
  747. # like userinfo and authority.
  748. self.scheme = options[:scheme] if options[:scheme]
  749. self.user = options[:user] if options[:user]
  750. self.password = options[:password] if options[:password]
  751. self.userinfo = options[:userinfo] if options[:userinfo]
  752. self.host = options[:host] if options[:host]
  753. self.port = options[:port] if options[:port]
  754. self.authority = options[:authority] if options[:authority]
  755. self.path = options[:path] if options[:path]
  756. self.query = options[:query] if options[:query]
  757. self.query_values = options[:query_values] if options[:query_values]
  758. self.fragment = options[:fragment] if options[:fragment]
  759. end
  760. end
  761. ##
  762. # Freeze URI, initializing instance variables.
  763. #
  764. # @return [Addressable::URI] The frozen URI object.
  765. def freeze
  766. self.normalized_scheme
  767. self.normalized_user
  768. self.normalized_password
  769. self.normalized_userinfo
  770. self.normalized_host
  771. self.normalized_port
  772. self.normalized_authority
  773. self.normalized_site
  774. self.normalized_path
  775. self.normalized_query
  776. self.normalized_fragment
  777. self.hash
  778. super
  779. end
  780. ##
  781. # The scheme component for this URI.
  782. #
  783. # @return [String] The scheme component.
  784. def scheme
  785. return instance_variable_defined?(:@scheme) ? @scheme : nil
  786. end
  787. ##
  788. # The scheme component for this URI, normalized.
  789. #
  790. # @return [String] The scheme component, normalized.
  791. def normalized_scheme
  792. self.scheme && @normalized_scheme ||= (begin
  793. if self.scheme =~ /^\s*ssh\+svn\s*$/i
  794. "svn+ssh"
  795. else
  796. Addressable::URI.normalize_component(
  797. self.scheme.strip.downcase,
  798. Addressable::URI::CharacterClasses::SCHEME
  799. )
  800. end
  801. end)
  802. end
  803. ##
  804. # Sets the scheme component for this URI.
  805. #
  806. # @param [String, #to_str] new_scheme The new scheme component.
  807. def scheme=(new_scheme)
  808. if new_scheme && !new_scheme.respond_to?(:to_str)
  809. raise TypeError, "Can't convert #{new_scheme.class} into String."
  810. elsif new_scheme
  811. new_scheme = new_scheme.to_str
  812. end
  813. if new_scheme && new_scheme !~ /[a-z][a-z0-9\.\+\-]*/i
  814. raise InvalidURIError, "Invalid scheme format."
  815. end
  816. @scheme = new_scheme
  817. @scheme = nil if @scheme.to_s.strip.empty?
  818. # Reset dependant values
  819. @normalized_scheme = nil
  820. @uri_string = nil
  821. @hash = nil
  822. # Ensure we haven't created an invalid URI
  823. validate()
  824. end
  825. ##
  826. # The user component for this URI.
  827. #
  828. # @return [String] The user component.
  829. def user
  830. return instance_variable_defined?(:@user) ? @user : nil
  831. end
  832. ##
  833. # The user component for this URI, normalized.
  834. #
  835. # @return [String] The user component, normalized.
  836. def normalized_user
  837. self.user && @normalized_user ||= (begin
  838. if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
  839. (!self.password || self.password.strip.empty?)
  840. nil
  841. else
  842. Addressable::URI.normalize_component(
  843. self.user.strip,
  844. Addressable::URI::CharacterClasses::UNRESERVED
  845. )
  846. end
  847. end)
  848. end
  849. ##
  850. # Sets the user component for this URI.
  851. #
  852. # @param [String, #to_str] new_user The new user component.
  853. def user=(new_user)
  854. if new_user && !new_user.respond_to?(:to_str)
  855. raise TypeError, "Can't convert #{new_user.class} into String."
  856. end
  857. @user = new_user ? new_user.to_str : nil
  858. # You can't have a nil user with a non-nil password
  859. if password != nil
  860. @user = EMPTY_STR if @user.nil?
  861. end
  862. # Reset dependant values
  863. @userinfo = nil
  864. @normalized_userinfo = nil
  865. @authority = nil
  866. @normalized_user = nil
  867. @uri_string = nil
  868. @hash = nil
  869. # Ensure we haven't created an invalid URI
  870. validate()
  871. end
  872. ##
  873. # The password component for this URI.
  874. #
  875. # @return [String] The password component.
  876. def password
  877. return instance_variable_defined?(:@password) ? @password : nil
  878. end
  879. ##
  880. # The password component for this URI, normalized.
  881. #
  882. # @return [String] The password component, normalized.
  883. def normalized_password
  884. self.password && @normalized_password ||= (begin
  885. if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
  886. (!self.user || self.user.strip.empty?)
  887. nil
  888. else
  889. Addressable::URI.normalize_component(
  890. self.password.strip,
  891. Addressable::URI::CharacterClasses::UNRESERVED
  892. )
  893. end
  894. end)
  895. end
  896. ##
  897. # Sets the password component for this URI.
  898. #
  899. # @param [String, #to_str] new_password The new password component.
  900. def password=(new_password)
  901. if new_password && !new_password.respond_to?(:to_str)
  902. raise TypeError, "Can't convert #{new_password.class} into String."
  903. end
  904. @password = new_password ? new_password.to_str : nil
  905. # You can't have a nil user with a non-nil password
  906. @password ||= nil
  907. @user ||= nil
  908. if @password != nil
  909. @user = EMPTY_STR if @user.nil?
  910. end
  911. # Reset dependant values
  912. @userinfo = nil
  913. @normalized_userinfo = nil
  914. @authority = nil
  915. @normalized_password = nil
  916. @uri_string = nil
  917. @hash = nil
  918. # Ensure we haven't created an invalid URI
  919. validate()
  920. end
  921. ##
  922. # The userinfo component for this URI.
  923. # Combines the user and password components.
  924. #
  925. # @return [String] The userinfo component.
  926. def userinfo
  927. current_user = self.user
  928. current_password = self.password
  929. (current_user || current_password) && @userinfo ||= (begin
  930. if current_user && current_password
  931. "#{current_user}:#{current_password}"
  932. elsif current_user && !current_password
  933. "#{current_user}"
  934. end
  935. end)
  936. end
  937. ##
  938. # The userinfo component for this URI, normalized.
  939. #
  940. # @return [String] The userinfo component, normalized.
  941. def normalized_userinfo
  942. self.userinfo && @normalized_userinfo ||= (begin
  943. current_user = self.normalized_user
  944. current_password = self.normalized_password
  945. if !current_user && !current_password
  946. nil
  947. elsif current_user && current_password
  948. "#{current_user}:#{current_password}"
  949. elsif current_user && !current_password
  950. "#{current_user}"
  951. end
  952. end)
  953. end
  954. ##
  955. # Sets the userinfo component for this URI.
  956. #
  957. # @param [String, #to_str] new_userinfo The new userinfo component.
  958. def userinfo=(new_userinfo)
  959. if new_userinfo && !new_userinfo.respond_to?(:to_str)
  960. raise TypeError, "Can't convert #{new_userinfo.class} into String."
  961. end
  962. new_user, new_password = if new_userinfo
  963. [
  964. new_userinfo.to_str.strip[/^(.*):/, 1],
  965. new_userinfo.to_str.strip[/:(.*)$/, 1]
  966. ]
  967. else
  968. [nil, nil]
  969. end
  970. # Password assigned first to ensure validity in case of nil
  971. self.password = new_password
  972. self.user = new_user
  973. # Reset dependant values
  974. @authority = nil
  975. @uri_string = nil
  976. @hash = nil
  977. # Ensure we haven't created an invalid URI
  978. validate()
  979. end
  980. ##
  981. # The host component for this URI.
  982. #
  983. # @return [String] The host component.
  984. def host
  985. return instance_variable_defined?(:@host) ? @host : nil
  986. end
  987. ##
  988. # The host component for this URI, normalized.
  989. #
  990. # @return [String] The host component, normalized.
  991. def normalized_host
  992. self.host && @normalized_host ||= (begin
  993. if !self.host.strip.empty?
  994. result = ::Addressable::IDNA.to_ascii(
  995. URI.unencode_component(self.host.strip.downcase)
  996. )
  997. if result =~ /[^\.]\.$/
  998. # Single trailing dots are unnecessary.
  999. result = result[0...-1]
  1000. end
  1001. result
  1002. else
  1003. EMPTY_STR
  1004. end
  1005. end)
  1006. end
  1007. ##
  1008. # Sets the host component for this URI.
  1009. #
  1010. # @param [String, #to_str] new_host The new host component.
  1011. def host=(new_host)
  1012. if new_host && !new_host.respond_to?(:to_str)
  1013. raise TypeError, "Can't convert #{new_host.class} into String."
  1014. end
  1015. @host = new_host ? new_host.to_str : nil
  1016. unreserved = CharacterClasses::UNRESERVED
  1017. sub_delims = CharacterClasses::SUB_DELIMS
  1018. if @host != nil && (@host =~ /[<>{}\/\?\#\@]/ ||
  1019. (@host[/^\[(.*)\]$/, 1] != nil && @host[/^\[(.*)\]$/, 1] !~
  1020. Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
  1021. raise InvalidURIError, "Invalid character in host: '#{@host.to_s}'"
  1022. end
  1023. # Reset dependant values
  1024. @authority = nil
  1025. @normalized_host = nil
  1026. @uri_string = nil
  1027. @hash = nil
  1028. # Ensure we haven't created an invalid URI
  1029. validate()
  1030. end
  1031. ##
  1032. # This method is same as URI::Generic#host except
  1033. # brackets for IPv6 (and 'IPvFuture') addresses are removed.
  1034. #
  1035. # @see Addressable::URI#host
  1036. #
  1037. # @return [String] The hostname for this URI.
  1038. def hostname
  1039. v = self.host
  1040. /\A\[(.*)\]\z/ =~ v ? $1 : v
  1041. end
  1042. ##
  1043. # This method is same as URI::Generic#host= except
  1044. # the argument can be a bare IPv6 address (or 'IPvFuture').
  1045. #
  1046. # @see Addressable::URI#host=
  1047. #
  1048. # @param [String, #to_str] new_hostname The new hostname for this URI.
  1049. def hostname=(new_hostname)
  1050. if new_hostname && !new_hostname.respond_to?(:to_str)
  1051. raise TypeError, "Can't convert #{new_hostname.class} into String."
  1052. end
  1053. v = new_hostname ? new_hostname.to_str : nil
  1054. v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
  1055. self.host = v
  1056. end
  1057. ##
  1058. # The authority component for this URI.
  1059. # Combines the user, password, host, and port components.
  1060. #
  1061. # @return [String] The authority component.
  1062. def authority
  1063. self.host && @authority ||= (begin
  1064. authority = ""
  1065. if self.userinfo != nil
  1066. authority << "#{self.userinfo}@"
  1067. end
  1068. authority << self.host
  1069. if self.port != nil
  1070. authority << ":#{self.port}"
  1071. end
  1072. authority
  1073. end)
  1074. end
  1075. ##
  1076. # The authority component for this URI, normalized.
  1077. #
  1078. # @return [String] The authority component, normalized.
  1079. def normalized_authority
  1080. self.authority && @normalized_authority ||= (begin
  1081. authority = ""
  1082. if self.normalized_userinfo != nil
  1083. authority << "#{self.normalized_userinfo}@"
  1084. end
  1085. authority << self.normalized_host
  1086. if self.normalized_port != nil
  1087. authority << ":#{self.normalized_port}"
  1088. end
  1089. authority
  1090. end)
  1091. end
  1092. ##
  1093. # Sets the authority component for this URI.
  1094. #
  1095. # @param [String, #to_str] new_authority The new authority component.
  1096. def authority=(new_authority)
  1097. if new_authority
  1098. if !new_authority.respond_to?(:to_str)
  1099. raise TypeError, "Can't convert #{new_authority.class} into String."
  1100. end
  1101. new_authority = new_authority.to_str
  1102. new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
  1103. if new_userinfo
  1104. new_user = new_userinfo.strip[/^([^:]*):?/, 1]
  1105. new_password = new_userinfo.strip[/:(.*)$/, 1]
  1106. end
  1107. new_host = new_authority.gsub(
  1108. /^([^\[\]]*)@/, EMPTY_STR
  1109. ).gsub(
  1110. /:([^:@\[\]]*?)$/, EMPTY_STR
  1111. )
  1112. new_port =
  1113. new_authority[/:([^:@\[\]]*?)$/, 1]
  1114. end
  1115. # Password assigned first to ensure validity in case of nil
  1116. self.password = defined?(new_password) ? new_password : nil
  1117. self.user = defined?(new_user) ? new_user : nil
  1118. self.host = defined?(new_host) ? new_host : nil
  1119. self.port = defined?(new_port) ? new_port : nil
  1120. # Reset dependant values
  1121. @userinfo = nil
  1122. @normalized_userinfo = nil
  1123. @uri_string = nil
  1124. @hash = nil
  1125. # Ensure we haven't created an invalid URI
  1126. validate()
  1127. end
  1128. ##
  1129. # The origin for this URI, serialized to ASCII, as per
  1130. # RFC 6454, section 6.2.
  1131. #
  1132. # @return [String] The serialized origin.
  1133. def origin
  1134. return (if self.scheme && self.authority
  1135. if self.normalized_port
  1136. (
  1137. "#{self.normalized_scheme}://#{self.normalized_host}" +
  1138. ":#{self.normalized_port}"
  1139. )
  1140. else
  1141. "#{self.normalized_scheme}://#{self.normalized_host}"
  1142. end
  1143. else
  1144. "null"
  1145. end)
  1146. end
  1147. # Returns an array of known ip-based schemes. These schemes typically
  1148. # use a similar URI form:
  1149. # <code>//<user>:<password>@<host>:<port>/<url-path></code>
  1150. def self.ip_based_schemes
  1151. return self.port_mapping.keys
  1152. end
  1153. # Returns a hash of common IP-based schemes and their default port
  1154. # numbers. Adding new schemes to this hash, as necessary, will allow
  1155. # for better URI normalization.
  1156. def self.port_mapping
  1157. PORT_MAPPING
  1158. end
  1159. ##
  1160. # The port component for this URI.
  1161. # This is the port number actually given in the URI. This does not
  1162. # infer port numbers from default values.
  1163. #
  1164. # @return [Integer] The port component.
  1165. def port
  1166. return instance_variable_defined?(:@port) ? @port : nil
  1167. end
  1168. ##
  1169. # The port component for this URI, normalized.
  1170. #
  1171. # @return [Integer] The port component, normalized.
  1172. def normalized_port
  1173. if URI.port_mapping[self.normalized_scheme] == self.port
  1174. nil
  1175. else
  1176. self.port
  1177. end
  1178. end
  1179. ##
  1180. # Sets the port component for this URI.
  1181. #
  1182. # @param [String, Integer, #to_s] new_port The new port component.
  1183. def port=(new_port)
  1184. if new_port != nil && new_port.respond_to?(:to_str)
  1185. new_port = Addressable::URI.unencode_component(new_port.to_str)
  1186. end
  1187. if new_port != nil && !(new_port.to_s =~ /^\d+$/)
  1188. raise InvalidURIError,
  1189. "Invalid port number: #{new_port.inspect}"
  1190. end
  1191. @port = new_port.to_s.to_i
  1192. @port = nil if @port == 0
  1193. # Reset dependant values
  1194. @authority = nil
  1195. @normalized_port = nil
  1196. @uri_string = nil
  1197. @hash = nil
  1198. # Ensure we haven't created an invalid URI
  1199. validate()
  1200. end
  1201. ##
  1202. # The inferred port component for this URI.
  1203. # This method will normalize to the default port for the URI's scheme if
  1204. # the port isn't explicitly specified in the URI.
  1205. #
  1206. # @return [Integer] The inferred port component.
  1207. def inferred_port
  1208. if self.port.to_i == 0
  1209. self.default_port
  1210. else
  1211. self.port.to_i
  1212. end
  1213. end
  1214. ##
  1215. # The default port for this URI's scheme.
  1216. # This method will always returns the default port for the URI's scheme
  1217. # regardless of the presence of an explicit port in the URI.
  1218. #
  1219. # @return [Integer] The default port.
  1220. def default_port
  1221. URI.port_mapping[self.scheme.strip.downcase] if self.scheme
  1222. end
  1223. ##
  1224. # The combination of components that represent a site.
  1225. # Combines the scheme, user, password, host, and port components.
  1226. # Primarily useful for HTTP and HTTPS.
  1227. #
  1228. # For example, <code>"http://example.com/path?query"</code> would have a
  1229. # <code>site</code> value of <code>"http://example.com"</code>.
  1230. #
  1231. # @return [String] The components that identify a site.
  1232. def site
  1233. (self.scheme || self.authority) && @site ||= (begin
  1234. site_string = ""
  1235. site_string << "#{self.scheme}:" if self.scheme != nil
  1236. site_string << "//#{self.authority}" if self.authority != nil
  1237. site_string
  1238. end)
  1239. end
  1240. ##
  1241. # The normalized combination of components that represent a site.
  1242. # Combines the scheme, user, password, host, and port components.
  1243. # Primarily useful for HTTP and HTTPS.
  1244. #
  1245. # For example, <code>"http://example.com/path?query"</code> would have a
  1246. # <code>site</code> value of <code>"http://example.com"</code>.
  1247. #
  1248. # @return [String] The normalized components that identify a site.
  1249. def normalized_site
  1250. self.site && @normalized_site ||= (begin
  1251. site_string = ""
  1252. if self.normalized_scheme != nil
  1253. site_string << "#{self.normalized_scheme}:"
  1254. end
  1255. if self.normalized_authority != nil
  1256. site_string << "//#{self.normalized_authority}"
  1257. end
  1258. site_string
  1259. end)
  1260. end
  1261. ##
  1262. # Sets the site value for this URI.
  1263. #
  1264. # @param [String, #to_str] new_site The new site value.
  1265. def site=(new_site)
  1266. if new_site
  1267. if !new_site.respond_to?(:to_str)
  1268. raise TypeError, "Can't convert #{new_site.class} into String."
  1269. end
  1270. new_site = new_site.to_str
  1271. # These two regular expressions derived from the primary parsing
  1272. # expression
  1273. self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
  1274. self.authority = new_site[
  1275. /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
  1276. ]
  1277. else
  1278. self.scheme = nil
  1279. self.authority = nil
  1280. end
  1281. end
  1282. ##
  1283. # The path component for this URI.
  1284. #
  1285. # @return [String] The path component.
  1286. def path
  1287. return instance_variable_defined?(:@path) ? @path : EMPTY_STR
  1288. end
  1289. NORMPATH = /^(?!\/)[^\/:]*:.*$/
  1290. ##
  1291. # The path component for this URI, normalized.
  1292. #
  1293. # @return [String] The path component, normalized.
  1294. def normalized_path
  1295. @normalized_path ||= (begin
  1296. path = self.path.to_s
  1297. if self.scheme == nil && path =~ NORMPATH
  1298. # Relative paths with colons in the first segment are ambiguous.
  1299. path = path.sub(":", "%2F")
  1300. end
  1301. # String#split(delimeter, -1) uses the more strict splitting behavior
  1302. # found by default in Python.
  1303. result = (path.strip.split(SLASH, -1).map do |segment|
  1304. Addressable::URI.normalize_component(
  1305. segment,
  1306. Addressable::URI::CharacterClasses::PCHAR
  1307. )
  1308. end).join(SLASH)
  1309. result = URI.normalize_path(result)
  1310. if result.empty? &&
  1311. ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
  1312. result = SLASH
  1313. end
  1314. result
  1315. end)
  1316. end
  1317. ##
  1318. # Sets the path component for this URI.
  1319. #
  1320. # @param [String, #to_str] new_path The new path component.
  1321. def path=(new_path)
  1322. if new_path && !new_path.respond_to?(:to_str)
  1323. raise TypeError, "Can't convert #{new_path.class} into String."
  1324. end
  1325. @path = (new_path || EMPTY_STR).to_str
  1326. if !@path.empty? && @path[0..0] != SLASH && host != nil
  1327. @path = "/#{@path}"
  1328. end
  1329. # Reset dependant values
  1330. @normalized_path = nil
  1331. @uri_string = nil
  1332. @hash = nil
  1333. end
  1334. ##
  1335. # The basename, if any, of the file in the path component.
  1336. #
  1337. # @return [String] The path's basename.
  1338. def basename
  1339. # Path cannot be nil
  1340. return File.basename(self.path).gsub(/;[^\/]*$/, EMPTY_STR)
  1341. end
  1342. ##
  1343. # The extname, if any, of the file in the path component.
  1344. # Empty string if there is no extension.
  1345. #
  1346. # @return [String] The path's extname.
  1347. def extname
  1348. return nil unless self.path
  1349. return File.extname(self.basename)
  1350. end
  1351. ##
  1352. # The query component for this URI.
  1353. #
  1354. # @return [String] The query component.
  1355. def query
  1356. return instance_variable_defined?(:@query) ? @query : nil
  1357. end
  1358. ##
  1359. # The query component for this URI, normalized.
  1360. #
  1361. # @return [String] The query component, normalized.
  1362. def normalized_query(*flags)
  1363. modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
  1364. # Make sure possible key-value pair delimiters are escaped.
  1365. modified_query_class.sub!("\\&", "").sub!("\\;", "")
  1366. pairs = (self.query || "").split("&", -1)
  1367. pairs.sort! if flags.include?(:sorted)
  1368. component = (pairs.map do |pair|
  1369. Addressable::URI.normalize_component(pair, modified_query_class, "+")
  1370. end).join("&")
  1371. component == "" ? nil : component
  1372. end
  1373. ##
  1374. # Sets the query component for this URI.
  1375. #
  1376. # @param [String, #to_str] new_query The new query component.
  1377. def query=(new_query)
  1378. if new_query && !new_query.respond_to?(:to_str)
  1379. raise TypeError, "Can't convert #{new_query.class} into String."
  1380. end
  1381. @query = new_query ? new_query.to_str : nil
  1382. # Reset dependant values
  1383. @normalized_query = nil
  1384. @uri_string = nil
  1385. @hash = nil
  1386. end
  1387. ##
  1388. # Converts the query component to a Hash value.
  1389. #
  1390. # @param [Class] return_type The return type desired. Value must be either
  1391. # `Hash` or `Array`.
  1392. #
  1393. # @return [Hash, Array] The query string parsed as a Hash or Array object.
  1394. #
  1395. # @example
  1396. # Addressable::URI.parse("?one=1&two=2&three=3").query_values
  1397. # #=> {"one" => "1", "two" => "2", "three" => "3"}
  1398. # Addressable::URI.parse("?one=two&one=three").query_values(Array)
  1399. # #=> [["one", "two"], ["one", "three"]]
  1400. # A

Large files files are truncated, but you can click here to view the full file