The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF BCP14 (RFC2119 & RFC8174)
SPDX-FileCopyrightText: 2023 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership. This program and the accompanying materials are made available under the terms of the Apache License Version 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.0 SPDX-FileType: DOCUMENTATION SPDX-License-Identifier: Apache-2.0
The Transport & Session Layer is responsible for bidirectional point-2-point communication between uEntities (uE). The purpose of this layer of uProtocol is to define a common API for sending and receiving messages across different transport protocols like Eclipse Zenoh, MQTT 5 or Android Binder, runtime environments like Android, Linux or MCUs, and programming languages like Java, Rust, Python or C/C++.
This specification defines the transport layer’s abstract API which is mapped to supported programming languages by means of uProtocol’s language specific libraries. The API is implemented for particular transport protocols and programming languages by uProtocol Client libraries.
The Transport Layer API is defined using UML2 notation.
classDiagram
class UTransport {
<<interface>>
send(message : UMessage)
receive(sourceFilter: UUri, sinkFilter: UUri [0..1]) UMessage
registerListener(sourceFilter: UUri, sinkFilter: UUri [0..1], listener: UListener)
unregisterListener(sourceFilter: UUri, sinkFilter: UUri [0..1], listener: UListener)
}
class UListener {
<<interface>>
onReceive(message : UMessage)
}
UTransport ..> UListener
Note
|
The data types used in the following sections are defined in uProtocol Basic Types. |
A uEntity registers a UListener
with the transport in order to process (incoming) messages that are of interest to the uEntity.
A transport invokes this method for each newly arrived message that matches the criteria specified as part of registering the listener.
onReceive(message: UMessage)
Parameter | Type | Description |
---|---|---|
message |
The newly received message. |
sequenceDiagram
participant L as listener:UListener
participant T as transport:UTransport
T-)L : onReceive(UMessage)
activate L
deactivate L
This is the main entry point into a transport’s messaging functionality.
Clients use this method to transmit a single message.
send(message: UMessage)
Parameter | Type | Description |
---|---|---|
message |
The message to send. |
The successful completion of this method means that the given message has been handed over to the underyling communication protocol’s message delivery mechanism. For a hub-and-spoke based communication protocol like MQTT, this typically means that the send
method implementation has received the MQTT PUBACK
packet, which indicates that the message has been transferred successfully to the broker from where the message remains to be retrieved by potential recipients. For a peer-to-peer based protocol like HTTP, this typically means that the send
method implementation has received the peer’s HTTP response message, which indicates that the message has been transferred successfully to the peer’s HTTP endpoint. Based on that, a client using this method should not assume that the given message has already reached its destination nor that it has already been processed once the method has completed successfully.
On the other hand, unsuccessful completion of this method does not necessarily mean that the given message has not been sent at all. For example, an MQTT based implementation might lose its connection to the MQTT broker after it has sent its MQTT PUBLISH
packet but before it has received the broker’s PUBACK
. In such a case, clients should use the returned error to determine, if another attempt to send the message is feasible or not. For example, if the initial attempt to send the message has failed with a UCode.INVALID_ARGUMENT
, then trying to send the same unaltered message again will most likely yield the same result. However, if the initial attempt failed with a UCode.UNAVAILABLE
, then resending the message using some back-off mechanism will likely succeed eventually.
Note
|
The above strategy for retrying failed attempts to send a message results in at-least-once delivery. Recipient(s) of these messages should therefore be Idempotent Processors. |
UTransport implementations
-
MUST preserve all of the message’s meta data and payload during transmission
sequenceDiagram
actor C as Client
participant T as transport:UTransport
C->>T : send(UMessage)
activate T
opt error while sending
Note right of T: message may or may<br>not have been sent
T--)C : error : Ustatus
end
deactivate T
Clients use this method to receive a single message matching given filter criteria.
receive(sourceFilter: UUri, sinkFilter: UUri [0..1]) : UMessage
Parameter | Type | Description |
---|---|---|
sourceFilter |
The source address pattern that messages need to match. |
|
sinkFilter |
The sink address pattern that messages need to match. If omitted, a message MUST NOT contain any sink address in order to match. |
|
result |
UMessage |
The least recent message that matches the given filter criteria and has not expired yet. |
This method implements the pull delivery method on top of the underlying communication protocol.
UTransport implementations
-
MUST fail with a
UCode.UNIMPLEMENTED
if the transport does not support the pull delivery method -
MUST fail with a
UCode.NOT_FOUND
if there are no matching messages available
sequenceDiagram
actor C as Client
participant T as transport:UTransport
C->>T : receive(UUri, UUri)
activate T
alt pull not supported
T--)C : error : UStatus(UCode.UNIMPLEMENTED)
else no message available
T--)C : error : UStatus(UCode.NOT_FOUND)
else
T--)C : matching message : UMessage
end
deactivate T
Clients use this method to register a listener for messages matching given filter criteria.
registerListener(sourceFilter: UUri, sinkFilter: UUri [0..1], listener: UListener)
Parameter | Type | Description |
---|---|---|
sourceFilter |
The source address pattern that messages need to match. |
|
sinkFilter |
The sink address pattern that messages need to match. If omitted, a message must not contain any sink address in order to match. |
|
listener |
The listener to be registered. |
This API is used to implement the push delivery method on top of the underlying communication protocol. After this method has completed successfully, the given listener will be invoked for each message that matches the given source and sink filter patterns according to the rules defined by the UUri specification.
UTransport implementations
-
MUST fail with a
UCode.UNIMPLEMENTED
if the transport does not support the push delivery method. In that case, the unregisterListener method MUST also fail accordingly. -
MUST fail with a
UCode.RESOURCE_EXHAUSTED
, if the maximum number of listeners is reached -
MUST support registering more than one listener for any given address patterns
-
MUST support registering the same listener for multiple address patterns
-
MUST document the maximum supported number of listeners per address pattern.
sequenceDiagram
actor C as Client
participant T as transport:UTransport
C->>T : register(UUri, UUri, UListener)
activate T
opt error
alt push not supported
T--)C : error : UStatus(UCode.UNIMPLEMENTED)
else max listeners exceeded
T--)C : error : UStatus(UCode.RESOURCE_EXHAUSTED)
else other
T--)C : error : UStatus
end
end
deactivate T
Clients use this method to unregister a previously registered listener. After this method has returned successfully, the listener will no longer be invoked for any (matching) messages.
unregisterListener(sourceFilter: UUri, sinkFilter: UUri [0..1], listener: UListener)
Parameter | Type | Description |
---|---|---|
sourceFilter |
The source address pattern that the listener had been registered for. |
|
sinkFilter |
The sink address pattern that the listener had been registered for. |
|
listener |
The listener to be unregistered. |
UTransport implementations
-
MUST fail with a
UCode.UNIMPLEMENTED
if the transport does not support the push Message Delivery. In that case, the RegisterListener method MUST also fail accordingly. -
MUST fail with a
UCode.NOT_FOUND
, if no such listener had been registered before
sequenceDiagram
actor C as Client
participant T as transport:UTransport
C->>T : unregister(UUri, UUri, UListener)
activate T
opt error
alt push not supported
T--)C : error : UStatus(UCode.UNIMPLEMENTED)
else no such listener
T--)C : error : UStatus(UCode.NOT_FOUND)
else other
T--)C : error : UStatus
end
end
deactivate T
Transport API implementations
-
MUST support at least one of push or pull delivery methods and MAY support both
-
MUST document the delivery methods they support
Communication protocols like MQTT, HTTP define a specific Protocol Data Unit (PDU) for conveying control information and user data. A uProtocol Client implements the Transport Layer API defined above on top of such a communication protocol.
A communication protocol binding defines how the uProtocol Transport Layer API maps to the communication protocol’s message exchange pattern(s) and how uProtocol messages are mapped to the protocol’s PDU. Many communication protocols distinguish between a message’s metadata and the (raw) payload. This is often reflected by the structure of the protocol’s PDU. For example, HTTP supports header fields and a body which can be used to convey a uProtocol message’s attributes and payload respectively.
uProtocol defines bindings to the following communication protocols:
Each uProtocol Client MUST employ exactly one of these bindings for implementing the Transport Layer API.
Additional bindings MAY be defined in future versions of uProtocol.
A binding MAY employ CloudEvents as a means to map uProtocol messages to the communication protocol’s PDU. In order to provide for consistency across implementations, such bindings MUST adhere to UMessage mapping to CloudEvents