Rebus.Message (rebus v0.4.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. The
D-Bus message protocol
gives the wire layout and the header fields each message type requires.
Build outbound messages with new/2 and read a body's signature with
signature/1.
Unix file descriptors
The unix_fds struct field holds the descriptors themselves and is
separate from the :unix_fds header field, which is only their count.
Build an outbound message with fds: [fd, ...], where each fd is a
Rebus.UnixFD handle or a raw descriptor number, in any mix; each h
value in the body is a zero-based index into that list. Sending neither
closes nor transfers a handle, and a raw number stays the caller's to keep
open until the send returns and to close afterwards.
A received message holds Rebus.UnixFD handles. On a Rebus.call/3
reply, the calling process owns them. On the message of an inbound call
delivered to a Rebus.Service handler, the connection owns them and the
handler borrows them until the call completes. 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
A descriptor in unix_fds. A received message holds Rebus.UnixFD
handles; one built to be sent may hold handles, raw descriptor numbers, or
both.
Functions
Decodes one complete message from a binary.
Encodes a message in the D-Bus wire format, as iodata.
The largest D-Bus array payload accepted or emitted, in bytes.
The largest complete D-Bus message accepted from the wire, in bytes.
The most fixed-width scalar array elements one encode or decode may produce.
The maximum number of Unix file descriptors accepted in one message.
Builds a D-Bus message of type, validating it.
Creates a new D-Bus message, raising on error.
Parses one message from the front of a binary that may hold part of a message, or more than one.
Gets the signature from the message header fields.
Gets the message type as an integer code.
Gets the message type from an integer code.
Checks that a message is well formed.
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: [unix_fd()], version: non_neg_integer() }
D-Bus message structure
@type unix_fd() :: Rebus.UnixFD.t() | Rebus.UnixFD.raw()
A descriptor in unix_fds. A received message holds Rebus.UnixFD
handles; one built to be sent may hold handles, raw descriptor numbers, or
both.
Functions
Decodes one complete message from a binary.
Returns {:ok, message}, or {:error, reason} when the binary is not a
well-formed D-Bus message. Use parse/1 for a binary that may hold part of
a message, or more than one.
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
:signal
@spec encode(t(), :little | :big) :: {:ok, iodata()} | {:error, encoding_error()}
Encodes a message in the D-Bus wire format, as iodata.
endianness is :little by default, or :big.
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))
trueReturn values
{:ok, iodata}- the encoded frame.{:error, :invalid_body}- the body does not match its signature.{:error, :invalid_header_fields}- a header field value is invalid.{:error, :invalid_message}- the fixed header is invalid, or a required header field is missing.{:error, :message_too_large}- the frame exceeds the D-Bus message or header-fields limit, or an array in it exceedsmax_array_size/0.{:error, :resource_limit}- a local structural, nesting or scalar cap was exceeded.
@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. max_scalar_elements/0 is
a separate, local limit on element counts.
@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. A connection can accept less
with the :max_message_size option of Rebus.connect/2.
@spec max_scalar_elements() :: pos_integer()
The most fixed-width scalar array elements one encode or decode may produce.
This is a local safety cap of 1,000,000 elements, not a D-Bus limit. One encode operation shares it across every fixed-width scalar array in the message.
@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()}
Builds a D-Bus message of type, validating it.
type is :method_call, :method_return, :error or :signal. The
message's serial is set to 1; the connection assigns the real serial as it
writes the frame.
Options
:path- the object path. Required for method calls and signals.:interface- the interface name. Required for signals.:member- the method or signal name. Required for method calls and signals.:error_name- the D-Bus error name. Required for errors.:reply_serial- the serial of the call being answered. Required for method returns and errors.:destination- the connection to send the message to.:sender- the sending connection's name, normally set by the bus.:body- the body, as a list of values. Defaults to[].:signature- the body's signature. Defaults to one inferred from the body, where:infinity,:negative_infinityand:naninferd.:fds- Unix file descriptors to send, whichhvalues in the body index:Rebus.UnixFDhandles, raw descriptor numbers, or both. Rebus never closes or transfers them. Whether a handle is open is checked only when a connection accepts the message for sending.:flags- a list of:no_reply_expected,:no_auto_startand:allow_interactive_authorization. Defaults to[].:version- the protocol version. Defaults to1.
Examples
iex> {:ok, message} = Rebus.Message.new(:method_call,
...> path: "/com/example/Object",
...> member: "TestMethod"
...> )
iex> message.type
:method_callErrors
:invalid_type-typeis 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 with the signature.:invalid_signature- the signature is not a binary, or 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 the message type requires 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 cap was 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 one message from the front of a binary that may hold part of a message, or more than one.
This is the function to use on a stream. It decodes the first message once
the binary holds all of it, and returns nil until then.
Return values
{:ok, message, rest}- a complete message, and the bytes after it.nil- the binary does not yet hold a complete message.{:error, reason}- the data is malformed. An invalid endianness, message type or protocol version is rejected as soon as the 12-byte fixed header is available.:message_too_largeis returned as soon as the header-fields length shows that the complete message would exceedmax_message_size/0, and for a body array whose declared length exceedsmax_array_size/0.
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()}
Checks that a message is well formed.
The message type must be valid, the header fields the type requires must
be present, every header field must have the right type, and the body
signature must be valid. new/2 runs these checks already; this is for a
message built or changed by hand.
Examples
iex> message = Rebus.Message.new!(:method_call, path: "/test", member: "Test")
iex> Rebus.Message.validate(message)
:ok