uri-ni 0.1.0 → 0.1.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 +4 -4
- data/.gitignore +1 -0
- data/README.md +15 -7
- data/lib/uri/ni/version.rb +1 -1
- data/lib/uri/ni.rb +131 -52
- data/uri-ni.gemspec +2 -1
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eff8af7f69a76b24ce06948c53ad963d39552ec2dee0b08179496a1a8eb5d2a1
|
|
4
|
+
data.tar.gz: d7d285ac8eb47acf9acd0cd3de7dbcad6110c9e80f1860817534b18c1e25d87c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bbdf0f3d6fd23db734e13403e20d96f6e29730c43f93351556a47ab10250508b743497c02870bcb7447d643e31254686a889277e5bbf10941caef329b6b71d2
|
|
7
|
+
data.tar.gz: b777dc59e94aff9e947cfd4661b64aecc727baeae4a2c5cc14fed58c8b59fd4495bb6ae935864a2e61a8fb9dbb86098e3665475baa2c59127975d97b734f5046
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -48,20 +48,28 @@ result and compute a new digest with it:
|
|
|
48
48
|
ni
|
|
49
49
|
# => #<URI::NI ni:///lol;wut>
|
|
50
50
|
ni.compute 'derp'
|
|
51
|
-
|
|
51
|
+
# URI::InvalidComponentError: Can't resolve a Digest context for the algorithm lol.
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
The purpose of this configuration is so that the parser doesn't croak
|
|
55
|
+
on unexpected input, but otherwise assumes you know what you're
|
|
56
|
+
doing. As such, there is no attempt to measure or otherwise divine the
|
|
57
|
+
representation of any updates to the `digest` component:
|
|
56
58
|
|
|
57
|
-
```
|
|
58
|
-
ni
|
|
59
|
-
|
|
59
|
+
```ruby
|
|
60
|
+
ni = URI::NI.compute 'some data'
|
|
61
|
+
# => #<URI::NI ni:///sha-256;EweZDmulyhRes16ZGCqb7EZTG8VN32VqYCx4D6AkDe4>
|
|
62
|
+
ni.digest = 'whatever'
|
|
63
|
+
# => "whatever"
|
|
64
|
+
ni
|
|
65
|
+
# => #<URI::NI ni:///sha-256;d2hhdGV2ZXI>
|
|
60
66
|
```
|
|
61
67
|
|
|
62
68
|
In addition to computing new digest URIs, this module will return the
|
|
63
|
-
interesting part of its contents in binary,
|
|
69
|
+
interesting part of its contents in binary, hexadecimal, base64, and
|
|
64
70
|
(with a soft dependency), [base32](https://rubygems.org/gems/base32).
|
|
71
|
+
There are accessors and mutators for `digest`, `hexdigest`,
|
|
72
|
+
`b32digest`, and `b64digest`.
|
|
65
73
|
|
|
66
74
|
Finally, this module will also reuse any extant `Digest::Instance`
|
|
67
75
|
object as long as it is in the inventory, and furthermore the
|
data/lib/uri/ni/version.rb
CHANGED
data/lib/uri/ni.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
1
2
|
require 'uri/ni/version'
|
|
2
3
|
require 'uri'
|
|
3
4
|
require 'uri/generic'
|
|
@@ -79,6 +80,65 @@ class URI::NI < URI::Generic
|
|
|
79
80
|
m.captures
|
|
80
81
|
end
|
|
81
82
|
|
|
83
|
+
def assert_radix radix
|
|
84
|
+
raise ArgumentError,
|
|
85
|
+
"Radix must be 16, 32, 64, or 256, not #{radix.inspect}" unless
|
|
86
|
+
[256, 64, 32, 16].include? radix
|
|
87
|
+
radix
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# assertions about data representation
|
|
91
|
+
ASSERT = {
|
|
92
|
+
256 => [/.*/, ''],
|
|
93
|
+
64 => [/^[0-9A-Za-z+\/_-]*=*$/, 'Data %s is not in base64'],
|
|
94
|
+
32 => [/^[2-7A-Za-z]*=*$/, 'Data %s is not in base32'],
|
|
95
|
+
16 => [/^[0-9A-Fa-f]*$/, 'Data %s is not in hexadecimal'],
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def assert_repr data, radix
|
|
99
|
+
re, error = ASSERT[radix]
|
|
100
|
+
raise ArgumentError, error % data unless re.match data
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# from whatever to binary
|
|
104
|
+
DECODE = {
|
|
105
|
+
256 => -> x { x },
|
|
106
|
+
64 => -> x { Base64.decode64 x.tr('-_', '+/') },
|
|
107
|
+
32 => -> x { require 'base32'; Base32.decode x },
|
|
108
|
+
16 => -> x { [x].pack 'H*' },
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# from binary to whatever
|
|
112
|
+
ENCODE = {
|
|
113
|
+
256 => -> x { x },
|
|
114
|
+
64 => -> x { Base64.urlsafe_encode64(x).tr '=', '' },
|
|
115
|
+
32 => -> x { require 'base32'; Base32.encode(x).tr '=', '' },
|
|
116
|
+
16 => -> x { x.unpack1 'H*' },
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# canonical and alternative representations
|
|
120
|
+
CANON = {
|
|
121
|
+
256 => -> x { x },
|
|
122
|
+
64 => -> x { x.tr('=', '').tr '+/', '-_' },
|
|
123
|
+
32 => -> x { x.tr('=', '').upcase },
|
|
124
|
+
16 => -> x { x.downcase },
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# note if we put the padding here then we sanitize input as well
|
|
128
|
+
|
|
129
|
+
ALT = {
|
|
130
|
+
256 => -> x { x },
|
|
131
|
+
64 => -> x { x.tr('=', '').tr '-_', '+/' },
|
|
132
|
+
32 => -> x { x.tr('=', '').downcase },
|
|
133
|
+
16 => -> x { x.upcase },
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
def transcode data, from: 256, to: 256, alt: false
|
|
137
|
+
assert_repr data, from
|
|
138
|
+
data = ENCODE[to].call(DECODE[from].call data) unless from == to
|
|
139
|
+
alt ? ALT[to].call(data) : CANON[to].call(data)
|
|
140
|
+
end
|
|
141
|
+
|
|
82
142
|
protected
|
|
83
143
|
|
|
84
144
|
# holy crap you can override these?
|
|
@@ -127,8 +187,8 @@ class URI::NI < URI::Generic
|
|
|
127
187
|
else
|
|
128
188
|
# make sure we're all on the same page hurr
|
|
129
189
|
self.algorithm = algorithm ||= self.algorithm
|
|
130
|
-
raise
|
|
131
|
-
"
|
|
190
|
+
raise URI::InvalidComponentError,
|
|
191
|
+
"Can't resolve a Digest context for the algorithm #{algorithm}." unless
|
|
132
192
|
ctx = DIGESTS[algorithm]
|
|
133
193
|
ctx = ctx.new
|
|
134
194
|
end
|
|
@@ -157,8 +217,7 @@ class URI::NI < URI::Generic
|
|
|
157
217
|
end
|
|
158
218
|
|
|
159
219
|
self.set_path("/#{algorithm};" +
|
|
160
|
-
ctx.base64digest.
|
|
161
|
-
|
|
220
|
+
ctx.base64digest.tr('+/', '-_').tr('=', ''))
|
|
162
221
|
self
|
|
163
222
|
end
|
|
164
223
|
|
|
@@ -184,7 +243,7 @@ class URI::NI < URI::Generic
|
|
|
184
243
|
def algorithm= algo
|
|
185
244
|
a, b = assert_path
|
|
186
245
|
self.path = "/#{algo}"
|
|
187
|
-
self.
|
|
246
|
+
self.set_digest(b, radix: 64) if b
|
|
188
247
|
a.to_sym if a
|
|
189
248
|
end
|
|
190
249
|
|
|
@@ -223,46 +282,51 @@ class URI::NI < URI::Generic
|
|
|
223
282
|
# @return [String] The digest of the URI in the given representation
|
|
224
283
|
#
|
|
225
284
|
def digest radix: 256, alt: false
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
# XXX do not use urlsafe_decode64; it will complain if the
|
|
229
|
-
# thingies aren't aligned
|
|
230
|
-
Base64.decode64(raw_digest.tr('-_', '+/'))
|
|
231
|
-
when 64
|
|
232
|
-
b64digest alt: alt
|
|
233
|
-
when 32
|
|
234
|
-
b32digest alt: alt
|
|
235
|
-
when 16
|
|
236
|
-
hexdigest alt: alt
|
|
237
|
-
else
|
|
238
|
-
raise ArgumentError, "Radix must be 16, 32, 64, 256, not #{radix}"
|
|
239
|
-
end
|
|
285
|
+
assert_radix radix
|
|
286
|
+
transcode raw_digest, from: 64, to: radix, alt: alt
|
|
240
287
|
end
|
|
241
288
|
|
|
242
|
-
# Set the digest to the data. Data may
|
|
243
|
-
# +Digest::Instance
|
|
244
|
-
#
|
|
245
|
-
# RFC 3548} base64url, i.e. +\+/+ will be replaced with +-_+ and
|
|
246
|
-
# padding (+=+) will be removed. +Digest::Instance+ objects will
|
|
289
|
+
# Set the digest to the data, with an optional radix. Data may
|
|
290
|
+
# either be a +Digest::Instance+—in which case the radix is
|
|
291
|
+
# ignored–a string, or +nil+. +Digest::Instance+ objects will
|
|
247
292
|
# just be run through #compute, with all that entails.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
293
|
+
#
|
|
294
|
+
# @param value [String, nil, Digest::Instance] The new digest
|
|
295
|
+
# @param radix [256, 64, 32, 16] The radix of the encoding (default 256)
|
|
296
|
+
# @return [String] The _old_ digest in the given radix
|
|
297
|
+
#
|
|
298
|
+
def set_digest value, radix: 256
|
|
299
|
+
assert_radix radix
|
|
300
|
+
|
|
301
|
+
a, d = assert_path
|
|
302
|
+
|
|
303
|
+
case value
|
|
251
304
|
when Digest::Instance
|
|
252
|
-
compute
|
|
305
|
+
compute value
|
|
253
306
|
when String
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
data = data.tr('+/', '-_').tr('=', '')
|
|
257
|
-
self.path = a ? "/#{a};#{data}" : "/;#{data}"
|
|
307
|
+
value = transcode value, from: radix, to: 64
|
|
308
|
+
self.path = a ? "/#{a};#{value}" : "/;#{value}"
|
|
258
309
|
when nil
|
|
259
310
|
self.path = a ? "/#{a}" : ?/
|
|
260
311
|
else
|
|
261
312
|
raise ArgumentError,
|
|
262
|
-
"
|
|
313
|
+
"Value must be a string or Digest::Instance, not #{value.class}"
|
|
263
314
|
end
|
|
264
315
|
|
|
265
|
-
|
|
316
|
+
# bail out if nil
|
|
317
|
+
return unless d
|
|
318
|
+
transcode d, from: 64, to: radix
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Set the digest to the data. Data may either be a
|
|
322
|
+
# +Digest::Instance+ or a _binary_ string. +Digest::Instance+
|
|
323
|
+
# objects will just be run through #compute, with all that entails.
|
|
324
|
+
#
|
|
325
|
+
# @param value [String, nil, Digest::Instance] the new digest
|
|
326
|
+
# @return [String, nil, Digest::Instance] the value passed in
|
|
327
|
+
#
|
|
328
|
+
def digest= value
|
|
329
|
+
return set_digest value
|
|
266
330
|
end
|
|
267
331
|
|
|
268
332
|
# Return the digest in its hexadecimal notation. Optionally give
|
|
@@ -273,44 +337,59 @@ class URI::NI < URI::Generic
|
|
|
273
337
|
# @return [String] The hexadecimal digest
|
|
274
338
|
#
|
|
275
339
|
def hexdigest alt: false
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
340
|
+
transcode raw_digest, from: 64, to: 16, alt: alt
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Set the digest value, assuming a hexadecimal input.
|
|
344
|
+
# @param value [String, nil, Digest::Instance] the new digest
|
|
345
|
+
# @return [String, nil, Digest::Instance] the value passed in
|
|
346
|
+
def hexdigest= value
|
|
347
|
+
set_digest value, radix: 16
|
|
279
348
|
end
|
|
280
349
|
|
|
281
350
|
# Return the digest in its base32 notation. Optionally give
|
|
282
351
|
# +alt:+ a truthy value to return an alternate (lowercase)
|
|
283
|
-
# representation. Note this method requires
|
|
352
|
+
# representation. Note this method requires the base32 module.
|
|
284
353
|
#
|
|
285
354
|
# @param alt [false, true] Return the alternative representation
|
|
286
355
|
# @return [String] The base32 digest
|
|
287
356
|
#
|
|
288
357
|
def b32digest alt: false
|
|
289
|
-
|
|
290
|
-
ret = Base32.encode(digest).gsub(/=+/, '')
|
|
291
|
-
return ret.downcase if alt
|
|
292
|
-
ret.upcase
|
|
358
|
+
transcode raw_digest, from: 64, to: 32, alt: alt
|
|
293
359
|
end
|
|
294
360
|
|
|
295
|
-
#
|
|
296
|
-
#
|
|
297
|
-
#
|
|
361
|
+
# Set the digest value, assuming a base32 input (requires base32).
|
|
362
|
+
# @param value [String, nil, Digest::Instance] the new digest
|
|
363
|
+
# @return [String, nil, Digest::Instance] the value passed in
|
|
364
|
+
def b32digest= value
|
|
365
|
+
set_digest value, radix: 32
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Return the digest in its base64 notation. Note it is the
|
|
369
|
+
# _default_ representation that is URL-safe, for parity with the
|
|
370
|
+
# identifier itself. Give +alt:+ a truthy value to return a plain
|
|
371
|
+
# (_non_-URL-safe) base64 representation.
|
|
298
372
|
#
|
|
299
373
|
# @param alt [false, true] Return the alternative representation
|
|
300
374
|
# @return [String] The base64 digest
|
|
301
375
|
#
|
|
302
376
|
def b64digest alt: false
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
377
|
+
transcode raw_digest, from: 64, to: 64, alt: alt
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# Set the digest value, assuming a base64 input.
|
|
381
|
+
# @param value [String, nil, Digest::Instance] the new digest
|
|
382
|
+
# @return [String, nil, Digest::Instance] the value passed in
|
|
383
|
+
def b64digest= value
|
|
384
|
+
set_digest value, radix: 64
|
|
306
385
|
end
|
|
307
386
|
|
|
308
387
|
# Returns a +/.well-known/...+, either HTTPS or HTTP URL, given the
|
|
309
388
|
# contents of the +ni:+ URI.
|
|
310
389
|
#
|
|
311
390
|
# @param authority [#to_s, URI] Override the authority part of the URI
|
|
312
|
-
# @param https [true, false]
|
|
313
|
-
# @return [URI::HTTPS, URI::HTTP]
|
|
391
|
+
# @param https [true, false] Whether the URL is to be HTTPS.
|
|
392
|
+
# @return [URI::HTTPS, URI::HTTP] The generated URL.
|
|
314
393
|
#
|
|
315
394
|
def to_www https: true, authority: nil
|
|
316
395
|
a, d = assert_path
|
|
@@ -327,13 +406,13 @@ class URI::NI < URI::Generic
|
|
|
327
406
|
if authority
|
|
328
407
|
uhp = []
|
|
329
408
|
if authority.is_a? URI
|
|
330
|
-
raise
|
|
409
|
+
raise URI::InvalidComponentError, "Bad authority #{authority}" unless
|
|
331
410
|
%i[userinfo host port].all? {|c| authority.respond_to? c }
|
|
332
411
|
uhp = [authority.userinfo, authority.host, authority.port]
|
|
333
412
|
uhp[2] = nil if authority.port == authority.class::DEFAULT_PORT
|
|
334
413
|
else
|
|
335
414
|
authority = authority.to_s
|
|
336
|
-
uhp = AUTH_RE.match(authority) or raise
|
|
415
|
+
uhp = AUTH_RE.match(authority) or raise URI::InvalidComponentError,
|
|
337
416
|
"Invalid authority #{authority}"
|
|
338
417
|
uhp = uhp.captures
|
|
339
418
|
end
|
data/uri-ni.gemspec
CHANGED
|
@@ -24,10 +24,11 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.require_paths = ['lib']
|
|
25
25
|
|
|
26
26
|
# ruby
|
|
27
|
-
spec.required_ruby_version =
|
|
27
|
+
spec.required_ruby_version = '>= 2.3.0'
|
|
28
28
|
|
|
29
29
|
# dev/test dependencies
|
|
30
30
|
spec.add_development_dependency 'bundler', '~> 2'
|
|
31
|
+
spec.add_development_dependency 'base32', '~> 0.3'
|
|
31
32
|
# bundler put these in the gemfile i dunno wtf
|
|
32
33
|
#spec.add_development_dependency 'rake', '~> 12.0'
|
|
33
34
|
#spec.add_development_dependency 'rspec', '~> 3.0'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uri-ni
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dorian Taylor
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-01-
|
|
11
|
+
date: 2020-01-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: base32
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.3'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.3'
|
|
27
41
|
description: ''
|
|
28
42
|
email:
|
|
29
43
|
- code@doriantaylor.com
|
|
@@ -57,7 +71,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
57
71
|
requirements:
|
|
58
72
|
- - ">="
|
|
59
73
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
74
|
+
version: 2.3.0
|
|
61
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
76
|
requirements:
|
|
63
77
|
- - ">="
|