Class: Kobako::RPC::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/kobako/rpc/server.rb

Overview

Kobako::RPC::Server — per-Sandbox host-side RPC coordinator. Maintains the Namespace / Member registry, owns the HandleTable, and routes incoming Requests to the resolved Service object (docs/behavior.md B-07..B-21).

Public API:

server = Kobako::RPC::Server.new
namespace = server.define(:MyService)    # => Kobako::RPC::Namespace
namespace.bind(:KV, kv_object)           # => namespace (chainable)
server.to_preamble                       # => array for Frame 1
server.dispatch(request_bytes)           # => msgpack bytes (delegated to Dispatcher)

Namespaces live at Kobako::RPC::Namespace (lib/kobako/rpc/namespace.rb). The opaque Handle allocator lives at Kobako::RPC::HandleTable (lib/kobako/rpc/handle_table.rb). Dispatch helpers live at Kobako::RPC::Dispatcher (lib/kobako/rpc/dispatcher.rb).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(handle_table: HandleTable.new) ⇒ Server

Build a fresh Server. handle_table is an internal seam that injects a pre-configured HandleTable; tests pass one whose next_id is pinned near MAX_ID to exercise the B-21 cap-exhaustion path without 2³¹ allocations. Production callers leave it at the default.



36
37
38
39
40
# File 'lib/kobako/rpc/server.rb', line 36

def initialize(handle_table: HandleTable.new)
  @namespaces = {} # : Hash[String, Kobako::RPC::Namespace]
  @handle_table = handle_table
  @sealed = false
end

Instance Attribute Details

#handle_tableObject (readonly)

Expose the HandleTable for tests and wire-layer Handle wrapping.



144
145
146
# File 'lib/kobako/rpc/server.rb', line 144

def handle_table
  @handle_table
end

Instance Method Details

#bound?(target) ⇒ Boolean

Returns true when target (a “Namespace::Member” path) resolves to a bound member, false otherwise.

Returns:

  • (Boolean)


74
75
76
77
# File 'lib/kobako/rpc/server.rb', line 74

def bound?(target)
  namespace, member_name, = parse_target(target)
  !namespace.nil? && !member_name.nil? && !namespace[member_name].nil?
end

#define(name) ⇒ Object

Declare or retrieve the Namespace named name (idempotent — docs/behavior.md B-10). name is a constant-form name as a Symbol or String (must satisfy Namespace::NAME_PATTERN). Returns the Kobako::RPC::Namespace for that name, creating it if it does not exist. Raises ArgumentError when name is malformed, or when called after the owning Sandbox has been sealed by its first invocation (docs/behavior.md B-07).

Raises:

  • (ArgumentError)


48
49
50
51
52
53
54
55
56
57
58
# File 'lib/kobako/rpc/server.rb', line 48

def define(name)
  raise ArgumentError, "cannot define after first Sandbox invocation" if @sealed

  name_str = name.to_s
  unless Namespace::NAME_PATTERN.match?(name_str)
    raise ArgumentError,
          "Namespace name must match #{Namespace::NAME_PATTERN.inspect} (got #{name.inspect})"
  end

  @namespaces[name_str] ||= Namespace.new(name_str)
end

#dispatch(request_bytes) ⇒ Object

Dispatch a single RPC request and return the encoded response bytes (docs/behavior.md B-12). request_bytes is a msgpack-encoded Request envelope. Called by the Rust ext from inside __kobako_dispatch. Always returns a binary String — never raises. Delegates to Dispatcher.dispatch which reifies any failure as a Response.error envelope so the guest sees the failure as a normal RPC error rather than a wasm trap.



139
140
141
# File 'lib/kobako/rpc/server.rb', line 139

def dispatch(request_bytes)
  Dispatcher.dispatch(request_bytes, self)
end

#empty?Boolean

Returns true when no namespaces have been declared, false otherwise.

Returns:

  • (Boolean)


90
91
92
# File 'lib/kobako/rpc/server.rb', line 90

def empty?
  @namespaces.empty?
end

#encoded_preambleObject

Encode the preamble as msgpack bytes for stdin Frame 1 delivery (docs/behavior.md B-02). Uses plain MessagePack (no kobako ext types) because the preamble contains only strings — no Handles or Fault envelopes. Structure: [[“Namespace”, [“MemberA”, “MemberB”]], …]. Returns a binary String of msgpack bytes.



109
110
111
# File 'lib/kobako/rpc/server.rb', line 109

def encoded_preamble
  MessagePack.pack(to_preamble)
end

#lookup(target) ⇒ Object

Resolve a target path of the form “Namespace::Member” to the bound Host object. target is a two-level path using the :: separator. Returns the bound Host object. Raises KeyError when the namespace or the member is not bound.

Raises:

  • (KeyError)


64
65
66
67
68
69
70
# File 'lib/kobako/rpc/server.rb', line 64

def lookup(target)
  namespace, member_name, namespace_name = parse_target(target)
  raise KeyError, "no namespace named #{namespace_name.inspect}" if namespace.nil?
  raise KeyError, "no member #{target.inspect} bound on server" unless member_name

  namespace.fetch(member_name)
end

#namespacesObject

Returns all declared Kobako::RPC::Namespace instances as an Array.



80
81
82
# File 'lib/kobako/rpc/server.rb', line 80

def namespaces
  @namespaces.values
end

#reset_handles!Object

Reset the HandleTable for a new invocation boundary. Called by Sandbox before each invocation (docs/behavior.md B-19).



128
129
130
# File 'lib/kobako/rpc/server.rb', line 128

def reset_handles!
  @handle_table.reset!
end

#seal!Object

Mark the Server as sealed. Called by Sandbox on the first invocation. After sealing, #define raises ArgumentError. Idempotent.



115
116
117
118
# File 'lib/kobako/rpc/server.rb', line 115

def seal!
  @sealed = true
  self
end

#sealed?Boolean

Returns true when #seal! has been called, false otherwise.

Returns:

  • (Boolean)


121
122
123
# File 'lib/kobako/rpc/server.rb', line 121

def sealed?
  @sealed
end

#sizeObject

Returns the number of declared namespaces as an Integer.



85
86
87
# File 'lib/kobako/rpc/server.rb', line 85

def size
  @namespaces.size
end

#to_preambleObject

Structured Frame 1 description. Called by Sandbox#eval when assembling stdin Frame 1 (docs/behavior.md B-02). Returns an unencoded preamble array — an Array of two-element [name, members] arrays, one per declared namespace.



99
100
101
# File 'lib/kobako/rpc/server.rb', line 99

def to_preamble
  @namespaces.values.map(&:to_preamble)
end