Skip to content

Latest commit

 

History

History
427 lines (331 loc) · 14 KB

uri.adoc

File metadata and controls

427 lines (331 loc) · 14 KB

uProtocol URI (UUri)

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

1. Overview

uProtocol uses Uniform Resource Identifiers (URI) to uniquely identify (and/or address) resources such as devices, software components, topics, methods, etc on a computer network. RFC3986 defines the structure of a URI as follows:

RFC3986 URI
Figure 1. URI

Using this terminology, a uProtocol URI (UUri) can be structured like this:

uProtocol Uri Specification
Figure 2. UUri Specification

A UUri can be represented as:

In the remainder of this document, the term UUri is used to refer to an instantiation of the data model, whereas the term URI is used to refer to its textual representation.

2. Data Model

The diagram below shows a UUri and its properties using UML2 notation.

UUri Data Model
classDiagram

class UUri {
  authority_name : String
  ue_id : UInt32
  ue_version_major : UInt8
  resource_id : UInt16
}
Loading

Each uProtocol Language Library MUST implement the data model using the type and property names as defined in the following sections.

Each uProtocol Language Library MUST support writing and reading of an instance of the data model to/from a protobuf as defined in uri.proto.

The remainder of this section defines the requirements for the data model by means of invariants which must hold true at all times. Additionally, there are some predicates that can be used by uEntities to determine if a UUri contains the information they are interested in. Both the invariants and the predicates are (formally) defined by means of Object Constraint Language (OCL) expressions on the UUri data model. The OCL Tutorial provides an overview of the basic concepts used in the definitions.

2.1. Authority

An authority represents the deployment location of a specific software entity. The location is represented by means of a logical identifier like a domain name (e.g. mcu1.example.com) or a Vehicle Identification Number (VIN).

A UUri’s authority_name MUST NOT exceed 128 characters in length.

inv: authority_name.length() <= 128

2.2. uEntity

A uProtocol software entity (uEntity) is a piece of software deployed somewhere on a network host. uEntities are uniquely identified within a system by means of the type and version of the service interface that they implement and an instance identifier in case multiple instances of the same service are deployed in the system.

Note
uEntities which produce events for consumption by other uEntities assume a Service role.
uEntities which consume events produced by other uEntities assume an Application role.
uEntities MAY assume both the Service and Application roles.

A UUri’s ue_id property value determines the type and instance of the service being referred to:

  • The value’s least significant 16 bits contain the service ID, representing the service interface type.

  • The value’s most significant 16 bits contain the service instance ID.

2.3. Resource

A service interface consists of resources and methods. A resource usually represents the state of a property of the service (instance) while methods are used to manage the state of a service (instance).

Resources and methods are uniquely identified within a service interface by means of their numeric identifier.

A UUri’s resource_id contains the identifier of the resource or method being referred to.

3. Mapping to URI

It is often helpful to represent UUris in a textual format, for example when serializing uProtocol messages to a transport’s Protocol Data Unit (PDU). This section defines how UUris can be mapped to and from a URI according to the URI-Reference rule of RFC3986.

The URIs mapped from UUris use the syntax defined in RFC3986, Appendix A with a few modifications:

A URI MUST NOT use any scheme other than up and MUST NOT have a query component and MUST NOT have a fragment component.

URI = "up" ":" hier-part
relative-ref = relative-part

The authority component of a URI MUST consist of characters according to the following rule:

lc-unreserved = *( %x61-7A / DIGIT / "-" / "." / "_" / "~" )
               ; lowercase only unreserved
authority = IP-literal / IPv4address / lc-unreserved / "*"

In particular, the authority MUST NOT contain any userinfo nor port.

Note
The ABNF fragments above only contain the rules that differ from the original definitions in RFC3986.

A URI’s authority MUST be mapped to/from the UUri’s authority_name property following the rules defined in RFC3986, Section 3.2.2. In particular, the URI MUST NOT contain an authority if authority_name is empty and vice versa.

A URI’s path MUST be mapped to/from the UUri’s ue_id, ue_version_major and resource_id properties.

Each property value MUST be mapped to a segment following the rules defined in RFC3986, Section 3.3.

The ue_id, ue_version_major and resource_id MUST be mapped to the upper-case base16 encoding of the corresponding property values. Leading zeros (0) MAY be omitted.

3.1. Examples

The examples below can be used as test vectors for implementing client libraries.

authority entity version resource URI

""

0x0000

0x03

0x8000

/0/3/8000

"192.168.1.10"

0x0000

0xFF

0x8000

//192.168.1.10/0/FF/8000

4. Serialization

Each uProtocol Language Library MUST provide means to serialize UUris to the URI format and vice versa. A concrete implementation should follow common practices for the particular programming language.

For example, a Java library might implement a UriSerializer class providing corresponding static methods.

public final class UriSerializer {
  /**
   * @returns The UUri parsed from the given string representation.
   * @throws UuriSerializationException if the given string is not a valid URI.
                        The exception may contain details regarding the violated
                        constraint(s).
   */
  public static UUri deserialize(String: uri) throws UuriSerializationException {
    ...
  }
  /**
   * @returns The given UUri's string representation.
   * @throws UuriSerializationException if the UUri cannot be serialized.
   */
  public static String serialize(UUri: uuri) throws UuriSerializationException {
    ...
  }
}

Alternatively, the UUri class might provide corresponding methods.

public class UUri {
  /**
   * @returns The UUri parsed from the given string representation.
   * @throws UuriSerializationException if the given string is not a valid
                         URI. The exception may contain details
   *                     regarding the violated constraint(s).
   */
  public static UUri fromUri(String: uri) throws UuriSerializationException {
    ...
  }
  /**
   * @returns The given UUri's string representation.
   * @throws UuriSerializationException if this UUri cannot be serialized.
   */
  public final String toUri() throws UuriSerializationException {
    ...
  }
}

Similarly, a Rust library might implement a UriSerializer struct providing corresponding functions

pub struct UriSerializer {}

impl UriSerializer {
  pub fn try_deserialize(uri: &str) -> Result<UUri, UuriSerializationError> {
    ...
  }
  pub fn try_serialize(uuri: &UUri) -> Result<String, UuriSerializationError> {
    ...
  }
}

or implement the functions on the UUri struct

impl UUri {
  pub fn try_from_uri(uri: &str) -> Result<UUri, UuriSerializationError> {
    ...
  }
  pub fn try_to_uri(&self) -> Result<String, UuriSerializationError> {
    ...
  }
}

5. Pattern Matching

A UUri can be used to define a pattern that other UUris can then be matched against. For that purpose, a UUri

  • MAY have its authority_name set to the * (U+002A, Asterisk) character in order to match any (including no) authority.

  • MAY have the service ID part of its ue_id set to 0xFFFF in order to match any service type.

  • MAY have the service instance ID part of its ue_id set to 0xFFFF in order to match any service instance. 0x0000 is the default instance ID used when there is only a single instance of a service.

  • MAY have its ue_version_major set to 0xFF in order to match any version.

  • MAY have its resource_id set to 0xFFFF in order to match any resource.

A candidate UUri matches a particular pattern UUri if all of the candidate UUri’s properties match the pattern UUri’s corresponding properties according to the rules defined by the predicates below.

context (pattern) UUri
def: matches_authority(candidate : UUri) : Boolean =
  self.authority_name = '*'
  or
  self.authority_name = candidate.authority_name

def: service_type() : UInt32 = ue_id & 0x0000_FFFF
def: service_instance() : UInt32 = ue_id & 0xFFFF_0000

def: matches_entity_type(candidate : UUri) : Boolean =
  self.service_type() = 0x0000_FFFF
  or
  self.service_type() = candidate.service_type()

def: matches_entity_instance(candidate : UUri) : Boolean =
  self.service_instance() = 0xFFFF_0000
  or
  self.service_instance() = candidate.service_instance()

def: matches_entity_version(candidate : UUri) : Boolean =
  self.ue_version_major = 0xFF
  or
  self.ue_version_major = candidate.ue_version_major

def: matches_resource(candidate : UUri) : Boolean =
  self.resource_id = 0xFFFF
  or
  self.resource_id = candidate.resource_id

def: matches(candidate : UUri) : Boolean =
  self.matches_authority(candidate)
  and
  self.matches_entity_type(candidate)
  and
  self.matches_entity_instance(candidate)
  and
  self.matches_entity_version(candidate)
  and
  self.matches_resource(candidate)

Each uProtocol Language Library MUST provide means to perform UUri pattern matching according to the matches predicate as defined above.

5.1. Examples

5.1.1. Matching on Authority

The following pattern UUri

UUri {
  authority_name: "192.168.1.100",
  ue_id: 0xFFFF_FFFF                // any instance, any service
  ue_version_major: 0xFF,           // any
  resource_id: 0xFFFF               // any
}

will match the following URIs:

//192.168.1.100/0/3/8000
//192.168.1.100/1/3/8
//192.168.1.100/1A/2/2

But not these:

//192.168.1.200/0/3/8000  // wrong authority
/1/3/8                    // no authority

5.1.2. Matching on Entity

The following pattern UUri

UUri {
  authority_name: "*",       // any
  ue_id: 0xFFFF_0000,        // any instance of service 0x0000
  ue_version_major: 0x03,
  resource_id: 0xFFFF        // any
}

will match the following URIs:

//other-vcu.my-vehicle/0/3/8000
/20000/3/2

But not these:

//vcu.other.device/1/3/8000    // wrong service ID (0x0001)
/20010/3/2                     // wrong service ID (0x0010)

5.1.3. Matching on Resource

The following pattern UUri

UUri {
  authority_name: "",        // local
  ue_id: 0x0000_0000,        // default instance of service 0x0000
  ue_version_major: 0xFF,    // any
  resource_id: 0x0001
}

will match the following URIs:

/0/3/1
/0/2/1

But not these:

//vcu.other.device/0/3/1     // non-local authority
/0/3/3                       // wrong resource

6. Best Practices

The numerical identifiers of a uService’s type and its resources are defined in the service’s proto3 definition by means of corresponding Protobuf Options.

Applications can determine these identifiers during runtime from the client stubs generated from a uService proto3 file via the corresponding MessageDescriptors.