fields-addressable 2.2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2291 @@
1
+ # encoding:utf-8
2
+ #--
3
+ # Addressable, Copyright (c) 2006-2010 Bob Aman
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ #++
24
+
25
+ require "addressable/version"
26
+ require "addressable/idna"
27
+
28
+ module Addressable
29
+ ##
30
+ # This is an implementation of a URI parser based on
31
+ # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
32
+ # <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
33
+ class URI
34
+ ##
35
+ # Raised if something other than a uri is supplied.
36
+ class InvalidURIError < StandardError
37
+ end
38
+
39
+ ##
40
+ # Container for the character classes specified in
41
+ # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
42
+ module CharacterClasses
43
+ ALPHA = "a-zA-Z"
44
+ DIGIT = "0-9"
45
+ GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
46
+ SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
47
+ RESERVED = GEN_DELIMS + SUB_DELIMS
48
+ UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
49
+ PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
50
+ SCHEME = ALPHA + DIGIT + "\\-\\+\\."
51
+ AUTHORITY = PCHAR
52
+ PATH = PCHAR + "\\/"
53
+ QUERY = PCHAR + "\\/\\?"
54
+ FRAGMENT = PCHAR + "\\/\\?"
55
+ end
56
+
57
+ ##
58
+ # Returns a URI object based on the parsed string.
59
+ #
60
+ # @param [String, Addressable::URI, #to_str] uri
61
+ # The URI string to parse.
62
+ # No parsing is performed if the object is already an
63
+ # <code>Addressable::URI</code>.
64
+ #
65
+ # @return [Addressable::URI] The parsed URI.
66
+ def self.parse(uri)
67
+ # If we were given nil, return nil.
68
+ return nil unless uri
69
+ # If a URI object is passed, just return itself.
70
+ return uri if uri.kind_of?(self)
71
+
72
+ # If a URI object of the Ruby standard library variety is passed,
73
+ # convert it to a string, then parse the string.
74
+ # We do the check this way because we don't want to accidentally
75
+ # cause a missing constant exception to be thrown.
76
+ if uri.class.name =~ /^URI\b/
77
+ uri = uri.to_s
78
+ end
79
+
80
+ if !uri.respond_to?(:to_str)
81
+ raise TypeError, "Can't convert #{uri.class} into String."
82
+ end
83
+ # Otherwise, convert to a String
84
+ uri = uri.to_str
85
+
86
+ # This Regexp supplied as an example in RFC 3986, and it works great.
87
+ uri_regex =
88
+ /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?((?:(?:\#\!)|(?:\/\#\/)|[^?#])*)(\?([^#]*))?(#(.*))?$/
89
+ scan = uri.scan(uri_regex)
90
+ fragments = scan[0]
91
+ scheme = fragments[1]
92
+ authority = fragments[3]
93
+ path = fragments[4]
94
+ query = fragments[6]
95
+ fragment = fragments[8]
96
+ user = nil
97
+ password = nil
98
+ host = nil
99
+ port = nil
100
+ if authority != nil
101
+ # The Regexp above doesn't split apart the authority.
102
+ userinfo = authority[/^([^\[\]]*)@/, 1]
103
+ if userinfo != nil
104
+ user = userinfo.strip[/^([^:]*):?/, 1]
105
+ password = userinfo.strip[/:(.*)$/, 1]
106
+ end
107
+ host = authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
108
+ port = authority[/:([^:@\[\]]*?)$/, 1]
109
+ end
110
+ if port == ""
111
+ port = nil
112
+ end
113
+
114
+ return Addressable::URI.new(
115
+ :scheme => scheme,
116
+ :user => user,
117
+ :password => password,
118
+ :host => host,
119
+ :port => port,
120
+ :path => path,
121
+ :query => query,
122
+ :fragment => fragment
123
+ )
124
+ end
125
+
126
+ ##
127
+ # Converts an input to a URI. The input does not have to be a valid
128
+ # URI — the method will use heuristics to guess what URI was intended.
129
+ # This is not standards-compliant, merely user-friendly.
130
+ #
131
+ # @param [String, Addressable::URI, #to_str] uri
132
+ # The URI string to parse.
133
+ # No parsing is performed if the object is already an
134
+ # <code>Addressable::URI</code>.
135
+ # @param [Hash] hints
136
+ # A <code>Hash</code> of hints to the heuristic parser.
137
+ # Defaults to <code>{:scheme => "http"}</code>.
138
+ #
139
+ # @return [Addressable::URI] The parsed URI.
140
+ def self.heuristic_parse(uri, hints={})
141
+ # If we were given nil, return nil.
142
+ return nil unless uri
143
+ # If a URI object is passed, just return itself.
144
+ return uri if uri.kind_of?(self)
145
+ if !uri.respond_to?(:to_str)
146
+ raise TypeError, "Can't convert #{uri.class} into String."
147
+ end
148
+ # Otherwise, convert to a String
149
+ uri = uri.to_str.dup
150
+ hints = {
151
+ :scheme => "http"
152
+ }.merge(hints)
153
+ case uri
154
+ when /^http:\/+/
155
+ uri.gsub!(/^http:\/+/, "http://")
156
+ when /^feed:\/+http:\/+/
157
+ uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
158
+ when /^feed:\/+/
159
+ uri.gsub!(/^feed:\/+/, "feed://")
160
+ when /^file:\/+/
161
+ uri.gsub!(/^file:\/+/, "file:///")
162
+ end
163
+ parsed = self.parse(uri)
164
+ if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
165
+ parsed = self.parse(hints[:scheme] + "://" + uri)
166
+ end
167
+ if parsed.path.include?(".")
168
+ new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
169
+ if new_host
170
+ parsed.defer_validation do
171
+ new_path = parsed.path.gsub(
172
+ Regexp.new("^" + Regexp.escape(new_host)), "")
173
+ parsed.host = new_host
174
+ parsed.path = new_path
175
+ parsed.scheme = hints[:scheme] unless parsed.scheme
176
+ end
177
+ end
178
+ end
179
+ return parsed
180
+ end
181
+
182
+ ##
183
+ # Converts a path to a file scheme URI. If the path supplied is
184
+ # relative, it will be returned as a relative URI. If the path supplied
185
+ # is actually a non-file URI, it will parse the URI as if it had been
186
+ # parsed with <code>Addressable::URI.parse</code>. Handles all of the
187
+ # various Microsoft-specific formats for specifying paths.
188
+ #
189
+ # @param [String, Addressable::URI, #to_str] path
190
+ # Typically a <code>String</code> path to a file or directory, but
191
+ # will return a sensible return value if an absolute URI is supplied
192
+ # instead.
193
+ #
194
+ # @return [Addressable::URI]
195
+ # The parsed file scheme URI or the original URI if some other URI
196
+ # scheme was provided.
197
+ #
198
+ # @example
199
+ # base = Addressable::URI.convert_path("/absolute/path/")
200
+ # uri = Addressable::URI.convert_path("relative/path")
201
+ # (base + uri).to_s
202
+ # #=> "file:///absolute/path/relative/path"
203
+ #
204
+ # Addressable::URI.convert_path(
205
+ # "c:\\windows\\My Documents 100%20\\foo.txt"
206
+ # ).to_s
207
+ # #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
208
+ #
209
+ # Addressable::URI.convert_path("http://example.com/").to_s
210
+ # #=> "http://example.com/"
211
+ def self.convert_path(path)
212
+ # If we were given nil, return nil.
213
+ return nil unless path
214
+ # If a URI object is passed, just return itself.
215
+ return path if path.kind_of?(self)
216
+ if !path.respond_to?(:to_str)
217
+ raise TypeError, "Can't convert #{path.class} into String."
218
+ end
219
+ # Otherwise, convert to a String
220
+ path = path.to_str.strip
221
+
222
+ path.gsub!(/^file:\/?\/?/, "") if path =~ /^file:\/?\/?/
223
+ path = "/" + path if path =~ /^([a-zA-Z])[\|:]/
224
+ uri = self.parse(path)
225
+
226
+ if uri.scheme == nil
227
+ # Adjust windows-style uris
228
+ uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
229
+ "/#{$1.downcase}:/"
230
+ end
231
+ uri.path.gsub!(/\\/, "/")
232
+ if File.exists?(uri.path) &&
233
+ File.stat(uri.path).directory?
234
+ uri.path.gsub!(/\/$/, "")
235
+ uri.path = uri.path + '/'
236
+ end
237
+
238
+ # If the path is absolute, set the scheme and host.
239
+ if uri.path =~ /^\//
240
+ uri.scheme = "file"
241
+ uri.host = ""
242
+ end
243
+ uri.normalize!
244
+ end
245
+
246
+ return uri
247
+ end
248
+
249
+ ##
250
+ # Joins several URIs together.
251
+ #
252
+ # @param [String, Addressable::URI, #to_str] *uris
253
+ # The URIs to join.
254
+ #
255
+ # @return [Addressable::URI] The joined URI.
256
+ #
257
+ # @example
258
+ # base = "http://example.com/"
259
+ # uri = Addressable::URI.parse("relative/path")
260
+ # Addressable::URI.join(base, uri)
261
+ # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
262
+ def self.join(*uris)
263
+ uri_objects = uris.collect do |uri|
264
+ if !uri.respond_to?(:to_str)
265
+ raise TypeError, "Can't convert #{uri.class} into String."
266
+ end
267
+ uri.kind_of?(self) ? uri : self.parse(uri.to_str)
268
+ end
269
+ result = uri_objects.shift.dup
270
+ for uri in uri_objects
271
+ result.join!(uri)
272
+ end
273
+ return result
274
+ end
275
+
276
+ ##
277
+ # Percent encodes a URI component.
278
+ #
279
+ # @param [String, #to_str] component The URI component to encode.
280
+ #
281
+ # @param [String, Regexp] character_class
282
+ # The characters which are not percent encoded. If a <code>String</code>
283
+ # is passed, the <code>String</code> must be formatted as a regular
284
+ # expression character class. (Do not include the surrounding square
285
+ # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
286
+ # everything but the letters 'b' through 'z' and the numbers '0' through
287
+ # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
288
+ # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
289
+ # useful <code>String</code> values may be found in the
290
+ # <code>Addressable::URI::CharacterClasses</code> module. The default
291
+ # value is the reserved plus unreserved character classes specified in
292
+ # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
293
+ #
294
+ # @return [String] The encoded component.
295
+ #
296
+ # @example
297
+ # Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
298
+ # => "simple%2Fex%61mple"
299
+ # Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
300
+ # => "simple%2Fex%61mple"
301
+ # Addressable::URI.encode_component(
302
+ # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
303
+ # )
304
+ # => "simple%2Fexample"
305
+ def self.encode_component(component, character_class=
306
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
307
+ return nil if component.nil?
308
+ if !component.respond_to?(:to_str)
309
+ raise TypeError, "Can't convert #{component.class} into String."
310
+ end
311
+ component = component.to_str
312
+ if ![String, Regexp].include?(character_class.class)
313
+ raise TypeError,
314
+ "Expected String or Regexp, got #{character_class.inspect}"
315
+ end
316
+ if character_class.kind_of?(String)
317
+ character_class = /[^#{character_class}]/
318
+ end
319
+ if component.respond_to?(:force_encoding)
320
+ # We can't perform regexps on invalid UTF sequences, but
321
+ # here we need to, so switch to ASCII.
322
+ component = component.dup
323
+ component.force_encoding(Encoding::ASCII_8BIT)
324
+ end
325
+ return component.gsub(character_class) do |sequence|
326
+ (sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join("")
327
+ end
328
+ end
329
+
330
+ class << self
331
+ alias_method :encode_component, :encode_component
332
+ end
333
+
334
+ ##
335
+ # Unencodes any percent encoded characters within a URI component.
336
+ # This method may be used for unencoding either components or full URIs,
337
+ # however, it is recommended to use the <code>unencode_component</code>
338
+ # alias when unencoding components.
339
+ #
340
+ # @param [String, Addressable::URI, #to_str] uri
341
+ # The URI or component to unencode.
342
+ #
343
+ # @param [Class] returning
344
+ # The type of object to return.
345
+ # This value may only be set to <code>String</code> or
346
+ # <code>Addressable::URI</code>. All other values are invalid. Defaults
347
+ # to <code>String</code>.
348
+ #
349
+ # @return [String, Addressable::URI]
350
+ # The unencoded component or URI.
351
+ # The return type is determined by the <code>returning</code> parameter.
352
+ def self.unencode(uri, returning=String)
353
+ return nil if uri.nil?
354
+ if !uri.respond_to?(:to_str)
355
+ raise TypeError, "Can't convert #{uri.class} into String."
356
+ end
357
+ if ![String, ::Addressable::URI].include?(returning)
358
+ raise TypeError,
359
+ "Expected Class (String or Addressable::URI), " +
360
+ "got #{returning.inspect}"
361
+ end
362
+ result = uri.to_str.gsub(/%[0-9a-f]{2}/i) do |sequence|
363
+ sequence[1..3].to_i(16).chr
364
+ end
365
+ result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
366
+ if returning == String
367
+ return result
368
+ elsif returning == ::Addressable::URI
369
+ return ::Addressable::URI.parse(result)
370
+ end
371
+ end
372
+
373
+ class << self
374
+ alias_method :unescape, :unencode
375
+ alias_method :unencode_component, :unencode
376
+ alias_method :unescape_component, :unencode
377
+ end
378
+
379
+
380
+ ##
381
+ # Normalizes the encoding of a URI component.
382
+ #
383
+ # @param [String, #to_str] component The URI component to encode.
384
+ #
385
+ # @param [String, Regexp] character_class
386
+ # The characters which are not percent encoded. If a <code>String</code>
387
+ # is passed, the <code>String</code> must be formatted as a regular
388
+ # expression character class. (Do not include the surrounding square
389
+ # brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
390
+ # everything but the letters 'b' through 'z' and the numbers '0' through
391
+ # '9' to be percent encoded. If a <code>Regexp</code> is passed, the
392
+ # value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
393
+ # useful <code>String</code> values may be found in the
394
+ # <code>Addressable::URI::CharacterClasses</code> module. The default
395
+ # value is the reserved plus unreserved character classes specified in
396
+ # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
397
+ #
398
+ # @return [String] The normalized component.
399
+ #
400
+ # @example
401
+ # Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
402
+ # => "simple%2Fex%61mple"
403
+ # Addressable::URI.normalize_component(
404
+ # "simpl%65/%65xampl%65", /[^b-zB-Z]/
405
+ # )
406
+ # => "simple%2Fex%61mple"
407
+ # Addressable::URI.normalize_component(
408
+ # "simpl%65/%65xampl%65",
409
+ # Addressable::URI::CharacterClasses::UNRESERVED
410
+ # )
411
+ # => "simple%2Fexample"
412
+ def self.normalize_component(component, character_class=
413
+ CharacterClasses::RESERVED + CharacterClasses::UNRESERVED)
414
+ return nil if component.nil?
415
+ if !component.respond_to?(:to_str)
416
+ raise TypeError, "Can't convert #{component.class} into String."
417
+ end
418
+ component = component.to_str
419
+ if ![String, Regexp].include?(character_class.class)
420
+ raise TypeError,
421
+ "Expected String or Regexp, got #{character_class.inspect}"
422
+ end
423
+ if character_class.kind_of?(String)
424
+ character_class = /[^#{character_class}]/
425
+ end
426
+ if component.respond_to?(:force_encoding)
427
+ # We can't perform regexps on invalid UTF sequences, but
428
+ # here we need to, so switch to ASCII.
429
+ component = component.dup
430
+ component.force_encoding(Encoding::ASCII_8BIT)
431
+ end
432
+ unencoded = self.unencode_component(component)
433
+ begin
434
+ encoded = self.encode_component(
435
+ Addressable::IDNA.unicode_normalize_kc(unencoded),
436
+ character_class
437
+ )
438
+ rescue ArgumentError
439
+ encoded = self.encode_component(unencoded)
440
+ end
441
+ return encoded
442
+ end
443
+
444
+ ##
445
+ # Percent encodes any special characters in the URI.
446
+ #
447
+ # @param [String, Addressable::URI, #to_str] uri
448
+ # The URI to encode.
449
+ #
450
+ # @param [Class] returning
451
+ # The type of object to return.
452
+ # This value may only be set to <code>String</code> or
453
+ # <code>Addressable::URI</code>. All other values are invalid. Defaults
454
+ # to <code>String</code>.
455
+ #
456
+ # @return [String, Addressable::URI]
457
+ # The encoded URI.
458
+ # The return type is determined by the <code>returning</code> parameter.
459
+ def self.encode(uri, returning=String)
460
+ return nil if uri.nil?
461
+ if !uri.respond_to?(:to_str)
462
+ raise TypeError, "Can't convert #{uri.class} into String."
463
+ end
464
+ if ![String, ::Addressable::URI].include?(returning)
465
+ raise TypeError,
466
+ "Expected Class (String or Addressable::URI), " +
467
+ "got #{returning.inspect}"
468
+ end
469
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
470
+ encoded_uri = Addressable::URI.new(
471
+ :scheme => self.encode_component(uri_object.scheme,
472
+ Addressable::URI::CharacterClasses::SCHEME),
473
+ :authority => self.encode_component(uri_object.authority,
474
+ Addressable::URI::CharacterClasses::AUTHORITY),
475
+ :path => self.encode_component(uri_object.path,
476
+ Addressable::URI::CharacterClasses::PATH),
477
+ :query => self.encode_component(uri_object.query,
478
+ Addressable::URI::CharacterClasses::QUERY),
479
+ :fragment => self.encode_component(uri_object.fragment,
480
+ Addressable::URI::CharacterClasses::FRAGMENT)
481
+ )
482
+ if returning == String
483
+ return encoded_uri.to_s
484
+ elsif returning == ::Addressable::URI
485
+ return encoded_uri
486
+ end
487
+ end
488
+
489
+ class << self
490
+ alias_method :escape, :encode
491
+ end
492
+
493
+ ##
494
+ # Normalizes the encoding of a URI. Characters within a hostname are
495
+ # not percent encoded to allow for internationalized domain names.
496
+ #
497
+ # @param [String, Addressable::URI, #to_str] uri
498
+ # The URI to encode.
499
+ #
500
+ # @param [Class] returning
501
+ # The type of object to return.
502
+ # This value may only be set to <code>String</code> or
503
+ # <code>Addressable::URI</code>. All other values are invalid. Defaults
504
+ # to <code>String</code>.
505
+ #
506
+ # @return [String, Addressable::URI]
507
+ # The encoded URI.
508
+ # The return type is determined by the <code>returning</code> parameter.
509
+ def self.normalized_encode(uri, returning=String)
510
+ if !uri.respond_to?(:to_str)
511
+ raise TypeError, "Can't convert #{uri.class} into String."
512
+ end
513
+ if ![String, ::Addressable::URI].include?(returning)
514
+ raise TypeError,
515
+ "Expected Class (String or Addressable::URI), " +
516
+ "got #{returning.inspect}"
517
+ end
518
+ uri_object = uri.kind_of?(self) ? uri : self.parse(uri.to_str)
519
+ components = {
520
+ :scheme => self.unencode_component(uri_object.scheme),
521
+ :user => self.unencode_component(uri_object.user),
522
+ :password => self.unencode_component(uri_object.password),
523
+ :host => self.unencode_component(uri_object.host),
524
+ :port => uri_object.port,
525
+ :path => self.unencode_component(uri_object.path),
526
+ :query => self.unencode_component(uri_object.query),
527
+ :fragment => self.unencode_component(uri_object.fragment)
528
+ }
529
+ components.each do |key, value|
530
+ if value != nil
531
+ begin
532
+ components[key] =
533
+ Addressable::IDNA.unicode_normalize_kc(value.to_str)
534
+ rescue ArgumentError
535
+ # Likely a malformed UTF-8 character, skip unicode normalization
536
+ components[key] = value.to_str
537
+ end
538
+ end
539
+ end
540
+ encoded_uri = Addressable::URI.new(
541
+ :scheme => self.encode_component(components[:scheme],
542
+ Addressable::URI::CharacterClasses::SCHEME),
543
+ :user => self.encode_component(components[:user],
544
+ Addressable::URI::CharacterClasses::UNRESERVED),
545
+ :password => self.encode_component(components[:password],
546
+ Addressable::URI::CharacterClasses::UNRESERVED),
547
+ :host => components[:host],
548
+ :port => components[:port],
549
+ :path => self.encode_component(components[:path],
550
+ Addressable::URI::CharacterClasses::PATH),
551
+ :query => self.encode_component(components[:query],
552
+ Addressable::URI::CharacterClasses::QUERY),
553
+ :fragment => self.encode_component(components[:fragment],
554
+ Addressable::URI::CharacterClasses::FRAGMENT)
555
+ )
556
+ if returning == String
557
+ return encoded_uri.to_s
558
+ elsif returning == ::Addressable::URI
559
+ return encoded_uri
560
+ end
561
+ end
562
+
563
+ ##
564
+ # Encodes a set of key/value pairs according to the rules for the
565
+ # <code>application/x-www-form-urlencoded</code> MIME type.
566
+ #
567
+ # @param [#to_hash, #to_ary] form_values
568
+ # The form values to encode.
569
+ #
570
+ # @param [TrueClass, FalseClass] sort
571
+ # Sort the key/value pairs prior to encoding.
572
+ # Defaults to <code>false</code>.
573
+ #
574
+ # @return [String]
575
+ # The encoded value.
576
+ def self.form_encode(form_values, sort=false)
577
+ if form_values.respond_to?(:to_hash)
578
+ form_values = form_values.to_hash.to_a
579
+ elsif form_values.respond_to?(:to_ary)
580
+ form_values = form_values.to_ary
581
+ else
582
+ raise TypeError, "Can't convert #{form_values.class} into Array."
583
+ end
584
+ form_values = form_values.map do |(key, value)|
585
+ [key.to_s, value.to_s]
586
+ end
587
+ if sort
588
+ # Useful for OAuth and optimizing caching systems
589
+ form_values = form_values.sort
590
+ end
591
+ escaped_form_values = form_values.map do |(key, value)|
592
+ # Line breaks are CRLF pairs
593
+ [
594
+ self.encode_component(
595
+ key.gsub(/(\r\n|\n|\r)/, "\r\n"),
596
+ CharacterClasses::UNRESERVED
597
+ ).gsub("%20", "+"),
598
+ self.encode_component(
599
+ value.gsub(/(\r\n|\n|\r)/, "\r\n"),
600
+ CharacterClasses::UNRESERVED
601
+ ).gsub("%20", "+")
602
+ ]
603
+ end
604
+ return (escaped_form_values.map do |(key, value)|
605
+ "#{key}=#{value}"
606
+ end).join("&")
607
+ end
608
+
609
+ ##
610
+ # Decodes a <code>String</code> according to the rules for the
611
+ # <code>application/x-www-form-urlencoded</code> MIME type.
612
+ #
613
+ # @param [String, #to_str] encoded_value
614
+ # The form values to decode.
615
+ #
616
+ # @return [Array]
617
+ # The decoded values.
618
+ # This is not a <code>Hash</code> because of the possibility for
619
+ # duplicate keys.
620
+ def self.form_unencode(encoded_value)
621
+ if !encoded_value.respond_to?(:to_str)
622
+ raise TypeError, "Can't convert #{encoded_value.class} into String."
623
+ end
624
+ encoded_value = encoded_value.to_str
625
+ split_values = encoded_value.split("&").map do |pair|
626
+ pair.split("=", 2)
627
+ end
628
+ return split_values.map do |(key, value)|
629
+ [
630
+ key ? self.unencode_component(
631
+ key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
632
+ value ? (self.unencode_component(
633
+ value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
634
+ ]
635
+ end
636
+ end
637
+
638
+ ##
639
+ # Creates a new uri object from component parts.
640
+ #
641
+ # @option [String, #to_str] scheme The scheme component.
642
+ # @option [String, #to_str] user The user component.
643
+ # @option [String, #to_str] password The password component.
644
+ # @option [String, #to_str] userinfo
645
+ # The userinfo component. If this is supplied, the user and password
646
+ # components must be omitted.
647
+ # @option [String, #to_str] host The host component.
648
+ # @option [String, #to_str] port The port component.
649
+ # @option [String, #to_str] authority
650
+ # The authority component. If this is supplied, the user, password,
651
+ # userinfo, host, and port components must be omitted.
652
+ # @option [String, #to_str] path The path component.
653
+ # @option [String, #to_str] query The query component.
654
+ # @option [String, #to_str] fragment The fragment component.
655
+ #
656
+ # @return [Addressable::URI] The constructed URI object.
657
+ def initialize(options={})
658
+ if options.has_key?(:authority)
659
+ if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
660
+ raise ArgumentError,
661
+ "Cannot specify both an authority and any of the components " +
662
+ "within the authority."
663
+ end
664
+ end
665
+ if options.has_key?(:userinfo)
666
+ if (options.keys & [:user, :password]).any?
667
+ raise ArgumentError,
668
+ "Cannot specify both a userinfo and either the user or password."
669
+ end
670
+ end
671
+
672
+ self.defer_validation do
673
+ # Bunch of crazy logic required because of the composite components
674
+ # like userinfo and authority.
675
+ self.scheme = options[:scheme] if options[:scheme]
676
+ self.user = options[:user] if options[:user]
677
+ self.password = options[:password] if options[:password]
678
+ self.userinfo = options[:userinfo] if options[:userinfo]
679
+ self.host = options[:host] if options[:host]
680
+ self.port = options[:port] if options[:port]
681
+ self.authority = options[:authority] if options[:authority]
682
+ self.path = options[:path] if options[:path]
683
+ self.query = options[:query] if options[:query]
684
+ self.fragment = options[:fragment] if options[:fragment]
685
+ end
686
+ end
687
+
688
+ ##
689
+ # The scheme component for this URI.
690
+ #
691
+ # @return [String] The scheme component.
692
+ def scheme
693
+ return @scheme ||= nil
694
+ end
695
+
696
+ ##
697
+ # The scheme component for this URI, normalized.
698
+ #
699
+ # @return [String] The scheme component, normalized.
700
+ def normalized_scheme
701
+ @normalized_scheme ||= (begin
702
+ if self.scheme != nil
703
+ if self.scheme =~ /^\s*ssh\+svn\s*$/i
704
+ "svn+ssh"
705
+ else
706
+ Addressable::URI.normalize_component(
707
+ self.scheme.strip.downcase,
708
+ Addressable::URI::CharacterClasses::SCHEME
709
+ )
710
+ end
711
+ else
712
+ nil
713
+ end
714
+ end)
715
+ end
716
+
717
+ ##
718
+ # Sets the scheme component for this URI.
719
+ #
720
+ # @param [String, #to_str] new_scheme The new scheme component.
721
+ def scheme=(new_scheme)
722
+ # Check for frozenness
723
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
724
+
725
+ if new_scheme && !new_scheme.respond_to?(:to_str)
726
+ raise TypeError, "Can't convert #{new_scheme.class} into String."
727
+ elsif new_scheme
728
+ new_scheme = new_scheme.to_str
729
+ end
730
+ if new_scheme && new_scheme !~ /[a-z][a-z0-9\.\+\-]*/i
731
+ raise InvalidURIError, "Invalid scheme format."
732
+ end
733
+ @scheme = new_scheme
734
+ @scheme = nil if @scheme.to_s.strip == ""
735
+
736
+ # Reset dependant values
737
+ @normalized_scheme = nil
738
+ @uri_string = nil
739
+ @hash = nil
740
+
741
+ # Ensure we haven't created an invalid URI
742
+ validate()
743
+ end
744
+
745
+ ##
746
+ # The user component for this URI.
747
+ #
748
+ # @return [String] The user component.
749
+ def user
750
+ return @user ||= nil
751
+ end
752
+
753
+ ##
754
+ # The user component for this URI, normalized.
755
+ #
756
+ # @return [String] The user component, normalized.
757
+ def normalized_user
758
+ @normalized_user ||= (begin
759
+ if self.user
760
+ if normalized_scheme =~ /https?/ && self.user.strip == "" &&
761
+ (!self.password || self.password.strip == "")
762
+ nil
763
+ else
764
+ Addressable::URI.normalize_component(
765
+ self.user.strip,
766
+ Addressable::URI::CharacterClasses::UNRESERVED
767
+ )
768
+ end
769
+ else
770
+ nil
771
+ end
772
+ end)
773
+ end
774
+
775
+ ##
776
+ # Sets the user component for this URI.
777
+ #
778
+ # @param [String, #to_str] new_user The new user component.
779
+ def user=(new_user)
780
+ # Check for frozenness
781
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
782
+
783
+ if new_user && !new_user.respond_to?(:to_str)
784
+ raise TypeError, "Can't convert #{new_user.class} into String."
785
+ end
786
+ @user = new_user ? new_user.to_str : nil
787
+
788
+ # You can't have a nil user with a non-nil password
789
+ @password ||= nil
790
+ if @password != nil
791
+ @user = "" if @user.nil?
792
+ end
793
+
794
+ # Reset dependant values
795
+ @userinfo = nil
796
+ @normalized_userinfo = nil
797
+ @authority = nil
798
+ @normalized_user = nil
799
+ @uri_string = nil
800
+ @hash = nil
801
+
802
+ # Ensure we haven't created an invalid URI
803
+ validate()
804
+ end
805
+
806
+ ##
807
+ # The password component for this URI.
808
+ #
809
+ # @return [String] The password component.
810
+ def password
811
+ return @password ||= nil
812
+ end
813
+
814
+ ##
815
+ # The password component for this URI, normalized.
816
+ #
817
+ # @return [String] The password component, normalized.
818
+ def normalized_password
819
+ @normalized_password ||= (begin
820
+ if self.password
821
+ if normalized_scheme =~ /https?/ && self.password.strip == "" &&
822
+ (!self.user || self.user.strip == "")
823
+ nil
824
+ else
825
+ Addressable::URI.normalize_component(
826
+ self.password.strip,
827
+ Addressable::URI::CharacterClasses::UNRESERVED
828
+ )
829
+ end
830
+ else
831
+ nil
832
+ end
833
+ end)
834
+ end
835
+
836
+ ##
837
+ # Sets the password component for this URI.
838
+ #
839
+ # @param [String, #to_str] new_password The new password component.
840
+ def password=(new_password)
841
+ # Check for frozenness
842
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
843
+
844
+ if new_password && !new_password.respond_to?(:to_str)
845
+ raise TypeError, "Can't convert #{new_password.class} into String."
846
+ end
847
+ @password = new_password ? new_password.to_str : nil
848
+
849
+ # You can't have a nil user with a non-nil password
850
+ @password ||= nil
851
+ @user ||= nil
852
+ if @password != nil
853
+ @user = "" if @user.nil?
854
+ end
855
+
856
+ # Reset dependant values
857
+ @userinfo = nil
858
+ @normalized_userinfo = nil
859
+ @authority = nil
860
+ @normalized_password = nil
861
+ @uri_string = nil
862
+ @hash = nil
863
+
864
+ # Ensure we haven't created an invalid URI
865
+ validate()
866
+ end
867
+
868
+ ##
869
+ # The userinfo component for this URI.
870
+ # Combines the user and password components.
871
+ #
872
+ # @return [String] The userinfo component.
873
+ def userinfo
874
+ @userinfo ||= (begin
875
+ current_user = self.user
876
+ current_password = self.password
877
+ if !current_user && !current_password
878
+ nil
879
+ elsif current_user && current_password
880
+ "#{current_user}:#{current_password}"
881
+ elsif current_user && !current_password
882
+ "#{current_user}"
883
+ end
884
+ end)
885
+ end
886
+
887
+ ##
888
+ # The userinfo component for this URI, normalized.
889
+ #
890
+ # @return [String] The userinfo component, normalized.
891
+ def normalized_userinfo
892
+ @normalized_userinfo ||= (begin
893
+ current_user = self.normalized_user
894
+ current_password = self.normalized_password
895
+ if !current_user && !current_password
896
+ nil
897
+ elsif current_user && current_password
898
+ "#{current_user}:#{current_password}"
899
+ elsif current_user && !current_password
900
+ "#{current_user}"
901
+ end
902
+ end)
903
+ end
904
+
905
+ ##
906
+ # Sets the userinfo component for this URI.
907
+ #
908
+ # @param [String, #to_str] new_userinfo The new userinfo component.
909
+ def userinfo=(new_userinfo)
910
+ # Check for frozenness
911
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
912
+
913
+ if new_userinfo && !new_userinfo.respond_to?(:to_str)
914
+ raise TypeError, "Can't convert #{new_userinfo.class} into String."
915
+ end
916
+ new_user, new_password = if new_userinfo
917
+ [
918
+ new_userinfo.to_str.strip[/^(.*):/, 1],
919
+ new_userinfo.to_str.strip[/:(.*)$/, 1]
920
+ ]
921
+ else
922
+ [nil, nil]
923
+ end
924
+
925
+ # Password assigned first to ensure validity in case of nil
926
+ self.password = new_password
927
+ self.user = new_user
928
+
929
+ # Reset dependant values
930
+ @authority = nil
931
+ @uri_string = nil
932
+ @hash = nil
933
+
934
+ # Ensure we haven't created an invalid URI
935
+ validate()
936
+ end
937
+
938
+ ##
939
+ # The host component for this URI.
940
+ #
941
+ # @return [String] The host component.
942
+ def host
943
+ return @host ||= nil
944
+ end
945
+
946
+ ##
947
+ # The host component for this URI, normalized.
948
+ #
949
+ # @return [String] The host component, normalized.
950
+ def normalized_host
951
+ @normalized_host ||= (begin
952
+ if self.host != nil
953
+ if self.host.strip != ""
954
+ result = ::Addressable::IDNA.to_ascii(
955
+ self.class.unencode_component(self.host.strip.downcase)
956
+ )
957
+ if result[-1..-1] == "."
958
+ # Trailing dots are unnecessary
959
+ result = result[0...-1]
960
+ end
961
+ result
962
+ else
963
+ ""
964
+ end
965
+ else
966
+ nil
967
+ end
968
+ end)
969
+ end
970
+
971
+ ##
972
+ # Sets the host component for this URI.
973
+ #
974
+ # @param [String, #to_str] new_host The new host component.
975
+ def host=(new_host)
976
+ # Check for frozenness
977
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
978
+
979
+ if new_host && !new_host.respond_to?(:to_str)
980
+ raise TypeError, "Can't convert #{new_host.class} into String."
981
+ end
982
+ @host = new_host ? new_host.to_str : nil
983
+
984
+ # Reset dependant values
985
+ @authority = nil
986
+ @normalized_host = nil
987
+ @uri_string = nil
988
+ @hash = nil
989
+
990
+ # Ensure we haven't created an invalid URI
991
+ validate()
992
+ end
993
+
994
+ ##
995
+ # The authority component for this URI.
996
+ # Combines the user, password, host, and port components.
997
+ #
998
+ # @return [String] The authority component.
999
+ def authority
1000
+ @authority ||= (begin
1001
+ if self.host.nil?
1002
+ nil
1003
+ else
1004
+ authority = ""
1005
+ if self.userinfo != nil
1006
+ authority << "#{self.userinfo}@"
1007
+ end
1008
+ authority << self.host
1009
+ if self.port != nil
1010
+ authority << ":#{self.port}"
1011
+ end
1012
+ authority
1013
+ end
1014
+ end)
1015
+ end
1016
+
1017
+ ##
1018
+ # The authority component for this URI, normalized.
1019
+ #
1020
+ # @return [String] The authority component, normalized.
1021
+ def normalized_authority
1022
+ @normalized_authority ||= (begin
1023
+ if self.normalized_host.nil?
1024
+ nil
1025
+ else
1026
+ authority = ""
1027
+ if self.normalized_userinfo != nil
1028
+ authority << "#{self.normalized_userinfo}@"
1029
+ end
1030
+ authority << self.normalized_host
1031
+ if self.normalized_port != nil
1032
+ authority << ":#{self.normalized_port}"
1033
+ end
1034
+ authority
1035
+ end
1036
+ end)
1037
+ end
1038
+
1039
+ ##
1040
+ # Sets the authority component for this URI.
1041
+ #
1042
+ # @param [String, #to_str] new_authority The new authority component.
1043
+ def authority=(new_authority)
1044
+ # Check for frozenness
1045
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1046
+
1047
+ if new_authority
1048
+ if !new_authority.respond_to?(:to_str)
1049
+ raise TypeError, "Can't convert #{new_authority.class} into String."
1050
+ end
1051
+ new_authority = new_authority.to_str
1052
+ new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
1053
+ if new_userinfo
1054
+ new_user = new_userinfo.strip[/^([^:]*):?/, 1]
1055
+ new_password = new_userinfo.strip[/:(.*)$/, 1]
1056
+ end
1057
+ new_host =
1058
+ new_authority.gsub(/^([^\[\]]*)@/, "").gsub(/:([^:@\[\]]*?)$/, "")
1059
+ new_port =
1060
+ new_authority[/:([^:@\[\]]*?)$/, 1]
1061
+ end
1062
+
1063
+ # Password assigned first to ensure validity in case of nil
1064
+ self.password = defined?(new_password) ? new_password : nil
1065
+ self.user = defined?(new_user) ? new_user : nil
1066
+ self.host = defined?(new_host) ? new_host : nil
1067
+ self.port = defined?(new_port) ? new_port : nil
1068
+
1069
+ # Reset dependant values
1070
+ @inferred_port = nil
1071
+ @userinfo = nil
1072
+ @normalized_userinfo = nil
1073
+ @uri_string = nil
1074
+ @hash = nil
1075
+
1076
+ # Ensure we haven't created an invalid URI
1077
+ validate()
1078
+ end
1079
+
1080
+ ##
1081
+ # The origin for this URI, serialized to ASCII, as per
1082
+ # draft-ietf-websec-origin-00, section 5.2.
1083
+ #
1084
+ # @return [String] The serialized origin.
1085
+ def origin
1086
+ return (if self.scheme && self.authority
1087
+ if self.normalized_port
1088
+ (
1089
+ "#{self.normalized_scheme}://#{self.normalized_host}" +
1090
+ ":#{self.normalized_port}"
1091
+ )
1092
+ else
1093
+ "#{self.normalized_scheme}://#{self.normalized_host}"
1094
+ end
1095
+ else
1096
+ "null"
1097
+ end)
1098
+ end
1099
+
1100
+ # Returns an array of known ip-based schemes. These schemes typically
1101
+ # use a similar URI form:
1102
+ # <code>//<user>:<password>@<host>:<port>/<url-path></code>
1103
+ def self.ip_based_schemes
1104
+ return self.port_mapping.keys
1105
+ end
1106
+
1107
+ # Returns a hash of common IP-based schemes and their default port
1108
+ # numbers. Adding new schemes to this hash, as necessary, will allow
1109
+ # for better URI normalization.
1110
+ def self.port_mapping
1111
+ @port_mapping ||= {
1112
+ "http" => 80,
1113
+ "https" => 443,
1114
+ "ftp" => 21,
1115
+ "tftp" => 69,
1116
+ "sftp" => 22,
1117
+ "ssh" => 22,
1118
+ "svn+ssh" => 22,
1119
+ "telnet" => 23,
1120
+ "nntp" => 119,
1121
+ "gopher" => 70,
1122
+ "wais" => 210,
1123
+ "ldap" => 389,
1124
+ "prospero" => 1525
1125
+ }
1126
+ end
1127
+
1128
+ ##
1129
+ # The port component for this URI.
1130
+ # This is the port number actually given in the URI. This does not
1131
+ # infer port numbers from default values.
1132
+ #
1133
+ # @return [Integer] The port component.
1134
+ def port
1135
+ return @port ||= nil
1136
+ end
1137
+
1138
+ ##
1139
+ # The port component for this URI, normalized.
1140
+ #
1141
+ # @return [Integer] The port component, normalized.
1142
+ def normalized_port
1143
+ @normalized_port ||= (begin
1144
+ if self.class.port_mapping[normalized_scheme] == self.port
1145
+ nil
1146
+ else
1147
+ self.port
1148
+ end
1149
+ end)
1150
+ end
1151
+
1152
+ ##
1153
+ # Sets the port component for this URI.
1154
+ #
1155
+ # @param [String, Integer, #to_s] new_port The new port component.
1156
+ def port=(new_port)
1157
+ # Check for frozenness
1158
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1159
+
1160
+ if new_port != nil && new_port.respond_to?(:to_str)
1161
+ new_port = Addressable::URI.unencode_component(new_port.to_str)
1162
+ end
1163
+ if new_port != nil && !(new_port.to_s =~ /^\d+$/)
1164
+ raise InvalidURIError,
1165
+ "Invalid port number: #{new_port.inspect}"
1166
+ end
1167
+
1168
+ @port = new_port.to_s.to_i
1169
+ @port = nil if @port == 0
1170
+
1171
+ # Reset dependant values
1172
+ @authority = nil
1173
+ @inferred_port = nil
1174
+ @normalized_port = nil
1175
+ @uri_string = nil
1176
+ @hash = nil
1177
+
1178
+ # Ensure we haven't created an invalid URI
1179
+ validate()
1180
+ end
1181
+
1182
+ ##
1183
+ # The inferred port component for this URI.
1184
+ # This method will normalize to the default port for the URI's scheme if
1185
+ # the port isn't explicitly specified in the URI.
1186
+ #
1187
+ # @return [Integer] The inferred port component.
1188
+ def inferred_port
1189
+ @inferred_port ||= (begin
1190
+ if port.to_i == 0
1191
+ if scheme
1192
+ self.class.port_mapping[scheme.strip.downcase]
1193
+ else
1194
+ nil
1195
+ end
1196
+ else
1197
+ port.to_i
1198
+ end
1199
+ end)
1200
+ end
1201
+
1202
+ ##
1203
+ # The combination of components that represent a site.
1204
+ # Combines the scheme, user, password, host, and port components.
1205
+ # Primarily useful for HTTP and HTTPS.
1206
+ #
1207
+ # For example, <code>"http://example.com/path?query"</code> would have a
1208
+ # <code>site</code> value of <code>"http://example.com"</code>.
1209
+ #
1210
+ # @return [String] The components that identify a site.
1211
+ def site
1212
+ @site ||= (begin
1213
+ if self.scheme || self.authority
1214
+ site_string = ""
1215
+ site_string << "#{self.scheme}:" if self.scheme != nil
1216
+ site_string << "//#{self.authority}" if self.authority != nil
1217
+ site_string
1218
+ else
1219
+ nil
1220
+ end
1221
+ end)
1222
+ end
1223
+
1224
+ ##
1225
+ # The normalized combination of components that represent a site.
1226
+ # Combines the scheme, user, password, host, and port components.
1227
+ # Primarily useful for HTTP and HTTPS.
1228
+ #
1229
+ # For example, <code>"http://example.com/path?query"</code> would have a
1230
+ # <code>site</code> value of <code>"http://example.com"</code>.
1231
+ #
1232
+ # @return [String] The normalized components that identify a site.
1233
+ def normalized_site
1234
+ @site ||= (begin
1235
+ if self.normalized_scheme || self.normalized_authority
1236
+ site_string = ""
1237
+ if self.normalized_scheme != nil
1238
+ site_string << "#{self.normalized_scheme}:"
1239
+ end
1240
+ if self.normalized_authority != nil
1241
+ site_string << "//#{self.normalized_authority}"
1242
+ end
1243
+ site_string
1244
+ else
1245
+ nil
1246
+ end
1247
+ end)
1248
+ end
1249
+
1250
+ ##
1251
+ # Sets the site value for this URI.
1252
+ #
1253
+ # @param [String, #to_str] new_site The new site value.
1254
+ def site=(new_site)
1255
+ if new_site
1256
+ if !new_site.respond_to?(:to_str)
1257
+ raise TypeError, "Can't convert #{new_site.class} into String."
1258
+ end
1259
+ new_site = new_site.to_str
1260
+ # These two regular expressions derived from the primary parsing
1261
+ # expression
1262
+ self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
1263
+ self.authority = new_site[
1264
+ /^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
1265
+ ]
1266
+ else
1267
+ self.scheme = nil
1268
+ self.authority = nil
1269
+ end
1270
+ end
1271
+
1272
+ ##
1273
+ # The path component for this URI.
1274
+ #
1275
+ # @return [String] The path component.
1276
+ def path
1277
+ @path ||= ""
1278
+ return @path
1279
+ end
1280
+
1281
+ ##
1282
+ # The path component for this URI, normalized.
1283
+ #
1284
+ # @return [String] The path component, normalized.
1285
+ def normalized_path
1286
+ @normalized_path ||= (begin
1287
+ if self.scheme == nil && self.path != nil && self.path != "" &&
1288
+ self.path =~ /^(?!\/)[^\/:]*:.*$/
1289
+ # Relative paths with colons in the first segment are ambiguous.
1290
+ self.path.sub!(":", "%2F")
1291
+ end
1292
+ # String#split(delimeter, -1) uses the more strict splitting behavior
1293
+ # found by default in Python.
1294
+ result = (self.path.strip.split("/", -1).map do |segment|
1295
+ Addressable::URI.normalize_component(
1296
+ segment,
1297
+ Addressable::URI::CharacterClasses::PCHAR
1298
+ )
1299
+ end).join("/")
1300
+ result = self.class.normalize_path(result)
1301
+ if result == "" &&
1302
+ ["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
1303
+ result = "/"
1304
+ end
1305
+ result
1306
+ end)
1307
+ end
1308
+
1309
+ ##
1310
+ # Sets the path component for this URI.
1311
+ #
1312
+ # @param [String, #to_str] new_path The new path component.
1313
+ def path=(new_path)
1314
+ # Check for frozenness
1315
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1316
+
1317
+ if new_path && !new_path.respond_to?(:to_str)
1318
+ raise TypeError, "Can't convert #{new_path.class} into String."
1319
+ end
1320
+ @path = (new_path || "").to_str
1321
+ if @path != "" && @path[0..0] != "/" && host != nil
1322
+ @path = "/#{@path}"
1323
+ end
1324
+
1325
+ # Reset dependant values
1326
+ @normalized_path = nil
1327
+ @uri_string = nil
1328
+ @hash = nil
1329
+ end
1330
+
1331
+ ##
1332
+ # The basename, if any, of the file in the path component.
1333
+ #
1334
+ # @return [String] The path's basename.
1335
+ def basename
1336
+ # Path cannot be nil
1337
+ return File.basename(self.path).gsub(/;[^\/]*$/, "")
1338
+ end
1339
+
1340
+ ##
1341
+ # The extname, if any, of the file in the path component.
1342
+ # Empty string if there is no extension.
1343
+ #
1344
+ # @return [String] The path's extname.
1345
+ def extname
1346
+ return nil unless self.path
1347
+ return File.extname(self.basename)
1348
+ end
1349
+
1350
+ ##
1351
+ # The query component for this URI.
1352
+ #
1353
+ # @return [String] The query component.
1354
+ def query
1355
+ return @query ||= nil
1356
+ end
1357
+
1358
+ ##
1359
+ # The query component for this URI, normalized.
1360
+ #
1361
+ # @return [String] The query component, normalized.
1362
+ def normalized_query
1363
+ @normalized_query ||= (begin
1364
+ if self.query
1365
+ Addressable::URI.normalize_component(
1366
+ self.query.strip,
1367
+ Addressable::URI::CharacterClasses::QUERY
1368
+ )
1369
+ else
1370
+ nil
1371
+ end
1372
+ end)
1373
+ end
1374
+
1375
+ ##
1376
+ # Sets the query component for this URI.
1377
+ #
1378
+ # @param [String, #to_str] new_query The new query component.
1379
+ def query=(new_query)
1380
+ # Check for frozenness
1381
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1382
+
1383
+ if new_query && !new_query.respond_to?(:to_str)
1384
+ raise TypeError, "Can't convert #{new_query.class} into String."
1385
+ end
1386
+ @query = new_query ? new_query.to_str : nil
1387
+
1388
+ # Reset dependant values
1389
+ @normalized_query = nil
1390
+ @uri_string = nil
1391
+ @hash = nil
1392
+ end
1393
+
1394
+ ##
1395
+ # Converts the query component to a Hash value.
1396
+ #
1397
+ # @option [Symbol] notation
1398
+ # May be one of <code>:flat</code>, <code>:dot</code>, or
1399
+ # <code>:subscript</code>. The <code>:dot</code> notation is not
1400
+ # supported for assignment. Default value is <code>:subscript</code>.
1401
+ #
1402
+ # @return [Hash, Array] The query string parsed as a Hash or Array object.
1403
+ #
1404
+ # @example
1405
+ # Addressable::URI.parse("?one=1&two=2&three=3").query_values
1406
+ # #=> {"one" => "1", "two" => "2", "three" => "3"}
1407
+ # Addressable::URI.parse("?one[two][three]=four").query_values
1408
+ # #=> {"one" => {"two" => {"three" => "four"}}}
1409
+ # Addressable::URI.parse("?one.two.three=four").query_values(
1410
+ # :notation => :dot
1411
+ # )
1412
+ # #=> {"one" => {"two" => {"three" => "four"}}}
1413
+ # Addressable::URI.parse("?one[two][three]=four").query_values(
1414
+ # :notation => :flat
1415
+ # )
1416
+ # #=> {"one[two][three]" => "four"}
1417
+ # Addressable::URI.parse("?one.two.three=four").query_values(
1418
+ # :notation => :flat
1419
+ # )
1420
+ # #=> {"one.two.three" => "four"}
1421
+ # Addressable::URI.parse(
1422
+ # "?one[two][three][]=four&one[two][three][]=five"
1423
+ # ).query_values
1424
+ # #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
1425
+ # Addressable::URI.parse(
1426
+ # "?one=two&one=three").query_values(:notation => :flat_array)
1427
+ # #=> [['one', 'two'], ['one', 'three']]
1428
+ def query_values(options={})
1429
+ defaults = {:notation => :subscript}
1430
+ options = defaults.merge(options)
1431
+ if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
1432
+ raise ArgumentError,
1433
+ "Invalid notation. Must be one of: " +
1434
+ "[:flat, :dot, :subscript, :flat_array]."
1435
+ end
1436
+ dehash = lambda do |hash|
1437
+ hash.each do |(key, value)|
1438
+ if value.kind_of?(Hash)
1439
+ hash[key] = dehash.call(value)
1440
+ end
1441
+ end
1442
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
1443
+ hash.sort.inject([]) do |accu, (key, value)|
1444
+ accu << value; accu
1445
+ end
1446
+ else
1447
+ hash
1448
+ end
1449
+ end
1450
+ return nil if self.query == nil
1451
+ empty_accumulator = :flat_array == options[:notation] ? [] : {}
1452
+ return ((self.query.split("&").map do |pair|
1453
+ pair.split("=", 2) if pair && pair != ""
1454
+ end).compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
1455
+ value = true if value.nil?
1456
+ key = self.class.unencode_component(key)
1457
+ if value != true
1458
+ value = self.class.unencode_component(value.gsub(/\+/, " "))
1459
+ end
1460
+ if options[:notation] == :flat
1461
+ if accumulator[key]
1462
+ raise ArgumentError, "Key was repeated: #{key.inspect}"
1463
+ end
1464
+ accumulator[key] = value
1465
+ elsif options[:notation] == :flat_array
1466
+ accumulator << [key, value]
1467
+ else
1468
+ if options[:notation] == :dot
1469
+ array_value = false
1470
+ subkeys = key.split(".")
1471
+ elsif options[:notation] == :subscript
1472
+ array_value = !!(key =~ /\[\]$/)
1473
+ subkeys = key.split(/[\[\]]+/)
1474
+ end
1475
+ current_hash = accumulator
1476
+ for i in 0...(subkeys.size - 1)
1477
+ subkey = subkeys[i]
1478
+ current_hash[subkey] = {} unless current_hash[subkey]
1479
+ current_hash = current_hash[subkey]
1480
+ end
1481
+ if array_value
1482
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
1483
+ current_hash[subkeys.last] << value
1484
+ else
1485
+ current_hash[subkeys.last] = value
1486
+ end
1487
+ end
1488
+ accumulator
1489
+ end).inject(empty_accumulator.dup) do |accumulator, (key, value)|
1490
+ if options[:notation] == :flat_array
1491
+ accumulator << [key, value]
1492
+ else
1493
+ accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
1494
+ end
1495
+ accumulator
1496
+ end
1497
+ end
1498
+
1499
+ ##
1500
+ # Sets the query component for this URI from a Hash object.
1501
+ # This method produces a query string using the :subscript notation.
1502
+ # An empty Hash will result in a nil query.
1503
+ #
1504
+ # @param [Hash, #to_hash, Array] new_query_values The new query values.
1505
+ def query_values=(new_query_values)
1506
+ # Check for frozenness
1507
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1508
+ if new_query_values == nil
1509
+ self.query = nil
1510
+ return nil
1511
+ end
1512
+
1513
+ if !new_query_values.is_a?(Array)
1514
+ if !new_query_values.respond_to?(:to_hash)
1515
+ raise TypeError,
1516
+ "Can't convert #{new_query_values.class} into Hash."
1517
+ end
1518
+ new_query_values = new_query_values.to_hash
1519
+ new_query_values = new_query_values.map do |key, value|
1520
+ key = key.to_s if key.kind_of?(Symbol)
1521
+ [key, value]
1522
+ end
1523
+ # Useful default for OAuth and caching.
1524
+ # Only to be used for non-Array inputs. Arrays should preserve order.
1525
+ new_query_values.sort!
1526
+ end
1527
+ # new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
1528
+
1529
+ # Algorithm shamelessly stolen from Julien Genestoux, slightly modified
1530
+ buffer = ""
1531
+ stack = []
1532
+ e = lambda do |component|
1533
+ component = component.to_s if component.kind_of?(Symbol)
1534
+ self.class.encode_component(component, CharacterClasses::UNRESERVED)
1535
+ end
1536
+ new_query_values.each do |key, value|
1537
+ if value.kind_of?(Hash)
1538
+ stack << [key, value]
1539
+ elsif value.kind_of?(Array)
1540
+ stack << [
1541
+ key,
1542
+ value.inject({}) { |accu, x| accu[accu.size.to_s] = x; accu }
1543
+ ]
1544
+ elsif value == true
1545
+ buffer << "#{e.call(key)}&"
1546
+ else
1547
+ buffer << "#{e.call(key)}=#{e.call(value)}&"
1548
+ end
1549
+ end
1550
+ stack.each do |(parent, hash)|
1551
+ (hash.sort_by { |key| key.to_s }).each do |(key, value)|
1552
+ if value.kind_of?(Hash)
1553
+ stack << ["#{parent}[#{key}]", value]
1554
+ elsif value == true
1555
+ buffer << "#{parent}[#{e.call(key)}]&"
1556
+ else
1557
+ buffer << "#{parent}[#{e.call(key)}]=#{e.call(value)}&"
1558
+ end
1559
+ end
1560
+ end
1561
+ self.query = buffer.chop
1562
+ end
1563
+
1564
+ ##
1565
+ # The HTTP request URI for this URI. This is the path and the
1566
+ # query string.
1567
+ #
1568
+ # @return [String] The request URI required for an HTTP request.
1569
+ def request_uri
1570
+ return nil if self.absolute? && self.scheme !~ /^https?$/
1571
+ return (
1572
+ (self.path != "" ? self.path : "/") +
1573
+ (self.query ? "?#{self.query}" : "")
1574
+ )
1575
+ end
1576
+
1577
+ ##
1578
+ # Sets the HTTP request URI for this URI.
1579
+ #
1580
+ # @param [String, #to_str] new_request_uri The new HTTP request URI.
1581
+ def request_uri=(new_request_uri)
1582
+ if !new_request_uri.respond_to?(:to_str)
1583
+ raise TypeError, "Can't convert #{new_request_uri.class} into String."
1584
+ end
1585
+ if self.absolute? && self.scheme !~ /^https?$/
1586
+ raise InvalidURIError,
1587
+ "Cannot set an HTTP request URI for a non-HTTP URI."
1588
+ end
1589
+ new_request_uri = new_request_uri.to_str
1590
+ path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
1591
+ query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
1592
+ path_component = path_component.to_s
1593
+ path_component = (path_component != "" ? path_component : "/")
1594
+ self.path = path_component
1595
+ self.query = query_component
1596
+
1597
+ # Reset dependant values
1598
+ @uri_string = nil
1599
+ @hash = nil
1600
+ end
1601
+
1602
+ ##
1603
+ # The fragment component for this URI.
1604
+ #
1605
+ # @return [String] The fragment component.
1606
+ def fragment
1607
+ return @fragment ||= nil
1608
+ end
1609
+
1610
+ ##
1611
+ # The fragment component for this URI, normalized.
1612
+ #
1613
+ # @return [String] The fragment component, normalized.
1614
+ def normalized_fragment
1615
+ @normalized_fragment ||= (begin
1616
+ if self.fragment
1617
+ Addressable::URI.normalize_component(
1618
+ self.fragment.strip,
1619
+ Addressable::URI::CharacterClasses::FRAGMENT
1620
+ )
1621
+ else
1622
+ nil
1623
+ end
1624
+ end)
1625
+ end
1626
+
1627
+ ##
1628
+ # Sets the fragment component for this URI.
1629
+ #
1630
+ # @param [String, #to_str] new_fragment The new fragment component.
1631
+ def fragment=(new_fragment)
1632
+ # Check for frozenness
1633
+ raise TypeError, "Can't modify frozen URI." if self.frozen?
1634
+
1635
+ if new_fragment && !new_fragment.respond_to?(:to_str)
1636
+ raise TypeError, "Can't convert #{new_fragment.class} into String."
1637
+ end
1638
+ @fragment = new_fragment ? new_fragment.to_str : nil
1639
+
1640
+ # Reset dependant values
1641
+ @normalized_fragment = nil
1642
+ @uri_string = nil
1643
+ @hash = nil
1644
+
1645
+ # Ensure we haven't created an invalid URI
1646
+ validate()
1647
+ end
1648
+
1649
+ ##
1650
+ # Determines if the scheme indicates an IP-based protocol.
1651
+ #
1652
+ # @return [TrueClass, FalseClass]
1653
+ # <code>true</code> if the scheme indicates an IP-based protocol.
1654
+ # <code>false</code> otherwise.
1655
+ def ip_based?
1656
+ if self.scheme
1657
+ return self.class.ip_based_schemes.include?(
1658
+ self.scheme.strip.downcase)
1659
+ end
1660
+ return false
1661
+ end
1662
+
1663
+ ##
1664
+ # Determines if the URI is relative.
1665
+ #
1666
+ # @return [TrueClass, FalseClass]
1667
+ # <code>true</code> if the URI is relative. <code>false</code>
1668
+ # otherwise.
1669
+ def relative?
1670
+ return self.scheme.nil?
1671
+ end
1672
+
1673
+ ##
1674
+ # Determines if the URI is absolute.
1675
+ #
1676
+ # @return [TrueClass, FalseClass]
1677
+ # <code>true</code> if the URI is absolute. <code>false</code>
1678
+ # otherwise.
1679
+ def absolute?
1680
+ return !relative?
1681
+ end
1682
+
1683
+ ##
1684
+ # Joins two URIs together.
1685
+ #
1686
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
1687
+ #
1688
+ # @return [Addressable::URI] The joined URI.
1689
+ def join(uri)
1690
+ if !uri.respond_to?(:to_str)
1691
+ raise TypeError, "Can't convert #{uri.class} into String."
1692
+ end
1693
+ if !uri.kind_of?(self.class)
1694
+ # Otherwise, convert to a String, then parse.
1695
+ uri = self.class.parse(uri.to_str)
1696
+ end
1697
+ if uri.to_s == ""
1698
+ return self.dup
1699
+ end
1700
+
1701
+ joined_scheme = nil
1702
+ joined_user = nil
1703
+ joined_password = nil
1704
+ joined_host = nil
1705
+ joined_port = nil
1706
+ joined_path = nil
1707
+ joined_query = nil
1708
+ joined_fragment = nil
1709
+
1710
+ # Section 5.2.2 of RFC 3986
1711
+ if uri.scheme != nil
1712
+ joined_scheme = uri.scheme
1713
+ joined_user = uri.user
1714
+ joined_password = uri.password
1715
+ joined_host = uri.host
1716
+ joined_port = uri.port
1717
+ joined_path = self.class.normalize_path(uri.path)
1718
+ joined_query = uri.query
1719
+ else
1720
+ if uri.authority != nil
1721
+ joined_user = uri.user
1722
+ joined_password = uri.password
1723
+ joined_host = uri.host
1724
+ joined_port = uri.port
1725
+ joined_path = self.class.normalize_path(uri.path)
1726
+ joined_query = uri.query
1727
+ else
1728
+ if uri.path == nil || uri.path == ""
1729
+ joined_path = self.path
1730
+ if uri.query != nil
1731
+ joined_query = uri.query
1732
+ else
1733
+ joined_query = self.query
1734
+ end
1735
+ else
1736
+ if uri.path[0..0] == "/"
1737
+ joined_path = self.class.normalize_path(uri.path)
1738
+ else
1739
+ base_path = self.path.dup
1740
+ base_path = "" if base_path == nil
1741
+ base_path = self.class.normalize_path(base_path)
1742
+
1743
+ # Section 5.2.3 of RFC 3986
1744
+ #
1745
+ # Removes the right-most path segment from the base path.
1746
+ if base_path =~ /\//
1747
+ base_path.gsub!(/\/[^\/]+$/, "/")
1748
+ else
1749
+ base_path = ""
1750
+ end
1751
+
1752
+ # If the base path is empty and an authority segment has been
1753
+ # defined, use a base path of "/"
1754
+ if base_path == "" && self.authority != nil
1755
+ base_path = "/"
1756
+ end
1757
+
1758
+ joined_path = self.class.normalize_path(base_path + uri.path)
1759
+ end
1760
+ joined_query = uri.query
1761
+ end
1762
+ joined_user = self.user
1763
+ joined_password = self.password
1764
+ joined_host = self.host
1765
+ joined_port = self.port
1766
+ end
1767
+ joined_scheme = self.scheme
1768
+ end
1769
+ joined_fragment = uri.fragment
1770
+
1771
+ return Addressable::URI.new(
1772
+ :scheme => joined_scheme,
1773
+ :user => joined_user,
1774
+ :password => joined_password,
1775
+ :host => joined_host,
1776
+ :port => joined_port,
1777
+ :path => joined_path,
1778
+ :query => joined_query,
1779
+ :fragment => joined_fragment
1780
+ )
1781
+ end
1782
+ alias_method :+, :join
1783
+
1784
+ ##
1785
+ # Destructive form of <code>join</code>.
1786
+ #
1787
+ # @param [String, Addressable::URI, #to_str] The URI to join with.
1788
+ #
1789
+ # @return [Addressable::URI] The joined URI.
1790
+ #
1791
+ # @see Addressable::URI#join
1792
+ def join!(uri)
1793
+ replace_self(self.join(uri))
1794
+ end
1795
+
1796
+ ##
1797
+ # Merges a URI with a <code>Hash</code> of components.
1798
+ # This method has different behavior from <code>join</code>. Any
1799
+ # components present in the <code>hash</code> parameter will override the
1800
+ # original components. The path component is not treated specially.
1801
+ #
1802
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
1803
+ #
1804
+ # @return [Addressable::URI] The merged URI.
1805
+ #
1806
+ # @see Hash#merge
1807
+ def merge(hash)
1808
+ if !hash.respond_to?(:to_hash)
1809
+ raise TypeError, "Can't convert #{hash.class} into Hash."
1810
+ end
1811
+ hash = hash.to_hash
1812
+
1813
+ if hash.has_key?(:authority)
1814
+ if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
1815
+ raise ArgumentError,
1816
+ "Cannot specify both an authority and any of the components " +
1817
+ "within the authority."
1818
+ end
1819
+ end
1820
+ if hash.has_key?(:userinfo)
1821
+ if (hash.keys & [:user, :password]).any?
1822
+ raise ArgumentError,
1823
+ "Cannot specify both a userinfo and either the user or password."
1824
+ end
1825
+ end
1826
+
1827
+ uri = Addressable::URI.new
1828
+ uri.defer_validation do
1829
+ # Bunch of crazy logic required because of the composite components
1830
+ # like userinfo and authority.
1831
+ uri.scheme =
1832
+ hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
1833
+ if hash.has_key?(:authority)
1834
+ uri.authority =
1835
+ hash.has_key?(:authority) ? hash[:authority] : self.authority
1836
+ end
1837
+ if hash.has_key?(:userinfo)
1838
+ uri.userinfo =
1839
+ hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
1840
+ end
1841
+ if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
1842
+ uri.user =
1843
+ hash.has_key?(:user) ? hash[:user] : self.user
1844
+ uri.password =
1845
+ hash.has_key?(:password) ? hash[:password] : self.password
1846
+ end
1847
+ if !hash.has_key?(:authority)
1848
+ uri.host =
1849
+ hash.has_key?(:host) ? hash[:host] : self.host
1850
+ uri.port =
1851
+ hash.has_key?(:port) ? hash[:port] : self.port
1852
+ end
1853
+ uri.path =
1854
+ hash.has_key?(:path) ? hash[:path] : self.path
1855
+ uri.query =
1856
+ hash.has_key?(:query) ? hash[:query] : self.query
1857
+ uri.fragment =
1858
+ hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
1859
+ end
1860
+
1861
+ return uri
1862
+ end
1863
+
1864
+ ##
1865
+ # Destructive form of <code>merge</code>.
1866
+ #
1867
+ # @param [Hash, Addressable::URI, #to_hash] The components to merge with.
1868
+ #
1869
+ # @return [Addressable::URI] The merged URI.
1870
+ #
1871
+ # @see Addressable::URI#merge
1872
+ def merge!(uri)
1873
+ replace_self(self.merge(uri))
1874
+ end
1875
+
1876
+ ##
1877
+ # Returns the shortest normalized relative form of this URI that uses the
1878
+ # supplied URI as a base for resolution. Returns an absolute URI if
1879
+ # necessary. This is effectively the opposite of <code>route_to</code>.
1880
+ #
1881
+ # @param [String, Addressable::URI, #to_str] uri The URI to route from.
1882
+ #
1883
+ # @return [Addressable::URI]
1884
+ # The normalized relative URI that is equivalent to the original URI.
1885
+ def route_from(uri)
1886
+ uri = self.class.parse(uri).normalize
1887
+ normalized_self = self.normalize
1888
+ if normalized_self.relative?
1889
+ raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
1890
+ end
1891
+ if uri.relative?
1892
+ raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
1893
+ end
1894
+ if normalized_self == uri
1895
+ return Addressable::URI.parse("##{normalized_self.fragment}")
1896
+ end
1897
+ components = normalized_self.to_hash
1898
+ if normalized_self.scheme == uri.scheme
1899
+ components[:scheme] = nil
1900
+ if normalized_self.authority == uri.authority
1901
+ components[:user] = nil
1902
+ components[:password] = nil
1903
+ components[:host] = nil
1904
+ components[:port] = nil
1905
+ if normalized_self.path == uri.path
1906
+ components[:path] = nil
1907
+ if normalized_self.query == uri.query
1908
+ components[:query] = nil
1909
+ end
1910
+ else
1911
+ if uri.path != "/"
1912
+ components[:path].gsub!(
1913
+ Regexp.new("^" + Regexp.escape(uri.path)), "")
1914
+ end
1915
+ end
1916
+ end
1917
+ end
1918
+ # Avoid network-path references.
1919
+ if components[:host] != nil
1920
+ components[:scheme] = normalized_self.scheme
1921
+ end
1922
+ return Addressable::URI.new(
1923
+ :scheme => components[:scheme],
1924
+ :user => components[:user],
1925
+ :password => components[:password],
1926
+ :host => components[:host],
1927
+ :port => components[:port],
1928
+ :path => components[:path],
1929
+ :query => components[:query],
1930
+ :fragment => components[:fragment]
1931
+ )
1932
+ end
1933
+
1934
+ ##
1935
+ # Returns the shortest normalized relative form of the supplied URI that
1936
+ # uses this URI as a base for resolution. Returns an absolute URI if
1937
+ # necessary. This is effectively the opposite of <code>route_from</code>.
1938
+ #
1939
+ # @param [String, Addressable::URI, #to_str] uri The URI to route to.
1940
+ #
1941
+ # @return [Addressable::URI]
1942
+ # The normalized relative URI that is equivalent to the supplied URI.
1943
+ def route_to(uri)
1944
+ return self.class.parse(uri).route_from(self)
1945
+ end
1946
+
1947
+ ##
1948
+ # Returns a normalized URI object.
1949
+ #
1950
+ # NOTE: This method does not attempt to fully conform to specifications.
1951
+ # It exists largely to correct other people's failures to read the
1952
+ # specifications, and also to deal with caching issues since several
1953
+ # different URIs may represent the same resource and should not be
1954
+ # cached multiple times.
1955
+ #
1956
+ # @return [Addressable::URI] The normalized URI.
1957
+ def normalize
1958
+ # This is a special exception for the frequently misused feed
1959
+ # URI scheme.
1960
+ if normalized_scheme == "feed"
1961
+ if self.to_s =~ /^feed:\/*http:\/*/
1962
+ return self.class.parse(
1963
+ self.to_s[/^feed:\/*(http:\/*.*)/, 1]
1964
+ ).normalize
1965
+ end
1966
+ end
1967
+
1968
+ return Addressable::URI.new(
1969
+ :scheme => normalized_scheme,
1970
+ :authority => normalized_authority,
1971
+ :path => normalized_path,
1972
+ :query => normalized_query,
1973
+ :fragment => normalized_fragment
1974
+ )
1975
+ end
1976
+
1977
+ ##
1978
+ # Destructively normalizes this URI object.
1979
+ #
1980
+ # @return [Addressable::URI] The normalized URI.
1981
+ #
1982
+ # @see Addressable::URI#normalize
1983
+ def normalize!
1984
+ replace_self(self.normalize)
1985
+ end
1986
+
1987
+ ##
1988
+ # Creates a URI suitable for display to users. If semantic attacks are
1989
+ # likely, the application should try to detect these and warn the user.
1990
+ # See <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
1991
+ # section 7.6 for more information.
1992
+ #
1993
+ # @return [Addressable::URI] A URI suitable for display purposes.
1994
+ def display_uri
1995
+ display_uri = self.normalize
1996
+ display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
1997
+ return display_uri
1998
+ end
1999
+
2000
+ ##
2001
+ # Returns <code>true</code> if the URI objects are equal. This method
2002
+ # normalizes both URIs before doing the comparison, and allows comparison
2003
+ # against <code>Strings</code>.
2004
+ #
2005
+ # @param [Object] uri The URI to compare.
2006
+ #
2007
+ # @return [TrueClass, FalseClass]
2008
+ # <code>true</code> if the URIs are equivalent, <code>false</code>
2009
+ # otherwise.
2010
+ def ===(uri)
2011
+ if uri.respond_to?(:normalize)
2012
+ uri_string = uri.normalize.to_s
2013
+ else
2014
+ begin
2015
+ uri_string = ::Addressable::URI.parse(uri).normalize.to_s
2016
+ rescue InvalidURIError, TypeError
2017
+ return false
2018
+ end
2019
+ end
2020
+ return self.normalize.to_s == uri_string
2021
+ end
2022
+
2023
+ ##
2024
+ # Returns <code>true</code> if the URI objects are equal. This method
2025
+ # normalizes both URIs before doing the comparison.
2026
+ #
2027
+ # @param [Object] uri The URI to compare.
2028
+ #
2029
+ # @return [TrueClass, FalseClass]
2030
+ # <code>true</code> if the URIs are equivalent, <code>false</code>
2031
+ # otherwise.
2032
+ def ==(uri)
2033
+ return false unless uri.kind_of?(self.class)
2034
+ return self.normalize.to_s == uri.normalize.to_s
2035
+ end
2036
+
2037
+ ##
2038
+ # Returns <code>true</code> if the URI objects are equal. This method
2039
+ # does NOT normalize either URI before doing the comparison.
2040
+ #
2041
+ # @param [Object] uri The URI to compare.
2042
+ #
2043
+ # @return [TrueClass, FalseClass]
2044
+ # <code>true</code> if the URIs are equivalent, <code>false</code>
2045
+ # otherwise.
2046
+ def eql?(uri)
2047
+ return false unless uri.kind_of?(self.class)
2048
+ return self.to_s == uri.to_s
2049
+ end
2050
+
2051
+ ##
2052
+ # A hash value that will make a URI equivalent to its normalized
2053
+ # form.
2054
+ #
2055
+ # @return [Integer] A hash of the URI.
2056
+ def hash
2057
+ return @hash ||= (self.to_s.hash * -1)
2058
+ end
2059
+
2060
+ ##
2061
+ # Clones the URI object.
2062
+ #
2063
+ # @return [Addressable::URI] The cloned URI.
2064
+ def dup
2065
+ duplicated_uri = Addressable::URI.new(
2066
+ :scheme => self.scheme ? self.scheme.dup : nil,
2067
+ :user => self.user ? self.user.dup : nil,
2068
+ :password => self.password ? self.password.dup : nil,
2069
+ :host => self.host ? self.host.dup : nil,
2070
+ :port => self.port,
2071
+ :path => self.path ? self.path.dup : nil,
2072
+ :query => self.query ? self.query.dup : nil,
2073
+ :fragment => self.fragment ? self.fragment.dup : nil
2074
+ )
2075
+ return duplicated_uri
2076
+ end
2077
+
2078
+ ##
2079
+ # Freezes the URI object.
2080
+ #
2081
+ # @return [Addressable::URI] The frozen URI.
2082
+ def freeze
2083
+ # Unfortunately, because of the memoized implementation of many of the
2084
+ # URI methods, the default freeze method will cause unexpected errors.
2085
+ # As an alternative, we freeze the string representation of the URI
2086
+ # instead. This should generally produce the desired effect.
2087
+ self.to_s.freeze
2088
+ return self
2089
+ end
2090
+
2091
+ ##
2092
+ # Determines if the URI is frozen.
2093
+ #
2094
+ # @return [TrueClass, FalseClass]
2095
+ # <code>true</code> if the URI is frozen, <code>false</code> otherwise.
2096
+ def frozen?
2097
+ self.to_s.frozen?
2098
+ end
2099
+
2100
+ ##
2101
+ # Omits components from a URI.
2102
+ #
2103
+ # @param [Symbol] *components The components to be omitted.
2104
+ #
2105
+ # @return [Addressable::URI] The URI with components omitted.
2106
+ #
2107
+ # @example
2108
+ # uri = Addressable::URI.parse("http://example.com/path?query")
2109
+ # #=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path?query>
2110
+ # uri.omit(:scheme, :authority)
2111
+ # #=> #<Addressable::URI:0xcc4d86 URI:/path?query>
2112
+ def omit(*components)
2113
+ invalid_components = components - [
2114
+ :scheme, :user, :password, :userinfo, :host, :port, :authority,
2115
+ :path, :query, :fragment
2116
+ ]
2117
+ unless invalid_components.empty?
2118
+ raise ArgumentError,
2119
+ "Invalid component names: #{invalid_components.inspect}."
2120
+ end
2121
+ duplicated_uri = self.dup
2122
+ duplicated_uri.defer_validation do
2123
+ components.each do |component|
2124
+ duplicated_uri.send((component.to_s + "=").to_sym, nil)
2125
+ end
2126
+ duplicated_uri.user = duplicated_uri.normalized_user
2127
+ end
2128
+ duplicated_uri
2129
+ end
2130
+
2131
+ ##
2132
+ # Destructive form of omit.
2133
+ #
2134
+ # @param [Symbol] *components The components to be omitted.
2135
+ #
2136
+ # @return [Addressable::URI] The URI with components omitted.
2137
+ #
2138
+ # @see Addressable::URI#omit
2139
+ def omit!(*components)
2140
+ replace_self(self.omit(*components))
2141
+ end
2142
+
2143
+ ##
2144
+ # Converts the URI to a <code>String</code>.
2145
+ #
2146
+ # @return [String] The URI's <code>String</code> representation.
2147
+ def to_s
2148
+ @uri_string ||= (begin
2149
+ uri_string = ""
2150
+ uri_string << "#{self.scheme}:" if self.scheme != nil
2151
+ uri_string << "//#{self.authority}" if self.authority != nil
2152
+ uri_string << self.path.to_s
2153
+ uri_string << "?#{self.query}" if self.query != nil
2154
+ uri_string << "##{self.fragment}" if self.fragment != nil
2155
+ if uri_string.respond_to?(:force_encoding)
2156
+ uri_string.force_encoding(Encoding::UTF_8)
2157
+ end
2158
+ uri_string
2159
+ end)
2160
+ end
2161
+
2162
+ ##
2163
+ # URI's are glorified <code>Strings</code>. Allow implicit conversion.
2164
+ alias_method :to_str, :to_s
2165
+
2166
+ ##
2167
+ # Returns a Hash of the URI components.
2168
+ #
2169
+ # @return [Hash] The URI as a <code>Hash</code> of components.
2170
+ def to_hash
2171
+ return {
2172
+ :scheme => self.scheme,
2173
+ :user => self.user,
2174
+ :password => self.password,
2175
+ :host => self.host,
2176
+ :port => self.port,
2177
+ :path => self.path,
2178
+ :query => self.query,
2179
+ :fragment => self.fragment
2180
+ }
2181
+ end
2182
+
2183
+ ##
2184
+ # Returns a <code>String</code> representation of the URI object's state.
2185
+ #
2186
+ # @return [String] The URI object's state, as a <code>String</code>.
2187
+ def inspect
2188
+ sprintf("#<%s:%#0x URI:%s>", self.class.to_s, self.object_id, self.to_s)
2189
+ end
2190
+
2191
+ ##
2192
+ # This method allows you to make several changes to a URI simultaneously,
2193
+ # which separately would cause validation errors, but in conjunction,
2194
+ # are valid. The URI will be revalidated as soon as the entire block has
2195
+ # been executed.
2196
+ #
2197
+ # @param [Proc] block
2198
+ # A set of operations to perform on a given URI.
2199
+ def defer_validation(&block)
2200
+ raise LocalJumpError, "No block given." unless block
2201
+ @validation_deferred = true
2202
+ block.call()
2203
+ @validation_deferred = false
2204
+ validate
2205
+ return nil
2206
+ end
2207
+
2208
+ private
2209
+ ##
2210
+ # Resolves paths to their simplest form.
2211
+ #
2212
+ # @param [String] path The path to normalize.
2213
+ #
2214
+ # @return [String] The normalized path.
2215
+ def self.normalize_path(path)
2216
+ # Section 5.2.4 of RFC 3986
2217
+
2218
+ return nil if path.nil?
2219
+ normalized_path = path.dup
2220
+ previous_state = normalized_path.dup
2221
+ begin
2222
+ previous_state = normalized_path.dup
2223
+ normalized_path.gsub!(/\/\.\//, "/")
2224
+ normalized_path.gsub!(/\/\.$/, "/")
2225
+ parent = normalized_path[/\/([^\/]+)\/\.\.\//, 1]
2226
+ if parent != "." && parent != ".."
2227
+ normalized_path.gsub!(/\/#{parent}\/\.\.\//, "/")
2228
+ end
2229
+ parent = normalized_path[/\/([^\/]+)\/\.\.$/, 1]
2230
+ if parent != "." && parent != ".."
2231
+ normalized_path.gsub!(/\/#{parent}\/\.\.$/, "/")
2232
+ end
2233
+ normalized_path.gsub!(/^\.\.?\/?/, "")
2234
+ normalized_path.gsub!(/^\/\.\.?\//, "/")
2235
+
2236
+ # Non-standard
2237
+ normalized_path.gsub!(/^(\/\.\.?)+\/?$/, "/")
2238
+ end until previous_state == normalized_path
2239
+ return normalized_path
2240
+ end
2241
+
2242
+ ##
2243
+ # Ensures that the URI is valid.
2244
+ def validate
2245
+ return if !!@validation_deferred
2246
+ if self.scheme != nil &&
2247
+ (self.host == nil || self.host == "") &&
2248
+ (self.path == nil || self.path == "")
2249
+ raise InvalidURIError,
2250
+ "Absolute URI missing hierarchical segment: '#{self.to_s}'"
2251
+ end
2252
+ if self.host == nil
2253
+ if self.port != nil ||
2254
+ self.user != nil ||
2255
+ self.password != nil
2256
+ raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
2257
+ end
2258
+ end
2259
+ if self.path != nil && self.path != "" && self.path[0..0] != "/" &&
2260
+ self.authority != nil
2261
+ raise InvalidURIError,
2262
+ "Cannot have a relative path with an authority set: '#{self.to_s}'"
2263
+ end
2264
+ return nil
2265
+ end
2266
+
2267
+ ##
2268
+ # Replaces the internal state of self with the specified URI's state.
2269
+ # Used in destructive operations to avoid massive code repetition.
2270
+ #
2271
+ # @param [Addressable::URI] uri The URI to replace <code>self</code> with.
2272
+ #
2273
+ # @return [Addressable::URI] <code>self</code>.
2274
+ def replace_self(uri)
2275
+ # Reset dependant values
2276
+ instance_variables.each do |var|
2277
+ instance_variable_set(var, nil)
2278
+ end
2279
+
2280
+ @scheme = uri.scheme
2281
+ @user = uri.user
2282
+ @password = uri.password
2283
+ @host = uri.host
2284
+ @port = uri.port
2285
+ @path = uri.path
2286
+ @query = uri.query
2287
+ @fragment = uri.fragment
2288
+ return self
2289
+ end
2290
+ end
2291
+ end