addressable 2.6.0 → 2.8.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +35 -0
- data/Gemfile +14 -16
- data/README.md +12 -10
- data/Rakefile +3 -3
- data/lib/addressable/idna/native.rb +0 -1
- data/lib/addressable/idna/pure.rb +52 -51
- data/lib/addressable/idna.rb +0 -1
- data/lib/addressable/template.rb +28 -43
- data/lib/addressable/uri.rb +129 -79
- data/lib/addressable/version.rb +2 -3
- data/spec/addressable/idna_spec.rb +3 -2
- data/spec/addressable/net_http_compat_spec.rb +0 -1
- data/spec/addressable/security_spec.rb +0 -1
- data/spec/addressable/template_spec.rb +18 -1
- data/spec/addressable/uri_spec.rb +408 -208
- data/spec/spec_helper.rb +9 -0
- data/tasks/gem.rake +9 -7
- data/tasks/profile.rake +72 -0
- data/tasks/rspec.rake +1 -1
- metadata +15 -15
- data/spec/addressable/rack_mount_compat_spec.rb +0 -106
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,35 @@ 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
|
-
PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
|
49
|
-
SCHEME = ALPHA + DIGIT + "\\-\\+\\."
|
50
|
-
HOST = UNRESERVED + SUB_DELIMS + "\\[\\:\\]"
|
51
|
-
AUTHORITY = PCHAR
|
52
|
-
PATH = PCHAR + "\\/"
|
53
|
-
QUERY = PCHAR + "\\/\\?"
|
54
|
-
FRAGMENT = PCHAR + "\\/\\?"
|
51
|
+
RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze
|
52
|
+
UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze
|
53
|
+
PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
|
54
|
+
SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
|
55
|
+
HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
|
56
|
+
AUTHORITY = (PCHAR + "\\[\\:\\]").freeze
|
57
|
+
PATH = (PCHAR + "\\/").freeze
|
58
|
+
QUERY = (PCHAR + "\\/\\?").freeze
|
59
|
+
FRAGMENT = (PCHAR + "\\/\\?").freeze
|
60
|
+
end
|
61
|
+
|
62
|
+
module NormalizeCharacterClasses
|
63
|
+
HOST = /[^#{CharacterClasses::HOST}]/
|
64
|
+
UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
|
65
|
+
PCHAR = /[^#{CharacterClasses::PCHAR}]/
|
66
|
+
SCHEME = /[^#{CharacterClasses::SCHEME}]/
|
67
|
+
FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
|
68
|
+
QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
|
55
69
|
end
|
56
70
|
|
57
71
|
SLASH = '/'
|
@@ -73,7 +87,7 @@ module Addressable
|
|
73
87
|
"wais" => 210,
|
74
88
|
"ldap" => 389,
|
75
89
|
"prospero" => 1525
|
76
|
-
}
|
90
|
+
}.freeze
|
77
91
|
|
78
92
|
##
|
79
93
|
# Returns a URI object based on the parsed string.
|
@@ -207,7 +221,7 @@ module Addressable
|
|
207
221
|
fragments = match.captures
|
208
222
|
authority = fragments[3]
|
209
223
|
if authority && authority.length > 0
|
210
|
-
new_authority = authority.
|
224
|
+
new_authority = authority.tr("\\", "/").gsub(" ", "%20")
|
211
225
|
# NOTE: We want offset 4, not 3!
|
212
226
|
offset = match.offset(4)
|
213
227
|
uri = uri.dup
|
@@ -218,8 +232,9 @@ module Addressable
|
|
218
232
|
parsed = self.parse(hints[:scheme] + "://" + uri)
|
219
233
|
end
|
220
234
|
if parsed.path.include?(".")
|
221
|
-
|
222
|
-
|
235
|
+
if parsed.path[/\b@\b/]
|
236
|
+
parsed.scheme = "mailto" unless parsed.scheme
|
237
|
+
elsif new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
|
223
238
|
parsed.defer_validation do
|
224
239
|
new_path = parsed.path.sub(
|
225
240
|
Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
|
@@ -281,15 +296,15 @@ module Addressable
|
|
281
296
|
uri.path.sub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
|
282
297
|
"/#{$1.downcase}:/"
|
283
298
|
end
|
284
|
-
uri.path.
|
299
|
+
uri.path.tr!("\\", SLASH)
|
285
300
|
if File.exist?(uri.path) &&
|
286
301
|
File.stat(uri.path).directory?
|
287
|
-
uri.path.
|
302
|
+
uri.path.chomp!(SLASH)
|
288
303
|
uri.path = uri.path + '/'
|
289
304
|
end
|
290
305
|
|
291
306
|
# If the path is absolute, set the scheme and host.
|
292
|
-
if uri.path
|
307
|
+
if uri.path.start_with?(SLASH)
|
293
308
|
uri.scheme = "file"
|
294
309
|
uri.host = EMPTY_STR
|
295
310
|
end
|
@@ -326,6 +341,21 @@ module Addressable
|
|
326
341
|
return result
|
327
342
|
end
|
328
343
|
|
344
|
+
##
|
345
|
+
# Tables used to optimize encoding operations in `self.encode_component`
|
346
|
+
# and `self.normalize_component`
|
347
|
+
SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
|
348
|
+
hash[sequence] = sequence.unpack("C*").map do |c|
|
349
|
+
format("%02x", c)
|
350
|
+
end.join
|
351
|
+
end
|
352
|
+
|
353
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
|
354
|
+
hash[sequence] = sequence.unpack("C*").map do |c|
|
355
|
+
format("%%%02X", c)
|
356
|
+
end.join
|
357
|
+
end
|
358
|
+
|
329
359
|
##
|
330
360
|
# Percent encodes a URI component.
|
331
361
|
#
|
@@ -392,18 +422,20 @@ module Addressable
|
|
392
422
|
component.force_encoding(Encoding::ASCII_8BIT)
|
393
423
|
# Avoiding gsub! because there are edge cases with frozen strings
|
394
424
|
component = component.gsub(character_class) do |sequence|
|
395
|
-
|
425
|
+
SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
|
396
426
|
end
|
397
427
|
if upcase_encoded.length > 0
|
398
|
-
|
399
|
-
char
|
400
|
-
end
|
428
|
+
upcase_encoded_chars = upcase_encoded.chars.map do |char|
|
429
|
+
SEQUENCE_ENCODING_TABLE[char]
|
430
|
+
end
|
431
|
+
component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
|
432
|
+
&:upcase)
|
401
433
|
end
|
402
434
|
return component
|
403
435
|
end
|
404
436
|
|
405
437
|
class << self
|
406
|
-
alias_method :
|
438
|
+
alias_method :escape_component, :encode_component
|
407
439
|
end
|
408
440
|
|
409
441
|
##
|
@@ -442,15 +474,13 @@ module Addressable
|
|
442
474
|
"Expected Class (String or Addressable::URI), " +
|
443
475
|
"got #{return_type.inspect}"
|
444
476
|
end
|
445
|
-
|
446
|
-
|
447
|
-
uri.force_encoding("utf-8")
|
448
|
-
leave_encoded = leave_encoded.dup.force_encoding("utf-8")
|
449
|
-
result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
|
477
|
+
|
478
|
+
result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
|
450
479
|
c = sequence[1..3].to_i(16).chr
|
451
|
-
c.force_encoding(
|
480
|
+
c.force_encoding(sequence.encoding)
|
452
481
|
leave_encoded.include?(c) ? sequence : c
|
453
482
|
end
|
483
|
+
|
454
484
|
result.force_encoding("utf-8")
|
455
485
|
if return_type == String
|
456
486
|
return result
|
@@ -530,13 +560,17 @@ module Addressable
|
|
530
560
|
leave_re = if leave_encoded.length > 0
|
531
561
|
character_class = "#{character_class}%" unless character_class.include?('%')
|
532
562
|
|
533
|
-
"|%(?!#{leave_encoded.chars.
|
534
|
-
seq = char
|
563
|
+
"|%(?!#{leave_encoded.chars.flat_map do |char|
|
564
|
+
seq = SEQUENCE_ENCODING_TABLE[char]
|
535
565
|
[seq.upcase, seq.downcase]
|
536
|
-
end.
|
566
|
+
end.join('|')})"
|
537
567
|
end
|
538
568
|
|
539
|
-
character_class =
|
569
|
+
character_class = if leave_re
|
570
|
+
/[^#{character_class}]#{leave_re}/
|
571
|
+
else
|
572
|
+
/[^#{character_class}]/
|
573
|
+
end
|
540
574
|
end
|
541
575
|
# We can't perform regexps on invalid UTF sequences, but
|
542
576
|
# here we need to, so switch to ASCII.
|
@@ -860,12 +894,12 @@ module Addressable
|
|
860
894
|
else
|
861
895
|
Addressable::URI.normalize_component(
|
862
896
|
self.scheme.strip.downcase,
|
863
|
-
Addressable::URI::
|
897
|
+
Addressable::URI::NormalizeCharacterClasses::SCHEME
|
864
898
|
)
|
865
899
|
end
|
866
900
|
end
|
867
901
|
# All normalized values should be UTF-8
|
868
|
-
@normalized_scheme
|
902
|
+
force_utf8_encoding_if_needed(@normalized_scheme)
|
869
903
|
@normalized_scheme
|
870
904
|
end
|
871
905
|
|
@@ -880,7 +914,7 @@ module Addressable
|
|
880
914
|
new_scheme = new_scheme.to_str
|
881
915
|
end
|
882
916
|
if new_scheme && new_scheme !~ /\A[a-z][a-z0-9\.\+\-]*\z/i
|
883
|
-
raise InvalidURIError, "Invalid scheme format: #{new_scheme}"
|
917
|
+
raise InvalidURIError, "Invalid scheme format: '#{new_scheme}'"
|
884
918
|
end
|
885
919
|
@scheme = new_scheme
|
886
920
|
@scheme = nil if @scheme.to_s.strip.empty?
|
@@ -915,12 +949,12 @@ module Addressable
|
|
915
949
|
else
|
916
950
|
Addressable::URI.normalize_component(
|
917
951
|
self.user.strip,
|
918
|
-
Addressable::URI::
|
952
|
+
Addressable::URI::NormalizeCharacterClasses::UNRESERVED
|
919
953
|
)
|
920
954
|
end
|
921
955
|
end
|
922
956
|
# All normalized values should be UTF-8
|
923
|
-
@normalized_user
|
957
|
+
force_utf8_encoding_if_needed(@normalized_user)
|
924
958
|
@normalized_user
|
925
959
|
end
|
926
960
|
|
@@ -972,14 +1006,12 @@ module Addressable
|
|
972
1006
|
else
|
973
1007
|
Addressable::URI.normalize_component(
|
974
1008
|
self.password.strip,
|
975
|
-
Addressable::URI::
|
1009
|
+
Addressable::URI::NormalizeCharacterClasses::UNRESERVED
|
976
1010
|
)
|
977
1011
|
end
|
978
1012
|
end
|
979
1013
|
# All normalized values should be UTF-8
|
980
|
-
|
981
|
-
@normalized_password.force_encoding(Encoding::UTF_8)
|
982
|
-
end
|
1014
|
+
force_utf8_encoding_if_needed(@normalized_password)
|
983
1015
|
@normalized_password
|
984
1016
|
end
|
985
1017
|
|
@@ -1047,9 +1079,7 @@ module Addressable
|
|
1047
1079
|
end
|
1048
1080
|
end
|
1049
1081
|
# All normalized values should be UTF-8
|
1050
|
-
|
1051
|
-
@normalized_userinfo.force_encoding(Encoding::UTF_8)
|
1052
|
-
end
|
1082
|
+
force_utf8_encoding_if_needed(@normalized_userinfo)
|
1053
1083
|
@normalized_userinfo
|
1054
1084
|
end
|
1055
1085
|
|
@@ -1096,6 +1126,7 @@ module Addressable
|
|
1096
1126
|
# @return [String] The host component, normalized.
|
1097
1127
|
def normalized_host
|
1098
1128
|
return nil unless self.host
|
1129
|
+
|
1099
1130
|
@normalized_host ||= begin
|
1100
1131
|
if !self.host.strip.empty?
|
1101
1132
|
result = ::Addressable::IDNA.to_ascii(
|
@@ -1107,14 +1138,15 @@ module Addressable
|
|
1107
1138
|
end
|
1108
1139
|
result = Addressable::URI.normalize_component(
|
1109
1140
|
result,
|
1110
|
-
|
1141
|
+
NormalizeCharacterClasses::HOST
|
1142
|
+
)
|
1111
1143
|
result
|
1112
1144
|
else
|
1113
1145
|
EMPTY_STR.dup
|
1114
1146
|
end
|
1115
1147
|
end
|
1116
1148
|
# All normalized values should be UTF-8
|
1117
|
-
@normalized_host
|
1149
|
+
force_utf8_encoding_if_needed(@normalized_host)
|
1118
1150
|
@normalized_host
|
1119
1151
|
end
|
1120
1152
|
|
@@ -1172,7 +1204,7 @@ module Addressable
|
|
1172
1204
|
# Returns the top-level domain for this host.
|
1173
1205
|
#
|
1174
1206
|
# @example
|
1175
|
-
# Addressable::URI.parse("www.example.co.uk").tld # => "co.uk"
|
1207
|
+
# Addressable::URI.parse("http://www.example.co.uk").tld # => "co.uk"
|
1176
1208
|
def tld
|
1177
1209
|
PublicSuffix.parse(self.host, ignore_private: true).tld
|
1178
1210
|
end
|
@@ -1182,7 +1214,7 @@ module Addressable
|
|
1182
1214
|
#
|
1183
1215
|
# @param [String, #to_str] new_tld The new top-level domain.
|
1184
1216
|
def tld=(new_tld)
|
1185
|
-
replaced_tld =
|
1217
|
+
replaced_tld = host.sub(/#{tld}\z/, new_tld)
|
1186
1218
|
self.host = PublicSuffix::Domain.new(replaced_tld).to_s
|
1187
1219
|
end
|
1188
1220
|
|
@@ -1190,7 +1222,7 @@ module Addressable
|
|
1190
1222
|
# Returns the public suffix domain for this host.
|
1191
1223
|
#
|
1192
1224
|
# @example
|
1193
|
-
# Addressable::URI.parse("www.example.co.uk").domain # => "example.co.uk"
|
1225
|
+
# Addressable::URI.parse("http://www.example.co.uk").domain # => "example.co.uk"
|
1194
1226
|
def domain
|
1195
1227
|
PublicSuffix.domain(self.host, ignore_private: true)
|
1196
1228
|
end
|
@@ -1232,9 +1264,7 @@ module Addressable
|
|
1232
1264
|
authority
|
1233
1265
|
end
|
1234
1266
|
# All normalized values should be UTF-8
|
1235
|
-
|
1236
|
-
@normalized_authority.force_encoding(Encoding::UTF_8)
|
1237
|
-
end
|
1267
|
+
force_utf8_encoding_if_needed(@normalized_authority)
|
1238
1268
|
@normalized_authority
|
1239
1269
|
end
|
1240
1270
|
|
@@ -1468,7 +1498,7 @@ module Addressable
|
|
1468
1498
|
site_string
|
1469
1499
|
end
|
1470
1500
|
# All normalized values should be UTF-8
|
1471
|
-
@normalized_site
|
1501
|
+
force_utf8_encoding_if_needed(@normalized_site)
|
1472
1502
|
@normalized_site
|
1473
1503
|
end
|
1474
1504
|
|
@@ -1519,7 +1549,7 @@ module Addressable
|
|
1519
1549
|
result = path.strip.split(SLASH, -1).map do |segment|
|
1520
1550
|
Addressable::URI.normalize_component(
|
1521
1551
|
segment,
|
1522
|
-
Addressable::URI::
|
1552
|
+
Addressable::URI::NormalizeCharacterClasses::PCHAR
|
1523
1553
|
)
|
1524
1554
|
end.join(SLASH)
|
1525
1555
|
|
@@ -1531,7 +1561,7 @@ module Addressable
|
|
1531
1561
|
result
|
1532
1562
|
end
|
1533
1563
|
# All normalized values should be UTF-8
|
1534
|
-
@normalized_path
|
1564
|
+
force_utf8_encoding_if_needed(@normalized_path)
|
1535
1565
|
@normalized_path
|
1536
1566
|
end
|
1537
1567
|
|
@@ -1594,15 +1624,20 @@ module Addressable
|
|
1594
1624
|
modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
|
1595
1625
|
# Make sure possible key-value pair delimiters are escaped.
|
1596
1626
|
modified_query_class.sub!("\\&", "").sub!("\\;", "")
|
1597
|
-
pairs = (
|
1627
|
+
pairs = (query || "").split("&", -1)
|
1628
|
+
pairs.delete_if(&:empty?).uniq! if flags.include?(:compacted)
|
1598
1629
|
pairs.sort! if flags.include?(:sorted)
|
1599
1630
|
component = pairs.map do |pair|
|
1600
|
-
Addressable::URI.normalize_component(
|
1631
|
+
Addressable::URI.normalize_component(
|
1632
|
+
pair,
|
1633
|
+
Addressable::URI::NormalizeCharacterClasses::QUERY,
|
1634
|
+
"+"
|
1635
|
+
)
|
1601
1636
|
end.join("&")
|
1602
1637
|
component == "" ? nil : component
|
1603
1638
|
end
|
1604
1639
|
# All normalized values should be UTF-8
|
1605
|
-
@normalized_query
|
1640
|
+
force_utf8_encoding_if_needed(@normalized_query)
|
1606
1641
|
@normalized_query
|
1607
1642
|
end
|
1608
1643
|
|
@@ -1656,11 +1691,13 @@ module Addressable
|
|
1656
1691
|
# so it's best to make all changes in-place.
|
1657
1692
|
pair[0] = URI.unencode_component(pair[0])
|
1658
1693
|
if pair[1].respond_to?(:to_str)
|
1694
|
+
value = pair[1].to_str
|
1659
1695
|
# I loathe the fact that I have to do this. Stupid HTML 4.01.
|
1660
1696
|
# Treating '+' as a space was just an unbelievably bad idea.
|
1661
1697
|
# There was nothing wrong with '%20'!
|
1662
1698
|
# If it ain't broke, don't fix it!
|
1663
|
-
|
1699
|
+
value = value.tr("+", " ") if ["http", "https", nil].include?(scheme)
|
1700
|
+
pair[1] = URI.unencode_component(value)
|
1664
1701
|
end
|
1665
1702
|
if return_type == Hash
|
1666
1703
|
accu[pair[0]] = pair[1]
|
@@ -1791,14 +1828,12 @@ module Addressable
|
|
1791
1828
|
@normalized_fragment ||= begin
|
1792
1829
|
component = Addressable::URI.normalize_component(
|
1793
1830
|
self.fragment,
|
1794
|
-
Addressable::URI::
|
1831
|
+
Addressable::URI::NormalizeCharacterClasses::FRAGMENT
|
1795
1832
|
)
|
1796
1833
|
component == "" ? nil : component
|
1797
1834
|
end
|
1798
1835
|
# All normalized values should be UTF-8
|
1799
|
-
|
1800
|
-
@normalized_fragment.force_encoding(Encoding::UTF_8)
|
1801
|
-
end
|
1836
|
+
force_utf8_encoding_if_needed(@normalized_fragment)
|
1802
1837
|
@normalized_fragment
|
1803
1838
|
end
|
1804
1839
|
|
@@ -1917,7 +1952,7 @@ module Addressable
|
|
1917
1952
|
# Section 5.2.3 of RFC 3986
|
1918
1953
|
#
|
1919
1954
|
# Removes the right-most path segment from the base path.
|
1920
|
-
if base_path
|
1955
|
+
if base_path.include?(SLASH)
|
1921
1956
|
base_path.sub!(/\/[^\/]+$/, SLASH)
|
1922
1957
|
else
|
1923
1958
|
base_path = EMPTY_STR
|
@@ -2367,10 +2402,10 @@ module Addressable
|
|
2367
2402
|
#
|
2368
2403
|
# @param [Proc] block
|
2369
2404
|
# A set of operations to perform on a given URI.
|
2370
|
-
def defer_validation
|
2371
|
-
raise LocalJumpError, "No block given." unless
|
2405
|
+
def defer_validation
|
2406
|
+
raise LocalJumpError, "No block given." unless block_given?
|
2372
2407
|
@validation_deferred = true
|
2373
|
-
|
2408
|
+
yield
|
2374
2409
|
@validation_deferred = false
|
2375
2410
|
validate
|
2376
2411
|
return nil
|
@@ -2394,30 +2429,35 @@ module Addressable
|
|
2394
2429
|
def self.normalize_path(path)
|
2395
2430
|
# Section 5.2.4 of RFC 3986
|
2396
2431
|
|
2397
|
-
return
|
2432
|
+
return if path.nil?
|
2398
2433
|
normalized_path = path.dup
|
2399
|
-
|
2400
|
-
mod = nil
|
2434
|
+
loop do
|
2401
2435
|
mod ||= normalized_path.gsub!(RULE_2A, SLASH)
|
2402
2436
|
|
2403
2437
|
pair = normalized_path.match(RULE_2B_2C)
|
2404
|
-
|
2438
|
+
if pair
|
2439
|
+
parent = pair[1]
|
2440
|
+
current = pair[2]
|
2441
|
+
else
|
2442
|
+
parent = nil
|
2443
|
+
current = nil
|
2444
|
+
end
|
2445
|
+
|
2446
|
+
regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|"
|
2447
|
+
regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
|
2448
|
+
|
2405
2449
|
if pair && ((parent != SELF_REF && parent != PARENT) ||
|
2406
2450
|
(current != SELF_REF && current != PARENT))
|
2407
|
-
mod ||= normalized_path.gsub!(
|
2408
|
-
Regexp.new(
|
2409
|
-
"/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
|
2410
|
-
"(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
|
2411
|
-
), SLASH
|
2412
|
-
)
|
2451
|
+
mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH)
|
2413
2452
|
end
|
2414
2453
|
|
2415
2454
|
mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
|
2416
2455
|
# Non-standard, removes prefixed dotted segments from path.
|
2417
2456
|
mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
|
2418
|
-
|
2457
|
+
break if mod.nil?
|
2458
|
+
end
|
2419
2459
|
|
2420
|
-
|
2460
|
+
normalized_path
|
2421
2461
|
end
|
2422
2462
|
|
2423
2463
|
##
|
@@ -2506,5 +2546,15 @@ module Addressable
|
|
2506
2546
|
remove_instance_variable(:@uri_string) if defined?(@uri_string)
|
2507
2547
|
remove_instance_variable(:@hash) if defined?(@hash)
|
2508
2548
|
end
|
2549
|
+
|
2550
|
+
##
|
2551
|
+
# Converts the string to be UTF-8 if it is not already UTF-8
|
2552
|
+
#
|
2553
|
+
# @api private
|
2554
|
+
def force_utf8_encoding_if_needed(str)
|
2555
|
+
if str && str.encoding != Encoding::UTF_8
|
2556
|
+
str.force_encoding(Encoding::UTF_8)
|
2557
|
+
end
|
2558
|
+
end
|
2509
2559
|
end
|
2510
2560
|
end
|
data/lib/addressable/version.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
|
#
|
@@ -23,8 +22,8 @@ if !defined?(Addressable::VERSION)
|
|
23
22
|
module Addressable
|
24
23
|
module VERSION
|
25
24
|
MAJOR = 2
|
26
|
-
MINOR =
|
27
|
-
TINY =
|
25
|
+
MINOR = 8
|
26
|
+
TINY = 1
|
28
27
|
|
29
28
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
30
29
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# coding: utf-8
|
4
3
|
# Copyright (C) Bob Aman
|
5
4
|
#
|
6
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -294,7 +293,9 @@ begin
|
|
294
293
|
it_should_behave_like "converting from unicode to ASCII"
|
295
294
|
it_should_behave_like "converting from ASCII to unicode"
|
296
295
|
end
|
297
|
-
rescue LoadError
|
296
|
+
rescue LoadError => error
|
297
|
+
raise error if ENV["CI"] && TestHelper.native_supported?
|
298
|
+
|
298
299
|
# Cannot test the native implementation without libidn support.
|
299
300
|
warn('Could not load native IDN implementation.')
|
300
301
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# coding: utf-8
|
4
3
|
# Copyright (C) Bob Aman
|
5
4
|
#
|
6
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -19,6 +18,7 @@
|
|
19
18
|
require "spec_helper"
|
20
19
|
|
21
20
|
require "bigdecimal"
|
21
|
+
require "timeout"
|
22
22
|
require "addressable/template"
|
23
23
|
|
24
24
|
shared_examples_for 'expands' do |tests|
|
@@ -77,6 +77,15 @@ describe "==" do
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
describe "#to_regexp" do
|
81
|
+
it "does not match the first line of multiline strings" do
|
82
|
+
uri = "https://www.example.com/bar"
|
83
|
+
template = Addressable::Template.new(uri)
|
84
|
+
expect(template.match(uri)).not_to be_nil
|
85
|
+
expect(template.match("#{uri}\ngarbage")).to be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
80
89
|
describe "Type conversion" do
|
81
90
|
subject {
|
82
91
|
{
|
@@ -1340,6 +1349,14 @@ describe Addressable::Template do
|
|
1340
1349
|
expect(subject).not_to match("foo_bar*")
|
1341
1350
|
expect(subject).not_to match("foo_bar:20")
|
1342
1351
|
end
|
1352
|
+
|
1353
|
+
it 'should parse in a reasonable time' do
|
1354
|
+
expect do
|
1355
|
+
Timeout.timeout(0.1) do
|
1356
|
+
expect(subject).not_to match("0"*25 + "!")
|
1357
|
+
end
|
1358
|
+
end.not_to raise_error
|
1359
|
+
end
|
1343
1360
|
end
|
1344
1361
|
context "VARIABLE_LIST" do
|
1345
1362
|
subject { Addressable::Template::VARIABLE_LIST }
|