Rebus.Message (rebus v0.3.0)
View SourceConstructs, validates, encodes, and decodes D-Bus messages.
A message has a fixed header, typed header fields, and a body described by a
D-Bus signature. See the
D-Bus message protocol
for the wire layout and required header fields. Use new/2 for outbound
messages and signature/1 to read the body signature.
Unix file descriptors
The unix_fds struct field is separate from the :unix_fds header count.
Construct outbound messages with fds: [fd, ...]; h body values are their
zero-based wire indexes. Inbound descriptors appear in message.unix_fds
only on a Rebus.call/3 reply, and the calling process then owns them. See
Unix file descriptor passing.
Examples
# A method call
{:ok, message} =
Rebus.Message.new(:method_call,
path: "/com/example/Object",
interface: "com.example.Interface",
member: "Method",
destination: "com.example.Service",
body: [42, "hello"],
signature: "is"
)
# A signal
{:ok, message} =
Rebus.Message.new(:signal,
path: "/com/example/Object",
interface: "com.example.Interface",
member: "SignalName",
body: ["value"],
signature: "s"
)
# An error reply
{:ok, message} =
Rebus.Message.new(:error,
error_name: "com.example.Error.Failed",
reply_serial: 123,
body: ["Error message"],
signature: "s"
)
Summary
Types
A reason why new/2 or validate/1 rejected a message.
A reason why encode/2 could not produce a valid D-Bus frame.
Message flags
Header field keys
Message type
D-Bus message structure
Functions
Decodes a binary message.
Encodes a message to iodata format.
The largest D-Bus array payload accepted or emitted, in bytes.
The largest complete D-Bus message accepted from the wire, in bytes.
The maximum number of fixed-width scalar array elements per encode or decode.
The maximum number of Unix file descriptors accepted in one message.
Creates a new D-Bus message.
Creates a new D-Bus message, raising on error.
Parses a complete D-Bus message from a binary if sufficient data is available.
Gets the signature from the message header fields.
Gets the message type as an integer code.
Gets the message type from an integer code.
Validates that a message is well-formed according to D-Bus rules.
Types
@type construction_error() :: :invalid_body | :invalid_flags | :invalid_header_fields | :invalid_signature | :invalid_type | :invalid_unix_fds | :invalid_version | :message_too_large | :resource_limit | :unix_fd_limit | {:invalid_header_field, header_field()} | {:missing_header_field, header_field()} | {:unknown_header_field, term()}
A reason why new/2 or validate/1 rejected a message.
@type encoding_error() ::
:invalid_body
| :invalid_header_fields
| :invalid_message
| :invalid_unix_fds
| :message_too_large
| :resource_limit
| :unix_fd_limit
A reason why encode/2 could not produce a valid D-Bus frame.
@type flag() :: :no_reply_expected | :no_auto_start | :allow_interactive_authorization
Message flags
@type header_field() ::
:path
| :interface
| :member
| :error_name
| :reply_serial
| :destination
| :sender
| :signature
| :unix_fds
Header field keys
@type message_type() :: :method_call | :method_return | :error | :signal
Message type
@type t() :: %Rebus.Message{ body: [term()], body_length: non_neg_integer(), encoded_body: {[term()], binary(), binary()} | nil, flags: [flag()], header_fields: %{optional(header_field()) => term()}, serial: non_neg_integer(), type: message_type(), unix_fds: [Rebus.UnixFD.t()], version: non_neg_integer() }
D-Bus message structure
Functions
Decodes a binary message.
Parses a D-Bus message from binary format according to the wire format specification.
Parameters
binary- The binary data to decode
Examples
iex> message = Rebus.Message.new!(:signal, path: "/", interface: "org.example.Test", member: "Test")
iex> {:ok, encoded} = Rebus.Message.encode(message)
iex> {:ok, decoded} = Rebus.Message.decode(IO.iodata_to_binary(encoded))
iex> decoded.type
:signalReturns
{:ok, message} on success, {:error, reason} on failure.
@spec encode(t(), :little | :big) :: {:ok, iodata()} | {:error, encoding_error()}
Encodes a message to iodata format.
Returns the message encoded according to the D-Bus wire format specification.
The endianness can be specified as :little or :big (default: :little).
Parameters
message- The message to encodeendianness- Byte order (:littleor:big, default::little)
Examples
iex> message = Rebus.Message.new!(:signal, path: "/", interface: "org.example.Test", member: "Test")
iex> {:ok, iodata} = Rebus.Message.encode(message)
iex> is_binary(IO.iodata_to_binary(iodata))
trueReturns
{:ok, iodata} on success. Returns {:error, :invalid_body} when the
message body does not match its signature, {:error, :invalid_header_fields}
for invalid header values, or {:error, :invalid_message} for an invalid
fixed header or missing required fields. Returns {:error, :message_too_large}
when the encoded frame exceeds the D-Bus message or header-fields limits, or
an encoded array exceeds max_array_size/0, or {:error, :resource_limit}
when a local structural, nesting, or scalar cap is exhausted.
@spec max_array_size() :: pos_integer()
The largest D-Bus array payload accepted or emitted, in bytes.
This is the D-Bus protocol limit of 2^26 bytes. It is distinct from the
local scalar materialization cap exposed by max_scalar_elements/0.
@spec max_message_size() :: pos_integer()
The largest complete D-Bus message accepted from the wire, in bytes.
This is the D-Bus protocol limit of 2^27 bytes and includes the fixed header, header fields, alignment padding, and body.
@spec max_scalar_elements() :: pos_integer()
The maximum number of fixed-width scalar array elements per encode or decode.
This local safety cap is 1,000,000 elements. Encoding shares it cumulatively across every fixed-width scalar array in one encode operation; it is not a D-Bus wire-format limit.
@spec max_unix_fds() :: pos_integer()
The maximum number of Unix file descriptors accepted in one message.
This local bound applies to the D-Bus header count and the ancillary-data control buffer. It is deliberately lower than operating-system limits.
@spec new( message_type(), keyword() ) :: {:ok, t()} | {:error, construction_error()}
Creates a new D-Bus message.
Parameters
type- The message type (:method_call,:method_return,:error,:signal)opts- Keyword list of options::flags- A list of flags (default:[])::no_reply_expectedsuppresses a reply,:no_auto_startprevents service activation, and:allow_interactive_authorizationpermits interactive authorization:version- Protocol version (default:1):body- Message body as list of values (default:[]):signature- Message body signature (default: auto-generated from body;:infinity,:negative_infinity, and:naninferd):fds- Borrowed Unix file descriptors. Eachhvalue in the body is an index into this list. Rebus never closes outbound descriptors.:path- Object path; required for method calls and signals:interface- Interface name; required for signals:member- Method or signal name; required for method calls and signals:error_name- D-Bus error name; required for error replies:reply_serial- Request serial; required for method returns and errors:destination- Optional target connection name:sender- Optional sending connection name, normally supplied by a bus
Note
The serial number is initialized to 1. The transport layer that dispatches the message assigns its own serial number before writing the frame.
Examples
iex> {:ok, message} = Rebus.Message.new(:method_call,
...> path: "/com/example/Object",
...> member: "TestMethod"
...> )
iex> message.type
:method_callErrors
Returns {:error, reason} where reason is one of:
:invalid_type- the message type is not a D-Bus message type:invalid_flags- the flags are not a list, or include an unknown flag:invalid_version- the protocol version is unsupported:invalid_body- the body is not a list, or its values cannot be encoded by the signature:invalid_signature- the signature is not a binary, or is not a valid D-Bus type expression{:invalid_header_field, field}- the value given for that header field is not valid{:missing_header_field, field}- a header field required for the message type was not given:invalid_unix_fds- the descriptors do not match the body:unix_fd_limit- the message exceeds the Unix file descriptor limit:message_too_large- the encoded message, or an array within it, exceeds the D-Bus size limit:resource_limit- a local structural, nesting, or scalar materialization cap is exceeded
@spec new!( message_type(), keyword() ) :: t()
Creates a new D-Bus message, raising on error.
Same as new/2 but raises ArgumentError instead of returning {:error, reason}.
Parses a complete D-Bus message from a binary if sufficient data is available.
This function checks if the provided binary contains enough data to parse a complete
D-Bus message (both header and body). If it does, it extracts exactly the right
amount of data and passes it to decode/1. If the binary is too small, returns nil.
This is useful for streaming scenarios where you receive partial data and need to determine when you have a complete message.
Parameters
binary- The binary data that may contain a D-Bus message
Returns
{:ok, message, remaining_data}- If a complete message was successfully parsed{:error, reason}- If the binary contains sufficient data but parsing failed. Invalid endianness, message type, and protocol version are rejected as soon as the 12-byte fixed header is available.:message_too_largeis returned as soon as the header-fields length is available and the declared complete message would exceedmax_message_size/0, and for a body array whose declared length exceedsmax_array_size/0.nil- If the binary does not contain sufficient data for a complete message
Examples
# Insufficient data
iex> Rebus.Message.parse(<<1, 2, 3>>)
nil
Gets the signature from the message header fields.
Returns the signature string if present, or an empty string if not.
Examples
iex> message = Rebus.Message.new!(:signal, path: "/", interface: "org.example.Test", member: "Test", body: [42], signature: "i")
iex> Rebus.Message.signature(message)
"i"
iex> message = Rebus.Message.new!(:signal, path: "/", interface: "org.example.Test", member: "Test")
iex> Rebus.Message.signature(message)
""
@spec type_code(message_type()) :: non_neg_integer()
Gets the message type as an integer code.
@spec type_from_code(non_neg_integer()) :: {:ok, message_type()} | {:error, :invalid_message_type}
Gets the message type from an integer code.
@spec validate(t()) :: :ok | {:error, construction_error()}
Validates that a message is well-formed according to D-Bus rules.
Checks that:
- Message type is valid
- Required header fields are present for the message type
- Header field types are correct
- Message signature is valid
Examples
iex> message = Rebus.Message.new!(:method_call, path: "/test", member: "Test")
iex> Rebus.Message.validate(message)
:ok