Module: SecurityBox::Rpcs

Defined in:
lib/security_box/configuration.rb

Overview

Validated collection of guest-callable RPC handlers (stage 6). A handler is a name => callable pair; rpcs in a Configuration is a frozen hash, so the value survives #with and #to_h.

Handlers execute on the host (between guest steps, inside the RPC import) and are therefore host-only state: they are excluded from #fingerprint, and a Configuration carrying Procs is not Ractor-shareable — RactorPool rejects rpcs and strips them before a config crosses a Ractor boundary.

Validation (InvalidConfiguration on any violation):

- raw is a Hash of name => handler, or an Array of one-pair hashes
(the register DSL appends one pair per c.rpc call)
- name: non-empty String, unique
- handler: anything responding to #call; it receives the
JSON-parsed request args and must return a JSON-serializable
value (non-serializable results surface as inspect strings)
- at most MAX_RPCS handlers

Constant Summary collapse

MAX_RPCS =
64

Class Method Summary collapse

Class Method Details

.normalize(raw) ⇒ Object

Validates + normalizes raw and returns a frozen name => handler hash.



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/security_box/configuration.rb', line 379

def normalize(raw)
  return {}.freeze if raw.nil?

  pairs = case raw
          when Hash then raw.entries
          when Array
            unless raw.all? { |entry| entry.is_a?(Hash) && entry.size == 1 }
              raise InvalidConfiguration,
                    "rpcs must be a Hash or an Array of one-pair hashes, " \
                    "got #{raw.inspect[0, 80]}"
            end
            raw.flat_map(&:entries)
          else
            raise InvalidConfiguration,
                  "rpcs must be a Hash of name => handler, got #{raw.class}"
          end

  rpcs = {}
  pairs.each do |name, handler|
    unless name.is_a?(String) && !name.empty?
      raise InvalidConfiguration, "rpc name must be a non-empty String, got #{name.inspect}"
    end
    unless handler.respond_to?(:call)
      raise InvalidConfiguration,
            "rpc handler for #{name.inspect} must respond to #call, " \
            "got #{handler.inspect[0, 80]}"
    end
    raise InvalidConfiguration, "duplicate rpc name #{name.inspect}" if rpcs.key?(name)

    rpcs[name] = handler
  end
  return rpcs.freeze if rpcs.size <= MAX_RPCS

  raise InvalidConfiguration,
        "too many rpcs (#{rpcs.size}); the limit is #{MAX_RPCS}"
end