addressable 2.3.2 → 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 +7 -0
- data/CHANGELOG.md +189 -28
- data/Gemfile +22 -8
- data/README.md +95 -61
- data/Rakefile +16 -16
- data/addressable.gemspec +28 -0
- data/lib/addressable/idna/native.rb +31 -8
- data/lib/addressable/idna/pure.rb +49 -202
- data/lib/addressable/idna.rb +3 -2
- data/lib/addressable/template.rb +255 -64
- data/lib/addressable/uri.rb +663 -306
- data/lib/addressable/version.rb +5 -4
- data/lib/addressable.rb +4 -0
- data/spec/addressable/idna_spec.rb +116 -45
- data/spec/addressable/net_http_compat_spec.rb +6 -3
- data/spec/addressable/security_spec.rb +58 -0
- data/spec/addressable/template_spec.rb +645 -382
- data/spec/addressable/uri_spec.rb +3014 -1238
- data/spec/spec_helper.rb +33 -0
- data/tasks/clobber.rake +2 -0
- data/tasks/gem.rake +27 -17
- data/tasks/git.rake +3 -1
- data/tasks/metrics.rake +2 -0
- data/tasks/profile.rake +72 -0
- data/tasks/rspec.rake +10 -45
- data/tasks/yard.rake +2 -0
- metadata +75 -73
- data/tasks/rubyforge.rake +0 -89
- data/website/index.html +0 -110
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
#--
|
|
3
|
-
# Copyright (C)
|
|
4
|
+
# Copyright (C) Bob Aman
|
|
4
5
|
#
|
|
5
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
7
|
# you may not use this file except in compliance with the License.
|
|
@@ -21,23 +22,45 @@ require "idn"
|
|
|
21
22
|
module Addressable
|
|
22
23
|
module IDNA
|
|
23
24
|
def self.punycode_encode(value)
|
|
24
|
-
IDN::Punycode.encode(value)
|
|
25
|
+
IDN::Punycode.encode(value.to_s)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def self.punycode_decode(value)
|
|
28
|
-
IDN::Punycode.decode(value)
|
|
29
|
+
IDN::Punycode.decode(value.to_s)
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
class << self
|
|
33
|
+
# @deprecated Use {String#unicode_normalize(:nfkc)} instead
|
|
34
|
+
def unicode_normalize_kc(value)
|
|
35
|
+
value.to_s.unicode_normalize(:nfkc)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
extend Gem::Deprecate
|
|
39
|
+
deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
def self.to_ascii(value)
|
|
36
|
-
|
|
43
|
+
value.to_s.split('.', -1).map do |segment|
|
|
44
|
+
if segment.size > 0 && segment.size < 64
|
|
45
|
+
IDN::Idna.toASCII(segment, IDN::Idna::ALLOW_UNASSIGNED)
|
|
46
|
+
elsif segment.size >= 64
|
|
47
|
+
segment
|
|
48
|
+
else
|
|
49
|
+
''
|
|
50
|
+
end
|
|
51
|
+
end.join('.')
|
|
37
52
|
end
|
|
38
53
|
|
|
39
54
|
def self.to_unicode(value)
|
|
40
|
-
|
|
55
|
+
value.to_s.split('.', -1).map do |segment|
|
|
56
|
+
if segment.size > 0 && segment.size < 64
|
|
57
|
+
IDN::Idna.toUnicode(segment, IDN::Idna::ALLOW_UNASSIGNED)
|
|
58
|
+
elsif segment.size >= 64
|
|
59
|
+
segment
|
|
60
|
+
else
|
|
61
|
+
''
|
|
62
|
+
end
|
|
63
|
+
end.join('.')
|
|
41
64
|
end
|
|
42
65
|
end
|
|
43
66
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
#--
|
|
3
|
-
# Copyright (C)
|
|
4
|
+
# Copyright (C) Bob Aman
|
|
4
5
|
#
|
|
5
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
7
|
# you may not use this file except in compliance with the License.
|
|
@@ -64,7 +65,8 @@ module Addressable
|
|
|
64
65
|
# Converts from a Unicode internationalized domain name to an ASCII
|
|
65
66
|
# domain name as described in RFC 3490.
|
|
66
67
|
def self.to_ascii(input)
|
|
67
|
-
input = input.
|
|
68
|
+
input = input.to_s unless input.is_a?(String)
|
|
69
|
+
input = input.dup.force_encoding(Encoding::UTF_8).unicode_normalize(:nfkc)
|
|
68
70
|
if input.respond_to?(:force_encoding)
|
|
69
71
|
input.force_encoding(Encoding::ASCII_8BIT)
|
|
70
72
|
end
|
|
@@ -75,7 +77,7 @@ module Addressable
|
|
|
75
77
|
part.force_encoding(Encoding::ASCII_8BIT)
|
|
76
78
|
end
|
|
77
79
|
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
|
|
78
|
-
ACE_PREFIX + punycode_encode(
|
|
80
|
+
ACE_PREFIX + punycode_encode(part)
|
|
79
81
|
else
|
|
80
82
|
part
|
|
81
83
|
end
|
|
@@ -89,10 +91,16 @@ module Addressable
|
|
|
89
91
|
# Converts from an ASCII domain name to a Unicode internationalized
|
|
90
92
|
# domain name as described in RFC 3490.
|
|
91
93
|
def self.to_unicode(input)
|
|
94
|
+
input = input.to_s unless input.is_a?(String)
|
|
92
95
|
parts = input.split('.')
|
|
93
96
|
parts.map! do |part|
|
|
94
|
-
if part =~ /^#{ACE_PREFIX}/
|
|
95
|
-
|
|
97
|
+
if part =~ /^#{ACE_PREFIX}(.+)/
|
|
98
|
+
begin
|
|
99
|
+
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
|
|
100
|
+
rescue Addressable::IDNA::PunycodeBadInput
|
|
101
|
+
# toUnicode is explicitly defined as never-fails by the spec
|
|
102
|
+
part
|
|
103
|
+
end
|
|
96
104
|
else
|
|
97
105
|
part
|
|
98
106
|
end
|
|
@@ -104,12 +112,14 @@ module Addressable
|
|
|
104
112
|
output
|
|
105
113
|
end
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
class << self
|
|
116
|
+
# @deprecated Use {String#unicode_normalize(:nfkc)} instead
|
|
117
|
+
def unicode_normalize_kc(value)
|
|
118
|
+
value.to_s.unicode_normalize(:nfkc)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
extend Gem::Deprecate
|
|
122
|
+
deprecate :unicode_normalize_kc, "String#unicode_normalize(:nfkc)", 2023, 4
|
|
113
123
|
end
|
|
114
124
|
|
|
115
125
|
##
|
|
@@ -120,171 +130,12 @@ module Addressable
|
|
|
120
130
|
# The input string.
|
|
121
131
|
# @return [String] The downcased result.
|
|
122
132
|
def self.unicode_downcase(input)
|
|
133
|
+
input = input.to_s unless input.is_a?(String)
|
|
123
134
|
unpacked = input.unpack("U*")
|
|
124
135
|
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
|
|
125
136
|
return unpacked.pack("U*")
|
|
126
137
|
end
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def self.unicode_compose(unpacked)
|
|
130
|
-
unpacked_result = []
|
|
131
|
-
length = unpacked.length
|
|
132
|
-
|
|
133
|
-
return unpacked if length == 0
|
|
134
|
-
|
|
135
|
-
starter = unpacked[0]
|
|
136
|
-
starter_cc = lookup_unicode_combining_class(starter)
|
|
137
|
-
starter_cc = 256 if starter_cc != 0
|
|
138
|
-
for i in 1...length
|
|
139
|
-
ch = unpacked[i]
|
|
140
|
-
cc = lookup_unicode_combining_class(ch)
|
|
141
|
-
|
|
142
|
-
if (starter_cc == 0 &&
|
|
143
|
-
(composite = unicode_compose_pair(starter, ch)) != nil)
|
|
144
|
-
starter = composite
|
|
145
|
-
startercc = lookup_unicode_combining_class(composite)
|
|
146
|
-
else
|
|
147
|
-
unpacked_result << starter
|
|
148
|
-
starter = ch
|
|
149
|
-
startercc = cc
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
unpacked_result << starter
|
|
153
|
-
return unpacked_result
|
|
154
|
-
end
|
|
155
|
-
(class <<self; private :unicode_compose; end)
|
|
156
|
-
|
|
157
|
-
def self.unicode_compose_pair(ch_one, ch_two)
|
|
158
|
-
if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
|
|
159
|
-
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
|
|
160
|
-
# Hangul L + V
|
|
161
|
-
return HANGUL_SBASE + (
|
|
162
|
-
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
|
|
163
|
-
) * HANGUL_TCOUNT
|
|
164
|
-
elsif ch_one >= HANGUL_SBASE &&
|
|
165
|
-
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
|
|
166
|
-
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
|
|
167
|
-
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
|
|
168
|
-
# Hangul LV + T
|
|
169
|
-
return ch_one + (ch_two - HANGUL_TBASE)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
p = []
|
|
173
|
-
ucs4_to_utf8 = lambda do |ch|
|
|
174
|
-
# For some reason, rcov likes to drop BUS errors here.
|
|
175
|
-
if ch < 128
|
|
176
|
-
p << ch
|
|
177
|
-
elsif ch < 2048
|
|
178
|
-
p << (ch >> 6 | 192)
|
|
179
|
-
p << (ch & 63 | 128)
|
|
180
|
-
elsif ch < 0x10000
|
|
181
|
-
p << (ch >> 12 | 224)
|
|
182
|
-
p << (ch >> 6 & 63 | 128)
|
|
183
|
-
p << (ch & 63 | 128)
|
|
184
|
-
elsif ch < 0x200000
|
|
185
|
-
p << (ch >> 18 | 240)
|
|
186
|
-
p << (ch >> 12 & 63 | 128)
|
|
187
|
-
p << (ch >> 6 & 63 | 128)
|
|
188
|
-
p << (ch & 63 | 128)
|
|
189
|
-
elsif ch < 0x4000000
|
|
190
|
-
p << (ch >> 24 | 248)
|
|
191
|
-
p << (ch >> 18 & 63 | 128)
|
|
192
|
-
p << (ch >> 12 & 63 | 128)
|
|
193
|
-
p << (ch >> 6 & 63 | 128)
|
|
194
|
-
p << (ch & 63 | 128)
|
|
195
|
-
elsif ch < 0x80000000
|
|
196
|
-
p << (ch >> 30 | 252)
|
|
197
|
-
p << (ch >> 24 & 63 | 128)
|
|
198
|
-
p << (ch >> 18 & 63 | 128)
|
|
199
|
-
p << (ch >> 12 & 63 | 128)
|
|
200
|
-
p << (ch >> 6 & 63 | 128)
|
|
201
|
-
p << (ch & 63 | 128)
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
ucs4_to_utf8.call(ch_one)
|
|
206
|
-
ucs4_to_utf8.call(ch_two)
|
|
207
|
-
|
|
208
|
-
return lookup_unicode_composition(p)
|
|
209
|
-
end
|
|
210
|
-
(class <<self; private :unicode_compose_pair; end)
|
|
211
|
-
|
|
212
|
-
def self.unicode_sort_canonical(unpacked)
|
|
213
|
-
unpacked = unpacked.dup
|
|
214
|
-
i = 1
|
|
215
|
-
length = unpacked.length
|
|
216
|
-
|
|
217
|
-
return unpacked if length < 2
|
|
218
|
-
|
|
219
|
-
while i < length
|
|
220
|
-
last = unpacked[i-1]
|
|
221
|
-
ch = unpacked[i]
|
|
222
|
-
last_cc = lookup_unicode_combining_class(last)
|
|
223
|
-
cc = lookup_unicode_combining_class(ch)
|
|
224
|
-
if cc != 0 && last_cc != 0 && last_cc > cc
|
|
225
|
-
unpacked[i] = last
|
|
226
|
-
unpacked[i-1] = ch
|
|
227
|
-
i -= 1 if i > 1
|
|
228
|
-
else
|
|
229
|
-
i += 1
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
return unpacked
|
|
233
|
-
end
|
|
234
|
-
(class <<self; private :unicode_sort_canonical; end)
|
|
235
|
-
|
|
236
|
-
def self.unicode_decompose(unpacked)
|
|
237
|
-
unpacked_result = []
|
|
238
|
-
for cp in unpacked
|
|
239
|
-
if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
|
|
240
|
-
l, v, t = unicode_decompose_hangul(cp)
|
|
241
|
-
unpacked_result << l
|
|
242
|
-
unpacked_result << v if v
|
|
243
|
-
unpacked_result << t if t
|
|
244
|
-
else
|
|
245
|
-
dc = lookup_unicode_compatibility(cp)
|
|
246
|
-
unless dc
|
|
247
|
-
unpacked_result << cp
|
|
248
|
-
else
|
|
249
|
-
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
return unpacked_result
|
|
254
|
-
end
|
|
255
|
-
(class <<self; private :unicode_decompose; end)
|
|
256
|
-
|
|
257
|
-
def self.unicode_decompose_hangul(codepoint)
|
|
258
|
-
sindex = codepoint - HANGUL_SBASE;
|
|
259
|
-
if sindex < 0 || sindex >= HANGUL_SCOUNT
|
|
260
|
-
l = codepoint
|
|
261
|
-
v = t = nil
|
|
262
|
-
return l, v, t
|
|
263
|
-
end
|
|
264
|
-
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
|
|
265
|
-
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
|
|
266
|
-
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
|
|
267
|
-
if t == HANGUL_TBASE
|
|
268
|
-
t = nil
|
|
269
|
-
end
|
|
270
|
-
return l, v, t
|
|
271
|
-
end
|
|
272
|
-
(class <<self; private :unicode_decompose_hangul; end)
|
|
273
|
-
|
|
274
|
-
def self.lookup_unicode_combining_class(codepoint)
|
|
275
|
-
codepoint_data = UNICODE_DATA[codepoint]
|
|
276
|
-
(codepoint_data ?
|
|
277
|
-
(codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
|
|
278
|
-
0)
|
|
279
|
-
end
|
|
280
|
-
(class <<self; private :lookup_unicode_combining_class; end)
|
|
281
|
-
|
|
282
|
-
def self.lookup_unicode_compatibility(codepoint)
|
|
283
|
-
codepoint_data = UNICODE_DATA[codepoint]
|
|
284
|
-
(codepoint_data ?
|
|
285
|
-
codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
|
|
286
|
-
end
|
|
287
|
-
(class <<self; private :lookup_unicode_compatibility; end)
|
|
138
|
+
private_class_method :unicode_downcase
|
|
288
139
|
|
|
289
140
|
def self.lookup_unicode_lowercase(codepoint)
|
|
290
141
|
codepoint_data = UNICODE_DATA[codepoint]
|
|
@@ -292,22 +143,7 @@ module Addressable
|
|
|
292
143
|
(codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
|
|
293
144
|
codepoint)
|
|
294
145
|
end
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
def self.lookup_unicode_composition(unpacked)
|
|
298
|
-
return COMPOSITION_TABLE[unpacked]
|
|
299
|
-
end
|
|
300
|
-
(class <<self; private :lookup_unicode_composition; end)
|
|
301
|
-
|
|
302
|
-
HANGUL_SBASE = 0xac00
|
|
303
|
-
HANGUL_LBASE = 0x1100
|
|
304
|
-
HANGUL_LCOUNT = 19
|
|
305
|
-
HANGUL_VBASE = 0x1161
|
|
306
|
-
HANGUL_VCOUNT = 21
|
|
307
|
-
HANGUL_TBASE = 0x11a7
|
|
308
|
-
HANGUL_TCOUNT = 28
|
|
309
|
-
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
|
|
310
|
-
HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
|
|
146
|
+
private_class_method :lookup_unicode_lowercase
|
|
311
147
|
|
|
312
148
|
UNICODE_DATA_COMBINING_CLASS = 0
|
|
313
149
|
UNICODE_DATA_EXCLUSION = 1
|
|
@@ -317,14 +153,24 @@ module Addressable
|
|
|
317
153
|
UNICODE_DATA_LOWERCASE = 5
|
|
318
154
|
UNICODE_DATA_TITLECASE = 6
|
|
319
155
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
156
|
+
begin
|
|
157
|
+
if defined?(FakeFS)
|
|
158
|
+
fakefs_state = FakeFS.activated?
|
|
159
|
+
FakeFS.deactivate!
|
|
160
|
+
end
|
|
161
|
+
# This is a sparse Unicode table. Codepoints without entries are
|
|
162
|
+
# assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
|
|
163
|
+
UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file|
|
|
164
|
+
Marshal.load(file.read)
|
|
165
|
+
end
|
|
166
|
+
ensure
|
|
167
|
+
if defined?(FakeFS)
|
|
168
|
+
FakeFS.activate! if fakefs_state
|
|
169
|
+
end
|
|
324
170
|
end
|
|
325
171
|
|
|
326
172
|
COMPOSITION_TABLE = {}
|
|
327
|
-
|
|
173
|
+
UNICODE_DATA.each do |codepoint, data|
|
|
328
174
|
canonical = data[UNICODE_DATA_CANONICAL]
|
|
329
175
|
exclusion = data[UNICODE_DATA_EXCLUSION]
|
|
330
176
|
|
|
@@ -365,6 +211,7 @@ module Addressable
|
|
|
365
211
|
class PunycodeOverflow < StandardError; end
|
|
366
212
|
|
|
367
213
|
def self.punycode_encode(unicode)
|
|
214
|
+
unicode = unicode.to_s unless unicode.is_a?(String)
|
|
368
215
|
input = unicode.unpack("U*")
|
|
369
216
|
output = [0] * (ACE_MAX_LENGTH + 1)
|
|
370
217
|
input_length = input.size
|
|
@@ -473,7 +320,7 @@ module Addressable
|
|
|
473
320
|
outlen.times do |j|
|
|
474
321
|
c = output[j]
|
|
475
322
|
unless c >= 0 && c <= 127
|
|
476
|
-
raise
|
|
323
|
+
raise StandardError, "Invalid output char."
|
|
477
324
|
end
|
|
478
325
|
unless PUNYCODE_PRINT_ASCII[c]
|
|
479
326
|
raise PunycodeBadInput, "Input is invalid."
|
|
@@ -482,7 +329,7 @@ module Addressable
|
|
|
482
329
|
|
|
483
330
|
output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
|
|
484
331
|
end
|
|
485
|
-
|
|
332
|
+
private_class_method :punycode_encode
|
|
486
333
|
|
|
487
334
|
def self.punycode_decode(punycode)
|
|
488
335
|
input = []
|
|
@@ -604,22 +451,22 @@ module Addressable
|
|
|
604
451
|
|
|
605
452
|
output.pack("U*")
|
|
606
453
|
end
|
|
607
|
-
|
|
454
|
+
private_class_method :punycode_decode
|
|
608
455
|
|
|
609
456
|
def self.punycode_basic?(codepoint)
|
|
610
457
|
codepoint < 0x80
|
|
611
458
|
end
|
|
612
|
-
|
|
459
|
+
private_class_method :punycode_basic?
|
|
613
460
|
|
|
614
461
|
def self.punycode_delimiter?(codepoint)
|
|
615
462
|
codepoint == PUNYCODE_DELIMITER
|
|
616
463
|
end
|
|
617
|
-
|
|
464
|
+
private_class_method :punycode_delimiter?
|
|
618
465
|
|
|
619
466
|
def self.punycode_encode_digit(d)
|
|
620
467
|
d + 22 + 75 * ((d < 26) ? 1 : 0)
|
|
621
468
|
end
|
|
622
|
-
|
|
469
|
+
private_class_method :punycode_encode_digit
|
|
623
470
|
|
|
624
471
|
# Returns the numeric value of a basic codepoint
|
|
625
472
|
# (for use in representing integers) in the range 0 to
|
|
@@ -635,7 +482,7 @@ module Addressable
|
|
|
635
482
|
PUNYCODE_BASE
|
|
636
483
|
end
|
|
637
484
|
end
|
|
638
|
-
|
|
485
|
+
private_class_method :punycode_decode_digit
|
|
639
486
|
|
|
640
487
|
# Bias adaptation method
|
|
641
488
|
def self.punycode_adapt(delta, numpoints, firsttime)
|
|
@@ -652,7 +499,7 @@ module Addressable
|
|
|
652
499
|
|
|
653
500
|
k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
|
|
654
501
|
end
|
|
655
|
-
|
|
502
|
+
private_class_method :punycode_adapt
|
|
656
503
|
end
|
|
657
504
|
# :startdoc:
|
|
658
505
|
end
|
data/lib/addressable/idna.rb
CHANGED