addressable 2.8.0 → 2.8.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -39
- data/Gemfile +4 -2
- data/Rakefile +11 -5
- data/addressable.gemspec +9 -18
- data/lib/addressable/idna/native.rb +8 -3
- data/lib/addressable/idna/pure.rb +10 -183
- data/lib/addressable/idna.rb +0 -1
- data/lib/addressable/template.rb +12 -14
- data/lib/addressable/uri.rb +230 -184
- data/lib/addressable/version.rb +1 -2
- data/spec/addressable/idna_spec.rb +6 -6
- data/spec/addressable/net_http_compat_spec.rb +0 -1
- data/spec/addressable/security_spec.rb +0 -1
- data/spec/addressable/template_spec.rb +69 -265
- data/spec/addressable/uri_spec.rb +176 -1
- data/tasks/gem.rake +5 -2
- metadata +11 -10
data/lib/addressable/uri.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# encoding:utf-8
|
4
3
|
#--
|
5
4
|
# Copyright (C) Bob Aman
|
6
5
|
#
|
@@ -38,20 +37,27 @@ module Addressable
|
|
38
37
|
##
|
39
38
|
# Container for the character classes specified in
|
40
39
|
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
|
40
|
+
#
|
41
|
+
# Note: Concatenated and interpolated `String`s are not affected by the
|
42
|
+
# `frozen_string_literal` directive and must be frozen explicitly.
|
43
|
+
#
|
44
|
+
# Interpolated `String`s *were* frozen this way before Ruby 3.0:
|
45
|
+
# https://bugs.ruby-lang.org/issues/17104
|
41
46
|
module CharacterClasses
|
42
47
|
ALPHA = "a-zA-Z"
|
43
48
|
DIGIT = "0-9"
|
44
49
|
GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
|
45
50
|
SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
|
46
|
-
RESERVED = GEN_DELIMS + SUB_DELIMS
|
47
|
-
UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze
|
52
|
+
UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze
|
53
|
+
RESERVED_AND_UNRESERVED = RESERVED + UNRESERVED
|
54
|
+
PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
|
55
|
+
SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
|
56
|
+
HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
|
57
|
+
AUTHORITY = (PCHAR + "\\[\\]").freeze
|
58
|
+
PATH = (PCHAR + "\\/").freeze
|
59
|
+
QUERY = (PCHAR + "\\/\\?").freeze
|
60
|
+
FRAGMENT = (PCHAR + "\\/\\?").freeze
|
55
61
|
end
|
56
62
|
|
57
63
|
module NormalizeCharacterClasses
|
@@ -63,6 +69,18 @@ module Addressable
|
|
63
69
|
QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
|
64
70
|
end
|
65
71
|
|
72
|
+
module CharacterClassesRegexps
|
73
|
+
AUTHORITY = /[^#{CharacterClasses::AUTHORITY}]/
|
74
|
+
FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
|
75
|
+
HOST = /[^#{CharacterClasses::HOST}]/
|
76
|
+
PATH = /[^#{CharacterClasses::PATH}]/
|
77
|
+
QUERY = /[^#{CharacterClasses::QUERY}]/
|
78
|
+
RESERVED = /[^#{CharacterClasses::RESERVED}]/
|
79
|
+
RESERVED_AND_UNRESERVED = /[^#{CharacterClasses::RESERVED_AND_UNRESERVED}]/
|
80
|
+
SCHEME = /[^#{CharacterClasses::SCHEME}]/
|
81
|
+
UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
|
82
|
+
end
|
83
|
+
|
66
84
|
SLASH = '/'
|
67
85
|
EMPTY_STR = ''
|
68
86
|
|
@@ -112,7 +130,7 @@ module Addressable
|
|
112
130
|
uri = uri.to_str
|
113
131
|
rescue TypeError, NoMethodError
|
114
132
|
raise TypeError, "Can't convert #{uri.class} into String."
|
115
|
-
end
|
133
|
+
end unless uri.is_a?(String)
|
116
134
|
|
117
135
|
# This Regexp supplied as an example in RFC 3986, and it works great.
|
118
136
|
scan = uri.scan(URIREGEX)
|
@@ -133,15 +151,15 @@ module Addressable
|
|
133
151
|
user = userinfo.strip[/^([^:]*):?/, 1]
|
134
152
|
password = userinfo.strip[/:(.*)$/, 1]
|
135
153
|
end
|
154
|
+
|
136
155
|
host = authority.sub(
|
137
156
|
/^([^\[\]]*)@/, EMPTY_STR
|
138
157
|
).sub(
|
139
158
|
/:([^:@\[\]]*?)$/, EMPTY_STR
|
140
159
|
)
|
160
|
+
|
141
161
|
port = authority[/:([^:@\[\]]*?)$/, 1]
|
142
|
-
|
143
|
-
if port == EMPTY_STR
|
144
|
-
port = nil
|
162
|
+
port = nil if port == EMPTY_STR
|
145
163
|
end
|
146
164
|
|
147
165
|
return new(
|
@@ -184,7 +202,7 @@ module Addressable
|
|
184
202
|
uri = uri.to_s
|
185
203
|
end
|
186
204
|
|
187
|
-
|
205
|
+
unless uri.respond_to?(:to_str)
|
188
206
|
raise TypeError, "Can't convert #{uri.class} into String."
|
189
207
|
end
|
190
208
|
# Otherwise, convert to a String
|
@@ -276,7 +294,7 @@ module Addressable
|
|
276
294
|
return nil unless path
|
277
295
|
# If a URI object is passed, just return itself.
|
278
296
|
return path if path.kind_of?(self)
|
279
|
-
|
297
|
+
unless path.respond_to?(:to_str)
|
280
298
|
raise TypeError, "Can't convert #{path.class} into String."
|
281
299
|
end
|
282
300
|
# Otherwise, convert to a String
|
@@ -324,13 +342,13 @@ module Addressable
|
|
324
342
|
# #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
|
325
343
|
def self.join(*uris)
|
326
344
|
uri_objects = uris.collect do |uri|
|
327
|
-
|
345
|
+
unless uri.respond_to?(:to_str)
|
328
346
|
raise TypeError, "Can't convert #{uri.class} into String."
|
329
347
|
end
|
330
348
|
uri.kind_of?(self) ? uri : self.parse(uri.to_str)
|
331
349
|
end
|
332
350
|
result = uri_objects.shift.dup
|
333
|
-
|
351
|
+
uri_objects.each do |uri|
|
334
352
|
result.join!(uri)
|
335
353
|
end
|
336
354
|
return result
|
@@ -339,17 +357,13 @@ module Addressable
|
|
339
357
|
##
|
340
358
|
# Tables used to optimize encoding operations in `self.encode_component`
|
341
359
|
# and `self.normalize_component`
|
342
|
-
SEQUENCE_ENCODING_TABLE =
|
343
|
-
|
344
|
-
|
345
|
-
end.join
|
346
|
-
end
|
360
|
+
SEQUENCE_ENCODING_TABLE = (0..255).map do |byte|
|
361
|
+
format("%02x", byte).freeze
|
362
|
+
end.freeze
|
347
363
|
|
348
|
-
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE =
|
349
|
-
|
350
|
-
|
351
|
-
end.join
|
352
|
-
end
|
364
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = (0..255).map do |byte|
|
365
|
+
format("%%%02X", byte).freeze
|
366
|
+
end.freeze
|
353
367
|
|
354
368
|
##
|
355
369
|
# Percent encodes a URI component.
|
@@ -386,9 +400,7 @@ module Addressable
|
|
386
400
|
# "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
|
387
401
|
# )
|
388
402
|
# => "simple%2Fexample"
|
389
|
-
def self.encode_component(component, character_class=
|
390
|
-
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
|
391
|
-
upcase_encoded='')
|
403
|
+
def self.encode_component(component, character_class=CharacterClassesRegexps::RESERVED_AND_UNRESERVED, upcase_encoded='')
|
392
404
|
return nil if component.nil?
|
393
405
|
|
394
406
|
begin
|
@@ -416,16 +428,17 @@ module Addressable
|
|
416
428
|
component = component.dup
|
417
429
|
component.force_encoding(Encoding::ASCII_8BIT)
|
418
430
|
# Avoiding gsub! because there are edge cases with frozen strings
|
419
|
-
component = component.gsub(character_class) do |
|
420
|
-
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[
|
431
|
+
component = component.gsub(character_class) do |char|
|
432
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[char.ord]
|
421
433
|
end
|
422
434
|
if upcase_encoded.length > 0
|
423
|
-
upcase_encoded_chars = upcase_encoded.
|
424
|
-
SEQUENCE_ENCODING_TABLE[
|
435
|
+
upcase_encoded_chars = upcase_encoded.bytes.map do |byte|
|
436
|
+
SEQUENCE_ENCODING_TABLE[byte]
|
425
437
|
end
|
426
438
|
component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
|
427
439
|
&:upcase)
|
428
440
|
end
|
441
|
+
|
429
442
|
return component
|
430
443
|
end
|
431
444
|
|
@@ -469,20 +482,14 @@ module Addressable
|
|
469
482
|
"Expected Class (String or Addressable::URI), " +
|
470
483
|
"got #{return_type.inspect}"
|
471
484
|
end
|
472
|
-
uri = uri.dup
|
473
|
-
# Seriously, only use UTF-8. I'm really not kidding!
|
474
|
-
uri.force_encoding("utf-8")
|
475
|
-
|
476
|
-
unless leave_encoded.empty?
|
477
|
-
leave_encoded = leave_encoded.dup.force_encoding("utf-8")
|
478
|
-
end
|
479
485
|
|
480
|
-
result = uri.gsub(/%[0-9a-f]{2}/
|
486
|
+
result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
|
481
487
|
c = sequence[1..3].to_i(16).chr
|
482
|
-
c.force_encoding(
|
488
|
+
c.force_encoding(sequence.encoding)
|
483
489
|
leave_encoded.include?(c) ? sequence : c
|
484
490
|
end
|
485
|
-
|
491
|
+
|
492
|
+
result.force_encoding(Encoding::UTF_8)
|
486
493
|
if return_type == String
|
487
494
|
return result
|
488
495
|
elsif return_type == ::Addressable::URI
|
@@ -543,7 +550,7 @@ module Addressable
|
|
543
550
|
# )
|
544
551
|
# => "one two%2Fthree&four"
|
545
552
|
def self.normalize_component(component, character_class=
|
546
|
-
|
553
|
+
CharacterClassesRegexps::RESERVED_AND_UNRESERVED,
|
547
554
|
leave_encoded='')
|
548
555
|
return nil if component.nil?
|
549
556
|
|
@@ -561,10 +568,9 @@ module Addressable
|
|
561
568
|
leave_re = if leave_encoded.length > 0
|
562
569
|
character_class = "#{character_class}%" unless character_class.include?('%')
|
563
570
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
end.flatten.join('|')})"
|
571
|
+
bytes = leave_encoded.bytes
|
572
|
+
leave_encoded_pattern = bytes.map { |b| SEQUENCE_ENCODING_TABLE[b] }.join('|')
|
573
|
+
"|%(?!#{leave_encoded_pattern}|#{leave_encoded_pattern.upcase})"
|
568
574
|
end
|
569
575
|
|
570
576
|
character_class = if leave_re
|
@@ -580,7 +586,7 @@ module Addressable
|
|
580
586
|
unencoded = self.unencode_component(component, String, leave_encoded)
|
581
587
|
begin
|
582
588
|
encoded = self.encode_component(
|
583
|
-
|
589
|
+
unencoded.unicode_normalize(:nfc),
|
584
590
|
character_class,
|
585
591
|
leave_encoded
|
586
592
|
)
|
@@ -624,15 +630,15 @@ module Addressable
|
|
624
630
|
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
|
625
631
|
encoded_uri = Addressable::URI.new(
|
626
632
|
:scheme => self.encode_component(uri_object.scheme,
|
627
|
-
Addressable::URI::
|
633
|
+
Addressable::URI::CharacterClassesRegexps::SCHEME),
|
628
634
|
:authority => self.encode_component(uri_object.authority,
|
629
|
-
Addressable::URI::
|
635
|
+
Addressable::URI::CharacterClassesRegexps::AUTHORITY),
|
630
636
|
:path => self.encode_component(uri_object.path,
|
631
|
-
Addressable::URI::
|
637
|
+
Addressable::URI::CharacterClassesRegexps::PATH),
|
632
638
|
:query => self.encode_component(uri_object.query,
|
633
|
-
Addressable::URI::
|
639
|
+
Addressable::URI::CharacterClassesRegexps::QUERY),
|
634
640
|
:fragment => self.encode_component(uri_object.fragment,
|
635
|
-
Addressable::URI::
|
641
|
+
Addressable::URI::CharacterClassesRegexps::FRAGMENT)
|
636
642
|
)
|
637
643
|
if return_type == String
|
638
644
|
return encoded_uri.to_s
|
@@ -688,8 +694,7 @@ module Addressable
|
|
688
694
|
components.each do |key, value|
|
689
695
|
if value != nil
|
690
696
|
begin
|
691
|
-
components[key] =
|
692
|
-
Addressable::IDNA.unicode_normalize_kc(value.to_str)
|
697
|
+
components[key] = value.to_str.unicode_normalize(:nfc)
|
693
698
|
rescue ArgumentError
|
694
699
|
# Likely a malformed UTF-8 character, skip unicode normalization
|
695
700
|
components[key] = value.to_str
|
@@ -698,19 +703,19 @@ module Addressable
|
|
698
703
|
end
|
699
704
|
encoded_uri = Addressable::URI.new(
|
700
705
|
:scheme => self.encode_component(components[:scheme],
|
701
|
-
Addressable::URI::
|
706
|
+
Addressable::URI::CharacterClassesRegexps::SCHEME),
|
702
707
|
:user => self.encode_component(components[:user],
|
703
|
-
Addressable::URI::
|
708
|
+
Addressable::URI::CharacterClassesRegexps::UNRESERVED),
|
704
709
|
:password => self.encode_component(components[:password],
|
705
|
-
Addressable::URI::
|
710
|
+
Addressable::URI::CharacterClassesRegexps::UNRESERVED),
|
706
711
|
:host => components[:host],
|
707
712
|
:port => components[:port],
|
708
713
|
:path => self.encode_component(components[:path],
|
709
|
-
Addressable::URI::
|
714
|
+
Addressable::URI::CharacterClassesRegexps::PATH),
|
710
715
|
:query => self.encode_component(components[:query],
|
711
|
-
Addressable::URI::
|
716
|
+
Addressable::URI::CharacterClassesRegexps::QUERY),
|
712
717
|
:fragment => self.encode_component(components[:fragment],
|
713
|
-
Addressable::URI::
|
718
|
+
Addressable::URI::CharacterClassesRegexps::FRAGMENT)
|
714
719
|
)
|
715
720
|
if return_type == String
|
716
721
|
return encoded_uri.to_s
|
@@ -761,11 +766,11 @@ module Addressable
|
|
761
766
|
[
|
762
767
|
self.encode_component(
|
763
768
|
key.gsub(/(\r\n|\n|\r)/, "\r\n"),
|
764
|
-
|
769
|
+
CharacterClassesRegexps::UNRESERVED
|
765
770
|
).gsub("%20", "+"),
|
766
771
|
self.encode_component(
|
767
772
|
value.gsub(/(\r\n|\n|\r)/, "\r\n"),
|
768
|
-
|
773
|
+
CharacterClassesRegexps::UNRESERVED
|
769
774
|
).gsub("%20", "+")
|
770
775
|
]
|
771
776
|
end
|
@@ -837,7 +842,9 @@ module Addressable
|
|
837
842
|
end
|
838
843
|
end
|
839
844
|
|
840
|
-
|
845
|
+
reset_ivs
|
846
|
+
|
847
|
+
defer_validation do
|
841
848
|
# Bunch of crazy logic required because of the composite components
|
842
849
|
# like userinfo and authority.
|
843
850
|
self.scheme = options[:scheme] if options[:scheme]
|
@@ -852,7 +859,8 @@ module Addressable
|
|
852
859
|
self.query_values = options[:query_values] if options[:query_values]
|
853
860
|
self.fragment = options[:fragment] if options[:fragment]
|
854
861
|
end
|
855
|
-
|
862
|
+
|
863
|
+
to_s # force path validation
|
856
864
|
end
|
857
865
|
|
858
866
|
##
|
@@ -879,9 +887,7 @@ module Addressable
|
|
879
887
|
# The scheme component for this URI.
|
880
888
|
#
|
881
889
|
# @return [String] The scheme component.
|
882
|
-
|
883
|
-
return defined?(@scheme) ? @scheme : nil
|
884
|
-
end
|
890
|
+
attr_reader :scheme
|
885
891
|
|
886
892
|
##
|
887
893
|
# The scheme component for this URI, normalized.
|
@@ -889,8 +895,8 @@ module Addressable
|
|
889
895
|
# @return [String] The scheme component, normalized.
|
890
896
|
def normalized_scheme
|
891
897
|
return nil unless self.scheme
|
892
|
-
@normalized_scheme
|
893
|
-
if self.scheme =~ /^\s*ssh\+svn\s*$/i
|
898
|
+
if @normalized_scheme == NONE
|
899
|
+
@normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i
|
894
900
|
"svn+ssh".dup
|
895
901
|
else
|
896
902
|
Addressable::URI.normalize_component(
|
@@ -900,7 +906,7 @@ module Addressable
|
|
900
906
|
end
|
901
907
|
end
|
902
908
|
# All normalized values should be UTF-8
|
903
|
-
@normalized_scheme
|
909
|
+
force_utf8_encoding_if_needed(@normalized_scheme)
|
904
910
|
@normalized_scheme
|
905
911
|
end
|
906
912
|
|
@@ -921,7 +927,7 @@ module Addressable
|
|
921
927
|
@scheme = nil if @scheme.to_s.strip.empty?
|
922
928
|
|
923
929
|
# Reset dependent values
|
924
|
-
|
930
|
+
@normalized_scheme = NONE
|
925
931
|
remove_composite_values
|
926
932
|
|
927
933
|
# Ensure we haven't created an invalid URI
|
@@ -932,9 +938,7 @@ module Addressable
|
|
932
938
|
# The user component for this URI.
|
933
939
|
#
|
934
940
|
# @return [String] The user component.
|
935
|
-
|
936
|
-
return defined?(@user) ? @user : nil
|
937
|
-
end
|
941
|
+
attr_reader :user
|
938
942
|
|
939
943
|
##
|
940
944
|
# The user component for this URI, normalized.
|
@@ -942,8 +946,8 @@ module Addressable
|
|
942
946
|
# @return [String] The user component, normalized.
|
943
947
|
def normalized_user
|
944
948
|
return nil unless self.user
|
945
|
-
return @normalized_user
|
946
|
-
@normalized_user
|
949
|
+
return @normalized_user unless @normalized_user == NONE
|
950
|
+
@normalized_user = begin
|
947
951
|
if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
|
948
952
|
(!self.password || self.password.strip.empty?)
|
949
953
|
nil
|
@@ -955,7 +959,7 @@ module Addressable
|
|
955
959
|
end
|
956
960
|
end
|
957
961
|
# All normalized values should be UTF-8
|
958
|
-
@normalized_user
|
962
|
+
force_utf8_encoding_if_needed(@normalized_user)
|
959
963
|
@normalized_user
|
960
964
|
end
|
961
965
|
|
@@ -971,14 +975,14 @@ module Addressable
|
|
971
975
|
|
972
976
|
# You can't have a nil user with a non-nil password
|
973
977
|
if password != nil
|
974
|
-
@user = EMPTY_STR
|
978
|
+
@user = EMPTY_STR unless user
|
975
979
|
end
|
976
980
|
|
977
981
|
# Reset dependent values
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
+
@userinfo = nil
|
983
|
+
@normalized_userinfo = NONE
|
984
|
+
@authority = nil
|
985
|
+
@normalized_user = NONE
|
982
986
|
remove_composite_values
|
983
987
|
|
984
988
|
# Ensure we haven't created an invalid URI
|
@@ -989,9 +993,7 @@ module Addressable
|
|
989
993
|
# The password component for this URI.
|
990
994
|
#
|
991
995
|
# @return [String] The password component.
|
992
|
-
|
993
|
-
return defined?(@password) ? @password : nil
|
994
|
-
end
|
996
|
+
attr_reader :password
|
995
997
|
|
996
998
|
##
|
997
999
|
# The password component for this URI, normalized.
|
@@ -999,8 +1001,8 @@ module Addressable
|
|
999
1001
|
# @return [String] The password component, normalized.
|
1000
1002
|
def normalized_password
|
1001
1003
|
return nil unless self.password
|
1002
|
-
return @normalized_password
|
1003
|
-
@normalized_password
|
1004
|
+
return @normalized_password unless @normalized_password == NONE
|
1005
|
+
@normalized_password = begin
|
1004
1006
|
if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
|
1005
1007
|
(!self.user || self.user.strip.empty?)
|
1006
1008
|
nil
|
@@ -1012,9 +1014,7 @@ module Addressable
|
|
1012
1014
|
end
|
1013
1015
|
end
|
1014
1016
|
# All normalized values should be UTF-8
|
1015
|
-
|
1016
|
-
@normalized_password.force_encoding(Encoding::UTF_8)
|
1017
|
-
end
|
1017
|
+
force_utf8_encoding_if_needed(@normalized_password)
|
1018
1018
|
@normalized_password
|
1019
1019
|
end
|
1020
1020
|
|
@@ -1029,17 +1029,15 @@ module Addressable
|
|
1029
1029
|
@password = new_password ? new_password.to_str : nil
|
1030
1030
|
|
1031
1031
|
# You can't have a nil user with a non-nil password
|
1032
|
-
@password ||= nil
|
1033
|
-
@user ||= nil
|
1034
1032
|
if @password != nil
|
1035
|
-
|
1033
|
+
self.user = EMPTY_STR if user.nil?
|
1036
1034
|
end
|
1037
1035
|
|
1038
1036
|
# Reset dependent values
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1037
|
+
@userinfo = nil
|
1038
|
+
@normalized_userinfo = NONE
|
1039
|
+
@authority = nil
|
1040
|
+
@normalized_password = NONE
|
1043
1041
|
remove_composite_values
|
1044
1042
|
|
1045
1043
|
# Ensure we haven't created an invalid URI
|
@@ -1069,8 +1067,8 @@ module Addressable
|
|
1069
1067
|
# @return [String] The userinfo component, normalized.
|
1070
1068
|
def normalized_userinfo
|
1071
1069
|
return nil unless self.userinfo
|
1072
|
-
return @normalized_userinfo
|
1073
|
-
@normalized_userinfo
|
1070
|
+
return @normalized_userinfo unless @normalized_userinfo == NONE
|
1071
|
+
@normalized_userinfo = begin
|
1074
1072
|
current_user = self.normalized_user
|
1075
1073
|
current_password = self.normalized_password
|
1076
1074
|
if !current_user && !current_password
|
@@ -1082,9 +1080,7 @@ module Addressable
|
|
1082
1080
|
end
|
1083
1081
|
end
|
1084
1082
|
# All normalized values should be UTF-8
|
1085
|
-
|
1086
|
-
@normalized_userinfo.force_encoding(Encoding::UTF_8)
|
1087
|
-
end
|
1083
|
+
force_utf8_encoding_if_needed(@normalized_userinfo)
|
1088
1084
|
@normalized_userinfo
|
1089
1085
|
end
|
1090
1086
|
|
@@ -1110,7 +1106,7 @@ module Addressable
|
|
1110
1106
|
self.user = new_user
|
1111
1107
|
|
1112
1108
|
# Reset dependent values
|
1113
|
-
|
1109
|
+
@authority = nil
|
1114
1110
|
remove_composite_values
|
1115
1111
|
|
1116
1112
|
# Ensure we haven't created an invalid URI
|
@@ -1121,9 +1117,7 @@ module Addressable
|
|
1121
1117
|
# The host component for this URI.
|
1122
1118
|
#
|
1123
1119
|
# @return [String] The host component.
|
1124
|
-
|
1125
|
-
return defined?(@host) ? @host : nil
|
1126
|
-
end
|
1120
|
+
attr_reader :host
|
1127
1121
|
|
1128
1122
|
##
|
1129
1123
|
# The host component for this URI, normalized.
|
@@ -1151,9 +1145,7 @@ module Addressable
|
|
1151
1145
|
end
|
1152
1146
|
end
|
1153
1147
|
# All normalized values should be UTF-8
|
1154
|
-
|
1155
|
-
@normalized_host.force_encoding(Encoding::UTF_8)
|
1156
|
-
end
|
1148
|
+
force_utf8_encoding_if_needed(@normalized_host)
|
1157
1149
|
@normalized_host
|
1158
1150
|
end
|
1159
1151
|
|
@@ -1168,8 +1160,8 @@ module Addressable
|
|
1168
1160
|
@host = new_host ? new_host.to_str : nil
|
1169
1161
|
|
1170
1162
|
# Reset dependent values
|
1171
|
-
|
1172
|
-
|
1163
|
+
@authority = nil
|
1164
|
+
@normalized_host = nil
|
1173
1165
|
remove_composite_values
|
1174
1166
|
|
1175
1167
|
# Ensure we haven't created an invalid URI
|
@@ -1271,9 +1263,7 @@ module Addressable
|
|
1271
1263
|
authority
|
1272
1264
|
end
|
1273
1265
|
# All normalized values should be UTF-8
|
1274
|
-
|
1275
|
-
@normalized_authority.force_encoding(Encoding::UTF_8)
|
1276
|
-
end
|
1266
|
+
force_utf8_encoding_if_needed(@normalized_authority)
|
1277
1267
|
@normalized_authority
|
1278
1268
|
end
|
1279
1269
|
|
@@ -1302,14 +1292,14 @@ module Addressable
|
|
1302
1292
|
end
|
1303
1293
|
|
1304
1294
|
# Password assigned first to ensure validity in case of nil
|
1305
|
-
self.password =
|
1306
|
-
self.user =
|
1307
|
-
self.host =
|
1308
|
-
self.port =
|
1295
|
+
self.password = new_password
|
1296
|
+
self.user = new_user
|
1297
|
+
self.host = new_host
|
1298
|
+
self.port = new_port
|
1309
1299
|
|
1310
1300
|
# Reset dependent values
|
1311
|
-
|
1312
|
-
|
1301
|
+
@userinfo = nil
|
1302
|
+
@normalized_userinfo = NONE
|
1313
1303
|
remove_composite_values
|
1314
1304
|
|
1315
1305
|
# Ensure we haven't created an invalid URI
|
@@ -1357,16 +1347,16 @@ module Addressable
|
|
1357
1347
|
new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
|
1358
1348
|
end
|
1359
1349
|
|
1360
|
-
self.scheme =
|
1361
|
-
self.host =
|
1362
|
-
self.port =
|
1350
|
+
self.scheme = new_scheme
|
1351
|
+
self.host = new_host
|
1352
|
+
self.port = new_port
|
1363
1353
|
self.userinfo = nil
|
1364
1354
|
|
1365
1355
|
# Reset dependent values
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1356
|
+
@userinfo = nil
|
1357
|
+
@normalized_userinfo = NONE
|
1358
|
+
@authority = nil
|
1359
|
+
@normalized_authority = nil
|
1370
1360
|
remove_composite_values
|
1371
1361
|
|
1372
1362
|
# Ensure we haven't created an invalid URI
|
@@ -1393,9 +1383,7 @@ module Addressable
|
|
1393
1383
|
# infer port numbers from default values.
|
1394
1384
|
#
|
1395
1385
|
# @return [Integer] The port component.
|
1396
|
-
|
1397
|
-
return defined?(@port) ? @port : nil
|
1398
|
-
end
|
1386
|
+
attr_reader :port
|
1399
1387
|
|
1400
1388
|
##
|
1401
1389
|
# The port component for this URI, normalized.
|
@@ -1403,8 +1391,8 @@ module Addressable
|
|
1403
1391
|
# @return [Integer] The port component, normalized.
|
1404
1392
|
def normalized_port
|
1405
1393
|
return nil unless self.port
|
1406
|
-
return @normalized_port
|
1407
|
-
@normalized_port
|
1394
|
+
return @normalized_port unless @normalized_port == NONE
|
1395
|
+
@normalized_port = begin
|
1408
1396
|
if URI.port_mapping[self.normalized_scheme] == self.port
|
1409
1397
|
nil
|
1410
1398
|
else
|
@@ -1435,8 +1423,8 @@ module Addressable
|
|
1435
1423
|
@port = nil if @port == 0
|
1436
1424
|
|
1437
1425
|
# Reset dependent values
|
1438
|
-
|
1439
|
-
|
1426
|
+
@authority = nil
|
1427
|
+
@normalized_port = NONE
|
1440
1428
|
remove_composite_values
|
1441
1429
|
|
1442
1430
|
# Ensure we haven't created an invalid URI
|
@@ -1507,7 +1495,7 @@ module Addressable
|
|
1507
1495
|
site_string
|
1508
1496
|
end
|
1509
1497
|
# All normalized values should be UTF-8
|
1510
|
-
@normalized_site
|
1498
|
+
force_utf8_encoding_if_needed(@normalized_site)
|
1511
1499
|
@normalized_site
|
1512
1500
|
end
|
1513
1501
|
|
@@ -1537,9 +1525,7 @@ module Addressable
|
|
1537
1525
|
# The path component for this URI.
|
1538
1526
|
#
|
1539
1527
|
# @return [String] The path component.
|
1540
|
-
|
1541
|
-
return defined?(@path) ? @path : EMPTY_STR
|
1542
|
-
end
|
1528
|
+
attr_reader :path
|
1543
1529
|
|
1544
1530
|
NORMPATH = /^(?!\/)[^\/:]*:.*$/
|
1545
1531
|
##
|
@@ -1570,7 +1556,7 @@ module Addressable
|
|
1570
1556
|
result
|
1571
1557
|
end
|
1572
1558
|
# All normalized values should be UTF-8
|
1573
|
-
@normalized_path
|
1559
|
+
force_utf8_encoding_if_needed(@normalized_path)
|
1574
1560
|
@normalized_path
|
1575
1561
|
end
|
1576
1562
|
|
@@ -1588,7 +1574,7 @@ module Addressable
|
|
1588
1574
|
end
|
1589
1575
|
|
1590
1576
|
# Reset dependent values
|
1591
|
-
|
1577
|
+
@normalized_path = nil
|
1592
1578
|
remove_composite_values
|
1593
1579
|
|
1594
1580
|
# Ensure we haven't created an invalid URI
|
@@ -1618,9 +1604,7 @@ module Addressable
|
|
1618
1604
|
# The query component for this URI.
|
1619
1605
|
#
|
1620
1606
|
# @return [String] The query component.
|
1621
|
-
|
1622
|
-
return defined?(@query) ? @query : nil
|
1623
|
-
end
|
1607
|
+
attr_reader :query
|
1624
1608
|
|
1625
1609
|
##
|
1626
1610
|
# The query component for this URI, normalized.
|
@@ -1628,8 +1612,8 @@ module Addressable
|
|
1628
1612
|
# @return [String] The query component, normalized.
|
1629
1613
|
def normalized_query(*flags)
|
1630
1614
|
return nil unless self.query
|
1631
|
-
return @normalized_query
|
1632
|
-
@normalized_query
|
1615
|
+
return @normalized_query unless @normalized_query == NONE
|
1616
|
+
@normalized_query = begin
|
1633
1617
|
modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
|
1634
1618
|
# Make sure possible key-value pair delimiters are escaped.
|
1635
1619
|
modified_query_class.sub!("\\&", "").sub!("\\;", "")
|
@@ -1646,7 +1630,7 @@ module Addressable
|
|
1646
1630
|
component == "" ? nil : component
|
1647
1631
|
end
|
1648
1632
|
# All normalized values should be UTF-8
|
1649
|
-
@normalized_query
|
1633
|
+
force_utf8_encoding_if_needed(@normalized_query)
|
1650
1634
|
@normalized_query
|
1651
1635
|
end
|
1652
1636
|
|
@@ -1661,7 +1645,7 @@ module Addressable
|
|
1661
1645
|
@query = new_query ? new_query.to_str : nil
|
1662
1646
|
|
1663
1647
|
# Reset dependent values
|
1664
|
-
|
1648
|
+
@normalized_query = NONE
|
1665
1649
|
remove_composite_values
|
1666
1650
|
end
|
1667
1651
|
|
@@ -1761,20 +1745,20 @@ module Addressable
|
|
1761
1745
|
buffer = "".dup
|
1762
1746
|
new_query_values.each do |key, value|
|
1763
1747
|
encoded_key = URI.encode_component(
|
1764
|
-
key,
|
1748
|
+
key, CharacterClassesRegexps::UNRESERVED
|
1765
1749
|
)
|
1766
1750
|
if value == nil
|
1767
1751
|
buffer << "#{encoded_key}&"
|
1768
1752
|
elsif value.kind_of?(Array)
|
1769
1753
|
value.each do |sub_value|
|
1770
1754
|
encoded_value = URI.encode_component(
|
1771
|
-
sub_value,
|
1755
|
+
sub_value, CharacterClassesRegexps::UNRESERVED
|
1772
1756
|
)
|
1773
1757
|
buffer << "#{encoded_key}=#{encoded_value}&"
|
1774
1758
|
end
|
1775
1759
|
else
|
1776
1760
|
encoded_value = URI.encode_component(
|
1777
|
-
value,
|
1761
|
+
value, CharacterClassesRegexps::UNRESERVED
|
1778
1762
|
)
|
1779
1763
|
buffer << "#{encoded_key}=#{encoded_value}&"
|
1780
1764
|
end
|
@@ -1823,9 +1807,7 @@ module Addressable
|
|
1823
1807
|
# The fragment component for this URI.
|
1824
1808
|
#
|
1825
1809
|
# @return [String] The fragment component.
|
1826
|
-
|
1827
|
-
return defined?(@fragment) ? @fragment : nil
|
1828
|
-
end
|
1810
|
+
attr_reader :fragment
|
1829
1811
|
|
1830
1812
|
##
|
1831
1813
|
# The fragment component for this URI, normalized.
|
@@ -1833,8 +1815,8 @@ module Addressable
|
|
1833
1815
|
# @return [String] The fragment component, normalized.
|
1834
1816
|
def normalized_fragment
|
1835
1817
|
return nil unless self.fragment
|
1836
|
-
return @normalized_fragment
|
1837
|
-
@normalized_fragment
|
1818
|
+
return @normalized_fragment unless @normalized_fragment == NONE
|
1819
|
+
@normalized_fragment = begin
|
1838
1820
|
component = Addressable::URI.normalize_component(
|
1839
1821
|
self.fragment,
|
1840
1822
|
Addressable::URI::NormalizeCharacterClasses::FRAGMENT
|
@@ -1842,9 +1824,7 @@ module Addressable
|
|
1842
1824
|
component == "" ? nil : component
|
1843
1825
|
end
|
1844
1826
|
# All normalized values should be UTF-8
|
1845
|
-
|
1846
|
-
@normalized_fragment.force_encoding(Encoding::UTF_8)
|
1847
|
-
end
|
1827
|
+
force_utf8_encoding_if_needed(@normalized_fragment)
|
1848
1828
|
@normalized_fragment
|
1849
1829
|
end
|
1850
1830
|
|
@@ -1859,7 +1839,7 @@ module Addressable
|
|
1859
1839
|
@fragment = new_fragment ? new_fragment.to_str : nil
|
1860
1840
|
|
1861
1841
|
# Reset dependent values
|
1862
|
-
|
1842
|
+
@normalized_fragment = NONE
|
1863
1843
|
remove_composite_values
|
1864
1844
|
|
1865
1845
|
# Ensure we haven't created an invalid URI
|
@@ -2025,7 +2005,7 @@ module Addressable
|
|
2025
2005
|
#
|
2026
2006
|
# @see Hash#merge
|
2027
2007
|
def merge(hash)
|
2028
|
-
|
2008
|
+
unless hash.respond_to?(:to_hash)
|
2029
2009
|
raise TypeError, "Can't convert #{hash.class} into Hash."
|
2030
2010
|
end
|
2031
2011
|
hash = hash.to_hash
|
@@ -2419,7 +2399,27 @@ module Addressable
|
|
2419
2399
|
yield
|
2420
2400
|
@validation_deferred = false
|
2421
2401
|
validate
|
2422
|
-
|
2402
|
+
ensure
|
2403
|
+
@validation_deferred = false
|
2404
|
+
end
|
2405
|
+
|
2406
|
+
def encode_with(coder)
|
2407
|
+
instance_variables.each do |ivar|
|
2408
|
+
value = instance_variable_get(ivar)
|
2409
|
+
if value != NONE
|
2410
|
+
key = ivar.to_s.slice(1..-1)
|
2411
|
+
coder[key] = value
|
2412
|
+
end
|
2413
|
+
end
|
2414
|
+
nil
|
2415
|
+
end
|
2416
|
+
|
2417
|
+
def init_with(coder)
|
2418
|
+
reset_ivs
|
2419
|
+
coder.map.each do |key, value|
|
2420
|
+
instance_variable_set("@#{key}", value)
|
2421
|
+
end
|
2422
|
+
nil
|
2423
2423
|
end
|
2424
2424
|
|
2425
2425
|
protected
|
@@ -2440,30 +2440,35 @@ module Addressable
|
|
2440
2440
|
def self.normalize_path(path)
|
2441
2441
|
# Section 5.2.4 of RFC 3986
|
2442
2442
|
|
2443
|
-
return
|
2443
|
+
return if path.nil?
|
2444
2444
|
normalized_path = path.dup
|
2445
|
-
|
2446
|
-
mod = nil
|
2445
|
+
loop do
|
2447
2446
|
mod ||= normalized_path.gsub!(RULE_2A, SLASH)
|
2448
2447
|
|
2449
2448
|
pair = normalized_path.match(RULE_2B_2C)
|
2450
|
-
|
2449
|
+
if pair
|
2450
|
+
parent = pair[1]
|
2451
|
+
current = pair[2]
|
2452
|
+
else
|
2453
|
+
parent = nil
|
2454
|
+
current = nil
|
2455
|
+
end
|
2456
|
+
|
2457
|
+
regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|"
|
2458
|
+
regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
|
2459
|
+
|
2451
2460
|
if pair && ((parent != SELF_REF && parent != PARENT) ||
|
2452
2461
|
(current != SELF_REF && current != PARENT))
|
2453
|
-
mod ||= normalized_path.gsub!(
|
2454
|
-
Regexp.new(
|
2455
|
-
"/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
|
2456
|
-
"(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
|
2457
|
-
), SLASH
|
2458
|
-
)
|
2462
|
+
mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH)
|
2459
2463
|
end
|
2460
2464
|
|
2461
2465
|
mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
|
2462
2466
|
# Non-standard, removes prefixed dotted segments from path.
|
2463
2467
|
mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
|
2464
|
-
|
2468
|
+
break if mod.nil?
|
2469
|
+
end
|
2465
2470
|
|
2466
|
-
|
2471
|
+
normalized_path
|
2467
2472
|
end
|
2468
2473
|
|
2469
2474
|
##
|
@@ -2513,11 +2518,7 @@ module Addressable
|
|
2513
2518
|
# @return [Addressable::URI] <code>self</code>.
|
2514
2519
|
def replace_self(uri)
|
2515
2520
|
# Reset dependent values
|
2516
|
-
|
2517
|
-
if instance_variable_defined?(var) && var != :@validation_deferred
|
2518
|
-
remove_instance_variable(var)
|
2519
|
-
end
|
2520
|
-
end
|
2521
|
+
reset_ivs
|
2521
2522
|
|
2522
2523
|
@scheme = uri.scheme
|
2523
2524
|
@user = uri.user
|
@@ -2549,8 +2550,53 @@ module Addressable
|
|
2549
2550
|
#
|
2550
2551
|
# @api private
|
2551
2552
|
def remove_composite_values
|
2552
|
-
|
2553
|
-
|
2553
|
+
@uri_string = nil
|
2554
|
+
@hash = nil
|
2555
|
+
end
|
2556
|
+
|
2557
|
+
##
|
2558
|
+
# Converts the string to be UTF-8 if it is not already UTF-8
|
2559
|
+
#
|
2560
|
+
# @api private
|
2561
|
+
def force_utf8_encoding_if_needed(str)
|
2562
|
+
if str && str.encoding != Encoding::UTF_8
|
2563
|
+
str.force_encoding(Encoding::UTF_8)
|
2564
|
+
end
|
2554
2565
|
end
|
2566
|
+
|
2567
|
+
private
|
2568
|
+
|
2569
|
+
##
|
2570
|
+
# Resets instance variables
|
2571
|
+
#
|
2572
|
+
# @api private
|
2573
|
+
def reset_ivs
|
2574
|
+
@scheme = nil
|
2575
|
+
@user = nil
|
2576
|
+
@normalized_scheme = NONE
|
2577
|
+
@normalized_user = NONE
|
2578
|
+
@uri_string = nil
|
2579
|
+
@hash = nil
|
2580
|
+
@userinfo = nil
|
2581
|
+
@normalized_userinfo = NONE
|
2582
|
+
@authority = nil
|
2583
|
+
@password = nil
|
2584
|
+
@normalized_authority = nil
|
2585
|
+
@port = nil
|
2586
|
+
@normalized_password = NONE
|
2587
|
+
@host = nil
|
2588
|
+
@normalized_host = nil
|
2589
|
+
@normalized_port = NONE
|
2590
|
+
@path = EMPTY_STR
|
2591
|
+
@normalized_path = nil
|
2592
|
+
@normalized_query = NONE
|
2593
|
+
@fragment = nil
|
2594
|
+
@normalized_fragment = NONE
|
2595
|
+
@query = nil
|
2596
|
+
end
|
2597
|
+
|
2598
|
+
NONE = Module.new.freeze
|
2599
|
+
|
2600
|
+
private_constant :NONE
|
2555
2601
|
end
|
2556
2602
|
end
|