Class: URI::NI

Inherits:
Generic
  • Object
show all
Defined in:
lib/uri/ni/version.rb,
lib/uri/ni.rb

Constant Summary collapse

VERSION =
"0.1.5"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.algorithmsArray

Returns the set of supported algorithms.

Returns:

  • (Array)

    the algorithms



253
254
255
# File 'lib/uri/ni.rb', line 253

def self.algorithms
  DIGESTS.keys.sort
end

.compute(data = nil, algorithm: :"sha-256", blocksize: 65536, authority: nil, query: nil) {|ctx, buf| ... } ⇒ URI::NI

Compute an RFC6920 URI from a data source.

Parameters:

  • data (#to_s, IO, Digest, nil) (defaults to: nil)
  • algorithm (Symbol) (defaults to: :"sha-256")

    See available algorithms. Default: :“sha-256”

  • blocksize (Integer) (defaults to: 65536)

    The number or bytes per call to the Digest

  • authority (String, nil) (defaults to: nil)

    Optional authority (user, host, port)

  • query (String, nil) (defaults to: nil)

    Optional query string

Yields:

  • (ctx, buf)

    Passes the Digest and (maybe) the buffer

Yield Parameters:

  • ctx (Digest::Instance)

    The digest instance to the block

  • buf (String, nil)

    The current read buffer (if data is set)

Yield Returns:

  • (nil)

    The result of the block is ignored

Returns:



176
177
178
179
180
181
# File 'lib/uri/ni.rb', line 176

def self.compute data = nil, algorithm: :"sha-256", blocksize: 65536,
    authority: nil, query: nil, &block

  build({ scheme: 'ni' }).compute data, algorithm: algorithm,
    blocksize: blocksize, authority: authority, query: query, &block
end

.context(algorithm) ⇒ Digest:Instance

Return a Digest::Instance for a supported algorithm.

Parameters:

  • The (#to_s, #to_sym)

    algorithm

Returns:

  • (Digest:Instance)

    The digest context

Raises:

  • (ArgumentError)

    if the algorithm is unrecognized



187
188
189
190
191
192
193
194
# File 'lib/uri/ni.rb', line 187

def self.context algorithm
  raise ArgumentError, "Cannot coerce #{algorithm} to a symbol" unless
    algorithm.respond_to? :to_s # cheat: symbols respond to to_s
  algorithm = algorithm.to_s.to_sym unless algorithm.is_a? Symbol
  raise ArgumentError, "Unsupported digest algorithm #{algorithm}" unless
    ctx = DIGESTS[algorithm]
  ctx.new
end

.valid_algo?(algorithm) ⇒ true, false

Returns true if the algorithm is supported.

Parameters:

  • algorithm (Symbol, String)

    the algorithm identifier to test

Returns:

  • (true, false)

    whether it is supported



487
488
489
# File 'lib/uri/ni.rb', line 487

def self.valid_algo? algorithm
  DIGESTS.has_key? algorithm.to_s.downcase.to_sym
end

Instance Method Details

#algorithmSymbol?

Obtain the algorithm of the digest. May be nil.

Returns:

  • (Symbol, nil)


260
261
262
263
# File 'lib/uri/ni.rb', line 260

def algorithm
  algo = assert_path.first
  return algo.to_sym if algo
end

#algorithm=(algo) ⇒ Symbol?

Set the algorithm of the digest. Will croak if the path is malformed.

Returns:

  • (Symbol, nil)

    the old algorithm



268
269
270
271
272
273
# File 'lib/uri/ni.rb', line 268

def algorithm= algo
  a, b = assert_path
  self.path   = "/#{algo}"
  self.set_digest(b, radix: 64) if b
  a.to_sym if a
end

#authorityString?

Obtain the authority (userinfo@host:port) if present.

Returns:

  • (String, nil)

    the authority



278
279
280
281
282
# File 'lib/uri/ni.rb', line 278

def authority
  out = userinfo ? "#{userinfo}@#{host}" : host
  out += "#{out}:#{port}" if port
  out
end

#authority=(authority) ⇒ String?

Set the authority of the URI.

Returns:

  • (String, nil)

    the old authority



287
288
289
290
291
292
293
294
# File 'lib/uri/ni.rb', line 287

def authority= authority
  old = self.authority
  u, h, p = assert_authority authority unless authority.nil?
  set_userinfo u
  set_host     h
  set_port     p
  old
end

#b32digest(alt: false) ⇒ String

Return the digest in its base32 notation. Optionally give alt: a truthy value to return an alternate (lowercase) representation. Note this method requires the base32 module.

Parameters:

  • alt (false, true) (defaults to: false)

    Return the alternative representation

Returns:

  • (String)

    The base32 digest



382
383
384
# File 'lib/uri/ni.rb', line 382

def b32digest alt: false
  transcode raw_digest, from: 64, to: 32, alt: alt
end

#b32digest=(value) ⇒ String, ...

Set the digest value, assuming a base32 input (requires base32).

Parameters:

  • value (String, nil, Digest::Instance)

    the new digest

Returns:

  • (String, nil, Digest::Instance)

    the value passed in



389
390
391
# File 'lib/uri/ni.rb', line 389

def b32digest= value
  set_digest value, radix: 32
end

#b64digest(alt: false) ⇒ String

Return the digest in its base64 notation. Note it is the default representation that is URL-safe, for parity with the identifier itself. Give alt: a truthy value to return a plain (non-URL-safe) base64 representation.

Parameters:

  • alt (false, true) (defaults to: false)

    Return the alternative representation

Returns:

  • (String)

    The base64 digest



401
402
403
# File 'lib/uri/ni.rb', line 401

def b64digest alt: false
  transcode raw_digest, from: 64, to: 64, alt: alt
end

#b64digest=(value) ⇒ String, ...

Set the digest value, assuming a base64 input.

Parameters:

  • value (String, nil, Digest::Instance)

    the new digest

Returns:

  • (String, nil, Digest::Instance)

    the value passed in



408
409
410
# File 'lib/uri/ni.rb', line 408

def b64digest= value
  set_digest value, radix: 64
end

#compute(data = nil, algorithm: nil, blocksize: 65536, authority: nil, query: nil, &block) ⇒ Object

(Re)-compute a digest using existing information from an instance.

Raises:

  • (ArgumentError)

See Also:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/uri/ni.rb', line 198

def compute data = nil, algorithm: nil, blocksize: 65536,
    authority: nil, query: nil, &block

  # enforce block size
  raise ArgumentError,
    "Blocksize must be an integer >0, not #{blocksize}" unless
    blocksize.is_a? Integer and blocksize > 0

  # special case for when the data is a digest
  ctx = nil
  if data.is_a? Digest::Instance
    algorithm ||= algo_for data, algorithm
    ctx  = data
    data = nil # unset data
  else
    # make sure we're all on the same page hurr
    self.algorithm = algorithm ||= self.algorithm || :"sha-256"
    raise URI::InvalidComponentError,
      "Can't resolve a Digest context for the algorithm #{algorithm}." unless
      ctx = DIGESTS[algorithm]
    ctx = ctx.new
  end

  # deal with authority component
  if authm = AUTH_RE.match(authority.to_s)
    userinfo, host, port = authm.captures
    set_userinfo userinfo
    set_host     host.to_s
    set_port     port
  end

  # coerce data to something non-null
  data = data.to_s if (data.class.ancestors & [String, IO, NilClass]).empty?
  if data
    data = StringIO.new data unless data.is_a? IO

    # give us a default block
    block ||= -> x, y { x << y } # unless block_given?

    while buf = data.read(blocksize)
      block.call ctx, buf
    end
  elsif block
    block.call ctx, nil
  end

  self.set_path("/#{algorithm};" +
    ctx.base64digest.tr('+/', '-_').tr('=', ''))
  self
end

#digest(radix: 256, alt: false) ⇒ String

Return the digest in the hash. Optionally takes a radix: argument to specify binary, base64, base32, or hexadecimal representations. Another optional flag will return alternative representations for each: base64url (vanilla base64 is canonical), base32 in lowercase (uppercase is canonical), hexadecimal in uppercase (lowercase is canonical). The binary representation naturally has no alternative form. Base64/base32 values will be appropriately padded.

Parameters:

  • radix (256, 64, 32, 16) (defaults to: 256)

    The radix of the representation

  • alt (false, true) (defaults to: false)

    Return the alternative representation

Returns:

  • (String)

    The digest of the URI in the given representation



309
310
311
312
# File 'lib/uri/ni.rb', line 309

def digest radix: 256, alt: false
  assert_radix radix
  transcode raw_digest, from: 64, to: radix, alt: alt
end

#digest=(value) ⇒ String, ...

Set the digest to the data. Data may either be a Digest::Instance or a binary string. Digest::Instance objects will just be run through #compute, with all that entails.

Parameters:

  • value (String, nil, Digest::Instance)

    the new digest

Returns:

  • (String, nil, Digest::Instance)

    the value passed in



353
354
355
# File 'lib/uri/ni.rb', line 353

def digest= value
  return set_digest value
end

#hexdigest(alt: false) ⇒ String

Return the digest in its hexadecimal notation. Optionally give alt: a truthy value to return an alternate (uppercase) representation.

Parameters:

  • alt (false, true) (defaults to: false)

    Return the alternative representation

Returns:

  • (String)

    The hexadecimal digest



364
365
366
# File 'lib/uri/ni.rb', line 364

def hexdigest alt: false
  transcode raw_digest, from: 64, to: 16, alt: alt
end

#hexdigest=(value) ⇒ String, ...

Set the digest value, assuming a hexadecimal input.

Parameters:

  • value (String, nil, Digest::Instance)

    the new digest

Returns:

  • (String, nil, Digest::Instance)

    the value passed in



371
372
373
# File 'lib/uri/ni.rb', line 371

def hexdigest= value
  set_digest value, radix: 16
end

#set_digest(value, radix: 256) ⇒ String

Set the digest to the data, with an optional radix. Data may either be a Digest::Instance—in which case the radix is ignored—a string, or nil. Digest::Instance objects will just be run through #compute, with all that entails.

Parameters:

  • value (String, nil, Digest::Instance)

    The new digest

  • radix (256, 64, 32, 16) (defaults to: 256)

    The radix of the encoding (default 256)

Returns:

  • (String)

    The old digest in the given radix



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/uri/ni.rb', line 323

def set_digest value, radix: 256
  assert_radix radix

  a, d = assert_path

  case value
  when Digest::Instance
    compute value
  when String
    value = transcode value, from: radix, to: 64
    self.path = a ? "/#{a};#{value}" : "/;#{value}"
  when nil
    self.path = a ? "/#{a}" : ?/
  else
    raise ArgumentError,
      "Value must be a string or Digest::Instance, not #{value.class}"
  end

  # bail out if nil
  return unless d
  transcode d, from: 64, to: radix
end

#to_http(authority: nil) ⇒ URI::HTTP

Unconditionally returns an HTTP URL.

Parameters:

  • authority (#to_s, URI) (defaults to: nil)

    Override the authority part of the URI

Returns:

  • (URI::HTTP)


474
475
476
# File 'lib/uri/ni.rb', line 474

def to_http authority: nil
  to_www https: false, authority: authority
end

#to_https(authority: nil) ⇒ URI::HTTPS

Unconditionally returns an HTTPS URL.

Parameters:

  • authority (#to_s, URI) (defaults to: nil)

    Override the authority part of the URI

Returns:

  • (URI::HTTPS)


464
465
466
467
# File 'lib/uri/ni.rb', line 464

def to_https authority: nil
  # note we don't simply alias this
  to_www authority: authority
end

#to_www(https: true, authority: nil) ⇒ URI::HTTPS, URI::HTTP

Returns a /.well-known/..., either HTTPS or HTTP URL, given the contents of the ni: URI.

Parameters:

  • authority (#to_s, URI) (defaults to: nil)

    Override the authority part of the URI

  • https (true, false) (defaults to: true)

    Whether the URL is to be HTTPS.

Returns:

  • (URI::HTTPS, URI::HTTP)

    The generated URL.



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# File 'lib/uri/ni.rb', line 419

def to_www https: true, authority: nil
  a, d = assert_path
  components = {
    scheme:   "http#{https ? ?s : ''}",
    userinfo: userinfo,
    host:     host,
    port:     port,
    path:     "/.well-known/ni/#{a}/#{d}",
    query:    query,
    fragment: fragment,
  }

  if authority
    uhp = []
    if authority.is_a? URI
      raise URI::InvalidComponentError, "Bad authority #{authority}" unless
        %i[userinfo host port].all? {|c| authority.respond_to? c }
      uhp = [authority.userinfo, authority.host, authority.port]
      uhp[2] = nil if authority.port == authority.class::DEFAULT_PORT
    else
      authority = authority.to_s
      uhp = AUTH_RE.match(authority) or raise URI::InvalidComponentError,
        "Invalid authority #{authority}"
      uhp = uhp.captures
    end
    components[:userinfo] = uhp[0]
    components[:host]     = uhp[1]
    components[:port]     = uhp[2]
  end

  # pick the class
  cls = https ? URI::HTTPS : URI::HTTP

  # `normalize` should do this but doesn't
  components[:port] = nil if
    components[:port] and components[:port] == cls::DEFAULT_PORT

  cls.build(components).normalize
end