addressable 2.8.0 → 2.8.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +94 -39
- data/Gemfile +5 -2
- data/Rakefile +11 -8
- 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 +13 -15
- data/lib/addressable/uri.rb +231 -185
- 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 +7 -4
- metadata +8 -7
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
|
##
|
@@ -1553,7 +1539,7 @@ module Addressable
|
|
1553
1539
|
# Relative paths with colons in the first segment are ambiguous.
|
1554
1540
|
path = path.sub(":", "%2F")
|
1555
1541
|
end
|
1556
|
-
# String#split(
|
1542
|
+
# String#split(delimiter, -1) uses the more strict splitting behavior
|
1557
1543
|
# found by default in Python.
|
1558
1544
|
result = path.strip.split(SLASH, -1).map do |segment|
|
1559
1545
|
Addressable::URI.normalize_component(
|
@@ -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
|